Introduction
Laravel's notification system is a single API for sending messages across every channel you care about: email, database, Slack, SMS, broadcast, and custom. One notification class can deliver to all of them at once, and adding a new channel doesn't require touching the calling code.
Key Concepts
- Notification: A class that extends
Illuminate\Notifications\Notificationand describes the message content. - Channel: A transport:
mail,database,slack,vonage(SMS),broadcast, or a custom class. via()method: Returns the list of channels this notification should be sent to.Notifiabletrait: Added to the model (usuallyUser) that can receive notifications.
Real World Context
A billing app needs to email a receipt, store a row in the in-app notification center, and Slack the sales channel — all for one event. Building three separate mailers is repetitive; one InvoicePaid notification with three channel methods (toMail, toArray, toSlack) keeps the content in one place.
Deep Dive
Creating Notifications
bashphp artisan make:notification InvoicePaid
This creates app/Notifications/InvoicePaid.php:
php<?php namespace App\Notifications; use App\Models\Invoice; use Illuminate\Bus\Queueable; use Illuminate\Contracts\Queue\ShouldQueue; use Illuminate\Notifications\Messages\MailMessage; use Illuminate\Notifications\Notification; class InvoicePaid extends Notification implements ShouldQueue { use Queueable; public function __construct( public Invoice $invoice ) {} /** * Notification channels. */ public function via(object $notifiable): array { return ['mail', 'database']; } /** * Email representation. */ public function toMail(object $notifiable): MailMessage { return (new MailMessage) ->subject('Invoice Paid') ->greeting('Hello!') ->line('Your invoice has been paid.') ->line('Amount: $' . number_format($this->invoice->amount, 2)) ->action('View Invoice', url('/invoices/' . $this->invoice->id)) ->line('Thank you for your business!'); } /** * Database representation. */ public function toArray(object $notifiable): array { return [ 'invoice_id' => $this->invoice->id, 'amount' => $this->invoice->amount, 'message' => 'Invoice #' . $this->invoice->number . ' was paid.', ]; } }
Sending Notifications
Using the Notifiable Trait
phpuse App\Notifications\InvoicePaid; // Send to a user $user->notify(new InvoicePaid($invoice));
Using the Notification Facade
phpuse Illuminate\Support\Facades\Notification; // Send to multiple users Notification::send($users, new InvoicePaid($invoice)); // Send to anonymous recipient (for non-model notifications) Notification::route('mail', 'customer@example.com') ->route('slack', 'https://hooks.slack.com/...') ->notify(new InvoicePaid($invoice));
Notification Channels
The via() Method
phppublic function via(object $notifiable): array { // Always email return ['mail']; // Based on user preferences return $notifiable->notification_preferences; // Conditional channels $channels = ['database']; if ($notifiable->wants_email) { $channels[] = 'mail'; } if ($this->invoice->amount > 1000) { $channels[] = 'slack'; } return $channels; }
Mail Notifications
phppublic function toMail(object $notifiable): MailMessage { return (new MailMessage) ->subject('Order Shipped') ->greeting('Hello ' . $notifiable->name . '!') ->line('Your order has been shipped.') ->line('Tracking Number: ' . $this->order->tracking_number) ->action('Track Order', url('/orders/' . $this->order->id)) ->line('Thanks for shopping with us!') ->salutation('Best regards, The Team'); } // With custom view public function toMail(object $notifiable): MailMessage { return (new MailMessage) ->subject('Your Invoice') ->view('emails.invoice', [ 'invoice' => $this->invoice, 'user' => $notifiable, ]); } // Attachments public function toMail(object $notifiable): MailMessage { return (new MailMessage) ->subject('Monthly Report') ->line('Please find attached your monthly report.') ->attach('/path/to/report.pdf') ->attachData($this->pdf, 'report.pdf', [ 'mime' => 'application/pdf', ]); }
Database Notifications
bashphp artisan make:notifications-table php artisan migrate
Add trait to User model:
phpuse Illuminate\Notifications\Notifiable; class User extends Authenticatable { use Notifiable; }
Retrieve notifications:
php// All notifications $notifications = $user->notifications; // Unread only $unread = $user->unreadNotifications; // Mark as read $user->unreadNotifications->markAsRead(); // Mark single as read $notification->markAsRead(); // Delete old notifications $user->notifications()->where('created_at', '<', now()->subMonth())->delete();
In your views:
blade@foreach($user->unreadNotifications as $notification) <div class="notification"> {{ $notification->data['message'] }} <small>{{ $notification->created_at->diffForHumans() }}</small> </div> @endforeach
Common Pitfalls
- Sending notifications inline — a
mailnotification that goes out synchronously blocks the HTTP response while SMTP runs. Mark the notificationShouldQueueso it runs in the background. - Hardcoding channels in
via()—return ['mail', 'slack']ignores user preferences. Read from the notifiable (return $notifiable->preferred_channels) so opt-outs are respected.
Best Practices
- One notification class per logical message —
InvoicePaid,OrderShipped,PasswordReset. Don't overload one class with switches. - Put channel-specific formatting in dedicated methods —
toMail(),toArray(),toSlack(). This makes each channel's output easy to tweak without affecting the others.
Summary
- A notification is a class with channel-specific
to{Channel}()methods and avia()method. - Send via
$user->notify(...)(Notifiable trait) orNotification::send([...], ...)(facade). mail,database,slack,vonage, andbroadcastare the built-in channels.- Mark a notification
ShouldQueueto send it in the background.