86

I want to send a confirmation e-mail using laravel. The laravel Mail::send() function only seems to accept a path to a file on the system. The problem is that my mailtemplates are stored in the database and not in a file on the system.

How can I pass plain content to the email?

Example:

$content = "Hi,welcome user!";

Mail::send($content,$data,function(){});
jrenk
  • 1,387
  • 3
  • 24
  • 46
nexana
  • 1,096
  • 1
  • 9
  • 18

13 Answers13

180

update on 7/20/2022: For more current versions of Laravel, the setBody() method in the Mail::send() example below has been replaced with the text() or html() methods.

update: In Laravel 5 you can use raw instead:

Mail::raw('Hi, welcome user!', function ($message) {
  $message->to(..)
    ->subject(..);
});

This is how you do it:

Mail::send([], [], function ($message) {
  $message->to(..)
    ->subject(..)
    // here comes what you want
    ->setBody('Hi, welcome user!'); // assuming text/plain
    // or:
    ->setBody('<h1>Hi, welcome user!</h1>', 'text/html'); // for HTML rich messages
});
Bryan
  • 2,191
  • 20
  • 28
Jarek Tkaczyk
  • 78,987
  • 25
  • 159
  • 157
  • For **html** emails (needs extra argument) see @sajjad-ashraf 's answer. Might be good to merge? – Bryan Jan 06 '17 at 08:36
  • 1
    true, added as in @Sajjad's answer – Jarek Tkaczyk Jan 09 '17 at 00:44
  • is it possible to use onConnection in that case? – Hlorofos Feb 02 '17 at 08:31
  • 1
    and how to set text/html in laravel 5?? – Toskan Apr 01 '17 at 00:30
  • 3
    Unfortunately his won't work if you'd like to Mail::queue the message, throwing you the InvalidArgumentException exception 'Only mailables may be queued.' – Hlorofos Dec 21 '17 at 15:10
  • It is good to mention that is preferable to use Mailable. But for those like me that was looking for a solution for the Mail::raw callback scope, you can use anonymous function as [this answer](https://stackoverflow.com/a/4588842/5078248) shows. – Marcos Rocha Feb 19 '19 at 17:27
  • @MarcosRocha +1 feel free to suggest an edit to already accepted answer! it is how we improve and make (often pretty old) answers still relevant and better for newcomers – Jarek Tkaczyk Apr 13 '19 at 07:11
59

For Html emails

Mail::send(array(), array(), function ($message) use ($html) {
  $message->to(..)
    ->subject(..)
    ->from(..)
    ->setBody($html, 'text/html');
});
Sajjad Ashraf
  • 3,754
  • 1
  • 34
  • 35
  • Presumably you can set multiple bodies with different content types, and so have multipart emails with both `text/html` and `text/plain` parts to it? – Jason Sep 30 '15 at 17:04
  • 3
    @Jason The function (https://github.com/laravel/framework/blob/master/src/Illuminate/Mail/Mailer.php#L320) uses `addPart()` for the plain part instead of `setBody()` when a HTML part is set as well – Philip Apr 21 '16 at 23:50
  • 1
    Can you please help me with this i have body like this, $html = "Hello {{username}}", how can i pass username dynamically ? – Vipertecpro Feb 20 '19 at 10:05
18

It is not directly related to the question, but for the ones that search for setting the plain text version of your email while keeping the custom HTML version, you can use this example :

Mail::raw([], function($message) {
    $message->from('contact@company.com', 'Company name');
    $message->to('johndoe@gmail.com');
    $message->subject('5% off all our website');
    $message->setBody( '<html><h1>5% off its awesome</h1><p>Go get it now !</p></html>', 'text/html' );
    $message->addPart("5% off its awesome\n\nGo get it now!", 'text/plain');
});

If you would ask "but why not set first argument as plain text ?", I made a test and it only takes the html part, ignoring the raw part.

If you need to use additional variable, the anonymous function will need you to use use() statement as following :

Mail::raw([], function($message) use($html, $plain, $to, $subject, $formEmail, $formName){
    $message->from($fromEmail, $fromName);
    $message->to($to);
    $message->subject($subject);
    $message->setBody($html, 'text/html' ); // dont miss the '<html></html>' or your spam score will increase !
    $message->addPart($plain, 'text/plain');
});

Hope it helps you folks.

Anwar
  • 4,162
  • 4
  • 41
  • 62
13

The Mailer class passes a string to addContent which via various other methods calls views->make(). As a result passing a string of content directly won't work as it'll try and load a view by that name.

What you'll need to do is create a view which simply echos $content

// mail-template.php
<?php echo $content; ?>

And then insert your string into that view at runtime.

$content = "Hi,welcome user!";

$data = [
    'content' => $content
];

Mail::send('mail-template', $data, function() { });
Ben Swinburne
  • 25,669
  • 10
  • 69
  • 108
6

try

public function build()
{
    $message = 'Hi,welcome user!'
    return $this->html($message)->subject($message);
}
Muhammad Dyas Yaskur
  • 6,914
  • 10
  • 48
  • 73
jeidison farias
  • 490
  • 5
  • 9
6

I had a similar issue where the HTML and/or plain text of my email were not built by a view and I didn't want to create a dummy view for them (as proposed by @Matthew Odedoyin).

As others have commented, you can use $this->html() to set the HTML content of the message, but what if you want your email to have both HTML and plain text content?

Unfortunately $this->text() only takes a view, but I got around this by using:

$this->text(new HtmlString('Here is the plain text content'));

Which renders the content of the HTMLString instead of the view.

Matt Ke
  • 3,599
  • 12
  • 30
  • 49
Martin
  • 105
  • 1
  • 10
  • +1. This is the only answer that worked for me since I'm building the Mailable ahead of time, so I can't use the send() method. Disappointing that one would need this sort of workaround just to make a multipart email, when the HTML already has a way to be added as a string. – theberzi Feb 15 '21 at 13:36
  • This is exactly what I needed. Mailtrap has a 'Text' tab which shows the plain text version of the email, for anyone using Mailtrap for testing. – Sammy AbuKmeil Mar 10 '21 at 15:17
3

as you know

Only mailables may be queued.

meaning, if you use ShouldQueue interface

1) first, you should always do

php artisan queue:restart

2) second, in your mailable you can use html method (tested in laravel 5.8)

public function build(): self
{
    return $this
            ->html('
                <html>
                    <body>
                        ForwardEmail
                    </body>
                </html>
            ')
            ->subject(config('app.name') . ' ' . 'email forwarded')
            ->attachData($this->content, 'email.eml', [
                'mime' => 'application/eml',
    ]);
}
Yevgeniy Afanasyev
  • 37,872
  • 26
  • 173
  • 191
2

If you were using mailables. You can do something like this in the build method :

public function build()
{
 
    return $this->view('email')
        ->with(['html'=>'This is the message']);
}

And you just go ahead and create the blade view email.blade.php in your resource folder.

Then in the blade you can reference your string using laravel blade syntax

 <html>
    <body>
      {{$html}}
    </body>
  </html>

or

 <html>
    <body>
      {!!$html!!}
    </body>
 </html>

If your raw text contains HTML mark up I hope this works for those who have templates stored in the database and wants to take advantage of the Mailables class in Laravel.

Muhammad Dyas Yaskur
  • 6,914
  • 10
  • 48
  • 73
1

To send raw html, text etc using Laravel Mailables you can

override Mailable->send() in your Mailable and in there, use the method in previous responses:

send([], [], function($message){ $message->setBody() } )

No need to call $this->view() at your build function at all.

patrox
  • 408
  • 3
  • 11
1

NOTE: Below answer is for those who are looking for a flexible approach. i,e (with or without laravel template)

With Template

 $payload['message'] = View::make('emails.test-mail',$data)->render();

Without Template

$payload['message'] = "lorem ipsum";


Mail::raw([], function ($mail) use ($payload) {
    $mail->from($payload['from_email'])
        ->to($payload['to'])
        ->setBody($payload['message'], 'text/html')
        ->cc($payload['cc'])
        ->bcc($payload['bcc'])
        ->subject($payload['subject']);
    foreach ($payload['attachments'] as $file){
        $mail->attach($file);
    }
});
Soubhagya Kumar Barik
  • 1,979
  • 20
  • 26
0

This can be accomplished within a Mailable implementation, with plain text and html content parts:

  public function build() {

    // Text and html content sections we wish to use in place of view output
    $bodyHtml = ...
    $bodyText = ...

    // Internally, Mailer::renderView($view) interprets $view as the name of a blade template
    // unless, instead of string, it is set to an object implementing Htmlable,
    // in which case it returns the result $view->toHtml()
    $htmlViewAlternative = new class($bodyHtml) implements Htmlable {
      protected string $html;
      public function __construct($html) {
        $this->html = $html;
      }
      public function toHtml(): string {
        return $this->html;
      }
    };

    // We can now set both the html and text content sections without
    // involving blade templates. One minor hitch is the Mailable::view($view)
    // documents $view as being a string, which is incorrect if you follow
    // the convoluted downstream logic. 
    /** @noinspection PhpParamsInspection */
    return $this
      ->to(...)
      ->from(...)
      ->subject(...)
      ->view([
        'html' => $htmlViewAlternative,
        'raw' => $bodyText
      ]);
  }
0

Laravel mailable now has an ->html() function to be used instead of ->view() and works both with o without ->text()

realtebo
  • 23,922
  • 37
  • 112
  • 189
0

laravel 9 has built in function to send HTML without view. Here is the example:

\Illuminate\Support\Facades\Mail::html($content, function ($message) {
    $message->to("email@example.com")
        ->subject("Test dev 4")
        ->from("email@example.com");
});

and also if we use accepted answer will return:

Symfony\Component\Mime\Message::setBody(): Argument #1 ($body) must be of type ?Symfony\Component\Mime\Part\AbstractPart, string given, called in /Users/yaskur/Sites/laravel/mail-builder/vendor/laravel/framework/src/Illuminate/Support/Traits/ForwardsCalls.php on line 23

It's happened because laravel use new library to send email. Previously, use Swiftmailer and now use Symfony Mailer. To send HTML email without view you can also use below code:

Mail::raw("", function ($message) use ($content) {
    $body = new \Symfony\Component\Mime\Part\TextPart($content);
    $message->to("dyas@example.com")
        ->subject("Test dev")
        ->from("no-reply@example.com")
        ->setBody($body);
});
Muhammad Dyas Yaskur
  • 6,914
  • 10
  • 48
  • 73