Skip to content
Muhammet Şafak
tr
Web Development 4 min read

Laravel Notifications: Email and SMS

How I use Laravel Notification classes to send the same notification logic across multiple channels — email, SMS, and more.


As an application grows, so does its notification complexity: order confirmations should go by email, shipping updates by SMS, comment alerts as in-app notifications. Laravel’s notification system lets you manage all these different channels through a single class. Adding a new channel doesn’t require touching the existing notification logic — that’s the real value here.

How the notification system works

In Laravel, every notification is a class. That class defines which channels it should be sent through and what the message looks like on each channel. You can scaffold one with Artisan:

php artisan make:notification OrderShipped

The generated class lives at app/Notifications/OrderShipped.php. It contains two key methods: via and toMail (or channel-specific methods like toNexmo, toBroadcast, etc.).

Email notification

The via method returns which channels to use. The toMail method builds the mail message:

<?php

namespace App\Notifications;

use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\MailMessage;

class OrderShipped extends Notification
{
    protected $order;

    public function __construct($order)
    {
        $this->order = $order;
    }

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

    public function toMail($notifiable)
    {
        return (new MailMessage)
            ->subject('Your order has been shipped')
            ->line('Your order has been shipped with tracking number ' . $this->order->tracking_number . '.')
            ->action('Track Order', url('/orders/' . $this->order->id))
            ->line('Thank you for your purchase.');
    }
}

Sending the notification to a user is as simple as calling notify:

<?php

$user->notify(new OrderShipped($order));

The notify method is available on any model that uses the Notifiable trait. The User model includes it by default.

Adding an SMS channel

When you want to send the same notification via SMS, all you need is to add the SMS channel to via. Laravel ships with Nexmo (now Vonage) integration out of the box:

<?php

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

public function toNexmo($notifiable)
{
    return (new NexmoMessage)
        ->content('Your order ' . $this->order->tracking_number . ' has been shipped.');
}

I added the SMS channel without touching toMail. The notification logic lives in one place; each channel has its own method — that separation holds up well.

Letting the user decide the channel

In real projects, not every user wants every channel. One person might opt out of SMS; another might prefer email only. The via method receives the $notifiable object as a parameter, so you can make the decision based on user preferences:

<?php

public function via($notifiable)
{
    $channels = ['mail'];

    if ($notifiable->sms_notifications_enabled) {
        $channels[] = 'nexmo';
    }

    return $channels;
}

The notification class itself never changes — a single column in the users table drives the channel list.

Database notifications

If you want to show a notification inbox inside your application, the database channel handles that. Notifications are stored in the notifications table, making it straightforward to query unread counts and lists.

<?php

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

public function toArray($notifiable)
{
    return [
        'order_id'        => $this->order->id,
        'tracking_number' => $this->order->tracking_number,
    ];
}
<?php

// Fetch unread notifications
$user->unreadNotifications;

// Mark all as read
$user->unreadNotifications->markAsRead();

When I first used the database channel, I missed one thing: you need to run a migration to create the notifications table. Laravel generates it via php artisan notifications:table. I spent a while wondering why notifications were silently disappearing — the table simply didn’t exist.

Async dispatch with queues

Email and SMS delivery take time. Rather than making the user wait for a response after placing an order, you can push notifications onto a queue. Just implement the ShouldQueue interface on your notification class:

<?php

use Illuminate\Contracts\Queue\ShouldQueue;

class OrderShipped extends Notification implements ShouldQueue
{
    // ...
}

Queue infrastructure is a topic on its own — I’m not going to dive into it here — but adding ShouldQueue is the first practical step you can apply immediately in a real project.

Conclusion

Laravel’s notification system has a clean design precisely because it centralizes the “which channel” decision in a single class. Adding a new channel doesn’t alter existing logic; it’s just a new method. As a project grows and notification types multiply, the value of that separation compounds. In the old approach I used to write separate code per channel, and any change meant updating each one individually. Now the notification logic lives in one place.

One more thing to watch as notification classes get larger: the via method might end up making channel decisions not just based on user preferences, but also on conditions tied to the notification itself — for instance, send SMS only when the order amount exceeds a threshold, otherwise just send email. If that logic piles up inside via, the class starts to bloat. In those cases it’s cleaner to create separate notification classes and push the “which notification to send” decision up into the calling business logic. A notification class should know how to send; when and under what conditions is the concern of whoever calls it.

Tags: #Laravel
Share:

Comments

Sign in with your GitHub account to join the discussion. Comments are stored in GitHub Discussions.

Related Posts

Search the site

Start typing to search posts, projects and pages.

Esc to close Powered by Pagefind