Notificacions
Com enviar notificacions multicanal a Laravel: mail, base de dades, Slack, broadcast i canals personalitzats.
El sistema de notificacions#
A diferència dels correus electrònics, que sempre s'envien per un sol canal (el correu), les notificacions de Laravel estan dissenyades per ser multicanal des del primer moment. Una mateixa notificació pot enviar-se per correu electrònic, guardar-se a la base de dades, publicar-se a un canal de Slack i emetre's en temps real per WebSocket, tot amb una sola classe. Aquesta flexibilitat és enormement útil perquè cada usuari o situació pot requerir canals diferents: un administrador potser vol rebre alertes a Slack, un client prefereix correu electrònic i la interfície de l'aplicació necessita mostrar una campaneta amb les notificacions no llegides.
El concepte clau és que una notificació representa un event que ha passat (un article publicat, una comanda enviada, un pagament rebut) i els canals són simplement les vies per les quals es comunica aquest event. Separar el "què" del "com" fa que el sistema sigui molt flexible: pots afegir o treure canals sense canviar la lògica de negoci. Si demà vols afegir notificacions per SMS, només cal afegir un canal nou sense tocar el codi que dispara les notificacions.
Laravel inclou suport integrat per a diversos canals: mail (correus electrònics), database (emmagatzemades a la base de dades), broadcast (temps real via WebSocket), slack (missatges a Slack) i vonage (SMS). A més, la comunitat ha creat desenes de canals addicionals per a Telegram, Discord, WhatsApp, Twitter, push notifications i molts més. Cada canal defineix el seu propi format de missatge, cosa que permet adaptar el contingut al medi: un correu pot ser llarg i detallat, un SMS ha de ser breu i directe, i una notificació de Slack pot incloure botons d'acció.
Crear una notificació#
Per crear una notificació, utilitza la comanda Artisan. Cada notificació és una classe que encapsula el contingut del missatge per a tots els canals on es vol enviar. Aquesta estructura orientada a objectes fa que les notificacions siguin fàcils de testejar i mantenir, perquè tota la lògica d'una notificació viu en un sol lloc:
php artisan make:notification ArticlePublishedAixò genera una classe a app/Notifications/ArticlePublished.php. La classe conté tres elements fonamentals: el constructor (que rep les dades necessàries), el mètode via() (que determina els canals) i un mètode to*() per a cada canal (que formata el missatge). Vegem una notificació completa amb múltiples canals:
namespace App\Notifications;
use App\Models\Article;
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Messages\MailMessage;
use Illuminate\Notifications\Messages\SlackMessage;
use Illuminate\Notifications\Notification;
class ArticlePublished extends Notification implements ShouldQueue
{
use Queueable;
public function __construct(
public Article $article
) {}
public function via(object $notifiable): array
{
return ['mail', 'database', 'slack'];
}
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->subject("Nou article: {$this->article->title}")
->greeting("Hola, {$notifiable->name}!")
->line("S'ha publicat un nou article que et pot interessar:")
->line("**{$this->article->title}**")
->line($this->article->excerpt)
->action('Llegir article', route('articles.show', $this->article))
->line('Gràcies per seguir Laravel Andorra!');
}
public function toArray(object $notifiable): array
{
return [
'article_id' => $this->article->id,
'title' => $this->article->title,
'excerpt' => $this->article->excerpt,
'author' => $this->article->author->name,
'url' => route('articles.show', $this->article),
];
}
public function toSlack(object $notifiable): SlackMessage
{
return (new SlackMessage)
->text("Nou article publicat: {$this->article->title}")
->headerBlock("Nou article publicat")
->sectionBlock(function ($block) {
$block->text("*{$this->article->title}*\n{$this->article->excerpt}");
})
->actionsBlock(function ($block) {
$block->button('Llegir article')
->url(route('articles.show', $this->article));
});
}
}Fixa't que la classe implementa ShouldQueue. Això és important perquè les notificacions multicanal poden ser lentes: enviar un correu, escriure a la base de dades i publicar a Slack pot trigar diversos segons. Amb ShouldQueue, el procés es fa en segon pla i l'usuari no nota cap retard.
El mètode via()#
El mètode via() és el cor del sistema de notificacions perquè determina quins canals s'utilitzen per enviar la notificació. Rep l'entitat notificable (normalment un model User) com a paràmetre, cosa que permet prendre decisions dinàmiques sobre els canals. Això és molt potent perquè cada usuari pot tenir preferències de notificació diferents.
Per exemple, pots permetre que els usuaris configurin com volen rebre cada tipus de notificació:
public function via(object $notifiable): array
{
$channels = ['database']; // Sempre guardem a la base de dades
if ($notifiable->prefers_email_notifications) {
$channels[] = 'mail';
}
if ($notifiable->slack_webhook_url) {
$channels[] = 'slack';
}
if ($notifiable->prefers_push_notifications) {
$channels[] = 'broadcast';
}
return $channels;
}Aquesta flexibilitat permet construir sistemes de preferències de notificació on cada usuari escull els canals que vol. A la base de dades pots tenir una taula notification_preferences amb els canals activats per cada tipus de notificació, i el mètode via() consulta aquesta configuració per determinar on enviar cada notificació.
Un patró habitual és tenir un mètode al model User que retorna els canals preferits:
// Al model User
public function notificationChannels(string $notificationType): array
{
return $this->notificationPreferences()
->where('notification_type', $notificationType)
->pluck('channel')
->toArray();
}
// A la notificació
public function via(object $notifiable): array
{
return $notifiable->notificationChannels('article_published');
}Enviar notificacions#
Hi ha dues maneres d'enviar notificacions a Laravel, i cadascuna és adequada per a situacions diferents. La primera utilitza el trait Notifiable del model (que el model User ja inclou per defecte), i la segona utilitza la facade Notification. La diferència principal és que el trait és ideal per enviar a un sol destinatari, mentre que la facade permet enviar a múltiples destinataris de manera eficient.
use App\Notifications\ArticlePublished;
use Illuminate\Support\Facades\Notification;
// Opció 1: A través del model (un sol destinatari)
$user->notify(new ArticlePublished($article));
// Opció 2: A través de la facade (múltiples destinataris)
$subscribers = User::where('subscribed', true)->get();
Notification::send($subscribers, new ArticlePublished($article));La facade Notification::send() accepta qualsevol col·lecció iterable de models notificables. Internament, Laravel envia la notificació a cada destinatari de manera individual, aplicant el mètode via() per a cada un. Això significa que si un usuari només vol rebre correus i un altre només vol notificacions a la base de dades, el sistema ho gestiona automàticament.
Notificacions amb retard#
De vegades vols enviar una notificació però no immediatament. Per exemple, podries voler enviar un recordatori 24 hores després que un usuari hagi abandonat el carret de compra, o una notificació de seguiment una setmana després d'una compra:
// Enviar amb un retard
$user->notify(
(new CartAbandonedReminder($cart))->delay(now()->addHours(24))
);
// Retard diferent per canal
$user->notify(
(new OrderShipped($order))->delay([
'mail' => now()->addMinutes(5),
'slack' => now()->addSeconds(30),
'database' => now(), // immediatament
])
);El retard per canal és especialment útil: potser vols que la notificació aparegui immediatament a la interfície de l'aplicació (canal database/broadcast) però que el correu s'enviï uns minuts després per evitar que l'usuari rebi el correu quan encara té l'aplicació oberta.
Notificacions per correu#
El canal de correu és probablement el més utilitzat. El mètode toMail() construeix el missatge utilitzant l'objecte MailMessage, que proporciona una interfície fluida per definir l'assumpte, el salut, les línies de text, els botons d'acció i el peu. Laravel genera automàticament un correu HTML professional a partir d'aquesta definició, sense necessitat d'escriure cap plantilla Blade.
L'objecte MailMessage ofereix mètodes expressius per construir el contingut del correu:
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->from('info@laravelandorra.com', 'Laravel Andorra')
->subject("Nou comentari al teu article")
->greeting("Hola, {$notifiable->name}!")
->line("Has rebut un nou comentari al teu article:")
->line("**{$this->comment->article->title}**")
->line("\"{$this->comment->body}\"")
->line("Comentari de: {$this->comment->author->name}")
->action('Veure comentari', route('articles.show', [
'article' => $this->comment->article,
'#comment-' . $this->comment->id,
]))
->line('Gràcies per compartir el teu coneixement!')
->salutation('Salutacions, l\'equip de Laravel Andorra');
}Per defecte, l'assumpte del correu es genera automàticament a partir del nom de la classe de la notificació (per exemple, ArticlePublished es converteix en "Article Published"). Això rarament és adequat, especialment si treballes en un idioma diferent de l'anglès, per tant és recomanable definir sempre l'assumpte explícitament amb ->subject().
Notificacions amb error#
Quan una notificació comunica un error o un problema, pots canviar el tipus visual del correu per indicar-ho. Això modifica el color del botó d'acció i afegeix elements visuals que indiquen que quelcom ha anat malament:
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->error() // Canvia el color del botó a vermell
->subject('Error al processar el pagament')
->greeting("Hola, {$notifiable->name}")
->line('No hem pogut processar el teu pagament amb la targeta acabada en ' . $this->card->last_four . '.')
->line('Si us plau, actualitza les teves dades de pagament per evitar la suspensió del servei.')
->action('Actualitzar pagament', route('billing.payment-method'))
->line('Si creus que es tracta d\'un error, contacta amb el nostre suport.');
}Notificacions Markdown per correu#
Si necessites més control sobre el disseny del correu, pots utilitzar plantilles Markdown en lloc de l'API fluida de MailMessage. Això et permet crear correus amb taules, imatges, panells i qualsevol estructura HTML que necessitis, mantenint la comoditat de la sintaxi Markdown:
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->subject('Resum setmanal')
->markdown('notifications.weekly-summary', [
'user' => $notifiable,
'stats' => $this->stats,
'articles' => $this->articles,
]);
}{{-- resources/views/notifications/weekly-summary.blade.php --}}
<x-mail::message>
# Resum setmanal
Hola {{ $user->name }}, aquí tens el resum de la setmana:
<x-mail::table>
| Mètrica | Valor |
|:--------|------:|
| Articles publicats | {{ $stats['articles'] }} |
| Comentaris rebuts | {{ $stats['comments'] }} |
| Visites totals | {{ number_format($stats['views']) }} |
</x-mail::table>
@if ($articles->isNotEmpty())
## Articles més populars
@foreach ($articles as $article)
- [{{ $article->title }}]({{ route('articles.show', $article) }}) ({{ $article->views_count }} visites)
@endforeach
@endif
<x-mail::button :url="route('dashboard')">
Veure el dashboard
</x-mail::button>
Salutacions,<br>
{{ config('app.name') }}
</x-mail::message>Notificacions a la base de dades#
El canal de base de dades emmagatzema les notificacions en una taula, cosa que permet mostrar-les dins de la pròpia aplicació. Això és el que fa possible tenir una campaneta de notificacions a la capçalera amb el comptador de notificacions no llegides, un patró que els usuaris coneixen de plataformes com GitHub, Facebook o Slack.
Abans d'utilitzar aquest canal, has de crear la taula de notificacions. Laravel inclou una comanda Artisan que genera la migració:
php artisan notifications:table
php artisan migrateAquesta migració crea una taula notifications amb columnes per a l'ID (UUID), el tipus de notificació, el model notificable (polimòrfic), les dades JSON, i les marques de temps de lectura i creació. L'ús d'UUIDs com a clau primària és deliberat perquè les notificacions poden generar-se des de múltiples processos en paral·lel (workers de cua) i els UUIDs eviten col·lisions.
El mètode toArray() o toDatabase() defineix les dades que es guarden a la columna JSON. Pots retornar qualsevol estructura de dades, però és recomanable incloure tota la informació que la interfície necessita per mostrar la notificació sense fer consultes addicionals:
public function toArray(object $notifiable): array
{
return [
'article_id' => $this->article->id,
'title' => $this->article->title,
'excerpt' => Str::limit($this->article->excerpt, 100),
'author_name' => $this->article->author->name,
'author_avatar' => $this->article->author->avatar_url,
'url' => route('articles.show', $this->article),
'type' => 'new_article',
];
}Fixa't que guardem dades com el nom de l'autor i l'URL directament a la notificació. Això és una bona pràctica perquè les notificacions són un registre històric: si l'autor canvia de nom o l'article s'elimina, la notificació ha de seguir mostrant la informació original. Si guardessis només l'ID de l'article i fessis una consulta, perdries la informació quan l'article s'elimini.
Si vols que el format per a la base de dades sigui diferent del que retorna toArray() (que també s'utilitza per al canal broadcast), pots definir un mètode toDatabase() separat:
public function toDatabase(object $notifiable): array
{
return [
'article_id' => $this->article->id,
'title' => $this->article->title,
'url' => route('articles.show', $this->article),
];
}
public function toArray(object $notifiable): array
{
// Utilitzat per broadcast, pot incloure més dades
return [
'article_id' => $this->article->id,
'title' => $this->article->title,
'excerpt' => $this->article->excerpt,
'author' => $this->article->author->only('id', 'name', 'avatar_url'),
'url' => route('articles.show', $this->article),
];
}Llegir i gestionar notificacions#
El trait Notifiable del model User proporciona relacions Eloquent per accedir a les notificacions. Això fa que treballar amb notificacions sigui tan natural com treballar amb qualsevol altra relació del model:
// Totes les notificacions (ordenades per data, més recents primer)
$notifications = $user->notifications;
// Només les no llegides
$unread = $user->unreadNotifications;
// Amb paginació (recomanat per a interfícies)
$notifications = $user->notifications()->paginate(20);
// Filtrar per tipus
$articleNotifications = $user->notifications()
->where('type', ArticlePublished::class)
->latest()
->take(10)
->get();
// Accedir a les dades de la notificació
foreach ($user->unreadNotifications as $notification) {
$data = $notification->data; // L'array retornat per toArray/toDatabase
echo $data['title'];
echo $data['url'];
}Marcar notificacions com a llegides#
Gestionar l'estat de lectura és essencial per a l'experiència d'usuari. Laravel ofereix mètodes senzills per marcar notificacions individualment o en grup:
// Marcar una notificació com a llegida
$notification->markAsRead();
// Marcar totes les no llegides com a llegides
$user->unreadNotifications->markAsRead();
// Marcar com a no llegida
$notification->markAsUnread();
// Verificar si ha estat llegida
if ($notification->read()) {
// ...
}
if ($notification->unread()) {
// ...
}Un cas d'ús habitual és marcar les notificacions com a llegides quan l'usuari fa clic per veure-les. Pots crear un endpoint que gestioni aquesta acció:
// routes/web.php
Route::post('/notifications/{notification}/read', function (DatabaseNotification $notification) {
abort_unless($notification->notifiable_id === auth()->id(), 403);
$notification->markAsRead();
return redirect($notification->data['url']);
})->middleware('auth')->name('notifications.read');Eliminar notificacions#
Les notificacions antigues poden acumular-se i ocupar espai a la base de dades. Laravel permet eliminar notificacions fàcilment, i és recomanable configurar una tasca programada que netegi les notificacions antigues periòdicament:
// Eliminar una notificació
$notification->delete();
// Eliminar totes les notificacions llegides de fa més de 30 dies
$user->notifications()
->whereNotNull('read_at')
->where('read_at', '<', now()->subDays(30))
->delete();
// Al Scheduler, per netejar automàticament
Schedule::call(function () {
DatabaseNotification::where('read_at', '<', now()->subDays(90))->delete();
})->daily();Notificacions a Slack#
Les notificacions a Slack són molt útils per a equips de desenvolupament i operacions: alertes d'errors, notificacions de desplegaments, informes de vendes, activitat d'usuaris i qualsevol event que l'equip necessiti conèixer en temps real. Laravel utilitza l'API de Slack Block Kit per construir missatges rics amb seccions, botons, imatges i molt més.
Per enviar notificacions a Slack, primer instal·la el paquet necessari:
composer require laravel/slack-notification-channelA continuació, configura el webhook URL de Slack. Pots fer-ho al model notificable definint el mètode routeNotificationForSlack():
// Al model User (o qualsevol model notificable)
public function routeNotificationForSlack(): string
{
return $this->slack_webhook_url;
}El mètode toSlack() construeix el missatge utilitzant l'objecte SlackMessage. Slack utilitza un sistema de blocs (Block Kit) que permet crear missatges estructurats amb capçaleres, seccions de text, imatges, botons i molt més:
use Illuminate\Notifications\Messages\SlackMessage;
public function toSlack(object $notifiable): SlackMessage
{
return (new SlackMessage)
->text("Nova comanda #{$this->order->number}") // Fallback per a notificacions
->headerBlock("Nova comanda rebuda")
->sectionBlock(function ($block) {
$block->text(
"*Client:* {$this->order->user->name}\n" .
"*Total:* {$this->order->formatted_total}\n" .
"*Productes:* {$this->order->items->count()}"
);
})
->dividerBlock()
->sectionBlock(function ($block) {
$block->text("*Productes:*\n" . $this->order->items->map(
fn ($item) => "- {$item->product->name} x{$item->quantity}"
)->join("\n"));
})
->actionsBlock(function ($block) {
$block->button('Veure comanda')
->url(route('admin.orders.show', $this->order))
->primary();
$block->button('Processar')
->url(route('admin.orders.process', $this->order));
});
}El mètode text() defineix el text de reserva que es mostra a les notificacions push i als clients de Slack que no suporten Block Kit. Els blocs de la resta del missatge defineixen l'estructura visual: headerBlock() per al títol destacat, sectionBlock() per a seccions de text (que suporten Markdown de Slack amb *negreta* i _cursiva_), dividerBlock() per a línies separadores, i actionsBlock() per a botons interactius.
Notificacions broadcast#
El canal broadcast envia notificacions en temps real al navegador de l'usuari a través de WebSockets. Això és el que permet que la campaneta de notificacions s'actualitzi instantàniament sense que l'usuari hagi de refrescar la pàgina. Per a que funcioni, necessites configurar un servei de broadcast com Laravel Reverb, Pusher o Ably.
El canal broadcast utilitza el mètode toArray() (o toBroadcast() si vols un format diferent) per serialitzar les dades de la notificació. Aquestes dades s'envien al frontend a través del WebSocket:
use Illuminate\Notifications\Messages\BroadcastMessage;
public function toBroadcast(object $notifiable): BroadcastMessage
{
return new BroadcastMessage([
'article_id' => $this->article->id,
'title' => $this->article->title,
'excerpt' => Str::limit($this->article->excerpt, 80),
'author' => [
'name' => $this->article->author->name,
'avatar' => $this->article->author->avatar_url,
],
'url' => route('articles.show', $this->article),
'time' => now()->toISOString(),
]);
}Al frontend (amb Laravel Echo), pots escoltar les notificacions en temps real i actualitzar la interfície dinàmicament. Això crea una experiència d'usuari molt més interactiva que les notificacions que només es mostren quan l'usuari refresca la pàgina:
// Amb Laravel Echo
Echo.private(`App.Models.User.${userId}`)
.notification((notification) => {
console.log(notification.type);
console.log(notification.title);
// Actualitzar el comptador de notificacions
updateNotificationBadge();
// Mostrar un toast o popup
showToast(notification.title, notification.excerpt);
});Si vols personalitzar el nom del canal broadcast, pots sobreescriure el mètode broadcastType() a la notificació:
public function broadcastType(): string
{
return 'article.published';
}Notificacions on-demand#
Les notificacions on-demand permeten enviar notificacions a destinataris que no són models de la teva aplicació. Per exemple, podries voler enviar un correu a una adreça que no està registrada al sistema, o publicar un missatge a un canal de Slack sense que estigui vinculat a cap usuari. Això és útil per a alertes del sistema, notificacions a equips externs o confirmacions a adreces de correu introduïdes manualment.
La facade Notification proporciona el mètode route() per crear un destinatari ad-hoc:
use Illuminate\Support\Facades\Notification;
// Enviar un correu a una adreça no registrada
Notification::route('mail', 'client@external.com')
->route('slack', 'https://hooks.slack.com/services/...')
->notify(new SystemAlert($alertData));
// Amb nom personalitzat per al correu
Notification::route('mail', [
'admin@external.com' => 'Administrador extern',
])
->notify(new WeeklyReport($reportData));Cada crida a route() especifica un canal i l'adreça de destinació per a aquell canal. Pots encadenar múltiples crides a route() per enviar la mateixa notificació per diversos canals alhora. Això és molt pràctic per a alertes del sistema que han d'arribar tant per correu com per Slack.
Notificacions a la cua#
Enviar notificacions pot ser una operació costosa, especialment quan s'envien per múltiples canals o a molts destinataris. Per això, és altament recomanable que totes les notificacions en producció implementin la interfície ShouldQueue. Això fa que les notificacions s'enviïn en segon pla a través del sistema de cues de Laravel, sense fer esperar l'usuari.
use Illuminate\Bus\Queueable;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Notifications\Notification;
class ArticlePublished extends Notification implements ShouldQueue
{
use Queueable;
public function __construct(
public Article $article
) {}
// ...
}Amb ShouldQueue, quan crides $user->notify(new ArticlePublished($article)), la notificació es posa a la cua i es processa en segon pla. Pots configurar la connexió, la cua i el retard de la mateixa manera que amb qualsevol altre job:
$user->notify(
(new ArticlePublished($article))
->onConnection('redis')
->onQueue('notifications')
->delay(now()->addMinutes(5))
);Controlar si una notificació s'ha d'enviar#
De vegades vols que una notificació es posi a la cua però que es decideixi en el moment de processar-la si realment s'ha d'enviar. Per exemple, si un usuari cancel·la la subscripció entre el moment que la notificació entra a la cua i el moment que es processa, no vols enviar-la. El mètode shouldSend() permet fer aquesta verificació:
public function shouldSend(object $notifiable, string $channel): bool
{
// No enviar si l'usuari ha desactivat les notificacions
if ($notifiable->notifications_disabled) {
return false;
}
// No enviar per mail si l'article ja ha estat llegit
if ($channel === 'mail' && $notifiable->hasRead($this->article)) {
return false;
}
return true;
}Canals personalitzats#
Quan els canals integrats no cobreixen les teves necessitats, pots crear canals personalitzats. Per exemple, podries voler enviar notificacions per Telegram, Discord o un sistema intern de missatgeria. Crear un canal personalitzat és senzill: només cal una classe amb un mètode send().
La classe del canal rep el model notificable i la notificació, i s'encarrega d'enviar el missatge:
namespace App\Channels;
use Illuminate\Notifications\Notification;
use Illuminate\Support\Facades\Http;
class TelegramChannel
{
public function send(object $notifiable, Notification $notification): void
{
$message = $notification->toTelegram($notifiable);
$chatId = $notifiable->routeNotificationFor('telegram');
if (! $chatId) {
return;
}
Http::post("https://api.telegram.org/bot" . config('services.telegram.bot_token') . "/sendMessage", [
'chat_id' => $chatId,
'text' => $message['text'],
'parse_mode' => $message['parse_mode'] ?? 'HTML',
]);
}
}Per utilitzar el canal, afegeix-lo al mètode via() de la notificació passant el nom complet de la classe:
use App\Channels\TelegramChannel;
public function via(object $notifiable): array
{
return ['mail', 'database', TelegramChannel::class];
}
public function toTelegram(object $notifiable): array
{
return [
'text' => "<b>Nou article:</b> {$this->article->title}\n\n{$this->article->excerpt}",
'parse_mode' => 'HTML',
];
}Al model notificable, defineix el mètode routeNotificationForTelegram() per indicar on enviar les notificacions d'aquest canal:
// Al model User
public function routeNotificationForTelegram(): ?string
{
return $this->telegram_chat_id;
}Events de notificacions#
Laravel dispara events durant el cicle de vida de les notificacions, cosa que et permet executar accions addicionals com registrar enviaments, monitoritzar errors o actualitzar estadístiques. Els dos events principals són NotificationSending (abans d'enviar, permet cancel·lar) i NotificationSent (després d'enviar amb èxit).
use Illuminate\Notifications\Events\NotificationSending;
use Illuminate\Notifications\Events\NotificationSent;
class NotificationEventSubscriber
{
public function handleSending(NotificationSending $event): void
{
$notifiable = $event->notifiable;
$notification = $event->notification;
$channel = $event->channel;
logger()->info('Enviant notificació', [
'type' => get_class($notification),
'channel' => $channel,
'notifiable' => $notifiable->id,
]);
}
public function handleSent(NotificationSent $event): void
{
logger()->info('Notificació enviada', [
'type' => get_class($event->notification),
'channel' => $event->channel,
'notifiable' => $event->notifiable->id,
'response' => $event->response,
]);
}
}L'event NotificationSending permet cancel·lar l'enviament retornant false. Això és diferent del mètode shouldSend() de la notificació: shouldSend() es defineix dins de la notificació mateixa, mentre que un listener de NotificationSending és global i s'aplica a totes les notificacions. Pots utilitzar listeners globals per implementar funcionalitats transversals com límits d'enviament, modes de silenci o registres d'auditoria.
Testing de notificacions#
Laravel proporciona eines potents per testejar notificacions als tests automatitzats. La facade Notification::fake() intercepta totes les notificacions i evita que s'enviïn realment, permetent-te verificar quines notificacions s'haurien enviat, a qui i amb quines dades:
use App\Notifications\ArticlePublished;
use Illuminate\Support\Facades\Notification;
test('notifica als subscriptors quan es publica un article', function () {
Notification::fake();
$author = User::factory()->create();
$subscribers = User::factory()->count(3)->create(['subscribed' => true]);
$article = Article::factory()->create(['user_id' => $author->id]);
// Executar l'acció que envia la notificació
$article->publish();
// Verificar que s'ha enviat als subscriptors
Notification::assertSentTo($subscribers, ArticlePublished::class);
// Verificar que no s'ha enviat a l'autor
Notification::assertNotSentTo($author, ArticlePublished::class);
// Verificar amb condicions
Notification::assertSentTo(
$subscribers[0],
ArticlePublished::class,
function ($notification, $channels) use ($article) {
return $notification->article->id === $article->id
&& in_array('mail', $channels)
&& in_array('database', $channels);
}
);
// Verificar el nombre total
Notification::assertSentToTimes($subscribers[0], ArticlePublished::class, 1);
// Verificar que no s'ha enviat cap altra notificació
Notification::assertNothingSentTo($author);
});La funció de callback dins assertSentTo rep la instància de la notificació i l'array de canals, cosa que et permet verificar tant el contingut de la notificació com els canals per on s'ha enviat. Això és molt útil per assegurar que la lògica del mètode via() funciona correctament en funció de les preferències de l'usuari.