Skip to main content
Wenn Sie in Ihrem Paket eventgesteuerte Erweiterungspunkte anbieten möchten, entwerfen Sie die Bereitstellung der Event-Klassen und die Registrierung der Listener getrennt voneinander. Wenn Ihr Aufbau funktioniert, ohne dass Nutzer Verzeichnisse innerhalb von vendor zur automatischen Erkennung hinzufügen müssen, reduzieren Sie die Annahmen bei Installation, Update und Cache-Erstellung. Diese Seite untersucht die Implementierung in Laravel Framework v13.36.0 und wendet die grundlegenden Operationen aus Events auf die langfristige Wartung von Paketen an. Zum Laden von Providern siehe Paketerkennung, zur Testumgebung siehe Pakete testen.

Listener eines Pakets explizit registrieren

Verarbeitungen, die ein Paket standardmäßig ausführt, können Sie in boot() eines regulären Illuminate\Support\ServiceProvider mit Event::listen() registrieren. Das Standardziel der automatischen Listener-Erkennung in Laravel ist app/Listeners der Anwendung; src/Listeners innerhalb eines Pakets wird nicht automatisch durchsucht. Als Beispiel erstellen wir ein Paket, das ein Event für eine abgeschlossene Zustellung protokolliert. Das Event ist lediglich eine Klasse, die die für die Benachrichtigung benötigten Werte enthält.
Der Listener erhält im Konstruktor einen Logger und verarbeitet das Event in handle().
Im Provider registrieren Sie die Klassennamen des Events und des Listeners.
Dieser Aufbau setzt voraus, dass der Provider selbst geladen wird. Stellen Sie extra.laravel.providers in Composer oder eine explizite Registrierung durch die Nutzer bereit. Wenn das Event sowohl bei der HTTP-Verarbeitung als auch in Queue-Workern benötigt wird, schränken Sie die Registrierung nicht mit runningInConsole() ein.
Implementieren Sie DeferrableProvider nicht allein wegen der Listener-Registrierung in diesem Provider. Wenn die verzögerten Services nicht aufgelöst werden, werden auch die Listener nicht registriert. Lagern Sie Bindings rechenintensiver Services bei Bedarf in einen separaten deferred Provider aus.

Registrierung und Instanziierung erfolgen zu unterschiedlichen Zeitpunkten

Dispatcher::listen() fügt Listener für einen regulären Event-Namen einem internen Array hinzu. Wenn Sie wie oben einen Klassennamen übergeben, wird an dieser Stelle noch keine Listener-Instanz erzeugt. Bei synchronen Listenern löst createClassCallable() die Klasse nach dem Auslösen des Events über make() des Containers auf. Daher können Sie Abhängigkeitsinjektion im Konstruktor nutzen. Auf diesem Weg wird für handle() kein call() des Containers ausgeführt. Entwerfen Sie Listener so, dass beliebige Services in den Konstruktor injiziert werden und handle() das Event entgegennimmt. Gehen Sie nicht davon aus, dass es sich um denselben Mechanismus wie bei der Abhängigkeitsinjektion in handle() von Artisan-Befehlen handelt. Hier geht es um synchrone Listener. Wenn Sie ShouldQueue implementieren, wird ein separater Pfad für Queues verwendet. Prüfen Sie daher zu Transaktionen, Wiederholungsversuchen und Abhängigkeitsauflösung auch Events in der Queue verarbeiten und Queues.

event:cache verhindert keine explizite Registrierung

event:cache ist kein Befehl, der sämtliche im Event-Dispatcher registrierten Listener vollständig serialisiert. EventCacheCommand::getEvents() listet Illuminate\Foundation\Support\Providers\EventServiceProvider und davon abgeleitete Provider auf und speichert für jeden Provider die Ergebnisse der automatischen Erkennung sowie die Zuordnungen aus listens(). Event::listen() in ServiceProvider::boot() eines regulären Providers gehört nicht zu den gesammelten Einträgen. Andererseits wird boot() regulärer Provider auch bei einem Start mit vorhandenem Cache ausgeführt. Dass eine Registrierung nicht im Cache enthalten ist, bedeutet nicht, dass sie bei vorhandenem Cache nicht registriert wird. Ein Paket kann auch vom Foundation-EventServiceProvider erben und $listen verwenden. In der untersuchten Implementierung bezieht sich der Standardwert von shouldDiscoverEvents() jedoch auf die Basisklasse selbst. Allein durch das Erben wird das Listener-Verzeichnis des Pakets nicht automatisch erkannt. Außerdem werden $subscribe und $observers dieses Providers getrennt von der Schleife verarbeitet, die die Ergebnisse von getEvents() registriert. Schließen Sie nicht allein aus dem Cache für $listen, dass auch diese Registrierungen übersprungen werden.
Event::subscribe(SomeSubscriber::class) erzeugt den Subscriber bei der Registrierung über den Container und ruft subscribe() auf. Das ist nicht so verzögert wie Event::listen() mit einem Klassennamen. Vermeiden Sie Seiteneffekte wie externe Kommunikation im Konstruktor oder in subscribe() des Subscribers.

Doppelregistrierung mit der automatischen Erkennung vermeiden

Wenn Nutzer das Listener-Verzeichnis eines Pakets über withEvents(discover: [...]) hinzufügen und der Provider des Pakets denselben Listener zusätzlich explizit registriert, kann die Verarbeitung bei einem einzigen Event mehrfach ausgeführt werden. Dispatcher::listen() fügt für reguläre Events Einträge zu einem Array hinzu und entfernt Duplikate desselben Klassennamens nicht automatisch für den gesamten Dispatcher. Auch array_unique(), das der Foundation-EventServiceProvider innerhalb seiner eigenen Zuordnungen anwendet, fasst keine expliziten Registrierungen aus anderen Providern zusammen. Legen Sie die Verantwortung für die Registrierung auf eine Seite fest.
  • Wenn das Paket die Standard-Listener registriert, verlangen Sie von Nutzern nicht, dieses Verzeichnis zur automatischen Erkennung hinzuzufügen.
  • Wenn Nutzer die benötigten Listener auswählen, stellt das Paket die Event- und Listener-Klassen bereit und registriert denselben Listener nicht standardmäßig.
  • Wenn Sie von der automatischen Erkennung zur expliziten Registrierung wechseln, weisen Sie darauf hin, dass auch die Erkennungseinstellungen auf Nutzerseite und alte Event-Caches aktualisiert werden müssen.
Die Idempotenz der Geschäftslogik ist ebenfalls wichtig. Verdecken Sie eine verbleibende Doppelregistrierung jedoch nicht allein durch Idempotenz, sondern bereinigen Sie die Registrierungswege.

Das Verhalten vor und nach Updates prüfen

Um den Registrierungsstatus zu untersuchen, führen Sie event:list in einer Testanwendung aus, in der das Paket installiert ist. Stellen Sie sicher, dass der betreffende Listener sowohl ohne als auch mit Cache vorhanden ist.
Dies ist ein Vorgehen für eine Testanwendung. Die Ausgabe von event:list allein garantiert nicht, dass der Listener seine Verarbeitung abschließt oder nur einmal ausgeführt wird. Fügen Sie zusätzlich Tests hinzu, die tatsächlich Events auslösen. Als Beispiel registrieren Sie CourierServiceProvider im Basis-TestCase von Testbench und prüfen, dass der Logger genau einmal aufgerufen wird.
Event::fake() ist praktisch, um das Auslösen von Events zu prüfen, verhindert aber normalerweise die Ausführung der echten Listener. Da dieser Test Registrierung und Verarbeitung prüft, wird es hier nicht verwendet. Prüfen Sie die Kombination mit der automatischen Erkennung und den Start mit vorhandenem Cache ebenfalls in einer separaten Testanwendung bzw. einer neuen Anwendungsinstanz.
Wenn Sie event:cache nach dem Start ausführen, wird der bereits in diesem Prozess registrierte Dispatcher nicht aus dem Cache neu aufgebaut. Prüfen Sie das Verhalten mit Cache bei einem neuen Start. Damit aktualisierte Listener in lang laufenden Workern wirksam werden, ist außerdem ein Neustart gemäß Ihrem regulären Deployment-Ablauf erforderlich.

Checkliste für die langfristige Wartung

  • Dokumentieren Sie Event-Klassennamen, öffentliche Eigenschaften und die Strategie zur Listener-Registrierung als Vertrag gegenüber den Nutzern.
  • Wenn Sie Namen oder Payloads von Events ändern, die Nutzer abonnieren, kommunizieren Sie die Änderung gemäß der Versionskompatibilitätsverwaltung.
  • Nehmen Sie bei Updates mit Änderungen an der automatischen Erkennung oder an $listen die Neuerstellung von event:cache in den Deployment-Ablauf auf.
  • Unterscheiden Sie zwischen den Standard-Listenern des Pakets und eigenen Listenern der Nutzer, und lassen Sie denselben Standard-Listener nicht doppelt registrieren.
  • Führen Sie für jede unterstützte PHP- und Laravel-Version fortlaufend Tests durch, die sicherstellen, dass Listener genau einmal ausgeführt werden.

Herangezogene Primärquellen

Zuletzt geändert am 13. Oktober 2026