Skip to main content
Wil je in je package uitbreidingspunten aanbieden op basis van events, ontwerp dan het aanbieden van eventklassen en het registreren van listeners als afzonderlijke onderdelen. Een opzet die werkt zonder dat gebruikers mappen binnen vendor aan de automatische detectie hoeven toe te voegen, vermindert de aannames bij installatie, updates en het aanmaken van de cache. Deze pagina bekijkt de implementatie van Laravel Framework v13.36.0 en past de basisbewerkingen van Events toe op het onderhoud van een package op lange termijn. Voor het laden van providers zie je Package discovery, en voor de testomgeving Packages testen.

Listeners van een package expliciet registreren

Verwerking die je package standaard uitvoert, kun je registreren met Event::listen() in de boot() van een gewone Illuminate\Support\ServiceProvider. De standaardlocatie voor automatische detectie van listeners in Laravel is app/Listeners van de applicatie; src/Listeners binnen een package wordt niet automatisch doorzocht. Als voorbeeld maken we een package dat een event voor een afgeronde levering logt. Het event is een klasse die alleen de waarden bevat die nodig zijn voor de melding.
De listener ontvangt een logger in de constructor en verwerkt het event in handle().
In de provider registreer je de klassenamen van het event en de listener.
Deze opzet gaat ervan uit dat de provider zelf wordt geladen. Zorg voor extra.laravel.providers in Composer of voor een expliciete registratie door de gebruiker. Is het event zowel bij HTTP-verwerking als in queue workers nodig, beperk de registratie dan niet met runningInConsole().
Implementeer DeferrableProvider niet op deze provider alleen vanwege het registreren van listeners. Als de uitgestelde services niet worden geresolved, worden de listeners ook niet geregistreerd. Zware service-bindings kun je indien nodig onderbrengen in een aparte uitgestelde provider.

Registratie en instantiëring gebeuren op verschillende momenten

Dispatcher::listen() voegt de listener voor een gewone eventnaam toe aan een interne array. Geef je zoals hierboven een klassenaam door, dan wordt de listener op dat moment nog niet geïnstantieerd. Bij synchrone listeners resolvet createClassCallable() de klasse na het dispatchen van het event via make() van de container. Daardoor kun je dependency injection in de constructor gebruiken. Deze route voert geen call() van de container uit op handle(). Ontwerp de listener zo dat willekeurige services in de constructor worden geïnjecteerd en handle() het event ontvangt. Ga er niet van uit dat dit hetzelfde mechanisme is als dependency injection in handle() van Artisan-commands. Wat hier wordt beschreven, geldt voor synchrone listeners. Implementeer je ShouldQueue, dan volgt de listener een aparte route voor de queue. Bekijk in dat geval ook listeners in de queue en Queues voor transacties, nieuwe pogingen en het resolven van afhankelijkheden.

event:cache stopt expliciete registratie niet

event:cache is geen command dat alle bij de event-dispatcher geregistreerde listeners in hun geheel serialiseert. EventCacheCommand::getEvents() somt Illuminate\Foundation\Support\Providers\EventServiceProvider en de daarvan afgeleide providers op, en slaat per provider het resultaat van de automatische detectie en de mapping uit listens() op. Event::listen() in een gewone ServiceProvider::boot() valt niet onder deze verzameling. Tegelijkertijd wordt boot() van gewone providers ook uitgevoerd bij een start waarbij een cache aanwezig is. Niet in de cache staan is iets anders dan niet geregistreerd worden wanneer er een cache is. Je kunt ook $listen gebruiken in een package dat de Foundation-EventServiceProvider uitbreidt. In de bekeken implementatie heeft de standaardwaarde van shouldDiscoverEvents() echter betrekking op de basisklasse zelf. Alleen het uitbreiden ervan zorgt er niet voor dat de listenermap van je package automatisch wordt gedetecteerd. Daarnaast worden $subscribe en $observers van deze provider afzonderlijk verwerkt, los van de lus die het resultaat van getEvents() registreert. Concludeer dus niet op basis van alleen de cache van $listen dat ook deze registraties worden overgeslagen.
Event::subscribe(SomeSubscriber::class) maakt bij de registratie de subscriber aan via de container en roept subscribe() aan. Dit is niet even lui als Event::listen() met een klassenaam. Vermijd bijwerkingen zoals externe communicatie in de constructor van de subscriber en in subscribe().

Dubbele registratie met automatische detectie voorkomen

Voegt de gebruiker de listenermap van het package toe met withEvents(discover: [...]) en registreert de provider van het package dezelfde listener ook expliciet, dan kan de verwerking voor één event meerdere keren draaien. Dispatcher::listen() voegt voor gewone events elementen toe aan een array en ontdubbelt dezelfde klassenaam niet automatisch over de hele dispatcher. De array_unique() die de Foundation-EventServiceProvider binnen zijn eigen mapping uitvoert, voegt ook geen expliciete registraties van andere providers samen. Leg de verantwoordelijkheid voor de registratie bij één partij.
  • Registreert het package de standaardlisteners, vraag gebruikers dan niet om die map aan de automatische detectie toe te voegen.
  • Kiest de gebruiker de benodigde listeners, dan biedt het package de event- en listenerklassen aan en registreert het dezelfde listeners niet standaard.
  • Stap je over van automatische detectie naar expliciete registratie, vermeld dan dat ook de detectie-instellingen aan de kant van de gebruiker en een oude eventcache moeten worden bijgewerkt.
Idempotentie van de bedrijfslogica is ook belangrijk, maar verberg een dubbele registratie niet alleen met idempotentie: ruim de registratieroutes op.

Gedrag voor en na een update controleren

Om de registratiestatus te bekijken, voer je event:list uit in een testapplicatie waarin het package is geïnstalleerd. Controleer dat de betreffende listeners zowel zonder als met cache aanwezig zijn.
Dit is een procedure voor een testapplicatie. Alleen de uitvoer van event:list garandeert niet dat de listener zijn verwerking voltooit of maar één keer wordt uitgevoerd. Voeg daarom ook tests toe die een event echt dispatchen. Als voorbeeld registreer je CourierServiceProvider in de basis-TestCase van Testbench en controleer je dat de logger precies één keer wordt aangeroepen.
Event::fake() is handig om te controleren of een event is gedispatcht, maar stopt normaal gesproken de uitvoering van de echte listeners. Omdat deze test registratie en verwerking controleert, wordt het hier niet gebruikt. Controleer de combinatie met automatische detectie en een start met cache ook in een aparte testapplicatie en met een nieuwe applicatie-instantie.
Voer je event:cache uit na het opstarten, dan wordt de al geregistreerde dispatcher binnen dat proces niet opnieuw opgebouwd vanuit de cache. Controleer het gedrag met cache bij een nieuwe start. Om bijgewerkte listeners door te voeren in langlopende workers is ook een herstart volgens je gebruikelijke deploymentprocedure nodig.

Checklist voor onderhoud op lange termijn

  • Documenteer eventklassenamen, publieke properties en het registratiebeleid voor listeners als contract voor gebruikers.
  • Wijzig je de naam of payload van events waarop gebruikers zich abonneren, communiceer de wijziging dan volgens Versiecompatibiliteit beheren.
  • Neem bij updates die de automatische detectie of $listen wijzigen het opnieuw aanmaken van event:cache op in de deploymentprocedure.
  • Maak onderscheid tussen de standaardlisteners van het package en eigen listeners van gebruikers, en laat dezelfde standaardlistener niet dubbel registreren.
  • Blijf voor elke ondersteunde PHP- en Laravel-versie testen dat listeners precies één keer worden uitgevoerd.

Geraadpleegde primaire bronnen

Laatst gewijzigd op 13 oktober 2026