Laravel Queues in Action (2nd edition) is now available!

How Laravel's Notification System Works

Updated: Jan 6, 2019 — 3 min Read#notifications

Laravel is shipped with a Notifications system that makes it super easy to send notifications to your users through different notification channels, here's what a notification object might look like:

class TestNotification extends Notification
{
    public function via($notifiable)
    {
        return ['mail', 'database'];
    }

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

The via() method is used to set the channels Laravel should send the notification through, and you can define multiple methods to customize how the notification should be sent in each channel.

It all starts in Illuminate\Notifications\ChannelManager which implements two interfaces:

You can send notifications using the Illuminate\Support\Facades\Notification facade which uses the ChannelManager internally:

Notification::send($users, new TestNotification());

The send() method accepts a single notifiable or an array of notifiables, inside that method Laravel creates an instance of Illuminate\Notifications\NotificationSender which handles the actual action of sending the notification, it has three main tasks:

The first task is pretty simple, it just formats the given $notifiables value into an array of a Collection, that insures the value of notifiables is iteratable for later use.

The second task is simple as well, it checks if the Notification we're passing implements the Illuminate\Contracts\Queue\ShouldQueue interface, if so then it means that Notification should be dispatched to queue instead of sending right away.

The third task is where the actual work happens, let's first discover the scenario where a Notification should be queued.

Sending Notifications right away

If the notification should be sent right away the Channel Manager calls the sendNow() method of the NotificationSender, this method does the following:

  1. Makes sure a notification ID is set
  2. Send the notification instance to the different notification drivers/channels
  3. Fire a couple of events

First, Laravel fires the Illuminate\Notifications\Events\NotificationSending, if any of the listeners to that event returned false the notification won't be sent, you can use that to do any final checks.

And after the sending process a Illuminate\Notifications\Events\NotificationSent event is fired which you can use to do any logging or housekeeping.

To send the notification, the sender calls the build() factory method on the channel manager to build an instance of the channel that should be used and then calls the send() method on that channel.

Also I'd like to mention that if you take a look at the sendNow() method you'll find that it accepts a third parameter which is the channels that should be used to send the notification, you can use this to simply override the channels specified in the notification class itself, you can actually call sendNow() instead of send() to make laravel send the notification right away even if the notification class implements the shouldQueue interface:

Notification::sendNow($users, new TestNotification(), ['slack', 'mail']);

Sending Jobs To Queue

The simplest way for laravel to queue notifications is to create a single job that sends all the notifications to all the notifiers, but in case one notification failed this would cause the whole Job to be reported as failed even if some notifications were actually sent, and retrying the job would mean that notifications that were already sent successfully will be re-sent again.

To prevent this Laravel creates a queued job for every single notifiable and channel, for example let's say we want to notify 10 customers that the Privacy Policy was updated, and we need to send them an email as well as an SMS message, for that laravel is going to create 20 jobs that sends the notification to 10 clients through 2 different channels.

Laravel also assigns a unique ID for the notification per notifiable, it uses ramsey/uuid, this unique ID is used when we use the Database channel as the primary key for the notification record, or for when we broadcast the notification.

Long Story short:

Inside the Illuminate\Notifications\NotificationSender the queueNotification()method is called if the notification should be queued, and inside that method laravel dispatches an instance of Illuminate\Notifications\SendQueuedNotifications to the queue, this instance is the actual job the worker runs and it holds a reference to the notifiable, notification, channel, and some information about how the notification should be queued which includes:

How can I define these values?

You can set these values as public properties inside the Notification class:

class PolicyUpdateNotification extends Notification implements ShouldQueue
{
    public $connection = 'redis';

    public $queue = 'urgent';

    public $delay = 60;
}

Or you can use the methods of the Illuminate\Bus\Queueable trait if you use that trait inside your notification.

Notification::send($users, 
    (new TestNotification())->onConnection(...)->onQueue(...)->delay(...)
);

What happens inside the SendQueuedNotifications job?

What happens inside the dispatched job is fairly simple, it calls the sendNow() method of the notification manager which sends the notification right away.

It also does some queue-related housekeeping that we won't be looking into in this dive.

Hey! 👋 If you find this content useful, consider sponsoring me on GitHub.

You can also follow me on Twitter, I regularly post about all things Laravel including my latest video tutorials and blog posts.

By Mohamed Said

Hello! I'm a former Laravel core team member & VP of Engineering at Foodics. In this publication, I share everything I know about Laravel's core, packages, and tools.

You can find me on Twitter and Github.

This site was built using Wink. Follow the RSS Feed.