Mail & Notifications

Almost every real application needs to reach the user outside the request/response cycle at some point — a welcome email, a shipping confirmation, a password reset link, an in-app alert badge. Laravel gives you two closely related tools for this: Mailables, which represent a single email as an ordinary PHP class, and Notifications, a more flexible layer on top that can deliver the same message over several channels — mail, database, and others — from one class.

Mailables: An Email as a Class

Instead of scattering email-building logic through a controller, Laravel represents each distinct email your application sends as its own class, called a Mailable. Artisan scaffolds one for you:

php artisan make:mail OrderShipped

This creates app/Mail/OrderShipped.php, with a build() method whose job is to describe what the email looks like — its subject line and which Blade view renders the body:

<?php
// app/Mail/OrderShipped.php
namespace App\Mail;

use App\Models\Order;
use Illuminate\Mail\Mailable;

class OrderShipped extends Mailable
{
    public function __construct(public Order $order)
    {
    }

    public function build()
    {
        return $this->subject('Your order has shipped')
                    ->view('emails.orders.shipped');
    }
}

The constructor accepts whatever data the email needs — here, the Order — and because it's promoted to a public property, that same $order is automatically available inside the Blade view referenced by view(), exactly like any other view data.

Sending a Mailable

With the class written, sending the actual email is a single line, using the Mail facade from anywhere in your application — a controller, a job, a console command:

<?php
use App\Mail\OrderShipped;
use Illuminate\Support\Facades\Mail;

Mail::to($user)->send(new OrderShipped($order));

Mail::to() accepts a single user, an email address string, or a collection of recipients, and Laravel figures out where to send it — if you pass a model, it looks for an email attribute on it automatically.

Configuring a Mail Driver

Where the email actually goes is controlled entirely by config, not code — the MAIL_MAILER setting in your .env file:

# .env
MAIL_MAILER=smtp
MAIL_HOST=smtp.mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_FROM_ADDRESS=hello@example.com
Local development tip: Set MAIL_MAILER=log and Laravel won't attempt to send anything over the network at all — every email gets written straight to storage/logs/laravel.log instead, so you can inspect exactly what would have been sent without needing a real mail server configured.

Notifications: A More Flexible Alternative

A plain Mailable only ever produces an email. Often the same event — an invoice being paid, say — should also show up as an in-app alert, or eventually a Slack message or SMS, without duplicating the underlying logic. That's exactly what a Notification is for: one class that can describe how to deliver itself across multiple channels. Artisan scaffolds one the same way:

php artisan make:notification InvoicePaid

Choosing Channels with via()

Every notification class has a via() method that returns an array naming which channels to deliver through for a given notifiable — most commonly mail and database. For each channel listed, the notification must define a matching to*() method describing what that channel's version of the message looks like:

<?php
// app/Notifications/InvoicePaid.php
namespace App\Notifications;

use App\Models\Invoice;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\MailMessage;

class InvoicePaid extends Notification
{
    public function __construct(public Invoice $invoice)
    {
    }

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

    public function toMail($notifiable)
    {
        return (new MailMessage)
                    ->subject('Invoice Paid')
                    ->line('Your invoice has been paid.')
                    ->action('View Invoice', url('/invoices/'.$this->invoice->id));
    }

    public function toDatabase($notifiable)
    {
        return [
            'invoice_id' => $this->invoice->id,
            'message' => 'Your invoice has been paid.',
        ];
    }
}

Notice this is a single class defining both the email version (toMail(), using the fluent MailMessage builder) and the database version (toDatabase(), a plain array that gets stored as JSON) — one place to describe "an invoice was paid," rendered differently depending on where it's headed.

Sending a Notification

Notifications are sent through the model receiving them, not through a facade — which is why any model that should be notifiable, most commonly User, uses the Notifiable trait (already included on a fresh User model by default):

<?php
use App\Notifications\InvoicePaid;

$user->notify(new InvoicePaid($invoice));

Calling $user->notify() loops through every channel returned by via() and dispatches the notification down each one — in this case sending the email and writing a database row, from one call.

Database Notifications: An In-App Notification Bell

The database channel needs a table to write to. Laravel ships a stub migration for exactly this that you publish and run once per project:

php artisan notifications:table
php artisan migrate

After that, every notification sent through the database channel lands as a row in the notifications table, and the Notifiable trait exposes two convenient relationships for reading them back — exactly what you'd wire up behind a notification bell icon in a dashboard:

<?php
// every notification ever sent to this user
$user->notifications;

// only the ones not yet marked as read
$user->unreadNotifications;

Each notification instance also has a markAsRead() method, so an "unread" badge count and a full history list can both be built from the same table without any extra infrastructure.

Mailable or Notification: Which One?

Use a plain Mailable when...Use a Notification when...
The message is only ever an emailThe same event might need to reach the user through more than one channel
You need full control over a complex, highly custom email layoutAn in-app record of "this happened" (read/unread) is useful, not just a one-time email
Sending directly with Mail::to() reads more naturally at the call siteSending reads more naturally as "this user gets notified," via $user->notify()

In practice a lot of real Laravel apps use both: simple transactional emails (a welcome email, say) as plain Mailables, and anything that also deserves an in-app trail — invoices, comments, mentions — as Notifications.

Next up: Now that the server can reach users through mail and in-app alerts, the next lesson covers shaping the JSON your API actually returns — head to API Resources & REST APIs.