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

# Eventi e listener

> Come mantenere i componenti dell'applicazione debolmente accoppiati usando il sistema di eventi di Laravel.

## Cosa sono gli eventi

Il sistema di eventi di Laravel è una semplice implementazione del pattern Observer.
Emettendo eventi che rappresentano cose accadute nell'applicazione e definendo listener che vi reagiscono, riduci al minimo le dipendenze tra i componenti.

Ad esempio, quando emetti l'evento "ordine confermato", più listener come "invia email di conferma", "riduci lo stock" e "notifica Slack" operano indipendentemente.
Il codice dell'ordine non deve sapere nulla di email o notifiche Slack.

<Info>
  Le classi evento vanno in `app/Events`, i listener in `app/Listeners`.
  Se queste directory non esistono, i comandi Artisan le creano automaticamente.
</Info>

```mermaid theme={null}
flowchart TD
    A["Emissione evento<br>dispatch()"] --> B["Event dispatcher"]
    B --> C["Listener 1<br>(sincrono)"]
    B --> D["Listener 2<br>(sincrono)"]
    B --> E["Listener 3<br>implementa ShouldQueue"]
    C --> F["Esecuzione immediata"]
    D --> G["Esecuzione immediata"]
    E --> H["Messo in coda"]
    H --> I["Esecuzione asincrona dal worker"]
```

## Creazione di eventi e listener

Con `make:event` e `make:listener` generi gli scheletri.

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

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

Senza argomenti vengono chiesti interattivamente.

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

php artisan make:listener
```

## Registrazione degli eventi

### Event discovery (rilevamento automatico)

Di default Laravel scansiona la directory `app/Listeners` e registra i listener automaticamente.
Deduce la mappatura evento-listener dal tipo dell'argomento del metodo `handle` o `__invoke`.

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

class SendWelcomeEmail
{
    public function handle(UserRegistered $event): void
    {
        // Invia l'email di benvenuto
    }
}
```

Con i tipi union di PHP puoi ricevere più eventi in un unico metodo.

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

Per posizionare i listener in altre directory, indica ulteriori path di scansione in `bootstrap/app.php`.

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

Con i wildcard puoi indicare più directory.

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

Elenca i listener registrati con:

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

<Tip>
  In produzione conviene cachare il manifest dei listener.
  Al deploy esegui `php artisan optimize` o `php artisan event:cache`.
  Per svuotare la cache usa `php artisan event:clear`.
</Tip>

### Registrazione manuale

Puoi registrare manualmente con la facade `Event` in `boot` di `AppServiceProvider`.

```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,
    );
}
```

Puoi anche usare una closure.

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

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

## Definizione degli eventi

Le classi evento sono contenitori di dati: non hanno logica, mantengono solo informazioni sotto forma di proprietà.

```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,
    ) {}
}
```

Il trait `SerializesModels` gestisce correttamente la serializzazione dei modelli Eloquent quando i listener in coda serializzano l'evento.

## Emissione degli eventi

Emetti con il metodo statico `dispatch` o con l'helper `event()`.

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

// Metodo statico
UserRegistered::dispatch($user);

// Helper
event(new UserRegistered($user));
```

Ci sono metodi condizionali.

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

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

### Emissione dopo il commit della transazione

Se vuoi emettere l'evento solo dopo il commit di una transazione, implementa l'interfaccia `ShouldDispatchAfterCommit` sull'evento.
Se la transazione fallisce l'evento viene scartato.

```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,
    ) {}
}
```

## Implementazione dei listener

Il listener riceve l'evento nel metodo `handle`.
Nel costruttore, il service container inietta automaticamente le dipendenze.

```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));
    }
}
```

Restituendo `false` dal metodo `handle` interrompi la propagazione ai listener successivi.

## Listener in coda

Le operazioni lente (invio email, chiamate HTTP) possono essere eseguite in modo asincrono come listener in coda.
Implementa l'interfaccia `ShouldQueue` e all'emissione dell'evento il listener finisce automaticamente in coda.

```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
    {
        // Elaborazione asincrona dal worker
    }
}
```

<Info>
  Prima di usare listener in coda serve la configurazione delle code e l'avvio del worker.
  Consulta [Code e job](/it/queues).
</Info>

### Personalizzare connection, nome coda e delay

Con gli attribute PHP puoi impostare connection, nome coda e delay.

```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
    {
        // ...
    }
}
```

Puoi anche determinare i valori dinamicamente con metodi.

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

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

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

### Numero massimo di tentativi e timeout

Con `#[Tries]` e `#[Timeout]` controlli il comportamento in caso di fallimento.

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

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

### Gestione dei fallimenti

Definendo il metodo `failed` puoi gestire il post-fallimento dopo il numero massimo di tentativi.

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

public function failed(UserRegistered $event, Throwable $exception): void
{
    // Notifica amministratori, ecc.
}
```

## Event subscriber

Un subscriber raccoglie in un'unica classe più handler correlati.

### Creazione del subscriber

Nel metodo `subscribe` restituisci un array che mappa eventi a handler.

```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
    {
        // Gestione login
    }

    public function handleLogout(Logout $event): void
    {
        // Gestione logout
    }

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

### Registrazione del subscriber

Con l'event discovery attivo, i subscriber che restituiscono un array da `subscribe` vengono registrati automaticamente.
Per registrarlo manualmente usa `Event::subscribe` in `boot` di `AppServiceProvider`.

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

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

## Esempio pratico: invio di email di benvenuto alla registrazione

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

    Modifica `app/Events/UserRegistered.php` per mantenere l'utente registrato.

    ```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="Crea la classe listener">
    ```bash theme={null}
    php artisan make:listener SendWelcomeEmail --event=UserRegistered
    ```

    Per gestire l'invio email in coda 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="Emetti l'evento nel controller">
    Dopo la registrazione dell'utente, invoca `UserRegistered::dispatch()`.

    ```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');
        }
    }
    ```

    `RegisterController` emette solo l'evento `UserRegistered` senza sapere nulla dell'invio email.
    In futuro, aggiungere anche una notifica Slack non richiederà modifiche al controller.
  </Step>

  <Step title="Avvia il worker">
    Avvia il worker per elaborare i listener in coda.

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

<Tip>
  Con l'event discovery attivo non serve registrazione manuale in `AppServiceProvider`.
  I listener in `app/Listeners` vengono rilevati automaticamente.
</Tip>

<Warning>
  Con `php artisan event:list` verifichi l'elenco di eventi e listener registrati.
  Controllalo periodicamente per assicurarti che non siano registrati listener inaspettati.
</Warning>


## Related topics

- [Tecniche pratiche di Laravel Telescope](/it/blog/telescope-introduction.md)
- [FAQ sulla nuova struttura dell'app da Laravel 11](/it/advanced/app-structure-faq.md)
- [Eventi](/it/packages/laravel-copilot-sdk/events.md)
- [Eloquent Observers ed eventi del modello](/it/advanced/eloquent-observers.md)
- [Laravel Reverb](/it/reverb.md)
