Cache stampede prevention — the remember with locks pattern
Concept
Notifications in Laravel are messages sent through one or more channels (mail, database, Slack, SMS). Unlike events + listeners (which decoupled internal business logic), notifications are specifically designed for user-facing communications.
Why notifications vs mail directly: Notifications decouple the message from the transport. The same notification can be sent via email AND stored in the database AND sent to Slack — without changing the calling code. $user->notify(new InvoicePaid($invoice)) sends it via all configured channels.
Notifiable trait: Add to models that receive notifications. Provides notify(Notification $n), notifyNow(), routeNotificationFor($driver), and the notifications() relationship.
Notification routing: Each driver calls routeNotificationFor('mail'), routeNotificationFor('slack'), etc. on the Notifiable. Default: routeNotificationForMail() returns $this->email. Override to customize.
via(object $notifiable): array: Defines which channels to use for this notification. Return ['mail', 'database'] to use both.
Notification::send($notifiables, $notification) / Notification::sendNow(): Send to multiple notifiables at once.
Channels: Built-in: mail, database, broadcast, vonage (SMS), slack. Community: pusher, fcm, telegram, etc. via laravel-notification-channels organization.
Code Example
<?php
// Creating a notification
// php artisan make:notification InvoicePaid
namespace App\Notifications;
use App\Models\Invoice;
use Illuminate\Notifications\Notification;
use Illuminate\Notifications\Messages\MailMessage;
class InvoicePaid extends Notification
{
public function __construct(private readonly Invoice $invoice) {}
// Which channels to use
public function via(object $notifiable): array
{
return ['mail', 'database']; // email + store in DB
}
// Mail channel — build the email
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->subject("Invoice #{$this->invoice->number} Paid")
->greeting("Hello {$notifiable->name},")
->line("Your invoice of \${$this->invoice->total} has been paid.")
->action('View Invoice', url("/invoices/{$this->invoice->id}"))
->line('Thank you for your business!');
}
// Database channel — what to store
public function toDatabase(object $notifiable): array
{
return [
'invoice_id' => $this->invoice->id,
'amount' => $this->invoice->total,
'paid_at' => now()->toISOString(),
];
}
// Unique ID for deduplication (optional)
public function id(): string
{
return "invoice-paid-{$this->invoice->id}";
}
}
// Sending notifications
$user->notify(new InvoicePaid($invoice)); // async if ShouldQueue
$user->notifyNow(new InvoicePaid($invoice)); // always synchronous
// Send to multiple users
\Illuminate\Support\Facades\Notification::send(
User::where('role', 'admin')->get(),
new SystemAlertNotification($alert)
);
// Routing — customize email address
class User extends \Illuminate\Foundation\Auth\User
{
use \Illuminate\Notifications\Notifiable;
public function routeNotificationForMail(): string
{
return $this->notification_email ?? $this->email;
}
public function routeNotificationForSlack(): string
{
return $this->slack_webhook_url;
}
}