Documentación de Laravel 10.x
Aquí encontrarás fragmentos de código de Laravel y consejos útiles sobre desarrollo web.
En muchas aplicaciones web modernas, se utilizan WebSockets para implementar interfaces de usuario en tiempo real y actualizadas al instante. Cuando algunos datos se actualizan en el servidor, típicamente se envía un mensaje a través de una conexión WebSocket para ser manejado por el cliente. Los WebSockets proporcionan una alternativa más eficiente que estar consultando continuamente el servidor de tu aplicación en busca de cambios de datos que deben reflejarse en tu interfaz de usuario.
Por ejemplo, imagina que tu aplicación puede exportar los datos de un usuario a un archivo CSV y enviarlo por correo electrónico. Sin embargo, la creación de este archivo CSV lleva varios minutos, así que eliges crear y enviar el CSV dentro de un trabajo en cola. Cuando el CSV se haya creado y enviado por correo al usuario, podemos usar la difusión de eventos para despachar un evento App\Events\UserDataExported
que sea recibido por JavaScript de nuestra aplicación. Una vez que se recibe el evento, podemos mostrar un mensaje al usuario de que su CSV se le ha enviado por correo electrónico sin que nunca tenga que actualizar la página.
Para ayudarte a construir este tipo de características, Laravel facilita la "difusión" de tus eventos Laravel del lado del servidor sobre una conexión WebSocket. La difusión de tus eventos Laravel te permite compartir los mismos nombres y datos de eventos entre tu aplicación Laravel del lado del servidor y tu aplicación JavaScript del lado del cliente.
Los conceptos fundamentales detrás de la difusión son simples: los clientes se conectan a canales nombrados en el frontend, mientras que tu aplicación Laravel difunde eventos a estos canales en el backend. Estos eventos pueden contener cualquier dato adicional que desees poner a disposición del frontend.
De forma predeterminada, Laravel incluye dos controladores de difusión del lado del servidor entre los que puedes elegir: Pusher Channels y Ably. Sin embargo, los paquetes impulsados por la comunidad, como laravel-websockets y soketi, proporcionan controladores de difusión adicionales que no requieren proveedores comerciales de difusión.
Nota Antes de sumergirte en la difusión de eventos, asegúrate de haber leído la documentación de Laravel sobre eventos y listeners.
Para comenzar a usar la difusión de eventos de Laravel, necesitamos hacer algunas configuraciones dentro de la aplicación Laravel, así como instalar algunos paquetes.
La difusión de eventos se realiza mediante un controlador de difusión del lado del servidor que difunde tus eventos de Laravel para que Laravel Echo (una biblioteca de JavaScript) pueda recibirlos dentro del cliente del navegador. No te preocupes: repasaremos cada parte del proceso de instalación paso a paso.
Toda la configuración de difusión de eventos de tu aplicación está almacenada en el archivo de configuración config/broadcasting.php
. Laravel admite varios controladores de difusión de forma predeterminada: Pusher Channels, Redis y un controlador log
para desarrollo y depuración local. Además, se incluye un controlador null
que te permite desactivar por completo la difusión durante las pruebas. En el archivo de configuración config/broadcasting.php
se incluye un ejemplo de configuración para cada uno de estos controladores.
Antes de difundir eventos, primero deberás registrar el App\Providers\BroadcastServiceProvider
. En las nuevas aplicaciones de Laravel, solo necesitas descomentar este proveedor en el array providers
de tu archivo de configuración config/app.php
. Este BroadcastServiceProvider
contiene el código necesario para registrar las rutas y devoluciones de llamada de autorización de difusión.
También deberás configurar y ejecutar un trabajador de cola. Todas las difusiones de eventos se realizan mediante trabajos en cola para que el tiempo de respuesta de tu aplicación no se vea afectado seriamente por los eventos que se difunden.
Si planeas difundir tus eventos usando Pusher Channels, deberías instalar el SDK de PHP de Pusher Channels usando el administrador de paquetes Composer:
composer require pusher/pusher-php-server
A continuación, debes configurar tus credenciales de Pusher Channels en el archivo de configuración config/broadcasting.php
. Ya se incluye un ejemplo de configuración de Pusher Channels en este archivo, lo que te permite especificar rápidamente tu clave, secreto e ID de aplicación. Normalmente, estos valores deben establecerse a través de las variables de entorno PUSHER_APP_KEY
, PUSHER_APP_SECRET
y PUSHER_APP_ID
:
PUSHER_APP_ID=your-pusher-app-idPUSHER_APP_KEY=your-pusher-keyPUSHER_APP_SECRET=your-pusher-secretPUSHER_APP_CLUSTER=mt1
La configuración pusher
del archivo config/broadcasting.php
también te permite especificar options
adicionales que son compatibles con Channels, como el clúster.
A continuación, deberás cambiar el controlador de difusión a pusher
en tu archivo .env
:
BROADCAST_DRIVER=pusher
Finalmente, estás listo para instalar y configurar Laravel Echo, que recibirá los eventos de difusión en el lado del cliente.
Los paquetes laravel-websockets y soketi proporcionan servidores WebSocket compatibles con Pusher para Laravel. Estos paquetes te permiten aprovechar toda la potencia de la difusión de Laravel sin necesidad de un proveedor comercial de WebSocket. Para obtener más información sobre la instalación y el uso de estos paquetes, consulta nuestra documentación sobre alternativas de código abierto.
Nota La documentación a continuación discute cómo usar Ably en modo de "compatibilidad con Pusher". Sin embargo, el equipo de Ably recomienda y mantiene un emisor y cliente de Echo que puede aprovechar las capacidades únicas ofrecidas por Ably. Para obtener más información sobre el uso de los controladores mantenidos por Ably, consulta la documentación de Laravel broadcaster de Ably.
Si planeas difundir tus eventos usando Ably, debes instalar el SDK de PHP de Ably mediante el administrador de paquetes Composer:
composer require ably/ably-php
A continuación, debes configurar tus credenciales de Ably en el archivo de configuración config/broadcasting.php
. Un ejemplo de configuración de Ably ya está incluido en este archivo, lo que te permite especificar rápidamente tu clave. Típicamente, este valor debería establecerse a través de la variable de entorno ABLY_KEY
:
ABLY_KEY=your-ably-key
A continuación, deberás cambiar tu controlador de difusión a ably
en tu archivo .env
:
BROADCAST_DRIVER=ably
Finalmente, estás listo para instalar y configurar Laravel Echo, que recibirá los eventos de difusión en el lado del cliente.
El paquete laravel-websockets es un paquete puro de PHP compatible con Pusher para WebSocket para Laravel. Este paquete te permite aprovechar toda la potencia de la difusión de Laravel sin necesidad de un proveedor comercial de WebSocket. Para obtener más información sobre la instalación y el uso de este paquete, consulta su documentación oficial.
Soketi es un servidor WebSocket compatible con Pusher basado en Node para Laravel. En el fondo, Soketi utiliza µWebSockets.js para una escalabilidad y velocidad extremas. Este paquete te permite aprovechar toda la potencia de la difusión de Laravel sin necesidad de un proveedor comercial de WebSocket. Para obtener más información sobre la instalación y el uso de este paquete, consulta su documentación oficial.
Laravel Echo es una biblioteca de JavaScript que facilita la suscripción a canales y la escucha de eventos emitidos por tu controlador de difusión del lado del servidor. Puedes instalar Echo a través del administrador de paquetes NPM. En este ejemplo, también instalaremos el paquete pusher-js
ya que usaremos el emisor Pusher Channels:
npm install --save-dev laravel-echo pusher-js
Una vez que Echo esté instalado, estás listo para crear una nueva instancia de Echo en el JavaScript de tu aplicación. Un buen lugar para hacer esto es al final del archivo resources/js/bootstrap.js
que se incluye con el framework Laravel. Por defecto, este archivo ya incluye una configuración de ejemplo de Echo; simplemente necesitas descomentarla:
import Echo from 'laravel-echo';import Pusher from 'pusher-js'; window.Pusher = Pusher; window.Echo = new Echo({ broadcaster: 'pusher', key: import.meta.env.VITE_PUSHER_APP_KEY, cluster: import.meta.env.VITE_PUSHER_APP_CLUSTER, forceTLS: true});
Una vez que hayas descomentado y ajustado la configuración de Echo según tus necesidades, puedes compilar los activos de tu aplicación:
npm run dev
Nota Para obtener más información sobre cómo compilar los activos JavaScript de tu aplicación, consulta la documentación sobre Vite.
Si ya tienes una instancia de cliente Pusher Channels preconfigurada que te gustaría que Echo utilice, puedes pasarla a Echo a través de la opción de configuración client
:
import Echo from 'laravel-echo';import Pusher from 'pusher-js'; const options = { broadcaster: 'pusher', key: 'your-pusher-channels-key'} window.Echo = new Echo({ ...options, client: new Pusher(options.key, options)});
Nota La documentación a continuación discute cómo usar Ably en modo de "compatibilidad con Pusher". Sin embargo, el equipo de Ably recomienda y mantiene un emisor y cliente de Echo que puede aprovechar las capacidades únicas ofrecidas por Ably. Para obtener más información sobre el uso de los controladores mantenidos por Ably, consulta la documentación de Laravel broadcaster de Ably.
Laravel Echo es una biblioteca de JavaScript que facilita la suscripción a canales y la escucha de eventos emitidos por tu controlador de difusión del lado del servidor. Puedes instalar Echo a través del administrador de paquetes NPM. En este ejemplo, también instalaremos el paquete pusher-js
.
Puede que te preguntes por qué instalaríamos la biblioteca JavaScript pusher-js
aunque estamos utilizando Ably para difundir nuestros eventos. Afortunadamente, Ably incluye un modo de compatibilidad con Pusher que nos permite usar el protocolo Pusher al escuchar eventos en nuestra aplicación del lado del cliente:
npm install --save-dev laravel-echo pusher-js
Antes de continuar, debes habilitar el soporte del protocolo Pusher en la configuración de tu aplicación Ably. Puedes habilitar esta función dentro de la sección "Protocol Adapter Settings" de la pantalla de configuración de tu aplicación Ably.
Una vez que Echo esté instalado, estás listo para crear una nueva instancia de Echo en el JavaScript de tu aplicación. Un buen lugar para hacer esto es al final del archivo resources/js/bootstrap.js
que se incluye con el framework Laravel. Por defecto, este archivo ya incluye una configuración de ejemplo de Echo; sin embargo, la configuración predeterminada en el archivo bootstrap.js
está destinada a Pusher. Puedes copiar la configuración a continuación para transicionar tu configuración a Ably:
import Echo from 'laravel-echo';import Pusher from 'pusher-js'; window.Pusher = Pusher; window.Echo = new Echo({ broadcaster: 'pusher', key: import.meta.env.VITE_ABLY_PUBLIC_KEY, wsHost: 'realtime-pusher.ably.io', wsPort: 443, disableStats: true, encrypted: true,});
Ten en cuenta que nuestra configuración de Ably Echo hace referencia a una variable de entorno VITE_ABLY_PUBLIC_KEY
. El valor de esta variable debería ser tu clave pública de Ably. Tu clave pública es la parte de tu clave de Ably que ocurre antes del carácter :
.
Una vez que hayas descomentado y ajustado la configuración de Echo según tus necesidades, puedes compilar los activos de tu aplicación:
npm run dev
Nota Para obtener más información sobre cómo compilar los activos JavaScript de tu aplicación, consulta la documentación sobre Vite.
La difusión de eventos de Laravel te permite transmitir tus eventos Laravel del lado del servidor a tu aplicación JavaScript del lado del cliente utilizando un enfoque basado en controladores para WebSockets. Actualmente, Laravel incluye controladores para Pusher Channels y Ably. Los eventos se pueden consumir fácilmente en el lado del cliente utilizando el paquete JavaScript Laravel Echo.
Los eventos se transmiten a través de "canales", que pueden ser públicos o privados. Cualquier visitante de tu aplicación puede suscribirse a un canal público sin necesidad de autenticación o autorización; sin embargo, para suscribirse a un canal privado, un usuario debe estar autenticado y autorizado para escuchar en ese canal.
Nota Si deseas explorar alternativas de código abierto a Pusher, consulta las alternativas de código abierto.
Antes de sumergirnos en cada componente de la difusión de eventos, echemos un vistazo general utilizando como ejemplo una tienda de comercio electrónico.
En nuestra aplicación, supongamos que tenemos una página que permite a los usuarios ver el estado de envío de sus pedidos. También supongamos que se dispara un evento OrderShipmentStatusUpdated
cuando se procesa una actualización del estado de envío en la aplicación:
use App\Events\OrderShipmentStatusUpdated; OrderShipmentStatusUpdated::dispatch($order);
ShouldBroadcast
Cuando un usuario está viendo uno de sus pedidos, no queremos que tenga que actualizar la página para ver las actualizaciones de estado. En su lugar, queremos transmitir las actualizaciones a la aplicación a medida que se crean. Entonces, necesitamos marcar el evento OrderShipmentStatusUpdated
con la interfaz ShouldBroadcast
. Esto instruirá a Laravel a transmitir el evento cuando se dispare:
<?php namespace App\Events; use App\Models\Order;use Illuminate\Broadcasting\Channel;use Illuminate\Broadcasting\InteractsWithSockets;use Illuminate\Broadcasting\PresenceChannel;use Illuminate\Contracts\Broadcasting\ShouldBroadcast;use Illuminate\Queue\SerializesModels; class OrderShipmentStatusUpdated implements ShouldBroadcast{ /** * La instancia del pedido. * * @var \App\Order */ public $order;}
La interfaz ShouldBroadcast
requiere que nuestro evento defina un método broadcastOn
. Este método es responsable de devolver los canales en los que el evento debe transmitirse. Un esbozo vacío de este método ya está definido en las clases de eventos generadas, por lo que solo necesitamos completar sus detalles. Solo queremos que el creador del pedido pueda ver las actualizaciones de estado, así que transmitiremos el evento en un canal privado vinculado al pedido:
use Illuminate\Broadcasting\Channel;use Illuminate\Broadcasting\PrivateChannel; /** * Obtener el canal en el que se debe emitir el evento. */public function broadcastOn(): Channel{ return new PrivateChannel('orders.'.$this->order->id);}
Si deseas que el evento se transmita en varios canales, puedes devolver un array
en su lugar:
use Illuminate\Broadcasting\PrivateChannel; /** * Obtener los canales en los que se debe emitir el evento. * * @return array<int, \Illuminate\Broadcasting\Channel> */public function broadcastOn(): array{ return [ new PrivateChannel('orders.'.$this->order->id), // ... ];}
Recuerda, los usuarios deben estar autorizados para escuchar en canales privados. Podemos definir nuestras reglas de autorización de canal en el archivo routes/channels.php
de nuestra aplicación. En este ejemplo, necesitamos verificar que cualquier usuario que intente escuchar en el canal privado orders.1
sea realmente el creador del pedido:
use App\Models\Order;use App\Models\User; Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) { return $user->id === Order::findOrNew($orderId)->user_id;});
El método channel
acepta dos argumentos: el nombre del canal y un callback que devuelve true
o false
indicando si el usuario está autorizado para escuchar en el canal.
Todos los callbacks de autorización reciben al usuario autenticado actualmente como su primer argumento y cualquier parámetro de comodín adicional como sus argumentos subsiguientes. En este ejemplo, estamos usando el marcador de posición {orderId}
para indicar que la parte "ID" del nombre del canal es un comodín.
A continuación, lo único que queda es escuchar el evento en nuestra aplicación JavaScript. Podemos hacer esto usando Laravel Echo. Primero, usaremos el método private
para suscribirnos al canal privado. Luego, podemos usar el método listen
para escuchar el evento OrderShipmentStatusUpdated
. Por defecto, todas las propiedades públicas del evento se incluirán en el evento transmitido:
Echo.private(`orders.${orderId}`) .listen('OrderShipmentStatusUpdated', (e) => { console.log(e.order); });
Para informar a Laravel que un evento dado debe transmitirse, debes implementar la interfaz Illuminate\Contracts\Broadcasting\ShouldBroadcast
en la clase del evento. Esta interfaz ya está importada en todas las clases de eventos generadas por el framework, por lo que puedes agregarla fácilmente a cualquiera de tus eventos.
La interfaz ShouldBroadcast
requiere que implementes un solo método: broadcastOn
. El método broadcastOn
debe devolver un canal o un array de canales en los que el evento debe transmitirse. Los canales deben ser instancias de Channel
, PrivateChannel
o PresenceChannel
. Las instancias de Channel
representan canales públicos a los que cualquier usuario puede suscribirse, mientras que PrivateChannels
y PresenceChannels
representan canales privados que requieren autorización de canal:
<?php namespace App\Events; use App\Models\User;use Illuminate\Broadcasting\Channel;use Illuminate\Broadcasting\InteractsWithSockets;use Illuminate\Broadcasting\PresenceChannel;use Illuminate\Broadcasting\PrivateChannel;use Illuminate\Contracts\Broadcasting\ShouldBroadcast;use Illuminate\Queue\SerializesModels; class ServerCreated implements ShouldBroadcast{ use SerializesModels; /** * Crear una nueva instancia de evento. */ public function __construct( public User $user, ) {} /** * Obtener los canales en los que se debe emitir el evento. * * @return array<int, \Illuminate\Broadcasting\Channel> */ public function broadcastOn(): array { return [ new PrivateChannel('user.'.$this->user->id), ]; }}
Después de implementar la interfaz ShouldBroadcast
, solo necesitas disparar el evento como lo harías normalmente. Una vez que se haya disparado el evento, un trabajo en cola transmitirá automáticamente el evento usando tu controlador de difusión especificado.
De forma predeterminada, Laravel transmitirá el evento usando el nombre de clase del evento. Sin embargo, puedes personalizar el nombre de la transmisión definiendo un método broadcastAs
en el evento:
/** * El nombre de la emisión del evento. */public function broadcastAs(): string{ return 'server.created';}
Si personalizas el nombre de la transmisión usando el método broadcastAs
, asegúrate de registrar tu escucha con un carácter .
líder. Esto indicará a Echo que no debe agregar el espacio de nombres de la aplicación al evento:
.listen('.server.created', function (e) { ....});
Cuando se transmite un evento, todas sus propiedades public
se serializan automáticamente y se transmiten como la carga útil del evento, lo que te permite acceder a cualquiera de sus datos públicos desde tu aplicación JavaScript. Entonces, por ejemplo, si tu evento tiene una única propiedad pública $user
que contiene un modelo Eloquent, la carga útil del evento transmitido sería:
{ "user": { "id": 1, "name": "Patrick Stewart" ... }}
Sin embargo, si deseas tener un control más detallado sobre la carga útil de difusión, puedes agregar un método broadcastWith
a tu evento. Este método debe devolver el array de datos que deseas transmitir como la carga útil del evento:
/** * Obtener los datos para la emisión. * * @return array<string, mixed> */public function broadcastWith(): array{ return ['id' => $this->user->id];}
De forma predeterminada, cada evento de difusión se coloca en la cola predeterminada para la conexión de cola predeterminada especificada en tu archivo de configuración queue.php
. Puedes personalizar la conexión y el nombre de la cola que utiliza el controlador definiendo las propiedades connection
y queue
en la clase de tu evento:
/** * El nombre de la conexión de la cola que se utilizará al emitir el evento. * * @var string */public $connection = 'redis'; /** * El nombre de la cola en la que se colocará el trabajo de emisión. * * @var string */public $queue = 'default';
Alternativamente, puedes personalizar el nombre de la cola definiendo un método broadcastQueue
en tu evento:
/** * El nombre de la cola en la que se colocará el trabajo de emisión. */public function broadcastQueue(): string{ return 'default';}
Si deseas difundir tu evento utilizando la cola sync
en lugar del controlador de cola predeterminado, puedes implementar la interfaz ShouldBroadcastNow
en lugar de ShouldBroadcast
:
<?php use Illuminate\Contracts\Broadcasting\ShouldBroadcastNow; class OrderShipmentStatusUpdated implements ShouldBroadcastNow{ // ...}
A veces, deseas difundir tu evento solo si se cumple una condición determinada. Puedes definir estas condiciones agregando un método broadcastWhen
a la clase de tu evento:
/** * Determinar si este evento debe emitirse. */public function broadcastWhen(): bool{ return $this->order->value > 100;}
Cuando los eventos de difusión se despachan dentro de transacciones de base de datos, pueden procesarse por la cola antes de que la transacción de la base de datos se haya confirmado. Cuando esto sucede, las actualizaciones que hayas realizado en los modelos o registros de la base de datos durante la transacción de la base de datos pueden no reflejarse aún en la base de datos. Además, los modelos o registros de la base de datos creados dentro de la transacción pueden no existir en la base de datos. Si tu evento depende de estos modelos, pueden ocurrir errores inesperados cuando se procesa el trabajo que difunde el evento.
Si la opción de configuración after_commit
de tu conexión de cola está establecida en false
, aún puedes indicar que un evento de difusión en particular debe despacharse después de que se hayan confirmado todas las transacciones de la base de datos abiertas mediante la implementación de la interfaz ShouldDispatchAfterCommit
en la clase del evento:
<?php namespace App\Events; use Illuminate\Contracts\Broadcasting\ShouldBroadcast;use Illuminate\Contracts\Events\ShouldDispatchAfterCommit;use Illuminate\Queue\SerializesModels; class ServerCreated implements ShouldBroadcast, ShouldDispatchAfterCommit{ use SerializesModels;}
Nota Para obtener más información sobre cómo solucionar estos problemas, revisa la documentación sobre trabajos en cola y transacciones de base de datos.
Los canales privados requieren que autorices que el usuario actualmente autenticado realmente pueda escuchar en el canal. Esto se logra haciendo una solicitud HTTP a tu aplicación Laravel con el nombre del canal y permitiendo que tu aplicación determine si el usuario puede escuchar en ese canal. Al usar Laravel Echo, la solicitud HTTP para autorizar suscripciones a canales privados se realizará automáticamente; sin embargo, necesitas definir las rutas adecuadas para responder a estas solicitudes.
Afortunadamente, Laravel facilita la definición de las rutas para responder a las solicitudes de autorización de canal. En el App\Providers\BroadcastServiceProvider
incluido en tu aplicación Laravel, verás una llamada al método Broadcast::routes
. Este método registrará la ruta /broadcasting/auth
para manejar las solicitudes de autorización:
Broadcast::routes();
El método Broadcast::routes
colocará automáticamente sus rutas dentro del grupo de middleware web
; sin embargo, puedes pasar un array de atributos de ruta al método si deseas personalizar los atributos asignados:
Broadcast::routes($attributes);
De forma predeterminada, Echo usará el punto final /broadcasting/auth
para autorizar el acceso a los canales. Sin embargo, puedes especificar tu propio punto final de autorización pasando la opción de configuración authEndpoint
a tu instancia de Echo:
window.Echo = new Echo({ broadcaster: 'pusher', // ... authEndpoint: '/custom/endpoint/auth'});
Puedes personalizar la forma en que Laravel Echo realiza solicitudes de autorización proporcionando un autorizador personalizado al inicializar Echo:
window.Echo = new Echo({ // ... authorizer: (channel, options) => { return { authorize: (socketId, callback) => { axios.post('/api/broadcasting/auth', { socket_id: socketId, channel_name: channel.name }) .then(response => { callback(null, response.data); }) .catch(error => { callback(error); }); } }; },})
A continuación, necesitamos definir la lógica que determinará si el usuario actualmente autenticado puede escuchar en un canal dado. Esto se hace en el archivo routes/channels.php
que se incluye con tu aplicación. En este archivo, puedes usar el método Broadcast::channel
para registrar devoluciones de llamada de autorización de canal:
use App\Models\User; Broadcast::channel('orders.{orderId}', function (User $user, int $orderId) { return $user->id === Order::findOrNew($orderId)->user_id;});
El método channel
acepta dos argumentos: el nombre del canal y un callback que devuelve true
o false
indicando si el usuario está autorizado para escuchar en el canal.
Todos los callbacks de autorización reciben al usuario autenticado actualmente como su primer argumento y cualquier parámetro de comodín adicional como sus argumentos subsiguientes. En este ejemplo, estamos usando el marcador de posición {orderId}
para indicar que la parte "ID" del nombre del canal es un comodín.
Puedes ver una lista de las devoluciones de llamada de autorización de difusión de tu aplicación utilizando el comando Artisan channel:list
:
php artisan channel:list
Al igual que las rutas HTTP, las rutas de canal también pueden aprovechar el enlace de modelos implícito y explícito. Por ejemplo, en lugar de recibir un ID de pedido en forma de cadena o número, puedes solicitar una instancia real del modelo Order
:
use App\Models\Order;use App\Models\User; Broadcast::channel('orders.{order}', function (User $user, Order $order) { return $user->id === $order->user_id;});
Advertencia A diferencia del enlace de modelos de ruta HTTP, el enlace de modelos de canal no admite restricción automática de enlace de modelo implícita. Sin embargo, esto rara vez es un problema porque la mayoría de los canales se pueden limitar en función de la clave principal única de un solo modelo.
Los canales de difusión privados y de presencia autentican al usuario actual a través del guardia de autenticación predeterminado de tu aplicación. Si el usuario no está autenticado, la autorización del canal se deniega automáticamente y la devolución de llamada de autorización nunca se ejecuta. Sin embargo, puedes asignar varios guardias personalizados que deben autenticar la solicitud entrante si es necesario:
Broadcast::channel('channel', function () { // ...}, ['guards' => ['web', 'admin']]);
Si tu aplicación consume muchos canales diferentes, tu archivo routes/channels.php
podría volverse voluminoso. Entonces, en lugar de usar cierres para autorizar canales, puedes usar clases de canal. Para generar una clase de canal, utiliza el comando Artisan make:channel
. Este comando colocará una nueva clase de canal en el directorio App/Broadcasting
.
php artisan make:channel OrderChannel
A continuación, registra tu canal en tu archivo routes/channels.php
:
use App\Broadcasting\OrderChannel; Broadcast::channel('orders.{order}', OrderChannel::class);
Finalmente, puedes colocar la lógica de autorización para tu canal en el método join
de la clase del canal. Este método join
contendrá la misma lógica que normalmente habrías colocado en tu cierre de autorización de canal. También puedes aprovechar la unión de modelos de canal:
<?php namespace App\Broadcasting; use App\Models\Order;use App\Models\User; class OrderChannel{ /** * Crear una nueva instancia de canal. */ public function __construct() { // ... } /** * Autenticar el acceso del usuario al canal. */ public function join(User $user, Order $order): array|bool { return $user->id === $order->user_id; }}
Nota Al igual que muchas otras clases en Laravel, las clases de canal se resolverán automáticamente mediante el contenedor de servicios. Por lo tanto, puedes hacer type-hint de cualquier dependencia requerida por tu canal en su constructor.
Una vez que hayas definido un evento y lo hayas marcado con la interfaz ShouldBroadcast
, solo necesitas despachar el evento utilizando el método de despacho del evento. El despachador de eventos notará que el evento está marcado con la interfaz ShouldBroadcast
y encolará el evento para su difusión:
use App\Events\OrderShipmentStatusUpdated; OrderShipmentStatusUpdated::dispatch($order);
Al construir una aplicación que utiliza la difusión de eventos, ocasionalmente es posible que necesites difundir un evento a todos los suscriptores de un canal determinado excepto el usuario actual. Puedes lograr esto usando el ayudante broadcast
y el método toOthers
:
use App\Events\OrderShipmentStatusUpdated; broadcast(new OrderShipmentStatusUpdated($update))->toOthers();
Para entender mejor cuándo puedes querer usar el método toOthers
, imaginemos una aplicación de lista de tareas donde un usuario puede crear una nueva tarea ingresando un nombre de tarea. Para crear una tarea, tu aplicación podría realizar una solicitud a una URL /task
que difunde la creación de la tarea y devuelve una representación JSON de la nueva tarea. Cuando tu aplicación JavaScript recibe la respuesta del punto final, podría insertar directamente la nueva tarea en su lista de tareas de la siguiente manera:
axios.post('/task', task) .then((response) => { this.tasks.push(response.data); });
Sin embargo, recuerda que también difundimos la creación de la tarea. Si tu aplicación JavaScript también está escuchando este evento para agregar tareas a la lista de tareas, tendrás tareas duplicadas en tu lista: una del punto final y otra de la difusión. Puedes resolver esto usando el método toOthers
para instruir al difusor que no difunda el evento al usuario actual.
Advertencia Tu evento debe usar el rasgo
Illuminate\Broadcasting\InteractsWithSockets
para poder llamar al métodotoOthers
.
Cuando inicializas una instancia de Laravel Echo, se asigna un ID de socket a la conexión. Si estás utilizando una instancia global de Axios para realizar solicitudes HTTP desde tu aplicación JavaScript, el ID de socket se adjuntará automáticamente a cada solicitud saliente como encabezado X-Socket-ID
. Luego, cuando llamas al método toOthers
, Laravel extraerá el ID de socket del encabezado e instruirá al difusor que no difunda a ninguna conexión con ese ID de socket.
Si no estás utilizando una instancia global de Axios, deberás configurar manualmente tu aplicación JavaScript para enviar el encabezado X-Socket-ID
con todas las solicitudes salientes. Puedes recuperar el ID de socket utilizando el método Echo.socketId
:
var socketId = Echo.socketId();
Si tu aplicación interactúa con múltiples conexiones de difusión y deseas difundir un evento usando un difusor que no sea el predeterminado, puedes especificar a qué conexión enviar un evento mediante el método via
:
use App\Events\OrderShipmentStatusUpdated; broadcast(new OrderShipmentStatusUpdated($update))->via('pusher');
Alternativamente, puedes especificar la conexión de difusión del evento llamando al método broadcastVia
dentro del constructor del evento. Sin embargo, antes de hacerlo, debes asegurarte de que la clase del evento use el rasgo InteractsWithBroadcasting
:
<?php namespace App\Events; use Illuminate\Broadcasting\Channel;use Illuminate\Broadcasting\InteractsWithBroadcasting;use Illuminate\Broadcasting\InteractsWithSockets;use Illuminate\Broadcasting\PresenceChannel;use Illuminate\Broadcasting\PrivateChannel;use Illuminate\Contracts\Broadcasting\ShouldBroadcast;use Illuminate\Queue\SerializesModels; class OrderShipmentStatusUpdated implements ShouldBroadcast{ use InteractsWithBroadcasting; /** * Crear una nueva instancia de evento. */ public function __construct() { $this->broadcastVia('pusher'); }}
Una vez que hayas instalado e inicializado Laravel Echo, estarás listo para comenzar a escuchar eventos que se difunden desde tu aplicación Laravel. Primero, utiliza el método channel
para obtener una instancia de un canal, luego llama al método listen
para escuchar un evento específico:
Echo.channel(`orders.${this.order.id}`) .listen('OrderShipmentStatusUpdated', (e) => { console.log(e.order.name); });
Si deseas escuchar eventos en un canal privado, utiliza el método private
en su lugar. Puedes seguir encadenando llamadas al método listen
para escuchar varios eventos en un solo canal:
Echo.private(`orders.${this.order.id}`) .listen(/* ... */) .listen(/* ... */) .listen(/* ... */);
Si deseas dejar de escuchar un evento específico sin abandonar el canal, puedes usar el método stopListening
:
Echo.private(`orders.${this.order.id}`) .stopListening('OrderShipmentStatusUpdated')
Para abandonar un canal, puedes llamar al método leaveChannel
en tu instancia de Echo:
Echo.leaveChannel(`orders.${this.order.id}`);
Si deseas abandonar un canal y también sus canales privados y de presencia asociados, puedes llamar al método leave
:
Echo.leave(`orders.${this.order.id}`);
Puede que hayas notado en los ejemplos anteriores que no especificamos el espacio de nombres completo App\Events
para las clases de eventos. Esto se debe a que Echo asumirá automáticamente que los eventos están ubicados en el espacio de nombres App\Events
. Sin embargo, puedes configurar el espacio de nombres raíz al instanciar Echo pasando una opción de configuración namespace
:
window.Echo = new Echo({ broadcaster: 'pusher', // ... namespace: 'App.Other.Namespace'});
Alternativamente, puedes agregar un prefijo a las clases de eventos con un .
al suscribirte a ellas usando Echo. Esto te permitirá especificar siempre el nombre de clase completamente cualificado:
Echo.channel('orders') .listen('.Namespace\\Event\\Class', (e) => { // ... });
Los canales de presencia se construyen sobre la seguridad de los canales privados y, al mismo tiempo, ofrecen la característica adicional de tener conocimiento de quién está suscrito al canal. Esto facilita la creación de funciones potentes y colaborativas, como notificar a los usuarios cuando otro usuario está viendo la misma página o listar los habitantes de una sala de chat.
Todos los canales de presencia también son canales privados; por lo tanto, los usuarios deben estar autorizados para acceder a ellos. Sin embargo, al definir devoluciones de llamada de autorización para canales de presencia, no devolverás true
si el usuario está autorizado para unirse al canal. En su lugar, debes devolver un array de datos sobre el usuario.
Los datos devueltos por la devolución de llamada de autorización estarán disponibles para los escuchadores de eventos del canal de presencia en tu aplicación JavaScript. Si el usuario no está autorizado para unirse al canal de presencia, debes devolver false
o null
:
use App\Models\User; Broadcast::channel('chat.{roomId}', function (User $user, int $roomId) { if ($user->canJoinRoom($roomId)) { return ['id' => $user->id, 'name' => $user->name]; }});
Para unirte a un canal de presencia, puedes usar el método join
de Echo. El método join
devolverá una implementación de PresenceChannel
que, además de exponer el método listen
, te permite suscribirte a los eventos here
, joining
y leaving
.
Echo.join(`chat.${roomId}`) .here((users) => { // ... }) .joining((user) => { console.log(user.name); }) .leaving((user) => { console.log(user.name); }) .error((error) => { console.error(error); });
La devolución de llamada here
se ejecutará inmediatamente una vez que el canal se haya unido correctamente y recibirá un array que contiene la información del usuario para todos los demás usuarios actualmente suscritos al canal. El método joining
se ejecutará cuando un nuevo usuario se una a un canal, mientras que el método leaving
se ejecutará cuando un usuario abandone el canal. El método error
se ejecutará cuando el punto final de autenticación devuelva un código de estado HTTP que no sea 200 o si hay un problema al analizar el JSON devuelto.
Los canales de presencia pueden recibir eventos al igual que los canales públicos o privados. Usando el ejemplo de una sala de chat, es posible que deseemos difundir eventos NewMessage
al canal de presencia de la sala. Para hacerlo, devolveremos una instancia de PresenceChannel
desde el método broadcastOn
del evento:
/** * Obtener los canales en los que se debe emitir el evento. * * @return array<int, \Illuminate\Broadcasting\Channel> */public function broadcastOn(): array{ return [ new PresenceChannel('chat.'.$this->message->room_id), ];}
Al igual que con otros eventos, puedes usar el ayudante broadcast
y el método toOthers
para excluir al usuario actual de recibir la difusión:
broadcast(new NewMessage($message)); broadcast(new NewMessage($message))->toOthers();
Como es típico en otros tipos de eventos, puedes escuchar eventos enviados a canales de presencia usando el método listen
de Echo:
Echo.join(`chat.${roomId}`) .here(/* ... */) .joining(/* ... */) .leaving(/* ... */) .listen('NewMessage', (e) => { // ... });
Advertencia Antes de leer la siguiente documentación sobre la difusión de modelos, te recomendamos que te familiarices con los conceptos generales de los servicios de difusión de modelos de Laravel, así como con la forma de crear y escuchar manualmente eventos de difusión.
Es común difundir eventos cuando los modelos Eloquent de tu aplicación se crean, actualizan o eliminan. Por supuesto, esto se puede lograr fácilmente mediante la definición manual de eventos personalizados para cambios de estado en modelos Eloquent y marcándolos con la interfaz ShouldBroadcast
.
Sin embargo, si no estás utilizando estos eventos para ningún otro propósito en tu aplicación, puede ser engorroso crear clases de eventos con el único propósito de difundirlos. Para solucionar esto, Laravel te permite indicar que un modelo Eloquent debería difundir automáticamente sus cambios de estado.
Para empezar, tu modelo Eloquent debe utilizar el rasgo Illuminate\Database\Eloquent\BroadcastsEvents
. Además, el modelo debe definir un método broadcastOn
, que devolverá un array de canales en los que los eventos del modelo deben difundirse:
<?php namespace App\Models; use Illuminate\Broadcasting\Channel;use Illuminate\Broadcasting\PrivateChannel;use Illuminate\Database\Eloquent\BroadcastsEvents;use Illuminate\Database\Eloquent\Factories\HasFactory;use Illuminate\Database\Eloquent\Model;use Illuminate\Database\Eloquent\Relations\BelongsTo; class Post extends Model{ use BroadcastsEvents, HasFactory; /** * Obtener el usuario al que pertenece la publicación. */ public function user(): BelongsTo { return $this->belongsTo(User::class); } /** * Obtener los canales en los que se deben emitir eventos del modelo. * * @return array<int, \Illuminate\Broadcasting\Channel|\Illuminate\Database\Eloquent\Model> */ public function broadcastOn(string $event): array { return [$this, $this->user]; }}
Una vez que tu modelo incluye este rasgo y define sus canales de difusión, comenzará automáticamente a difundir eventos cuando se cree, actualice, elimine, archive o restaure una instancia del modelo.
Además, es posible que hayas notado que el método broadcastOn
del modelo recibe un argumento de cadena $event
. Este argumento contiene el tipo de evento que ha ocurrido en el modelo y tendrá un valor de created
, updated
, deleted
, trashed
o restored
. Al inspeccionar el valor de esta variable, puedes determinar a qué canales (si los hay) el modelo debe difundir para un evento en particular:
/** * Obtener los canales en los que se deben emitir eventos del modelo. * * @return array<string, array<int, \Illuminate\Broadcasting\Channel|\Illuminate\Database\Eloquent\Model>> */public function broadcastOn(string $event): array{ return match ($event) { 'deleted' => [], default => [$this, $this->user], };}
Ocasionalmente, es posible que desees personalizar cómo Laravel crea el evento de difusión de modelo subyacente. Puedes lograr esto definiendo un método newBroadcastableEvent
en tu modelo Eloquent. Este método debe devolver una instancia de Illuminate\Database\Eloquent\BroadcastableModelEventOccurred
:
use Illuminate\Database\Eloquent\BroadcastableModelEventOccurred; /** * Crear una nueva instancia de evento de modelo emitible para el modelo. */protected function newBroadcastableEvent(string $event): BroadcastableModelEventOccurred{ return (new BroadcastableModelEventOccurred( $this, $event ))->dontBroadcastToCurrentUser();}
Como habrás notado, el método broadcastOn
en el ejemplo del modelo no devolvió instancias de Channel
. En su lugar, se devolvieron directamente modelos Eloquent. Si el método broadcastOn
de tu modelo devuelve una instancia de modelo Eloquent (o está contenida en un array devuelto por el método), Laravel convertirá automáticamente la instancia en una instancia de canal privado para el modelo, utilizando el nombre de clase del modelo y el identificador de clave primaria del modelo como nombre del canal.
Así, una instancia de modelo App\Models\User
con un id
de 1
se convertiría en una instancia de Illuminate\Broadcasting\PrivateChannel
con un nombre de App.Models.User.1
. Por supuesto, además de devolver instancias de modelo Eloquent desde el método broadcastOn
de tu modelo, puedes devolver instancias completas de Channel
para tener un control total sobre los nombres de canal del modelo:
use Illuminate\Broadcasting\PrivateChannel; /** * Obtener los canales en los que se deben emitir eventos del modelo. * * @return array<int, \Illuminate\Broadcasting\Channel> */public function broadcastOn(string $event): array{ return [ new PrivateChannel('user.'.$this->id) ];}
Si planeas devolver explícitamente una instancia de canal desde el método broadcastOn
de tu modelo, puedes pasar una instancia de modelo Eloquent al constructor del canal. Al hacerlo, Laravel usará las convenciones de canal de modelo discutidas anteriormente para convertir el modelo Eloquent en una cadena de nombre de canal:
return [new Channel($this->user)];
Si necesitas determinar el nombre del canal de un modelo, puedes llamar al método broadcastChannel
en cualquier instancia de modelo. Por ejemplo, este método devuelve la cadena App.Models.User.1
para un modelo App\Models\User
con un id
de 1
:
$user->broadcastChannel()
Dado que los eventos de difusión del modelo no están asociados a un evento "real" dentro del directorio App\Events
de tu aplicación, se les asigna un nombre y una carga útil basados en convenciones. La convención de Laravel es emitir el evento usando el nombre de clase del modelo (sin incluir el espacio de nombres) y el nombre del evento del modelo que desencadenó la difusión.
Entonces, por ejemplo, una actualización del modelo App\Models\Post
emitiría un evento a tu aplicación del lado del cliente como PostUpdated
con la siguiente carga útil:
{ "model": { "id": 1, "title": "My first post" ... }, ... "socket": "someSocketId",}
La eliminación del modelo App\Models\User
emitiría un evento con el nombre UserDeleted
.
Si lo deseas, puedes definir un nombre de difusión personalizado y una carga útil agregando un método broadcastAs
y broadcastWith
a tu modelo. Estos métodos reciben el nombre del evento/operación del modelo que está ocurriendo, lo que te permite personalizar el nombre del evento y la carga útil para cada operación del modelo. Si se devuelve null
en el método broadcastAs
, Laravel usará las convenciones de nombre de evento de difusión de modelo discutidas anteriormente al emitir el evento:
/** * El nombre de la emisión del evento del modelo. */public function broadcastAs(string $event): string|null{ return match ($event) { 'created' => 'post.created', default => null, };} /** * Obtener los datos para la emisión del modelo. * * @return array<string, mixed> */public function broadcastWith(string $event): array{ return match ($event) { 'created' => ['title' => $this->title], default => ['model' => $this], };}
Una vez que hayas agregado el rasgo BroadcastsEvents
a tu modelo y hayas definido el método broadcastOn
de tu modelo, estás listo para comenzar a escuchar eventos de modelo difundidos en tu aplicación del lado del cliente. Antes de comenzar, es posible que desees consultar la documentación completa sobre escuchar eventos.
Primero, usa el método private
para recuperar una instancia de un canal, luego llama al método listen
para escuchar un evento específico. Típicamente, el nombre del canal dado al método private
debería corresponder a las convenciones de difusión de modelos de Laravel.
Una vez que hayas obtenido una instancia de canal, puedes usar el método listen
para escuchar un evento en particular. Dado que los eventos de difusión del modelo no están asociados a un evento "real" dentro del directorio App\Events
de tu aplicación, el nombre del evento debe tener un prefijo .
para indicar que no pertenece a un espacio de nombres en particular. Cada evento de difusión del modelo tiene una propiedad model
que contiene todas las propiedades que se pueden difundir del modelo:
Echo.private(`App.Models.User.${this.user.id}`) .listen('.PostUpdated', (e) => { console.log(e.model); });
Nota Cuando usas Pusher Channels, debes habilitar la opción "Client Events" en la sección "App Settings" de tu panel de aplicación para poder enviar eventos del cliente.
A veces, es posible que desees difundir un evento a otros clientes conectados sin afectar a tu aplicación Laravel en absoluto. Esto puede ser particularmente útil para cosas como notificaciones de "escritura", donde deseas alertar a los usuarios de tu aplicación que otro usuario está escribiendo un mensaje en una pantalla dada.
Para difundir eventos del cliente, puedes usar el método whisper
de Echo:
Echo.private(`chat.${roomId}`) .whisper('typing', { name: this.user.name });
Para escuchar eventos del cliente, puedes usar el método listenForWhisper
:
Echo.private(`chat.${roomId}`) .listenForWhisper('typing', (e) => { console.log(e.name); });
Al combinar la difusión de eventos con notificaciones, tu aplicación JavaScript puede recibir nuevas notificaciones a medida que ocurren sin necesidad de actualizar la página. Antes de comenzar, asegúrate de leer la documentación sobre el uso del canal de notificación de difusión.
Una vez que hayas configurado una notificación para usar el canal de difusión, puedes escuchar los eventos de difusión usando el método notification
de Echo. Recuerda, el nombre del canal debe coincidir con el nombre de la clase de la entidad que recibe las notificaciones:
Echo.private(`App.Models.User.${userId}`) .notification((notification) => { console.log(notification.type); });
En este ejemplo, todas las notificaciones enviadas a instancias de App\Models\User
a través del canal broadcast
serían recibidas por la devolución de llamada. Un callback de autorización de canal para el canal App.Models.User.{id}
está incluido en el BroadcastServiceProvider
predeterminado que se incluye con el framework Laravel.