---
model_tier: medium
name: laravel-notifications
description: "Use when sending notifications via mail, Slack, database, or custom channels — with queuing, on-demand recipients, and notification preferences."
domain: engineering
workspaces:
  - engineering
packs:
  - laravel
trust:
  level: professional
install:
  default: false
  removable: true
---

# laravel-notifications

## When to use

Use this skill when sending notifications to users or external systems:
- Email, Slack, SMS, or DB notifications
- Custom notification channels
- On-demand notifications (to non-user recipients)
- Notification preferences and opt-out logic

For **Mailables** (complex email templates, attachments), see [laravel-mail](../laravel-mail/SKILL.md).

## Procedure: Create a notification

1. **Generate class** — `php artisan make:notification InvoiceCreated`.
2. **Choose channels** — Mail, DB, Slack, or custom. Implement `via()`.
3. **Build content** — Implement `toMail()`, `toArray()`, etc. for each channel.
4. **Queue it** — Add `ShouldQueue` interface for non-blocking delivery.
5. **Verify** — Send test notification, confirm delivery on all channels.

### Example

```bash
php artisan make:notification InvoiceCreated
```

```php
class InvoiceCreated extends Notification implements ShouldQueue
{
    use Queueable;

    public function __construct(
        private readonly Invoice $invoice,
    ) {}

    /** @return array<int, string> */
    public function via(object $notifiable): array
    {
        return ['mail', 'database'];
    }

    public function toMail(object $notifiable): MailMessage
    {
        return (new MailMessage())
            ->subject('New Invoice #' . $this->invoice->getNumber())
            ->greeting('Hello ' . $notifiable->getName())
            ->line('A new invoice has been created.')
            ->action('View Invoice', url('/invoices/' . $this->invoice->getId()))
            ->line('Thank you for your business.');
    }

    /** @return array<string, mixed> */
    public function toArray(object $notifiable): array
    {
        return [
            'invoice_id' => $this->invoice->getId(),
            'amount' => $this->invoice->getAmount(),
        ];
    }
}
```

## Sending notifications

```php
// Via the Notifiable trait on the model
$user->notify(new InvoiceCreated($invoice));

// Via the Notification facade (multiple recipients)
Notification::send($users, new InvoiceCreated($invoice));

// On-demand (no user model needed)
Notification::route('mail', 'admin@example.com')
    ->route('slack', '#billing')
    ->notify(new InvoiceCreated($invoice));
```

## Database notifications

Requires the notifications table migration:

```bash
php artisan notifications:table
php artisan migrate
```

```php
// Read notifications
$user->notifications;           // all
$user->unreadNotifications;     // unread only

// Mark as read
$notification->markAsRead();
$user->unreadNotifications->markAsRead();
```

## Slack notifications

```php
public function toSlack(object $notifiable): SlackMessage
{
    return (new SlackMessage())
        ->text('Invoice #' . $this->invoice->getNumber() . ' created')
        ->headerBlock('New Invoice')
        ->sectionBlock(function (SectionBlock $block) {
            $block->text('Amount: ' . $this->invoice->getAmount());
        });
}
```

## Notification preferences

```php
public function via(object $notifiable): array
{
    // Respect user preferences
    $channels = ['database'];

    if ($notifiable->wantsEmailNotifications()) {
        $channels[] = 'mail';
    }

    if ($notifiable->wantsSlackNotifications()) {
        $channels[] = 'slack';
    }

    return $channels;
}
```

## Core rules

- **Always queue notifications** — implement `ShouldQueue` to avoid blocking requests.
- **Keep payloads small** in `toArray()` — store IDs, not full objects.
- **Use `via()` for channel logic** — don't hardcode channels, respect user preferences.
- **On-demand for external recipients** — use `Notification::route()` for non-user targets.
- **Database notifications for in-app** — use the notifications table for in-app notification centers.

## Output format

1. Notification class with via() and channel-specific methods
2. Queued dispatch with proper serialization

## Auto-trigger keywords

- notification
- notify
- Notifiable
- MailMessage
- SlackMessage
- DB notification

## Gotcha

- Don't send notifications synchronously in request lifecycle — always queue them.
- The model forgets to implement `toArray()` for DB notifications — it throws silently.
- `via()` method must return an array even for a single channel — `return ['mail']`, not `return 'mail'`.

## Do NOT

- Do NOT store large objects in DB notification `data` — use IDs and fetch on read.
- Do NOT use notifications for complex emails — use [Mailables](../laravel-mail/SKILL.md) instead.
