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.
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
.
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
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.
::
send
(
'emails.assignment-created'
,
[
'trainer'
=>
$trainer
,
'trainee'
=>
$trainee
],
function
(
$m
)
use
(
$trainer
,
$trainee
)
{
$m
->
from
(
$trainer
->
,
$trainer
->
name
);
$m
->
to
(
$trainee
->
,
$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.
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.
<?
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.
<?
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.
// Simple send
::
to
(
$user
)
->
send
(
new
AssignmentCreated
(
$trainer
,
$trainee
));
// With CC/BCC/etc.
::
to
(
$user1
))
->
cc
(
$user2
)
->
bcc
(
$user3
)
->
send
(
new
AssignmentCreated
(
$trainer
,
$trainee
));
// With collections
::
to
(
'[email protected]'
)
->
bcc
(
User
::
all
())
->
send
(
new
AssignmentCreated
(
$trainer
,
$trainee
))
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.
<!-- 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.
public
function
build
()
{
return
$this
->
subject
(
'You have a new assignment!'
)
->
view
(
'emails.assignment'
)
->
with
([
'assignment'
=>
$this
->
event
->
name
]);
}
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'
);
}
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)
subject($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 = [])
attachFromStorage($path, $name = null, array $options = [])
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.
public
function
build
()
{
return
$this
->
subject
(
'Howdy!'
)
->
withSwiftMessage
(
function
(
$swift
)
{
$swift
->
setReplyTo
(
'[email protected]'
);
})
->
view
(
'emails.howdy'
);
}
Example 15-8 shows three options for how to attach files or raw data to your email.
// 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.
<!-- 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 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.
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.
{{-- 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.
There are three types of components available:
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
.
Renders the provided text with a slightly lighter background than the rest of the message.
Converts the content passed into it via the Markdown table syntax.
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.
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.
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
);
});
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:
To queue a mail object instead of sending it immediately, simply pass your mailable object to Mail::queue()
instead of Mail::send()
:
::
queue
(
new
AssignmentCreated
(
$trainer
,
$trainee
));
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
);
::
later
(
$when
,
new
AssignmentCreated
(
$trainer
,
$trainee
));
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'
);
::
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.
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.
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 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.
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.
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.
<?
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.
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.
...
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
[];
}
}
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).
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).
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.
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).
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.
use
AppNotificationsWorkoutAvailable
;
...
$user
->
notify
(
new
WorkoutAvailable
(
$workout
));
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.
use
AppNotificationsWorkoutAvailable
;
...
Notification
::
send
(
$users
,
new
WorkoutAvailable
(
$workout
));
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, 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.
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
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:
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.
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:
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:
User
::
first
()
->
unreadNotifications
->
each
(
function
(
$notification
)
{
// Do something
});
And you can mark one or all notifications as read, as Example 15-22 demonstrates:
// Individual
User
::
first
()
->
unreadNotifications
->
each
(
function
(
$notification
)
{
if
(
$condition
)
{
$notification
->
markAsRead
();
}
});
// All
User
::
first
()
->
unreadNotifications
->
markAsRead
();
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
.
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.
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
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.
Let’s take a look at how to test mail and notifications.
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).
public
function
test_signup_triggers_welcome_email
()
{
::
fake
();
::
assertSent
(
WelcomeEmail
::
class
,
function
(
)
{
return
->
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.
}
Laravel provides a built-in set of assertions for testing your notifications. Example 15-24 demonstrates.
public
function
test_new_signups_triggers_admin_notification
()
{
Notification
::
fake
();
Notification
::
assertSentTo
(
$user
,
NewUsersSignedup
::
class
,
function
(
$notification
,
$channels
)
{
return
$notification
->
user
->
==
'[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
);
}
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.