vendor al descubrimiento automático, reduces las suposiciones necesarias durante la instalación, la actualización y la generación de la caché.
Esta página revisa la implementación de Laravel Framework v13.36.0 y aplica las operaciones básicas de los eventos al mantenimiento a largo plazo de un paquete. Para la carga de providers, consulta Descubrimiento de paquetes; para el entorno de pruebas, consulta Pruebas de paquetes.
Registrar explícitamente los listeners del paquete
Las tareas que el paquete ejecuta de forma predeterminada pueden registrarse conEvent::listen() en el método boot() de un Illuminate\Support\ServiceProvider normal. El destino predeterminado del descubrimiento automático de listeners de Laravel es app/Listeners de la aplicación; no recorre automáticamente el directorio src/Listeners del paquete.
Como ejemplo, crearemos un paquete que registra en el log los eventos de entrega completada. El evento es simplemente una clase que contiene los valores necesarios para la notificación.
handle().
extra.laravel.providers de Composer o un registro explícito por parte del usuario. Si el evento se necesita tanto en el procesamiento HTTP como en los workers de colas, no limites el registro con runningInConsole().
El registro y la creación de la instancia ocurren en momentos distintos
Dispatcher::listen() añade el listener a un array interno para el nombre de evento normal. Cuando se pasa un nombre de clase como en el ejemplo anterior, en este momento no se crea la instancia del listener.
En los listeners síncronos, después de disparar el evento, createClassCallable() resuelve la clase mediante el make() del contenedor. Por eso puedes utilizar la inyección de dependencias en el constructor.
Esta ruta no realiza un call() del contenedor sobre handle(). Diseña el listener de modo que los servicios arbitrarios se inyecten en el constructor y handle() reciba el evento. No supongas que funciona igual que la inyección de dependencias en handle() de los comandos Artisan.
Lo descrito aquí se refiere a los listeners síncronos. Si implementas ShouldQueue, se utiliza una ruta distinta para las colas, así que revisa también el procesamiento de eventos en cola y las colas en cuanto a transacciones, reintentos y resolución de dependencias.
event:cache no impide el registro explícito
event:cache no es un comando que serialice en bloque todos los listeners registrados en el Dispatcher de eventos.
EventCacheCommand::getEvents() enumera Illuminate\Foundation\Support\Providers\EventServiceProvider y sus providers derivados, y guarda el resultado del descubrimiento automático y el mapeo de listens() de cada provider. Las llamadas a Event::listen() escritas en el ServiceProvider::boot() normal no forman parte de esta recopilación.
Por otro lado, incluso en un arranque con caché se ejecuta el boot() de los providers normales. No estar incluido en la caché no es lo mismo que no registrarse cuando existe la caché.
También es posible usar
$listen en un paquete que herede del EventServiceProvider de Foundation. Sin embargo, en la implementación revisada, el valor predeterminado de shouldDiscoverEvents() es una comprobación sobre la propia clase base. Heredar de ella no hace que el directorio de listeners del paquete se descubra automáticamente.
Además, $subscribe y $observers de ese mismo provider se procesan aparte del bucle que registra el resultado de getEvents(). No concluyas, fijándote solo en la caché de $listen, que también se omiten estos registros.
Evitar el doble registro con el descubrimiento automático
Si el usuario añade el directorio de listeners del paquete conwithEvents(discover: [...]) y el provider del paquete también registra explícitamente los mismos listeners, el proceso puede ejecutarse varias veces para un mismo evento.
Dispatcher::listen() añade elementos al array para los eventos normales, y el Dispatcher en su conjunto no elimina automáticamente los nombres de clase duplicados. El array_unique() que el EventServiceProvider de Foundation aplica dentro de su propio mapeo tampoco unifica los registros explícitos realizados por otros providers.
Asigna la responsabilidad del registro a una sola parte.
- Si el paquete registra los listeners estándar, no pidas al usuario que añada ese directorio al descubrimiento automático.
- Si el usuario elige los listeners que necesita, el paquete proporciona las clases de evento y de listener, y no registra esos mismos listeners de forma predeterminada.
- Al migrar del descubrimiento automático al registro explícito, indica que también deben actualizarse la configuración de descubrimiento del usuario y las cachés de eventos antiguas.
Verificar el comportamiento antes y después de actualizar
Para comprobar el estado del registro, ejecutaevent:list en una aplicación de verificación que tenga instalado el paquete. Confirma que el listener existe tanto sin caché como con ella.
event:list por sí sola no garantiza que el listener complete su proceso ni que se ejecute una sola vez. Añade también pruebas que disparen el evento realmente.
Como ejemplo, después de registrar CourierServiceProvider en el TestCase base de Testbench, verificamos que el logger se llama exactamente una vez.
Event::fake() es útil para comprobar que se dispara un evento, pero normalmente impide la ejecución de los listeners reales. En esta prueba no se utiliza porque se verifican el registro y el procesamiento. Comprueba también la combinación con el descubrimiento automático y el arranque con caché en otra aplicación de verificación o en una nueva instancia de la aplicación.
Lista de comprobación para el mantenimiento a largo plazo
- Documenta los nombres de las clases de evento, sus propiedades públicas y la política de registro de listeners como un contrato con los usuarios.
- Cuando cambies el nombre o el payload de los eventos a los que se suscriben los usuarios, comunica el cambio siguiendo la gestión de compatibilidad de versiones.
- En las actualizaciones que modifiquen el descubrimiento automático o
$listen, incluye la regeneración deevent:cacheen el procedimiento de despliegue. - Distingue los listeners estándar del paquete de los listeners propios del usuario, y no provoques que el mismo listener estándar se registre dos veces.
- Mantén, para cada versión compatible de PHP y Laravel, pruebas que verifiquen que el listener se ejecuta una sola vez.