Skip to content

Creating Notifications

Create multi-channel notifications that send via email and database.

Terminal window
# Generate notification class
./vendor/bin/sail artisan make:notification BookingConfirmedNotification
<?php
namespace App\Notifications;
use App\Models\Booking;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Notification;
class BookingConfirmedNotification extends Notification implements ShouldQueue
{
use Queueable;
public function __construct(
public Booking $booking
) {
}
public function via(object $notifiable): array
{
return ['mail', 'database'];
}
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage())
->subject('Booking Confirmed: ' . $this->booking->reference)
->greeting('Hello ' . $notifiable->name . '!')
->line('Your booking has been confirmed.')
->line('**Reference:** ' . $this->booking->reference)
->action('View Booking', url('/admin/bookings/' . $this->booking->id))
->line('Thank you for using Volare!');
}
public function toArray(object $notifiable): array
{
return [
'title' => 'Booking Confirmed',
'message' => 'Booking ' . $this->booking->reference . ' confirmed.',
'booking_id' => $this->booking->id,
'reference' => $this->booking->reference,
];
}
}

From Observer:

use App\Notifications\BookingConfirmedNotification;
use Illuminate\Support\Facades\Notification;
public function updated(Booking $booking): void
{
if ($booking->isDirty('status') && $booking->status === BookingStatus::Confirmed) {
$user = $booking->user;
$user->notify(new BookingConfirmedNotification($booking));
}
}

From Controller/Service:

$user->notify(new BookingConfirmedNotification($booking));

To Multiple Users:

$users = User::role(Role::Admin->value)->get();
Notification::send($users, new BookingConfirmedNotification($booking));
use Illuminate\Support\Facades\Notification;
it('sends notification when booking is confirmed', function (): void {
Notification::fake();
$user = User::factory()->create();
$booking = Booking::factory()->create(['user_id' => $user->id]);
$booking->update(['status' => BookingStatus::Confirmed]);
Notification::assertSentTo($user, BookingConfirmedNotification::class);
});
it('notification contains booking details', function (): void {
Notification::fake();
$user = User::factory()->create();
$booking = Booking::factory()->create([
'user_id' => $user->id,
'reference' => 'BK12345',
]);
$user->notify(new BookingConfirmedNotification($booking));
Notification::assertSentTo($user, BookingConfirmedNotification::class, function ($notification) use ($booking, $user) {
$data = $notification->toArray($user);
return $data['booking_id'] === $booking->id
&& $data['reference'] === 'BK12345';
});
});
it('sends via mail and database channels', function (): void {
Notification::fake();
$user = User::factory()->create();
$booking = Booking::factory()->create(['user_id' => $user->id]);
$user->notify(new BookingConfirmedNotification($booking));
Notification::assertSentTo($user, BookingConfirmedNotification::class, function ($notification, $channels) {
return in_array('mail', $channels) && in_array('database', $channels);
});
});

Implement ShouldQueue to prevent blocking:

class MyNotification extends Notification implements ShouldQueue
{
use Queueable;
}

Queue worker must be running:

Terminal window
./vendor/bin/sail artisan queue:work

Always provide mail and database channels:

public function via(object $notifiable): array
{
return ['mail', 'database'];
}
  • Email for external delivery
  • Database for in-app notification bell

Use constructor property promotion:

public function __construct(
public Booking $booking,
public Offer $offer
) {
}

Models are serialized for queue, relationships preserved.

Use MailMessage fluent interface:

return (new MailMessage())
->subject('Clear, specific subject')
->greeting('Hello ' . $notifiable->name . '!')
->line('First line of content')
->line('**Bold:** for emphasis')
->action('Action Button Text', $url)
->line('Closing line');

Provide specific fields for Filament notification panel:

public function toArray(object $notifiable): array
{
return [
'title' => 'Short Title', // Required
'message' => 'Brief message', // Required
'record_id' => $this->model->id, // Optional
'type' => 'booking', // Optional
// Additional context fields...
];
}

Test notifications with:

  1. Recipient selection logic
  2. Channel verification (mail + database)
  3. Data payload validation
  4. Multiple recipient scenarios

Use Notification::fake() to avoid actual sending.

$user->notify(new MyNotification($model));
Notification::send($users, new MyNotification($model));
$admins = User::role(Role::Admin->value)->get();
Notification::send($admins, new MyNotification($model));
$admins = User::role(Role::Admin->value)->get();
$suppliers = User::where('supplier_id', $supplierId)->get();
$recipients = $admins->merge($suppliers)->unique('id');
Notification::send($recipients, new MyNotification($model));

Web UI: http://localhost:8025

View all sent emails without actual delivery.

See: Mailpit Setup

Terminal window
# Send test notification
./vendor/bin/sail artisan notification:test user@example.com

Notifications not sending:

  • Queue worker running? sail artisan queue:work
  • Check logs: storage/logs/laravel.log
  • Verify MAIL_* env vars

Email not received:

  • Check Mailpit UI (http://localhost:8025)
  • Verify via() includes ‘mail’
  • Check queue failed jobs table

Database notifications missing:

  • Verify via() includes ‘database’
  • Check notifications table directly
  • User must be Notifiable (default on User model)