Chapter 15. Mail and Notifications

Sending an application’s users notifications via email, Slack, SMS, or another notification system is a common but surprisingly complex requirement. Laravel’s mail and notification features provide consistent APIs that abstract away the need to pay too close attention to any particular provider. Just like in Chapter 14, you’ll write your code once and choose at the configuration level which provider you’ll use to send your email or notifications.

Mail

Laravel’s mail functionality is a convenience layer on top of Swift Mailer, and out of the box Laravel comes with drivers for Mailgun, Mandrill, Sparkpost, SES, SMTP, PHP Mail, and Sendmail.

For all of the cloud services, you’ll set your authentication information in config/services.php. However, if you take a look you’ll see there are already keys there—and in config/mail.php—that allow you to customize your application’s mail functionality in .env using variables like MAIL_DRIVER and MAILGUN_SECRET.

Cloud-based API Driver Dependencies

If you’re using any of the cloud-based API drivers, you’ll need to bring Guzzle in with Composer. You can run the following command to add it:

composer require guzzlehttp/guzzle

If you use the SES driver, you’ll need to run the following command:

composer require aws/aws-sdk-php:~3.0

“Classic” Mail

There are two different syntaxes in Laravel for sending mail: classic and mailable. The mailable syntax is the preferred syntax since 5.3, so we’re going to focus on that in this book. But for those who are working in 5.2 or earlier, here’s a quick look at how the classic syntax (Example 15-1) works.

Example 15-1. Basic “classic” mail syntax
Mail::send(
    'emails.assignment-created',
    ['trainer' => $trainer, 'trainee' => $trainee],
    function ($m) use ($trainer, $trainee) {
        $m->from($trainer->email, $trainer->name);
        $m->to($trainee->email, $trainee->name)->subject('A New Assignment!');
    }
);

The first parameter of Mail::send() is the name of the view. Keep in mind that emails.assignment-created means resources/views/emails/assignment-created​.blade.php or resources/views/emails/assignment-created.php.

The second parameter is an array of data that you want to pass to the view.

The third parameter is a closure, in which you define how and where to send the email: from, to, CC, BCC, subject, and any other metadata. Make sure to use any variables you want access to within the closure. And note that the closure is passed one parameter, which we’ve named $m; this is the message object.

Take a look at the old docs to learn more about the classic mail syntax.

Basic “Mailable” Mail Usage

Laravel 5.3 introduced a new mail syntax called the “mailable.” It works the same as the classic mail syntax, but instead of defining your mail messages in a closure, you instead create a specific PHP class to represent each mail.

To make a mailable, use the make:mail Artisan command:

php artisan make:mail AssignmentCreated

Example 15-2 shows what that class looks like.

Example 15-2. An autogenerated mailable PHP class
<?php

namespace AppMail;

use IlluminateBusQueueable;
use IlluminateMailMailable;
use IlluminateQueueSerializesModels;
use IlluminateContractsQueueShouldQueue;

class AssignmentCreated extends Mailable
{
    use Queueable, SerializesModels;

    /**
     * Create a new message instance
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Build the message
     *
     * @return $this
     */
    public function build()
    {
        return $this->view('view.name');
    }
}

This class probably looks familiar—it’s shaped almost the same as a Job. It even imports the Queuable trait for queuing your mail and the SerializesModels trait so any Eloquent models you pass to the constructor will be serialized correctly.

So, how does this work? The build() method on a mailable is where you’re going to define which view to use, what the subject is, and anything else you want to tweak about the mail except who it’s going to. The constructor is the place where you’ll pass in any data, and any public properties on your mailable class will be available to the template.

Take a look at Example 15-3 to see how we might update the autogenerated mailable for our assignment example.

Example 15-3. A sample mailable
<?php

namespace AppMail;

use IlluminateBusQueueable;
use IlluminateMailMailable;
use IlluminateQueueSerializesModels;
use IlluminateContractsQueueShouldQueue;

class AssignmentCreated extends Mailable
{
    use Queueable, SerializesModels;

    public $trainer;
    public $trainee;

    public function __construct($trainer, $trainee)
    {
        $this->trainer = $trainer;
        $this->trainee = $trainee;
    }

    public function build()
    {
        return $this->subject('New assignment from ' . $this->trainer->name)
            ->view('emails.assignment-created');
    }
}

Example 15-4 shows how to send a mailable.

Example 15-4. A few ways to send mailables
// Simple send
Mail::to($user)->send(new AssignmentCreated($trainer, $trainee));

// With CC/BCC/etc.
Mail::to($user1))
    ->cc($user2)
    ->bcc($user3)
    ->send(new AssignmentCreated($trainer, $trainee));

// With collections
Mail::to('[email protected]')
    ->bcc(User::all())
    ->send(new AssignmentCreated($trainer, $trainee))

Mail Templates

Mail templates are just like any other template. They can extend other templates, use sections, parse variables, contain conditional or looping directives, and do anything else you can do in a normal Blade view.

Take a look at Example 15-5 to see a possible emails.assignment-created template for Example 15-3.

Example 15-5. Sample assignment created email template
<!-- resources/views/emails/assignment-created.blade.php -->
<p>Hey {{ $trainee->name }}!</p>

<p>You have received a new training assignment from <b>{{ $trainer->name }}</b>.
Check out your <a href="{{ route('training-dashboard') }}">training
dashboard</a> now!</p>

In Example 15-3, both $trainer and $trainee are public properties on your mailable, which makes them available to the template.

If you want to explicitly define which variables are passed to the template, you can chain the with() method onto your build() call as in Example 15-6.

Example 15-6. Customizing the template variables
public function build()
{
    return $this->subject('You have a new assignment!')
        ->view('emails.assignment')
        ->with(['assignment' => $this->event->name]);
}

HTML Versus Plain-text Emails

So far we’ve used the view() method in our build() call stacks. This expects the template we’re referencing to pass back HTML. If you’d like to pass a plain-text version, the text() method defines your plain-text view:

public function build()
{
    return $this->view('emails.reminder')
        ->text('emails.reminder_plain');
}

Methods Available in build()

Here are a few of the methods available to you to customize your message in the build() method of your mailable:

from($address, $name = null)

Sets the “from” name and address—represents the author

subject($subject)

Sets the email subject

attach($file, array $options = [])

Attaches a file; valid options are mime for MIME type and as for display name

attachData($data, $name, array $options = [])

Attaches a file from a raw string; same options as attach()

attachFromStorage($path, $name = null, array $options = [])

Attaches a file stored on any of your filesystem disks

priority($level = n)

Set the email’s priority, where 1 is the highest and 5 is the lowest

Finally, if you want to perform any manual modifications on the underlying Swift message, you can do that using withSwiftMessage(), as shown in Example 15-7.

Example 15-7. Modifying the underlying SwiftMessage object
public function build()
{
    return $this->subject('Howdy!')
        ->withSwiftMessage(function ($swift) {
            $swift->setReplyTo('[email protected]');
        })
        ->view('emails.howdy');
}

Attachments and Inline Images

Example 15-8 shows three options for how to attach files or raw data to your email.

Example 15-8. Attaching files or data to mailables
// Attach a file using the local filename
public function build()
{
    return $this->subject('Your whitepaper download')
        ->attach(storage_path('pdfs/whitepaper.pdf'), [
            'mime' => 'application/pdf', // Optional
            'as' => 'whitepaper-barasa.pdf', // Optional
        ])
        ->view('emails.whitepaper');
}

// Attach a file passing the raw data
public function build()
{
    return $this->subject('Your whitepaper download')
        ->attachData(
            file_get_contents(storage_path('pdfs/whitepaper.pdf')),
            'whitepaper-barasa.pdf',
            [
                'mime' => 'application/pdf', // Optional
            ]
        )
        ->view('emails.whitepaper');
}

// Attach a file stored on one of your filesystem disks, like S3
public function build()
{
    return $this->subject('Your whitepaper download')
        ->view('emails.whitepaper')
        ->attachFromStorage('/pdfs/whitepaper.pdf');
}

And you can see how to embed images directly into your email in Example 15-9.

Example 15-9. Inlining images
<!-- emails/image.blade.php -->
Here is an image:

<img src="{{ $message->embed(storage_path('embed.jpg')) }}">

Or, the same image embedding the data:

<img src="{{ $message->embedData(
    file_get_contents(storage_path('embed.jpg')), 'embed.jpg'
) }}">

Markdown Mailables

Markdown mailables allow you to write your email content in Markdown, after which it will be converted into full HTML (and plain-text) emails with Laravel’s built-in, responsive HTML templates. You can also tweak these templates to make a customized email template that it’s simple for your developers and nondevelopers to create content for.

First, run the make:mail Artisan command with the markdown flag:

php artisan make:mail AssignmentCreated --markdown=emails.assignment-created

You can see an example of what the mail file it’ll generate looks like in Example 15-10.

Example 15-10. Generated Markdown mailable
class AssignmentCreated extends Mailable
{
    // ...

    public function build()
    {
        return $this->markdown('emails.assignment-created');
    }
}

As you can see, this is almost exactly the same as a normal mailable file in Laravel. The main difference is that you’re calling the markdown() method instead of the view() method. Also note that the template you’re referencing should represent a Markdown template, not a normal Blade template.

The difference is that, whereas a normal email template may be expected—with the use of includes and inheritance like any Blade file—to generate a full HTML email, Markdown templates simply pass Markdown content to a few predefined components. Framework and package-level components in Laravel are often nested with a package::component naming style, and as such the main body of your Markdown email should be passed into a component named mail::message. Take a look at Example 15-11 to see an example of a simple Markdown mail template.

Example 15-11. Simple assignment Markdown email
{{-- resources/views/emails/assignment-created.blade.php --}}
@component('mail::message')
# Hey {{ $trainee->name }}!

You have received a new training assignment from **{{ $trainer->name }}**

@component('mail::button', ['url' => route('training-dashboard')])
View Your Assigment
@endcomponent

Thanks,<br>
{{ config('app.name') }}
@endcomponent

As you can see in Example 15-11, there’s a parent mail::message component to which you pass the body of your email, but you’re also provided with other smaller components you can sprinkle into your emails. We used the mail::button component here, which takes a body (“View Your Assignment”) but also requires parameters to be passed, as an array to the second parameter of the @component directive.

Markdown components

There are three types of components available:

Button

Generates a centered button link. The button component requires a url attribute and allows an optional color attribute, to which you can pass primary, success, or error.

Panel

Renders the provided text with a slightly lighter background than the rest of the message.

Table

Converts the content passed into it via the Markdown table syntax.

Customizing the Components

These Markdown components are built into the core of the Laravel framework, but if you need to customize how they work, you can publish their files and edit them:

php artisan vendor:publish --tag=laravel-mail

You can learn more about customizing these files and their themes in the Laravel docs.

Rendering Mailables to the Browser

When you’re developing emails in your applications, it’s helpful to be able to preview how they’ll render. You can rely on a tool like Mailtrap for this, and that is a useful tool, but it can also be helpful to render the mails directly in your browser and see your changes made immediately.

Take a look at Example 15-12 to see a sample route you can add to your application to render a given mailable.

Example 15-12. Rendering a mailable to a route
Route::get('preview-assignment-created-mailable', function () {
    $trainer = Trainer::first();
    $trainee = Trainee::first();

    return new AppMailAssignmentCreated($trainer, $trainee);
});

In Laravel 5.8+ we can also quickly preview a notification in the browser.

Route::get('preview-notification', function () {
    $trainer = Trainer::first();
    $trainee = Trainee::first();

    return (new AppNotificationsAssignmentCreated($trainer, $trainee))
        ->toMail($trainee);
});

Queues

Sending email is a time-consuming task that can cause applications to slow down, so it’s common to move it to a background queue. It’s so common, in fact, that Laravel has a set of built-in tools to make it easier to queue your messages without writing queue jobs for each email:

queue()

To queue a mail object instead of sending it immediately, simply pass your mailable object to Mail::queue() instead of Mail::send():

    Mail::queue(new AssignmentCreated($trainer, $trainee));
later()

Mail::later() works the same as Mail::queue(), but it allows you to add a delay—either in minutes, or at a specific time by passing an instance of DateTime or Carbon—specifying when the email will be pulled from the queue and sent:

    $when = now()->addMinutes(30);
    Mail::later($when, new AssignmentCreated($trainer, $trainee));

Configuring Queues

Your queues must be configured correctly for these methods to work. Take a look at Chapter 16 to learn more about how queues work and how to get them running in your application.

For both queue() and later(), if you’d like to specify which queue or queue connection your mail is added to, use the onConnection() and onQueue() methods on your mailable object:

$message = (new AssignmentCreated($trainer, $trainee))
    ->onConnection('sqs')
    ->onQueue('emails');

Mail::to($user)->queue($message);

If you’d like to direct that a given mailable should always be queued, you can make the mailable implement the IlluminateContractsQueueShouldQueue interface.

Local Development

This is all well and good for sending mail in your production environments. But how do you test it all out? There are three primary tools you’ll want to consider: Laravel’s log driver, a Software as a Service (SaaS) app named Mailtrap, and the so-called “universal to” configuration option.

The log driver

Laravel provides a log driver that logs every email you try to send to your local laravel.log file (which is, by default, in storage/logs).

If you want to use this, edit .env and set MAIL_DRIVER to log. Now open up or tail storage/logs/laravel.log and send an email from your app. You’ll see something like this:

Message-ID: <04ee2e97289c68f0c9191f4b04fc0de1@localhost>
Date: Tue, 17 May 2016 02:52:46 +0000
Subject: Welcome to our app!
From: Matt Stauffer <[email protected]>
To: [email protected]
MIME-Version: 1.0
Content-Type: text/html; charset=utf-8
Content-Transfer-Encoding: quoted-printable

Welcome to our app!

In Laravel 5.7+, you can optionally specify that logged mail gets sent to a different log channel than the rest of your logs. Either modify config/mail.php or set the MAIL_LOG_CHANNEL variable in your .env file to the name of any existing log channel.

Mailtrap.io

Mailtrap is a service for capturing and inspecting emails in development environments. You send your mail to the Mailtrap servers via SMTP, but instead of sending those emails off to the intended recipients, Mailtrap captures them all and provides you with a web-based email client for inspecting them, regardless of which email address is in the to field.

To set up Mailtrap, sign up for a free account and visit the base dashboard for your demo. Copy your username and password from the SMTP column.

Then edit your app’s .env file and set the following values in the mail section:

MAIL_DRIVER=smtp
MAIL_HOST=mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=your_username_from_mailtrap_here
MAIL_PASSWORD=your_password_from_mailtrap_here
MAIL_ENCRYPTION=null

Now, any email you send from your app will show up in your Mailtrap inbox.

Universal to

If you’d like to inspect the emails in your preferred client, you can override the to field on each message with the “universal to” configuration setting. To set this up, add a to key to your config/mail.php file that looks something like this:

'to' => [
    'address' => '[email protected]',
    'name' => 'Matt Testing My Application'
],

Note that you’ll need to actually set up a real email driver with something like Mailgun or Sendmail in order to use this.

Notifications

Most of the mail that’s sent from web apps really has the purpose of notifying users that a particular action has happened or needs to happen. As users’ communication preferences grow more and more diverse, we gather ever more—and more disparate—packages to communicate via Slack, SMS, and other means.

Laravel 5.3 introduced a new concept in Laravel called, fittingly, notifications. Just like a mailable, a notification is a PHP class that represents a single communication that you might want to send to your users. For now, let’s imagine we’re notifying users of our physical training app that they have a new workout available.

Each class represents all of the information necessary to send notifications to your users using one or many notification channels. A single notification could send an email, send an SMS via Nexmo, send a WebSockets ping, add a record to a database, send a message to a Slack channel, and much more.

So, let’s create our notification:

php artisan make:notification WorkoutAvailable

Example 15-13 shows what that gives us.

Example 15-13. An autogenerated notification class
<?php

namespace AppNotifications;

use IlluminateBusQueueable;
use IlluminateNotificationsNotification;
use IlluminateContractsQueueShouldQueue;
use IlluminateNotificationsMessagesMailMessage;

class WorkoutAvailable extends Notification
{
    use Queueable;

    /**
     * Create a new notification instance
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Get the notification's delivery channels
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function via($notifiable)
    {
        return ['mail'];
    }

    /**
     * Get the mail representation of the notification
     *
     * @param  mixed  $notifiable
     * @return IlluminateNotificationsMessagesMailMessage
     */
    public function toMail($notifiable)
    {
        return (new MailMessage)
                    ->line('The introduction to the notification.')
                    ->action('Notification Action', url('/'))
                    ->line('Thank you for using our application!');
    }

    /**
     * Get the array representation of the notification
     *
     * @param  mixed  $notifiable
     * @return array
     */
    public function toArray($notifiable)
    {
        return [
            //
        ];
    }
}

We can learn a few things here. First, we’re going to pass relevant data into the constructor. Second, there’s a via() method that allows us to define, for a given user, which notification channels to use ($notifiable represents whatever entities you want to notify in your system; for most apps, it’ll be a user, but that’s not always the case). And third, there are individual methods for each notification channel that allow us to specifically define how to send one of these notifications through that channel.

When Would a $notifiable Not Be a User?

While the most common notification targets will be users, it’s possible you may want to notify something else. This may simply be because your application has multiple user types—so, you might want to be able to notify both trainers and trainees. But you also might find yourself wanting to notify a group, a company, or a server.

So, let’s modify this class for our WorkoutAvailable example. Take a look at Example 15-14.

Example 15-14. Our WorkoutAvailable notification class
...
class WorkoutAvailable extends Notification
{
    use Queueable;

    public $workout;

    public function __construct($workout)
    {
        $this->workout = $workout;
    }

    public function via($notifiable)
    {
        // This method doesn't exist on the User... we're going to make it up
        return $notifiable->preferredNotificationChannels();
    }

    public function toMail($notifiable)
    {
        return (new MailMessage)
            ->line('You have a new workout available!')
            ->action('Check it out now', route('workout.show', [$this->workout]))
            ->line('Thank you for training with us!');
    }

    public function toArray($notifiable)
    {
        return [];
    }
}

Defining the via() Method for Your Notifiables

As you can see in Example 15-14, we’re somehow responsible for deciding, for each notification and each notifiable, which notification channels we’re going to use.

You could just send everything as mail or just send everything as an SMS (Example 15-15).

Example 15-15. Simplest possible via() method
public function via($notifiable)
{
    return 'nexmo';
}

You could also let each user choose their one preferred method and save that on the User itself (Example 15-16).

Example 15-16. Customizing the via() method per user
public function via($notifiable)
{
    return $notifiable->preferred_notification_channel;
}

Or, as we imagined in Example 15-14, you could create a method on each notifiable that allows for some complex notification logic. For example, you could notify the user over certain channels during work hours and other channels in the evening. What is important is that via() is a PHP class method, so you can do whatever complex logic you want there.

Sending Notifications

There are two ways to send a notification: by using the Notification facade, or by adding the Notifiable trait to an Eloquent class (likely your User class).

Sending notifications using the Notifiable trait

Any model that imports the LaravelNotificationsNotifiable trait (which the AppUser class does by default) has a notify() method that can be passed a notification, which will look like Example 15-17.

Example 15-17. Sending a notification using the Notifiable trait
use AppNotificationsWorkoutAvailable;
...
$user->notify(new WorkoutAvailable($workout));

Sending notifications with the Notification facade

The Notification facade is the clumsier of the two methods, since you have to pass both the notifiable and the notification. However, it’s helpful because you can choose to pass more than one notifiable in at the same time, like you can see in Example 15-18.

Example 15-18. Sending notifications using the Notification facade
use AppNotificationsWorkoutAvailable;
...
Notification::send($users, new WorkoutAvailable($workout));

Queueing Notifications

Most of the notification drivers need to send HTTP requests to send their notifications, which could slow down your user experience, so you probably want to queue your notifications. All notifications import the Queuable trait by default, so all you need to do is add implements ShouldQueue to your notification and Laravel will instantly move it to a queue.

As with any other queued features, you’ll need to make sure you have your queue settings configured correctly and a queue worker running.

If you’d like to delay the delivery of a notification, you can run the delay() method on the notification:

$delayUntil = now()->addMinutes(15);

$user->notify((new WorkoutAvailable($workout))->delay($delayUntil));

Out-of-the-Box Notification Types

Out of the box, Laravel comes with notification drivers for email, database, broadcast, Nexmo SMS, and Slack. I’ll cover each briefly, but I’d recommend referring to the docs for more thorough introductions to each.

It’s also easy to create your own notification drivers, and dozens of people already have; you can find them at the Laravel Notification Channels website.

Email notifications

Let’s take a look at how the email from our earlier example, Example 15-14, is built:

public function toMail($notifiable)
{
    return (new MailMessage)
        ->line('You have a new workout available!')
        ->action('Check it out now', route('workouts.show', [$this->workout]))
        ->line('Thank you for training with us!');
}

The result is shown in Figure 15-1. The email notification system puts your application’s name in the header of the email; you can customize that app name in the name key of config/app.php.

This email is automatically sent to the email property on the notifiable, but you can customize this behavior by adding a method to your notifiable class named routeNotificationForMail() that returns the email address you’d like email notifications sent to.

The email’s subject is set by parsing the notification class name and converting it to words. So, our WorkoutAvailable notification would have the default subject of “Workout Available”. You can also customize this by chaining the subject() method on the MailMessage in the toMail() method.

If you want to modify the templates, publish them and edit to your heart’s content:

php artisan vendor:publish --tag=laravel-notifications
An email sent with with the default notification template
Figure 15-1. An email sent with the default notification template

Markdown mail notifications

If you like working with Markdown emails (see “Markdown Mailables”), you can also use the same markdown() method in your notifications, as shown in Example 15-19:

Example 15-19. Using the markdown() method with notifications
public function toMail($notifiable)
{
    return (new MailMessage)
        ->subject('Workout Available')
        ->markdown('emails.workout-available', ['workout' => $this->workout]);
}

You can also change the style of the default template to be an “error” message, which uses a bit of different language and changes the primary button color to red. Just add a call to the error() method to your MailMessage call chain in the toMail() method.

Database notifications

You can send notifications to a database table using the database notification channel. First, create your table with php artisan notifications:table. Next, create a toDatabase() method on your notification and return an array of data there. This data will be encoded as JSON and stored in the database table’s data column.

The Notifiable trait adds a notifications relationship to any model it’s imported in, allowing you to easily access records in the notifications table. So if you’re using database notifications, you could so something like Example 15-20:

Example 15-20. Iterating over a user’s database notifications
User::first()->notifications->each(function ($notification) {
    // Do something
});

The database notification channel also has the concept of whether or not a notification is “read.” You can scope to only the “unread” notifications as shown in Example 15-21:

Example 15-21. Iterating over a user’s unread database notifications
User::first()->unreadNotifications->each(function ($notification) {
    // Do something
});

And you can mark one or all notifications as read, as Example 15-22 demonstrates:

Example 15-22. Marking database notifications as read
// Individual
User::first()->unreadNotifications->each(function ($notification) {
    if ($condition) {
        $notification->markAsRead();
    }
});

// All
User::first()->unreadNotifications->markAsRead();

Broadcast notifications

The broadcast channel sends notifications out using Laravel’s event broadcasting features, which are powered by WebSockets (we’ll learn more about these in “Broadcasting Events over WebSockets, and Laravel Echo”).

Create a toBroadcast() method on your notification and return array of data, and if your app is correctly configured for event broadcasting, that data will be broadcast on a private channel named notifiable.id. The id will be the ID of the notifiable, and notifiable will be the notifiable’s fully qualified class name, with the slashes replaced by periods—for example, the private channel for the AppUser with the ID of 1 will be App.User.1.

SMS notifications

SMS notifications are sent via Nexmo, so if you want to send SMS notifications, sign up for a Nexmo account and follow the instructions in the docs. Like with the other channels, you’ll be setting up a toNexmo() method and customizing the SMS message there.

SMS Notification Package Extracted in 5.8+

In Laravel 5.8+, the SMS notification channel is a first-party package. If you want to use Nexmo SMS notifications, simply require this package with Composer:

composer require laravel/nexmo-notification-channel

Slack notifications

The slack notification channel allows you to customize the appearance of your notifications and even attach files to your notifications. Like with the other channels, you’ll set up a toSlack() method and customize the message there.

Slack notification package extracted in 5.8+

In Laravel 5.8+, the Slack notification channel is a first-party package. If you want to use Slack notifications, simply require this package with Composer.

composer require laravel/slack-notification-channel

Other notifications

Looking to send your notifications through other channels than those that come out of the box? There’s a robust community effort to provide an incredible variety of notification channels; check out what’s on offer at the Laravel Notifications Channels website.

Testing

Let’s take a look at how to test mail and notifications.

Mail

There are two options for testing mail in Laravel. If you’re using the traditional mail syntax (which is, by the way, not the preferred method in Laravel 5.3 and later), I’d recommend using a tool called MailThief, which Adam Wathan wrote for Tighten. Once you bring MailThief into your application with Composer, you can use MailThief::hijack() in your tests to make MailThief capture any calls to the Mail facade or any mailer classes.

MailThief then makes it possible to make assertions against the senders, recipients, CC and BCC values, and even content and attachments of your mail. Take a look at the GitHub repo to learn more, or bring it into your app:

composer require tightenco/mailthief --dev

If you’re using mailables, there’s a simple syntax for writing assertions against your sent mail (Example 15-23).

Example 15-23. Asserting against mailables
public function test_signup_triggers_welcome_email()
{
    Mail::fake();

    Mail::assertSent(WelcomeEmail::class, function ($mail) {
        return $mail->subject == 'Welcome!';
    });

    // You can also use assertSentTo() to explicitly test the recipients, and
    // you can use assertNotSent() to test that a specific mail wasn't sent.
}

Notifications

Laravel provides a built-in set of assertions for testing your notifications. Example 15-24 demonstrates.

Example 15-24. Asserting notifications were sent
public function test_new_signups_triggers_admin_notification()
{
    Notification::fake();

    Notification::assertSentTo($user, NewUsersSignedup::class,
        function ($notification, $channels) {
            return $notification->user->email == '[email protected]'
            && $channels == ['mail'];
    });

    // Assert that the email was sent to a given user
    Notification::assertSentTo(
        [$user],
        NewUsersSignedup::class
    );

    // You can also use assertNotSentTo()
    Notification::assertNotSentTo(
        [$userDidntSignUp], NewUsersSignedup::class
    );
}

TL;DR

Laravel’s mail and notification features provide simple, consistent interfaces to a variety of messaging systems. Laravel’s mail system uses “mailables,” PHP classes that represent emails, to provide a consistent syntax to different mail drivers. The notification system makes it easy to build a single notification that can be delivered in many different media—from emails to SMS messages to physical postcards.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset