Cargando…

Esto está tardando más de lo esperado.

Volver al centro de ayuda

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() Asunto y saludo del correo, título del push, title en la fila de la base de datos
description() 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ás mail cuando send_mail es verdadero, más NotificationChannels\WebPush\WebPushChannel cuando el usuario tiene filas en push_subscriptions.
  • toMail() construye un MailMessage con subject() como asunto y saludo, description() como única línea, y un botón de acción con la etiqueta de button() apuntando a url() cuando la hay. Usa la plantilla de correo estándar de Laravel.
  • toArray() guarda title, description, image, icon (inbox), color (bg-primary-700), url y target (null) en la columna data de la tabla notifications.
  • toWebPush() construye un WebPushMessage con título, cuerpo e ícono, y pone url() en data.url, que el service worker abre al tocar.

Dos cosas que la clase base no hace:

  • No encola. Usa Illuminate\Bus\Queueable pero no implementa ShouldQueue, así que el correo y las peticiones push salen durante la petición que llamó a notify(). Agrega implements ShouldQueue a 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