Laravel Notifications — Part 1: Understanding Laravel Notification thoroughly

Syed Sirajul Islam Anik
8 min readJun 12, 2020

--

Image from: https://csshint.com/wp-content/uploads/2019/07/CSS-Notification-Bell-Icon.jpg

The term notification means it all. This article tries to describe the thorough concept of Laravel Notifications and how it’s done. Notifications mean to notify someone about an event (for simplicity we’ll mention user in this article even tho notifications can be sent to anything). Laravel Notifications come handy. There are several ways of informing a user. To inform a user, you can use various mediums. Laravel by default is shipped with Database, Email, Broadcast drivers. But you can use other community-driven drivers as well. And you can also write your own medium too. Here, in this article, I will mainly describe the email and broadcast medium/channel. Even tho the Laravel Notifications was there for a long time, I will mainly describe from the perspective of Laravel 6.x. Not sure if they've changed it in other versions or not. Let’s get started. And before we dive in, if you don’t have any idea about Laravel broadcast, then you can check my other article on the broadcast topic.

Let’s assume we have a system where the user will get notifications when he places an order. He’ll receive notifications using email & broadcast. As we’re saying the user, which points out the User Model. That’s why we’ll need to use the following trait in our User model.

<?php// ...
use Illuminate\Notifications\Notifiable;
class User extends Authenticatable
{
use Notifiable;
}

As stated before, Notifications can be sent to anyone, you just need to add this trait to your model to where you want to send the notification.

To create a new notification, you’ll need to use the following command.

php artisan make:notification OrderPlacedNotification

The above command will put a new OrderPlacedNotification class in our app/Notifications directory. And the class look like,

<?php

namespace App\Notifications;

use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;

class OrderPlacedNotification extends Notification
{
use Queueable;

public function __construct () { }

public function via ($notifiable) {
return [ 'mail' ];
}

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

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

The class depicts that if we want to use this class, it’ll send a notification using mail (check via method) and in that mail, it’ll send whatever is given in the toMail method. We can define as many mediums as we want. But they should either be the shipped one, or custom defined one or installed from community-driven packages. So, if we want to send using other mediums too, then we need to add those mediums in via method’s return array. For our purpose, we’ll do like below.

public function via ($notifiable) {
return [ 'mail', 'broadcast' ];
}

So, our class can be rewritten as follows.

<?php

namespace App\Notifications;

use App\Order;
use Illuminate\Bus\Queueable;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;

class OrderPlacedNotification extends Notification
{
use Queueable;

private $order;

public function __construct (Order $order) {
// the order class
$this->order = $order;
}

public function via ($notifiable) {
return [ 'mail', 'broadcast' ];
}

public function toMail ($notifiable) {
$trackingId = $this->order->tracking_id;
$url = route('order.tracking', ['id' => $trackingId]);
return (new MailMessage)
->subject('Your order is placed')
->line('Your order tracking id is: ' . $trackingId)
->action('Track order from here', $url)
->line('Thank you for using our application!');
}

public function toArray ($notifiable) {
return [
'id' => $this->order->id,
'status' => 'placed',
];
}
}

Now, it’s our time to send the notification. We can send notification using the following ways

$order = new Order();
// save whatever you have
$user->notify($order);
$user->notifyNow($order);
// use Illuminate\Support\Facades\Notification;
// Notification::send($user, $order);
// Notification::sendNow($user, $order);

That’s it. Laravel will do the rest of the things for you. Both ways do the same thing. It’ll send the notification to the user. But why there is send, sendNow notify and notifyNow methods? Because, if you don’t want to have an impact on your response, you can either put the notifications in a queue and process the next statements or go synchronously. To put the notification in a queue, we’ll need to implement the ShouldQueue interface to our notification class. And also make sure that your queue workers are ready to serve the jobs.

<?php

namespace App\Notifications;
use Illuminate\Contracts\Queue\ShouldQueue;class NoficationClass extends Notification implements ShouldQueue
{
// just implemented the ShouldQueue interface
// interface don't have any methods to implement.
// thus everything remains the same
}

So, for the above case, send and notify will put the notification in a queue on the other hand notifyNow and sendNow will process notifications in a synchronous manner.

What more can be done?

For available mediums/channels, Laravel uses conventions.

  • As the via method receives a Model instance and if you don’t want to send notifications, you can then return an empty array. So the notification will not be sent. via method receives a $notifiable instance of a model. Using that instance you can return an array of preferred channels for the model.
  • Your notification class should have toMail toBroadcast toDatabase methods to use those mediums. If your medium is database or broadcast but doesn’t have corresponding toBroadcast or toDatabase methods then it’ll by default use the toArray.
  • Classes that use Notifiable trait (User in our case) can have routeNotificationForMail routeNotificationForDatabase receivesBroadcastNotificationsOn methods to route to the appropriate users. The trait already does the work for mail and database. But you need to implement the receivesBroadcastNotificationsOn method for the broadcast medium. Namespace.To.The.Class.table_primary_key convention will be used if the required method for broadcast is not defined.
  • If the notification class implements the ShouldQueue interface, then your class can have connection, queue, delay properties. And you can also use onConnection, onQueue, delay methods on the notification class. Because the notification class uses the trait Queueable. If you’re aware of Job middleware, then you can have middleware method to return an array of Job middlewares. Also, you can use the property middleware to your notification class to add middleware for the queue job. Your notification class can also have the tries and timeout properties that will be used if pushed to the queue.
  • If your notification is pushed to a queue and it fails for some reason, then you can handle it having failed method implemented in your Notification class. It’ll receive an Exception when the notification had failed.
  • Just like jobs, your notification can have retryAfter as property or method and retryUntil as property and method for retry mechanism.
  • When sending notification using $user->notify() you can send the notification to only one model instance whereas you can send same notification to multiple users using Notification::send($models, $class). The $models can be a singular object, a Collection instance, or an array of models.
  • If you use send or notify methods to send notifications, then it’ll only use the Notification class’s via method. But if you send using sendNow or notifyNow then you can specify multiple channels on the fly which will take precedence over the via method.
  • If your application supports localization, then you can set locale property in your Notification class. Your notification class has a public method locale to set the locale on the fly. Or you can set the locale on the fly as Notification::locale('bn')->send($user, $order). One more way of doing this can be that your notifiable class implements the HasLocalePreference interface. Which means the User class in our case. The earlier the better, set locale in your notification class.
  • Even after pushing the notification to the queue, you can stop sending a notification. To do that you’ll have to listen to NotificationSending event. Register a listener for the event. The event receives a $notifiable model instance (user class in our case), $notification instance and $channel the current channel the notification is to be sent (string). If the event listener returns false from the event handler then the notification will not be sent and discarded.
  • After a notification is sent successfully, it then fires an event name NotificationSent that receives $notifiable model instance, $notification instance, and $channel as the channel used for the notification (string), $response whatever was returned after the notification was handled by the channel.
  • When sending notification using the mail channel, toMail method in our notification class can return Mailable instance. If this is the case, then you’ll have to set the email and subject and everything on your own. Otherwise, it’ll parse the mail as a markdown, sets the receiver email address, and fires the mail. Check Laravel Mail & Laravel Notifications mail segment for a greater view. Moreover, if MailMessage is returned from the toMail method from our notification class, then it’ll check for routeNotificationForMail method in our User class. If the method is not defined, then it’ll assume we have email column in our table. If we define the routeNotificationForMail method, we can customize it as [ 'email_id' => 'Name' ]. The following snippet does the same. So, it’ll set the name and email for the email receiver.
public function routeNotificationForMail ($notification) {
return [
$this->email => $this->first_name . ' ' . $this->last_name,
];
}
  • For the broadcast driver/channel/medium, it looks for toBroadcast or toArray in our notification class in the order. These data will be passed through the WebSocket. Both the cases can return an array of data or an instance of Illuminate\Notifications\Messages\BroadcastMessage (toBroadcast makes sense to me, as toArray is also used by the database driver too. Returning BroadcastMessage instance from toArray doesn’t make sense to me). If it returns the instance, then it can have connection and queue and all other properties of queues as the class has the Queueable trait implemented.
  • When using the broadcast channel, your notification class can have broadcastOn method. If not, then it’ll look for the channel name in your Model::receivesBroadcastNotificationsOn method. If not found then it’ll use the convention as Namespace.To.The.Class.table_primary_key. This will be used as a private channel. The method can return a string for a single private channel or array of strings for multiple private channels.
  • Your notification class when using broadcast as a channel that can have broadcastType method which will be merged with the data with the key type as stated above. If you don’t implement the method, then the class name will be used as the value. I skipped this part in my broadcasting article. Check the documentation to know more.
  • The database channel is not discussed much in this article. You can read the pretty straight forward Laravel documentation. It’s helpful.

Adhoc/On-demand notification

Sometimes, you may wish to send notification on an Adhoc basis. To do that you can use as below

Notification::route('mail', 'taylor@example.com')
->route('nexmo', '5555555555')
->route('slack', 'https://hooks.slack.com/services/...')
->notify(new InvoicePaid($invoice));

For the above code, nexmo and slack drivers are not installed. You’ll need to install them before running the code.

This is pretty much all. If you still want to trace the calls then check the following classes on your own. Let me know if I missed any points.

Finally, remember one thing, if you send N users notifications through M channels, then the number of jobs to be processed is N*M. That means if you send the same notification to 2 users using 2 mediums, then 4 jobs need to be processed by the worker.

Happy coding. ❤

--

--

Syed Sirajul Islam Anik

software engineer with "Senior" tag | procrastinator | programmer | !polyglot | What else 🙄 — Open to Remote