Skip to content

07. Notifications & Events

Let's take Chirper to the next level by sending email notifications when a new Chirp is created.

In addition to support for sending email, Laravel provides support for sending notifications across a variety of delivery channels, including email, SMS, and Slack. Plus, a variety of community built notification channels have been created to send notification over dozens of different channels! Notifications may also be stored in a database so they may be displayed in your web interface.

Creating the notification

Artisan can, once again, do all the hard work for us with the following command:

php artisan make:notification NewChirp

This will create a new notification at app/Notifications/NewChirp.php that is ready for us to customize.

Let's open the NewChirp class and allow it to accept the Chirp that was just created, and then customize the message to include the author's name and a snippet from the message:

app/Notifications/NewChirp.php
<?php
namespace App\Notifications;
 
+use App\Models\Chirp;
use Illuminate\Bus\Queueable;
+use Illuminate\Support\Str;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
 
class NewChirp extends Notification
{
use Queueable;
 
/**
* Create a new notification instance.
*/
- public function __construct()
+ public function __construct(public Chirp $chirp)
{
//
}
 ...
/**
* Get the notification's delivery channels.
*
* @return array<int, string>
*/
public function via(object $notifiable): array
{
return ['mail'];
}
 
/**
* Get the mail representation of the notification.
*/
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
- ->line('The introduction to the notification.')
- ->action('Notification Action', url('/'))
+ ->subject("New Chirp from {$this->chirp->user->name}")
+ ->greeting("New Chirp from {$this->chirp->user->name}")
+ ->line(Str::limit($this->chirp->message, 50))
+ ->action('Go to Chirper', url('/'))
->line('Thank you for using our application!');
}
 ...
/**
* Get the array representation of the notification.
*
* @return array<string, mixed>
*/
public function toArray(object $notifiable): array
{
return [
//
];
}
 
}

We could send the notification directly from the store method on our ChirpController class, but that adds more work for the controller, which in turn can slow down the request, especially as we'll be querying the database and sending emails.

Instead, let's dispatch an event that we can listen for and process in a background queue to keep our application snappy.

Creating an event

Events are a great way to decouple various aspects of your application, since a single event can have multiple listeners that do not depend on each other.

Let's create our new event with the following command:

php artisan make:event ChirpCreated

This will create a new event class at app/Events/ChirpCreated.php.

Since we'll be dispatching events for each new Chirp that is created, let's update our ChirpCreated event to accept the newly created Chirp so we may pass it on to our notification:

app/Events/ChirpCreated.php
<?php
 
namespace App\Events;
 
+use App\Models\Chirp;
use Illuminate\Broadcasting\Channel;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Broadcasting\PresenceChannel;
use Illuminate\Broadcasting\PrivateChannel;
use Illuminate\Contracts\Broadcasting\ShouldBroadcast;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;
 
class ChirpCreated
{
use Dispatchable, InteractsWithSockets, SerializesModels;
 
/**
* Create a new event instance.
*/
- public function __construct()
+ public function __construct(public Chirp $chirp)
{
//
}
 ...
/**
* Get the channels the event should broadcast on.
*/
public function broadcastOn(): array
{
return [
new PrivateChannel('channel-name'),
];
}
 
}

Dispatching the event

Now that we have our event class, we're ready to dispatch it any time a Chirp is created. You may dispatch events anywhere in your application lifecycle, but as our event relates to the creation of an Eloquent model, we can configure our Chirp model to dispatch the event for us.

app/Models/Chirp.php
<?php
 
namespace App\Models;
 
+use App\Events\ChirpCreated;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
 
class Chirp extends Model
{
use HasFactory;
 
protected $fillable = [
'message',
];
 
+ protected $dispatchesEvents = [
+ 'created' => ChirpCreated::class,
+ ];
 
public function user(): BelongsTo
{
return $this->belongsTo(User::class);
}
}

Now any time a new Chirp is created, the ChirpCreated event will be dispatched.

Creating an event listener

Now that we're dispatching an event, we're ready to listen for that event and send our notification.

Let's create a listener that subscribes to our ChirpCreated event:

php artisan make:listener SendChirpCreatedNotifications --event=ChirpCreated

The new listener will be placed at app/Listeners/SendChirpCreatedNotifications.php. Let's update the listener to send our notifications.

app/Listeners/SendChirpCreatedNotifications.php
<?php
 
namespace App\Listeners;
 
use App\Events\ChirpCreated;
+use App\Models\User;
+use App\Notifications\NewChirp;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\InteractsWithQueue;
 
-class SendChirpCreatedNotifications
+class SendChirpCreatedNotifications implements ShouldQueue
{
 ...
/**
* Create the event listener.
*/
public function __construct()
{
//
}
 
/**
* Handle the event.
*/
public function handle(ChirpCreated $event): void
{
- //
+ foreach (User::whereNot('id', $event->chirp->user_id)->cursor() as $user) {
+ $user->notify(new NewChirp($event->chirp));
+ }
}
}

We've marked our listener with the ShouldQueue interface, which tells Laravel that the listener should be run in a queue. By default, the "database" queue will be used to process jobs asynchronously. To begin processing queued jobs, you should run the php artisan queue:work Artisan command in your terminal.

We've also configured our listener to send notifications to every user in the platform, except the author of the Chirp. In reality, this might annoy users, so you may want to implement a "following" feature so users only receive notifications for accounts they follow.

We've used a database cursor to avoid loading every user into memory at once.

Testing it out

You may utilize local email testing tools like Mailpit and HELO to catch any emails coming from your application so you may view them. If you are developing via Docker and Laravel Sail then Mailpit is included for you.

Alternatively, you may configure Laravel to write mail to a log file by editing the .env file in your project and changing the MAIL_MAILER environment variable to log. By default, emails will be written to a log file located at storage/logs/laravel.log.

We've configured our notification not to send to the Chirp author, so be sure to register at least two users accounts. Then, go ahead and post a new Chirp to trigger a notification.

If you're using Mailpit, navigate to http://localhost:8025/, where you will find the notification for the message you just chirped!

Mailpit

Sending emails in production

To send real emails in production, you will need an SMTP server, or a transactional email provider, such as Mailgun, Postmark, or Amazon SES. Laravel supports all of these out of the box. For more information, take a look at the Mail documentation.

Continue to learn about deploying your application...