0

Cache stampede prevention — the remember with locks pattern

Advanced5 min read·lv-20-006
performanceinterview

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
<?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;
    }
}