Laravel Notifications — Part 1: Understanding Laravel Notification thoroughly
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 isdatabase
orbroadcast
but doesn’t have correspondingtoBroadcast
ortoDatabase
methods then it’ll by default use thetoArray
. - Classes that use
Notifiable
trait (User in our case) can haverouteNotificationForMail
routeNotificationForDatabase
receivesBroadcastNotificationsOn
methods to route to the appropriate users. The trait already does the work for mail and database. But you need to implement thereceivesBroadcastNotificationsOn
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 haveconnection
,queue
,delay
properties. And you can also useonConnection
,onQueue
,delay
methods on the notification class. Because the notification class uses the traitQueueable
. If you’re aware of Job middleware, then you can havemiddleware
method to return an array of Job middlewares. Also, you can use the propertymiddleware
to your notification class to add middleware for the queue job. Your notification class can also have thetries
andtimeout
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 andretryUntil
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 usingNotification::send($models, $class)
. The$models
can be a singular object, aCollection
instance, or an array of models. - If you use
send
ornotify
methods to send notifications, then it’ll only use the Notification class’svia
method. But if you send usingsendNow
ornotifyNow
then you can specify multiple channels on the fly which will take precedence over thevia
method. - If your application supports localization, then you can set
locale
property in your Notification class. Your notification class has a public methodlocale
to set the locale on the fly. Or you can set the locale on the fly asNotification::locale('bn')->send($user, $order)
. One more way of doing this can be that your notifiable class implements theHasLocalePreference
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 returnsfalse
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 returnMailable
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 thetoMail
method from our notification class, then it’ll check forrouteNotificationForMail
method in our User class. If the method is not defined, then it’ll assume we haveemail
column in our table. If we define therouteNotificationForMail
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
ortoArray
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 ofIlluminate\Notifications\Messages\BroadcastMessage
(toBroadcast
makes sense to me, astoArray
is also used by the database driver too. ReturningBroadcastMessage
instance fromtoArray
doesn’t make sense to me). If it returns the instance, then it can haveconnection
andqueue
and all other properties of queues as the class has theQueueable
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 yourModel::receivesBroadcastNotificationsOn
method. If not found then it’ll use the convention asNamespace.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 keytype
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.
- ChannelManager which receives the Notification Facade calls. Also the instance for Notification Dispatcher & Notification Factory.
send
andsendNow
pushes to the NotificationSender which then handles everything.- MailChannel for the mail driver.
- DatabaseChannel for the database driver.
- BroadcastChannel for the broadcast driver.
- AnonymousNotifiable when used
Notification::route()->notify($class)
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. ❤