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
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 email | The same event might need to reach the user through more than one channel |
| You need full control over a complex, highly custom email layout | An 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 site | Sending 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.