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

# Registrazione e cache dei listener di eventi di un pacchetto

> Analisi dell'implementazione della registrazione degli eventi e di event:cache in Laravel 13: registrazione esplicita nei pacchetti, doppia registrazione con il rilevamento automatico, risoluzione delle dipendenze e metodi di verifica durante gli aggiornamenti.

Quando vuoi offrire nel pacchetto punti di estensione basati sugli eventi, progetta separatamente **la fornitura delle classi evento** e **la registrazione dei listener**. Una configurazione che funziona senza che l'utente debba aggiungere al rilevamento automatico directory all'interno di `vendor` riduce i presupposti richiesti durante l'installazione, l'aggiornamento e la creazione della cache.

Questa pagina esamina l'implementazione di Laravel Framework `v13.36.0` e applica le operazioni di base sugli [eventi](/it/events) alla manutenzione a lungo termine di un pacchetto. Per il caricamento dei provider consulta il [package auto-discovery](/it/advanced/package-discovery), e per l'ambiente di test consulta [Testare pacchetti Laravel](/it/advanced/package-testing).

## Registrare esplicitamente i listener del pacchetto

Le operazioni che il pacchetto esegue di default possono essere registrate con `Event::listen()` nel metodo `boot()` di un normale `Illuminate\Support\ServiceProvider`. La destinazione predefinita del rilevamento automatico dei listener di Laravel è la directory `app/Listeners` dell'applicazione: la directory `src/Listeners` all'interno del pacchetto non viene esaminata automaticamente.

Come esempio, creiamo un pacchetto che registra nel log un evento di consegna completata. L'evento è semplicemente una classe che contiene i valori necessari alla notifica.

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

namespace Acme\Courier\Events;

final class ShipmentDelivered
{
    public function __construct(public readonly string $shipmentId)
    {
    }
}
```

Il listener riceve il logger nel costruttore e gestisce l'evento in `handle()`.

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

namespace Acme\Courier\Listeners;

use Acme\Courier\Events\ShipmentDelivered;
use Psr\Log\LoggerInterface;

final class LogDeliveredShipment
{
    public function __construct(private readonly LoggerInterface $logger)
    {
    }

    public function handle(ShipmentDelivered $event): void
    {
        $this->logger->info('Shipment delivered', [
            'shipment_id' => $event->shipmentId,
        ]);
    }
}
```

Nel provider si registrano i nomi di classe dell'evento e del listener.

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

namespace Acme\Courier;

use Acme\Courier\Events\ShipmentDelivered;
use Acme\Courier\Listeners\LogDeliveredShipment;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\ServiceProvider;

class CourierServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        Event::listen(
            ShipmentDelivered::class,
            LogDeliveredShipment::class,
        );
    }
}
```

Questa configurazione presuppone che il provider stesso venga caricato. Prevedi `extra.laravel.providers` di Composer oppure una registrazione esplicita da parte dell'utente. Se l'evento è necessario sia nelle richieste HTTP sia nei queue worker, non limitare la registrazione con `runningInConsole()`.

<Warning>
  Non implementare `DeferrableProvider` in questo provider solo per la registrazione dei listener. Se i servizi differiti non vengono risolti, nemmeno i listener vengono registrati. Se necessario, separa i binding dei servizi pesanti in un [provider differito](/it/advanced/deferred-provider) distinto.
</Warning>

## Registrazione e creazione dell'istanza avvengono in momenti diversi

`Dispatcher::listen()` aggiunge il listener a un array interno per il nome di evento normale. Quando si passa un nome di classe come sopra, in questo momento l'istanza del listener non viene creata.

Per i listener sincroni, dopo l'emissione dell'evento `createClassCallable()` risolve la classe tramite `make()` del container. Per questo motivo è possibile usare la dependency injection nel costruttore.

```mermaid theme={null}
flowchart TD
    A["boot() del provider"] --> B["Salvataggio del nome di classe con Event::listen()"]
    B --> C["Emissione di ShipmentDelivered"]
    C --> D["Il Dispatcher seleziona i listener"]
    D --> E["Risoluzione del listener sincrono tramite il container"]
    E --> F["Passaggio dell'evento a handle()"]
```

Questo percorso non esegue `call()` del container su `handle()`. Progetta il listener in modo che **i servizi necessari vengano iniettati nel costruttore e `handle()` riceva l'evento**. Non considerarlo lo stesso meccanismo della [dependency injection in `handle()` dei comandi Artisan](/it/advanced/package-commands).

Qui si descrivono i listener sincroni. Se implementi `ShouldQueue` si segue un percorso separato per la coda, quindi consulta anche [la gestione in coda degli eventi](/it/events) e le [code](/it/queues) per transazioni, nuovi tentativi e risoluzione delle dipendenze.

## event:cache non blocca la registrazione esplicita

`event:cache` non è un comando che serializza in blocco tutti i listener registrati nel Dispatcher degli eventi.

`EventCacheCommand::getEvents()` elenca `Illuminate\Foundation\Support\Providers\EventServiceProvider` e i provider da esso derivati, e salva i risultati del rilevamento automatico e la mappatura di `listens()` di ciascun provider. Gli `Event::listen()` scritti nel `ServiceProvider::boot()` di un provider normale non rientrano in questa raccolta.

D'altra parte, anche quando la cache esiste, il `boot()` dei provider normali viene comunque eseguito. **Non essere inclusi nella cache e non essere registrati quando la cache esiste sono due cose diverse.**

| Metodo di registrazione | Contenuto salvato da `event:cache` | All'avvio con cache |
| - | - | - |
| `Event::listen()` nel `boot()` di un provider normale | Questa registrazione non viene raccolta | Viene registrata di nuovo in `boot()` |
| `$listen` dell'`EventServiceProvider` di Foundation | Mappatura salvata per nome di classe del provider | `getEvents()` legge la cache |
| Listener individuati dal rilevamento automatico | Risultati del rilevamento salvati nella mappatura | Si usa la cache invece di riesaminare le directory |

È possibile anche usare `$listen` in un pacchetto che estende l'`EventServiceProvider` di Foundation. Tuttavia, nell'implementazione esaminata, il valore predefinito di `shouldDiscoverEvents()` è una verifica riferita alla classe base stessa. **Estendere la classe non basta perché la directory dei listener del pacchetto venga rilevata automaticamente.**

Inoltre, `$subscribe` e `$observers` dello stesso provider vengono elaborati separatamente dal ciclo che registra i risultati di `getEvents()`. Non concludere, guardando solo la cache di `$listen`, che anche queste registrazioni vengano saltate.

<Tip>
  `Event::subscribe(SomeSubscriber::class)` crea il subscriber tramite il container al momento della registrazione e chiama `subscribe()`. Non è differito come `Event::listen()` con un nome di classe. Evita effetti collaterali come comunicazioni esterne nel costruttore del subscriber e in `subscribe()`.
</Tip>

## Evitare la doppia registrazione con il rilevamento automatico

Se l'utente aggiunge la directory dei listener del pacchetto con `withEvents(discover: [...])` e anche il provider del pacchetto registra esplicitamente gli stessi listener, l'elaborazione può essere eseguita più volte per un singolo evento.

Per gli eventi normali, `Dispatcher::listen()` aggiunge elementi a un array e non elimina automaticamente i nomi di classe duplicati a livello dell'intero Dispatcher. Anche `array_unique()`, eseguito dall'`EventServiceProvider` di Foundation all'interno della propria mappatura, non è un meccanismo che unifica le registrazioni esplicite effettuate da altri provider.

Assegna la responsabilità della registrazione a una sola parte.

* Se **il pacchetto registra i listener predefiniti**, non chiedere all'utente di aggiungere quella directory al rilevamento automatico.
* Se **l'utente sceglie i listener necessari**, il pacchetto fornisce le classi evento e listener e non registra di default gli stessi listener.
* Quando passi dal rilevamento automatico alla registrazione esplicita, indica all'utente di aggiornare anche la configurazione del rilevamento e la vecchia cache degli eventi.

Anche l'idempotenza della logica di business è importante, ma invece di nascondere la doppia registrazione affidandoti solo all'idempotenza, riordina i percorsi di registrazione.

## Verificare il comportamento prima e dopo l'aggiornamento

Per controllare lo stato delle registrazioni, esegui `event:list` in un'applicazione di verifica in cui è installato il pacchetto. Verifica che il listener interessato sia presente sia senza cache sia con la cache.

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

Questa è una procedura per l'app di verifica. Il solo output di `event:list` non garantisce che il listener completi l'elaborazione né che venga eseguito una sola volta. Aggiungi anche test che emettono realmente l'evento.

Come esempio, dopo aver registrato `CourierServiceProvider` nel [TestCase di base di Testbench](/it/advanced/package-testing), verifichiamo che il logger venga chiamato una sola volta.

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

namespace Acme\Courier\Tests\Feature;

use Acme\Courier\Events\ShipmentDelivered;
use Acme\Courier\Tests\TestCase;
use Illuminate\Support\Facades\Event;
use Psr\Log\LoggerInterface;

class PackageEventsTest extends TestCase
{
    public function test_delivery_is_logged_once(): void
    {
        $logger = $this->createMock(LoggerInterface::class);
        $logger->expects($this->once())
            ->method('info')
            ->with('Shipment delivered', ['shipment_id' => 'shipment-123']);

        $this->app->instance(LoggerInterface::class, $logger);

        Event::dispatch(new ShipmentDelivered('shipment-123'));
    }
}
```

`Event::fake()` è utile per verificare l'emissione degli eventi, ma normalmente impedisce l'esecuzione dei listener reali. In questo test non viene usato perché si verificano registrazione ed elaborazione. Verifica anche la combinazione con il rilevamento automatico e l'avvio con cache in un'app di verifica separata e in una nuova istanza dell'applicazione.

<Warning>
  Eseguire `event:cache` dopo l'avvio non ricostruisce dalla cache il Dispatcher già registrato in quel processo. Verifica il comportamento con cache in un nuovo avvio. Per applicare i listener aggiornati ai worker di lunga durata è necessario anche un riavvio secondo la normale procedura di deploy.
</Warning>

## Checklist per la manutenzione a lungo termine

* Documenta i nomi delle classi evento, le proprietà pubbliche e la politica di registrazione dei listener come contratto verso gli utenti.
* Quando modifichi il nome o il payload di eventi a cui gli utenti si iscrivono, comunica le modifiche secondo la [gestione della compatibilità tra versioni](/it/advanced/package-versioning).
* Negli aggiornamenti che modificano il rilevamento automatico o `$listen`, includi la ricreazione di `event:cache` nella procedura di deploy.
* Distingui i listener predefiniti del pacchetto dai listener personalizzati dell'utente ed evita che lo stesso listener predefinito venga registrato due volte.
* Mantieni, per ogni versione di PHP e Laravel supportata, test che verifichino che il listener venga eseguito una sola volta.

## Fonti primarie consultate

* [Documentazione ufficiale di Laravel: registrazione, rilevamento automatico, cache e subscriber degli eventi](https://github.com/laravel/docs/blob/73e61270287e4aaa1cecd77cd66aa6ef4f499828/events.md)
* [Dispatcher: listen, subscribe, createClassCallable](https://github.com/laravel/framework/blob/v13.36.0/src/Illuminate/Events/Dispatcher.php)
* [Foundation EventServiceProvider: register, getEvents, shouldDiscoverEvents](https://github.com/laravel/framework/blob/v13.36.0/src/Illuminate/Foundation/Support/Providers/EventServiceProvider.php)
* [EventCacheCommand: raccolta degli elementi da mettere in cache](https://github.com/laravel/framework/blob/v13.36.0/src/Illuminate/Foundation/Console/EventCacheCommand.php)


## Related topics

- [Argomenti avanzati](/it/advanced/index.md)
- [Registrazione e cache delle route di un pacchetto](/it/advanced/package-routes.md)
- [Eventi e listener](/it/events.md)
- [Registrazione dei comandi Artisan di un pacchetto e risoluzione delle dipendenze](/it/advanced/package-commands.md)
- [Sovrascrivere e aggiornare le view di un pacchetto](/it/advanced/package-views.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.