Envía notificaciones
Una clase, tres canales, y el usuario decide cuáles de ellos le llegan.
Esta guía cubre cómo se escribe una notificación, cómo decide el kit qué canales la llevan y dónde la lee el usuario. La necesitas la primera vez que tu producto tiene que avisarle a alguien que algo pasó.
Los tres canales
Toda notificación es una notificación de Laravel que extiende App\Notifications\Notification. Esa clase base le pregunta al destinatario qué canales usar, y el destinatario responde desde App\Traits\UserBase::channelNotifications():
| Canal | Cuándo se usa | Quién lo controla | Dónde llega |
|---|---|---|---|
database |
Siempre | Nadie: siempre está encendido | La campana de la barra superior y el centro de notificaciones |
mail |
Cuando el usuario tiene encendidas las notificaciones por correo | El usuario, desde su perfil (columna send_mail de users) |
Su bandeja de entrada, por el mailer por omisión |
| Web push | Cuando el usuario tiene al menos un navegador suscrito | El usuario, al permitir push en un navegador | Una notificación del sistema en ese dispositivo, con el sitio abierto o no |
Nada de lo que configures cambia esta lista. Una notificación que envías con $user->notify(...) va a la base de datos siempre, al correo si el usuario quiere correo, y a cada navegador que haya suscrito. La mitad de web push necesita la PWA y las llaves VAPID, que cubre /help/web-push-and-the-pwa; sin ellas el tercer canal simplemente nunca entra en la lista porque nadie puede suscribirse.
Hay un cuarto canal, App\Channels\SmsChannel, que envía a través de App\Services\SnsService y se usa sólo para el código de verificación telefónica. No forma parte de la clase base y ninguna preferencia del usuario lo alcanza.
Crea una notificación
El proyecto trae su propio stub, así que el generador te da una clase que ya extiende la base:
php artisan make:notification Orders/OrderShipped
Llena los tres métodos y, cuando la notificación apunte a algún lugar, el cuarto:
<?php
namespace App\Notifications\Orders;
use App\Models\Order;
use App\Notifications\Notification;
class OrderShipped extends Notification
{
public function __construct(public Order $order) {}
protected function subject(): string
{
return __('Your order :number is on its way', ['number' => $this->order->number]);
}
protected function description(): string
{
return __('It left our warehouse today. Track it from your account.');
}
protected function image(): string
{
return $this->order->product->photo;
}
protected function url(): ?string
{
return route('app.orders.show', $this->order);
}
}
Envíala como cualquier notificación de Laravel:
$order->user->notify(new OrderShipped($order));
| Método | Obligatorio | Qué alimenta |
|---|---|---|
subject() |
Sí | Asunto y saludo del correo, título del push, title en la fila de la base de datos |
description() |
Sí | Línea del cuerpo del correo, cuerpo del push, description en la fila |
image() |
Recomendado | Avatar que se muestra en la campana y en el centro, ícono del push. Por omisión: el avatar del usuario con sesión, o config('app.icon') cuando nadie tiene sesión |
url() |
Opcional | Enlace de la fila en la campana y en el centro, botón "Ver más" del correo, URL que se abre al tocar el push. Por omisión null: sin enlace en ningún lado |
button() |
Opcional | Etiqueta del botón del correo. Por omisión __('View more') |
Escribe cada texto a través de __(); el kit traduce lang/*.json a partir del inglés.
Cuida image(). El valor por omisión lee auth()->user(), lo cual es correcto cuando una persona con sesión provoca una notificación sobre sí misma, y está mal cuando la envía un job en cola o un comando: ahí no hay usuario con sesión, así que cae al ícono de la aplicación. Pasa el modelo en el constructor y devuelve su imagen, como hace el ejemplo.
Lo que la clase base hace por ti
Nunca escribes via(), toMail(), toArray() ni toWebPush() para el caso común. La clase base los construye todos a partir de tus cuatro métodos:
via()devuelve$notifiable->channelNotifications():database, másmailcuandosend_mailes verdadero, másNotificationChannels\WebPush\WebPushChannelcuando el usuario tiene filas enpush_subscriptions.toMail()construye unMailMessageconsubject()como asunto y saludo,description()como única línea, y un botón de acción con la etiqueta debutton()apuntando aurl()cuando la hay. Usa la plantilla de correo estándar de Laravel.toArray()guardatitle,description,image,icon(inbox),color(bg-primary-700),urlytarget(null) en la columnadatade la tablanotifications.toWebPush()construye unWebPushMessagecon título, cuerpo e ícono, y poneurl()endata.url, que el service worker abre al tocar.
Dos cosas que la clase base no hace:
- No encola. Usa
Illuminate\Bus\Queueablepero no implementaShouldQueue, así que el correo y las peticiones push salen durante la petición que llamó anotify(). Agregaimplements ShouldQueuea tu propia clase cuando una notificación se envía a muchas personas o desde una pantalla que debe seguir rápida, y asegúrate de que un worker esté corriendo (/help/queues-and-scheduled-work). - No elige idioma. Laravel renderiza la notificación en el idioma de la aplicación en el momento de enviarla. Para enviar en el idioma del destinatario, llama a
$user->notify((new OrderShipped($order))->locale($user->locale)).
Ajusta una notificación
Sobrescribe lo que necesites y deja el resto a la clase base.
Sólo algunos canales, sin importar lo que prefiera el usuario:
public function via(object $notifiable): array
{
return ['mail'];
}
El kit hace esto en CreatedUserNotification (sólo correo: un correo de bienvenida con la contraseña no tiene nada que hacer en la campana) y en TestWebPushNotification (sólo push: existe para probar que una suscripción del navegador funciona).
Un correo más completo, conservando el asunto:
public function toMail(object $notifiable): MailMessage
{
return parent::toMail($notifiable)
->line(__('Your tracking number is :number.', ['number' => $this->order->tracking]));
}
O una plantilla Markdown propia, como hacen RoleUpdateNotification y AnnouncementNotification:
public function toMail(object $notifiable): MailMessage
{
return (new MailMessage)
->subject($this->subject())
->markdown('emails.orders.shipped', ['order' => $this->order]);
}
Lo que nunca cambia: subject() y description() se definen en toda notificación, porque todos los canales los leen, e image() se define siempre que el valor por omisión sería incorrecto. No edites App\Notifications\Notification para tratar de forma especial una notificación. La base existe para que agregar un canal sea un cambio ahí y otro en channelNotifications(), y toda notificación del proyecto lo gane.
Lo que ve el usuario
La campana de la barra superior es el componente Livewire App\Livewire\Auth\Notifications\Notification. Muestra una insignia roja con el conteo de no leídas (+9 pasando de nueve), un desplegable con las últimas diez notificaciones agrupadas por día, cada una con su imagen, ícono, título y tiempo relativo, y un enlace "Marcar como leído" que marca todas las no leídas. Hacer clic en una fila la marca como leída y sigue su url.
"Ver todas las notificaciones" abre el centro de notificaciones en /account/notifications (ruta auth.notifications.center, componente App\Livewire\Auth\Notifications\Index): una lista paginada de veinte por página, con las filas no leídas resaltadas, cada una con título, descripción y fecha. User::notifications() ordena primero las no leídas y luego las más recientes. El resto del área de la cuenta se describe en /help/your-account-area.
Una notificación sin url() se renderiza como un enlace a #: la fila se lee pero no lleva a ningún lado. La image guardada en la fila se redimensiona con el helper de miniaturas del proyecto al mostrarse; cuando está vacía se usa el ícono de la aplicación.
Las preferencias del usuario
La página de perfil (/account) tiene una tarjeta "Notificaciones" con un solo interruptor, "Notificación por correo electrónico", ligado a la columna send_mail. Los usuarios nuevos empiezan con él encendido. No hay un interruptor aparte para la campana: el canal de base de datos siempre está encendido para que un usuario que apaga el correo siga encontrando todo en el centro.
Push no es una columna de preferencia. Un usuario está "suscrito" cuando su navegador registró una suscripción, y la enciende o apaga por navegador desde la misma tarjeta (el interruptor aparece sólo cuando la PWA y las llaves VAPID están configuradas). La migración 2026_06_01_180000_drop_send_webpush_from_users_table eliminó la vieja columna send_webpush por esa razón.
Qué mailer envía el correo
El canal mail usa el mailer por omisión, MAIL_MAILER en .env. .env.example trae MAIL_MAILER=failover, y el mailer failover definido en config/mail.php intenta primero mailgun y después ses, así que un correo de notificación no se pierde cuando un proveedor está caído. El remitente es MAIL_FROM_ADDRESS y MAIL_FROM_NAME. La lista mail_fallbacks de config/auth.php es un mecanismo distinto: aplica sólo al código de verificación de correo, no a las notificaciones. Los transportes, sus llaves y el proveedor de SMS están en /help/email-and-sms-delivery.
Cada correo enviado por el canal mail, y cada SMS, lo registran App\Listeners\LogNotificationSent y App\Listeners\LogNotificationFailed en la tabla communication_logs, con destinatario, clase de la notificación, asunto y resultado. Los administradores los leen en /admin/communication_logs; revisa /help/audit-trails-and-webhook-events.
Notificaciones que el kit ya envía
| Clase | Canales | Cuándo |
|---|---|---|
App\Notifications\User\CreatedUserNotification |
Sólo correo | Un administrador crea un usuario desde el panel de administración; incluye el correo de inicio de sesión y la contraseña cuando se definió una |
App\Notifications\User\RoleUpdateNotification |
Los canales del usuario | Un administrador cambia los roles de un usuario |
App\Notifications\Announcements\AnnouncementNotification |
Correo cuando el aviso tiene "enviar correo" y el usuario quiere correo; push cuando está suscrito; nunca la campana | Se publica un aviso (/help/announcements) |
App\Notifications\TestWebPushNotification |
Sólo push | El usuario pulsa "Prueba" junto al interruptor de push en su perfil |
App\Notifications\Auth\VerificationCodeNotification |
Sólo SMS | Se solicita un código de verificación telefónica |