Skip to main content
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 alla manutenzione a lungo termine di un pacchetto. Per il caricamento dei provider consulta il package auto-discovery, e per l’ambiente di test consulta Testare pacchetti Laravel.

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.
Il listener riceve il logger nel costruttore e gestisce l’evento in handle().
Nel provider si registrano i nomi di classe dell’evento e del listener.
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().
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 distinto.

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. 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. 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 e le code 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. È 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.
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().

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.
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, verifichiamo che il logger venga chiamato una sola volta.
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.
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.

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.
  • 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

Ultima modifica il 13 ottobre 2026