> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Eventos y listeners

> Aprende a mantener los componentes de tu aplicación desacoplados con el sistema de eventos de Laravel.

## Qué son los eventos

El sistema de eventos de Laravel es una implementación sencilla del patrón observer.
Al despachar los sucesos (eventos) que ocurren en la aplicación y definir listeners que reaccionan a ellos, se reduce al mínimo la dependencia entre componentes.

Por ejemplo, al despachar el evento «pedido confirmado» pueden ejecutarse de forma independiente varios listeners: «enviar correo de confirmación», «descontar stock», «notificar a Slack», etc.
El código de procesamiento del pedido no necesita saber absolutamente nada de la implementación del envío del correo ni de la notificación a Slack.

<Info>
  Las clases de evento se ubican en `app/Events` y las de listener en `app/Listeners`.
  Si esos directorios no existen, los comandos Artisan los crean automáticamente.
</Info>

```mermaid theme={null}
flowchart TD
    A["Se despacha el evento<br>dispatch()"] --> B["Dispatcher de eventos"]
    B --> C["Listener 1<br>(síncrono)"]
    B --> D["Listener 2<br>(síncrono)"]
    B --> E["Listener 3<br>Implementa ShouldQueue"]
    C --> F["Ejecución inmediata"]
    D --> G["Ejecución inmediata"]
    E --> H["Se encola"]
    H --> I["Un worker lo ejecuta de forma asíncrona"]
```

## Generar eventos y listeners

Utiliza los comandos Artisan `make:event` y `make:listener` para generar el esqueleto.

```bash theme={null}
php artisan make:event UserRegistered

php artisan make:listener SendWelcomeEmail --event=UserRegistered
```

Sin argumentos, los comandos te preguntan de forma interactiva.

```bash theme={null}
php artisan make:event

php artisan make:listener
```

## Registro de eventos

### Descubrimiento automático de eventos

Por defecto, Laravel escanea el directorio `app/Listeners` y registra los listeners automáticamente.
Deduce la asociación con el evento inspeccionando el tipo del argumento del método `handle` o `__invoke`.

```php theme={null}
use App\Events\UserRegistered;

class SendWelcomeEmail
{
    public function handle(UserRegistered $event): void
    {
        // Enviar el correo de bienvenida
    }
}
```

Usando tipos union de PHP puedes escuchar varios eventos en el mismo método.

```php theme={null}
public function handle(UserRegistered|UserUpdated $event): void
{
    // ...
}
```

Si tus listeners están en otro directorio, indícalo en `bootstrap/app.php`.

```php theme={null}
->withEvents(discover: [
    __DIR__.'/../app/Domain/Orders/Listeners',
])
```

Puedes usar comodines para incluir varios directorios de una vez.

```php theme={null}
->withEvents(discover: [
    __DIR__.'/../app/Domain/*/Listeners',
])
```

Puedes ver la lista de listeners registrados con este comando:

```bash theme={null}
php artisan event:list
```

<Tip>
  En producción, cachear el manifiesto de listeners mejora el rendimiento.
  Al desplegar ejecuta `php artisan optimize` o `php artisan event:cache`.
  Para borrar la caché usa `php artisan event:clear`.
</Tip>

### Registro manual

También puedes registrar los listeners manualmente en el método `boot` de `AppServiceProvider` con el facade `Event`.

```php theme={null}
use App\Events\UserRegistered;
use App\Listeners\SendWelcomeEmail;
use Illuminate\Support\Facades\Event;

public function boot(): void
{
    Event::listen(
        UserRegistered::class,
        SendWelcomeEmail::class,
    );
}
```

También puedes registrarlos como closure.

```php theme={null}
use App\Events\UserRegistered;
use Illuminate\Support\Facades\Event;

public function boot(): void
{
    Event::listen(function (UserRegistered $event) {
        // ...
    });
}
```

## Definir el evento

Las clases de evento son contenedores de datos. No contienen lógica y guardan como propiedades la información asociada al evento.

```php theme={null}
<?php

namespace App\Events;

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

class UserRegistered
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public function __construct(
        public User $user,
    ) {}
}
```

El trait `SerializesModels` garantiza que los modelos Eloquent se manejen correctamente cuando los listeners encolados serialicen el evento.

## Despachar eventos

Despacha eventos con el método estático `dispatch` o con el helper `event()`.

```php theme={null}
use App\Events\UserRegistered;

// Método estático
UserRegistered::dispatch($user);

// Función auxiliar
event(new UserRegistered($user));
```

También hay métodos para despachar de forma condicional.

```php theme={null}
UserRegistered::dispatchIf($condition, $user);

UserRegistered::dispatchUnless($condition, $user);
```

### Despachar tras una transacción de base de datos

Cuando quieras despachar el evento solo después de que la transacción se haya confirmado, implementa la interfaz `ShouldDispatchAfterCommit` en la clase del evento.
Si la transacción falla, el evento se descarta.

```php theme={null}
<?php

namespace App\Events;

use App\Models\User;
use Illuminate\Broadcasting\InteractsWithSockets;
use Illuminate\Contracts\Events\ShouldDispatchAfterCommit;
use Illuminate\Foundation\Events\Dispatchable;
use Illuminate\Queue\SerializesModels;

class UserRegistered implements ShouldDispatchAfterCommit
{
    use Dispatchable, InteractsWithSockets, SerializesModels;

    public function __construct(
        public User $user,
    ) {}
}
```

## Implementar el listener

El listener recibe el evento en su método `handle`.
En el constructor, el contenedor de servicios inyecta automáticamente las dependencias.

```php theme={null}
<?php

namespace App\Listeners;

use App\Events\UserRegistered;
use App\Mail\WelcomeMail;
use Illuminate\Support\Facades\Mail;

class SendWelcomeEmail
{
    public function __construct() {}

    public function handle(UserRegistered $event): void
    {
        Mail::to($event->user->email)
            ->send(new WelcomeMail($event->user));
    }
}
```

Si el método `handle` devuelve `false`, se detiene la propagación del evento a los siguientes listeners.

## Listeners encolados

Los procesos lentos, como el envío de correos o las peticiones HTTP, pueden ejecutarse de forma asíncrona con listeners encolados.
Con solo implementar la interfaz `ShouldQueue`, el listener se encolará automáticamente al despachar el evento.

```php theme={null}
<?php

namespace App\Listeners;

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

class SendWelcomeEmail implements ShouldQueue
{
    public function handle(UserRegistered $event): void
    {
        // El worker de la cola lo ejecutará de forma asíncrona
    }
}
```

<Info>
  Antes de usar listeners encolados necesitas configurar la cola y arrancar el worker.
  Consulta la página de [colas y jobs](/es/queues) para más detalles.
</Info>

### Personalizar conexión, nombre de cola y retraso

Con atributos PHP puedes definir la conexión, la cola y el retraso.

```php theme={null}
<?php

namespace App\Listeners;

use App\Events\UserRegistered;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Queue\Attributes\Connection;
use Illuminate\Queue\Attributes\Delay;
use Illuminate\Queue\Attributes\Queue;

#[Connection('redis')]
#[Queue('emails')]
#[Delay(10)]
class SendWelcomeEmail implements ShouldQueue
{
    public function handle(UserRegistered $event): void
    {
        // ...
    }
}
```

También puedes calcular los valores dinámicamente mediante métodos.

```php theme={null}
public function viaConnection(): string
{
    return 'redis';
}

public function viaQueue(): string
{
    return 'emails';
}

public function withDelay(UserRegistered $event): int
{
    return 10;
}
```

### Reintentos y timeout máximos

Con los atributos `#[Tries]` y `#[Timeout]` puedes controlar el comportamiento ante fallos.

```php theme={null}
use Illuminate\Queue\Attributes\Tries;
use Illuminate\Queue\Attributes\Timeout;

#[Tries(3)]
#[Timeout(30)]
class SendWelcomeEmail implements ShouldQueue
{
    // ...
}
```

### Gestión de fallos

Si defines el método `failed`, puedes ejecutar código adicional cuando el listener agote los reintentos.

```php theme={null}
use Throwable;

public function failed(UserRegistered $event, Throwable $exception): void
{
    // Notificar al administrador, etc.
}
```

## Suscriptores de eventos

Los suscriptores permiten agrupar varios handlers relacionados en una única clase.

### Crear un suscriptor

En el método `subscribe` devuelves un array con la asignación entre eventos y handlers.

```php theme={null}
<?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
    {
        // Lógica de login
    }

    public function handleLogout(Logout $event): void
    {
        // Lógica de logout
    }

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

### Registrar el suscriptor

Con el descubrimiento automático activado, los suscriptores cuyo `subscribe` devuelve un array se registran automáticamente.
Para registrarlos manualmente utiliza `Event::subscribe` en el método `boot` de `AppServiceProvider`.

```php theme={null}
use App\Listeners\UserActivitySubscriber;
use Illuminate\Support\Facades\Event;

public function boot(): void
{
    Event::subscribe(UserActivitySubscriber::class);
}
```

## Ejemplo práctico: enviar un correo de bienvenida al registrarse un usuario

<Steps>
  <Step title="Crear la clase de evento">
    ```bash theme={null}
    php artisan make:event UserRegistered
    ```

    Edita `app/Events/UserRegistered.php` para añadir una propiedad con el usuario registrado.

    ```php theme={null}
    <?php

    namespace App\Events;

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

    class UserRegistered
    {
        use Dispatchable, InteractsWithSockets, SerializesModels;

        public function __construct(
            public User $user,
        ) {}
    }
    ```
  </Step>

  <Step title="Crear la clase de listener">
    ```bash theme={null}
    php artisan make:listener SendWelcomeEmail --event=UserRegistered
    ```

    Para enviar el correo de forma asíncrona en la cola, implementa `ShouldQueue`.

    ```php theme={null}
    <?php

    namespace App\Listeners;

    use App\Events\UserRegistered;
    use App\Mail\WelcomeMail;
    use Illuminate\Contracts\Queue\ShouldQueue;
    use Illuminate\Support\Facades\Mail;

    class SendWelcomeEmail implements ShouldQueue
    {
        public function handle(UserRegistered $event): void
        {
            Mail::to($event->user->email)
                ->send(new WelcomeMail($event->user));
        }
    }
    ```
  </Step>

  <Step title="Despachar el evento desde el controlador">
    Llama a `UserRegistered::dispatch()` después de crear el usuario.

    ```php theme={null}
    <?php

    namespace App\Http\Controllers\Auth;

    use App\Events\UserRegistered;
    use App\Models\User;
    use Illuminate\Http\RedirectResponse;
    use Illuminate\Http\Request;

    class RegisterController extends Controller
    {
        public function store(Request $request): RedirectResponse
        {
            $user = User::create($request->validated());

            UserRegistered::dispatch($user);

            return redirect('/dashboard');
        }
    }
    ```

    El `RegisterController` se limita a despachar el evento `UserRegistered` sin saber nada del envío del correo.
    Si en el futuro quisiéramos «enviar además una notificación a Slack al registrarse», no habría que tocar el controlador.
  </Step>

  <Step title="Arrancar el worker">
    Arranca el worker para procesar los listeners encolados.

    ```bash theme={null}
    php artisan queue:work
    ```
  </Step>
</Steps>

<Tip>
  Con el descubrimiento automático activado no hace falta registrar los listeners manualmente en `AppServiceProvider`.
  Se detectan automáticamente todos los que estén en `app/Listeners`.
</Tip>

<Warning>
  Con `php artisan event:list` puedes revisar los eventos y listeners registrados.
  Revisa periódicamente que no haya listeners inesperados en la lista.
</Warning>


## Related topics

- [Técnicas prácticas de Laravel Telescope](/es/blog/telescope-introduction.md)
- [Observers de Eloquent y eventos del modelo](/es/advanced/eloquent-observers.md)
- [FAQ de la nueva estructura de aplicación de Laravel 11 y posteriores](/es/advanced/app-structure-faq.md)
- [Eventos](/es/packages/laravel-copilot-sdk/events.md)
- [Laravel Reverb](/es/reverb.md)
