Eventos y Listeners en Laravel 2026: Arquitectura Event-Driven y Preguntas de Entrevista

Domina los eventos y listeners de Laravel con el descubrimiento automático de Laravel 12, listeners asíncronos y patrones de arquitectura event-driven para entrevistas técnicas.

Eventos y listeners Laravel arquitectura event-driven

Los eventos y listeners de Laravel implementan el patrón Observer, desacoplando componentes al permitir que diferentes partes de una aplicación reaccionen a acciones sin dependencias directas. Cuando un usuario realiza un pedido, el evento OrderShipped se dispara, y listeners separados manejan las notificaciones, analítica y actualizaciones de inventario de manera independiente.

Descubrimiento Automático en Laravel 12

Laravel 12 descubre automáticamente los listeners en app/Listeners. Cualquier clase con un método handle o __invoke que tenga type hint de un evento se registra automáticamente, sin necesidad de archivo de configuración.

Cómo Funciona el Descubrimiento de Eventos en Laravel 12

Desde Laravel 11, el EventServiceProvider ya no existe. Laravel escanea el directorio Listeners y usa reflexión para determinar qué eventos maneja cada listener, basándose en el type hint de la firma del método handle.

app/Listeners/SendOrderConfirmation.phpphp
<?php

namespace App\Listeners;

use App\Events\OrderShipped;

class SendOrderConfirmation
{
    public function handle(OrderShipped $event): void
    {
        // Enviar email de confirmación a $event->order->user
    }
}

El listener anterior se registra automáticamente para eventos OrderShipped. No se requiere enlace manual en un service provider. Este enfoque reduce el código repetitivo y mantiene las relaciones evento-listener cerca del código que las implementa.

Para verificar los listeners registrados, ejecutar php artisan event:list. En producción, cachear el manifiesto con php artisan event:cache para evitar el escaneo del directorio en cada petición.

Creación de Eventos y Listeners con Artisan

Laravel proporciona comandos Artisan para generar eventos y listeners:

bash
# Generar una clase de evento
php artisan make:event OrderShipped

# Generar un listener vinculado a un evento
php artisan make:listener SendShipmentNotification --event=OrderShipped

La clase de evento generada usa tres traits: Dispatchable para el método estático dispatch(), InteractsWithSockets para broadcasting, y SerializesModels para serializar modelos Eloquent cuando los listeners están en cola.

app/Events/OrderShipped.phpphp
<?php

namespace App\Events;

use App\Models\Order;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Queue\SerializesModels;

class OrderShipped
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public function __construct(
        public Order $order,
    ) {}
}

La clase de evento es un contenedor de datos. Contiene el modelo Order y nada más. La lógica de negocio pertenece a los listeners, no a los eventos.

Dispatch de Eventos desde Controllers y Servicios

Despachar eventos usando el método estático dispatch() en la clase de evento:

app/Http/Controllers/OrderController.phpphp
<?php

namespace App\Http\Controllers;

use App\Events\OrderShipped;
use App\Models\Order;
use Illuminate\Http\Request;

class OrderController extends Controller
{
    public function ship(Request $request, Order $order)
    {
        // Lógica de envío aquí...
        
        OrderShipped::dispatch($order);
        
        return redirect()->route('orders.index');
    }
}

Para dispatch condicional, usar dispatchIf() o dispatchUnless():

php
// Solo despachar si el pedido tiene número de seguimiento
OrderShipped::dispatchIf($order->tracking_number !== null, $order);

¿Listo para aprobar tus entrevistas de Laravel?

Practica con nuestros simuladores interactivos, flashcards y tests técnicos.

Listeners Asíncronos para Operaciones Lentas

Los listeners que envían emails, llaman APIs externas o realizan cálculos pesados deben ejecutarse de manera asíncrona. Implementar ShouldQueue para poner el listener en la cola:

app/Listeners/SendShipmentNotification.phpphp
<?php

namespace App\Listeners;

use App\Events\OrderShipped;
use Illuminate\Contracts\Queue\ShouldQueue;

class SendShipmentNotification implements ShouldQueue
{
    public $queue = 'notifications';
    public $delay = 60; // Esperar 60 segundos antes de procesar

    public function handle(OrderShipped $event): void
    {
        // Enviar notificación a $event->order->user
    }
}

Personalizar la conexión y nombre de cola con las propiedades $connection y $queue, o usar métodos dinámicos:

php
public function viaConnection(): string
{
    return 'redis';
}

public function viaQueue(): string
{
    return $this->event->order->priority === 'high' 
        ? 'high-priority' 
        : 'default';
}

Cola Condicional con shouldQueue

A veces un listener solo debe ponerse en cola basándose en los datos del evento:

php
public function shouldQueue(OrderShipped $event): bool
{
    // Solo encolar para pedidos mayores a $100
    return $event->order->total >= 10000;
}

Transacciones de Base de Datos y Timing de Eventos

Los listeners en cola despachados dentro de una transacción pueden procesarse antes de que la transacción haga commit, causando errores de datos faltantes. Existen dos soluciones:

1. Configuración global en config/queue.php:

php
'connections' => [
    'redis' => [
        'after_commit' => true,
    ],
],

2. Por listener con ShouldQueueAfterCommit:

php
use Illuminate\Contracts\Queue\ShouldQueueAfterCommit;

class UpdateInventory implements ShouldQueueAfterCommit
{
    public function handle(OrderShipped $event): void
    {
        // Seguro de consultar el pedido, transacción commiteada
    }
}

Para los eventos mismos, implementar ShouldDispatchAfterCommit en la clase de evento:

php
use Illuminate\Contracts\Events\ShouldDispatchAfterCommit;

class OrderShipped implements ShouldDispatchAfterCommit
{
    // El evento solo se despacha después del commit de la transacción
}

Listeners Únicos para Evitar Duplicados

La interfaz ShouldBeUnique previene que listeners duplicados se encolen mientras otro está procesando:

php
use Illuminate\Contracts\Queue\ShouldBeUnique;
use Illuminate\Contracts\Queue\ShouldQueue;

class ProcessLicenseKey implements ShouldQueue, ShouldBeUnique
{
    public $uniqueFor = 3600; // El lock expira después de 1 hora

    public function uniqueId(LicensePurchased $event): string
    {
        return 'license:' . $event->license->id;
    }

    public function handle(LicensePurchased $event): void
    {
        // Generar y asignar clave de licencia
    }
}

Si el mismo ID de licencia dispara múltiples eventos rápidamente, solo el primer listener procesa. Los intentos subsiguientes encuentran el lock activo y omiten el encolado.

Event Subscribers para Eventos Relacionados

Cuando múltiples eventos relacionados comparten lógica de manejo común, los event subscribers agrupan handlers en una sola clase:

app/Listeners/UserActivitySubscriber.phpphp
<?php

namespace App\Listeners;

use Illuminate\Auth\Events\Login;
use Illuminate\Auth\Events\Logout;
use Illuminate\Events\Dispatcher;

class UserActivitySubscriber
{
    public function handleLogin(Login $event): void
    {
        // Registrar login exitoso, actualizar last_login
    }

    public function handleLogout(Logout $event): void
    {
        // Registrar logout, limpiar datos de sesión
    }

    public function subscribe(Dispatcher $events): array
    {
        return [
            Login::class => 'handleLogin',
            Logout::class => 'handleLogout',
        ];
    }
}

Los subscribers siguen las reglas de descubrimiento automático. Si los métodos handler están en la clase subscriber con parámetros correctamente tipados, Laravel los registra automáticamente.

Escuchar Múltiples Eventos con Union Types

Un solo listener puede responder a múltiples tipos de eventos usando union types de PHP 8:

php
public function handle(OrderShipped|OrderCancelled $event): void
{
    match (true) {
        $event instanceof OrderShipped => $this->notifyShipped($event),
        $event instanceof OrderCancelled => $this->notifyCancelled($event),
    };
}

Este patrón funciona bien para notificaciones que siguen lógica similar a través de diferentes tipos de eventos.

Diferir Eventos con Event::defer

Laravel 12 introdujo Event::defer() para retrasar el dispatch de eventos hasta que un bloque de código complete. Esto asegura que los listeners tengan acceso a todos los registros relacionados:

php
use Illuminate\Support\Facades\Event;

Event::defer(function () {
    $user = User::create(['name' => 'John']);
    $user->posts()->create(['title' => 'Welcome']);
});
// Los eventos para User y Post se despachan aquí

Si ocurre una excepción dentro del closure, los eventos diferidos nunca se despachan.

¡Empieza a practicar!

Pon a prueba tu conocimiento con nuestros simuladores de entrevista y tests técnicos.

Preguntas de Entrevista sobre Eventos y Listeners de Laravel

Las entrevistas técnicas frecuentemente evalúan la comprensión de arquitectura event-driven. Estas son preguntas que distinguen a desarrolladores Laravel experimentados:

P: ¿Cómo registra Laravel 12 los event listeners sin EventServiceProvider?

Laravel escanea app/Listeners usando reflexión. Lee el type hint en los métodos handle() o __invoke() para determinar a qué evento responde cada listener. Cachear el manifiesto en producción con php artisan event:cache.

P: ¿Cuándo debe un listener implementar ShouldQueue?

Cuando la operación toma más de unos pocos milisegundos: envío de emails, llamadas a APIs externas, generación de reportes, o procesamiento de archivos. Los listeners síncronos bloquean la respuesta HTTP.

P: ¿Qué problema resuelve ShouldQueueAfterCommit?

Los listeners en cola pueden procesarse antes de que la transacción de base de datos haga commit. Si el listener consulta un registro recién creado, falla porque los datos aún no están commiteados. ShouldQueueAfterCommit retrasa el dispatch hasta que la transacción complete.

P: ¿Cómo evitar el procesamiento duplicado para el mismo evento?

Implementar ShouldBeUnique. Definir uniqueId() para retornar una clave de caché basada en los datos del evento. El listener adquiere un lock atómico, y los dispatches duplicados se omiten si el lock está activo.

P: ¿Cuál es la diferencia entre un event listener y un event subscriber?

Un listener maneja un tipo de evento. Un subscriber es una clase que registra handlers para múltiples eventos relacionados en un solo lugar, útil para agrupar eventos de autenticación o registro de auditoría.

Qué Significan los Eventos de Laravel para Aplicaciones Escalables

  • Los eventos desacoplan componentes, permitiendo que funcionalidades como notificaciones y analítica evolucionen independientemente
  • Los listeners en cola descargan operaciones lentas, manteniendo las respuestas HTTP rápidas
  • ShouldQueueAfterCommit previene condiciones de carrera entre workers de cola y transacciones de base de datos
  • El descubrimiento automático en Laravel 12 elimina la sobrecarga de configuración, manteniendo la lógica de listeners cerca de la implementación
  • Los event subscribers consolidan el manejo de eventos relacionados, reduciendo la dispersión de archivos para patrones comunes como el seguimiento de actividad de usuario
  • El módulo /technologies/laravel/interview-questions/events-listeners en SharpSkill cubre estos patrones con preguntas interactivas
Reto diario

¿Sabrías detectar el bug en Laravel?

Un fragmento real, un bug oculto, un intento al día. Sin cuenta para probar.

Anthony Fillion-Maillet

Escrito por

Anthony Fillion-Maillet

Fundador de SharpSkill

Desarrollador fullstack desde hace más de 10 años. Dirige SharpSkill y responde por todo lo que se publica aquí.

Actualizado el 20 de agosto de 2026

Etiquetas

#laravel
#events
#listeners
#php
#event-driven
#queues

Compartir

Artículos relacionados