Dokumentation durchblättern
Module entwickeln
Events-Referenz
Schneespur löst im Betrieb fachliche Events aus; Module hängen sich daran, um auf Zustandsänderungen zu reagieren, ohne Core-Dateien anzufassen.
Schneespur löst während des Betriebs fachliche Events aus — etwa wenn ein Einsatz abgeschlossen, ein Kunde angelegt oder eine Schicht beendet wird. Module können auf diese Events hören und darauf reagieren, ohne eine einzige Core-Datei zu verändern.
Warum Events statt direkter Eingriffe
Ein Modul, das bei jedem abgeschlossenen Einsatz etwas tun soll, könnte theoretisch den Core-Code an der passenden Stelle ergänzen. Genau das vermeidet das Event-System: Der Core meldet „dieser Einsatz ist fertig”, und Ihr Modul entscheidet selbst, ob und wie es reagiert. Der Core weiß nichts von Ihrem Modul, Ihr Modul greift nicht in den Core ein — dadurch bleiben Updates konfliktfrei, und mehrere Module können unabhängig auf dasselbe Event reagieren.
Auf Events hören
Listener registrieren Sie in der boot()-Methode Ihres ServiceProviders (zum Unterschied
zwischen register() und boot() siehe Das ServiceProvider-Muster).
Für einfache Fälle genügt eine Closure:
$this->app['events']->listen(JobCompleted::class, function (JobCompleted $event) {
// React to job completion
Log::info('Job completed', ['job_id' => $event->job->id]);
});
Sobald die Reaktion umfangreicher wird, lagern Sie die Logik in eine eigene Listener-Klasse aus. Das hält den ServiceProvider übersichtlich und macht den Listener testbar:
$this->app['events']->listen(JobCompleted::class, MyJobCompletedListener::class);
Job-Events
Die Einsatz-Events bilden den Kern des Tagesgeschäfts ab — vom Beginn der Arbeit bis zum Abschluss eines Einsatzes.
JobStarted
Class: App\Events\JobStarted
Ausgelöst: wenn ein Einsatz begonnen wird (der Fahrer nimmt die Arbeit auf).
| Property | Typ | Beschreibung |
|---|---|---|
$job | App\Models\Job | der gestartete Einsatz |
JobCompleted
Class: App\Events\JobCompleted
Ausgelöst: wenn ein Einsatz abgeschlossen wird (der Fahrer markiert die Arbeit als
erledigt).
| Property | Typ | Beschreibung |
|---|---|---|
$job | App\Models\Job | der abgeschlossene Einsatz |
$weatherAvailable | bool | ob für diesen Einsatz Wetterdaten geholt wurden |
$isWeatherUpdate | bool | ob es sich um eine reine Wetter-Aktualisierung handelt (Standard: false) |
Dies ist das am häufigsten genutzte Event. Der Core-Listener
SendJobCompletedNotification reagiert darauf und verschickt Benachrichtigungen über die
NotificationChannelRegistry. Das ist ein gutes Beispiel dafür, wie der Core selbst nur
über dieses Event arbeitet — Ihr Modul hängt sich an derselben Stelle ein.
Kunden-Events
CustomerCreated
Class: App\Events\CustomerCreated
Ausgelöst: wenn ein neuer Kunde angelegt wird.
| Property | Typ | Beschreibung |
|---|---|---|
$customer | App\Models\Customer | der neue Kunde |
CustomerUpdated
Class: App\Events\Customer\CustomerUpdated
Ausgelöst: wenn ein Kundendatensatz geändert wird.
| Property | Typ | Beschreibung |
|---|---|---|
$customer | App\Models\Customer | der geänderte Kunde |
CustomerDeleted
Class: App\Events\Customer\CustomerDeleted
Ausgelöst: wenn ein Kunde gelöscht wird.
| Property | Typ | Beschreibung |
|---|---|---|
$customer | App\Models\Customer | der gelöschte Kunde |
Benutzer-Events
UserCreated
Class: App\Events\User\UserCreated
Ausgelöst: wenn ein neuer Benutzer angelegt wird (Verwaltung oder Fahrer).
| Property | Typ | Beschreibung |
|---|---|---|
$user | App\Models\User | der neue Benutzer |
UserLoggedIn
Class: App\Events\User\UserLoggedIn
Ausgelöst: wenn sich ein Benutzer anmeldet.
| Property | Typ | Beschreibung |
|---|---|---|
$user | App\Models\User | der angemeldete Benutzer |
UserLoggedOut
Class: App\Events\User\UserLoggedOut
Ausgelöst: wenn sich ein Benutzer abmeldet.
| Property | Typ | Beschreibung |
|---|---|---|
$user | App\Models\User | der abgemeldete Benutzer |
Schicht-Events
Die Schicht-Events tragen zwei Properties: die Schicht selbst und den Fahrer. So lässt sich direkt erkennen, wer eine Schicht begonnen oder beendet hat.
WorkShiftStarted
Class: App\Events\Shift\WorkShiftStarted
Ausgelöst: wenn ein Fahrer eine Schicht beginnt.
| Property | Typ | Beschreibung |
|---|---|---|
$workShift | App\Models\WorkShift | die begonnene Schicht |
$user | App\Models\User | der Fahrer |
WorkShiftEnded
Class: App\Events\Shift\WorkShiftEnded
Ausgelöst: wenn ein Fahrer eine Schicht beendet.
| Property | Typ | Beschreibung |
|---|---|---|
$workShift | App\Models\WorkShift | die beendete Schicht |
$user | App\Models\User | der Fahrer |
Modul-Events
Diese beiden Events feuern, wenn ein Modul über die Oberfläche aktiviert oder deaktiviert wird. Sie sind der saubere Ort für einmalige Einrichtungs- oder Aufräumschritte — siehe auch Modul-Lebenszyklus.
ModuleEnabled
Class: App\Events\Module\ModuleEnabled
Ausgelöst: wenn ein Modul über die Verwaltungsoberfläche aktiviert wird.
| Property | Typ | Beschreibung |
|---|---|---|
$module | App\Models\Module | das aktivierte Modul |
ModuleDisabled
Class: App\Events\Module\ModuleDisabled
Ausgelöst: wenn ein Modul über die Verwaltungsoberfläche deaktiviert wird.
| Property | Typ | Beschreibung |
|---|---|---|
$module | App\Models\Module | das deaktivierte Modul |
Wetter-Events
WeatherSnapshotCreated
Class: App\Events\WeatherSnapshotCreated
Ausgelöst: wenn Wetterdaten für einen Einsatz geholt und gespeichert werden.
| Property | Typ | Beschreibung |
|---|---|---|
$snapshot | App\Models\WeatherSnapshot | der neue Wetter-Snapshot |
GPS-/Standort-Events
Anders als die einsatzgebundenen Standortdaten, die der Core nur im Rahmen eines laufenden Einsatzes speichert, gibt es ein Event, das bei jedem Standort-Ping eines Fahrers feuert — auch dann, wenn gerade kein Einsatz aktiv ist.
GpsPointReceived
Class: App\Events\GpsPointReceived
Ausgelöst: bei jedem gültigen OwnTracks-Standort-Ping — unabhängig davon, ob der
Fahrer gerade einen aktiven Einsatz hat. Wird in OwnTracksController::store() ausgelöst.
| Property | Typ | Beschreibung |
|---|---|---|
$user | App\Models\User | der Fahrer, der den Ping gesendet hat |
$lat | float | geografische Breite |
$lon | float | geografische Länge |
$timestamp | int | Unix-Zeitstempel des Standorts |
$accuracy | ?int | gemeldete Genauigkeit in Metern (nullable) |
$activeJob | ?App\Models\Job | der aktive Einsatz des Fahrers oder null, wenn gerade keiner läuft |
Hinzugekommen in 1.1.6. Der entscheidende Unterschied zu den einsatzgebundenen Standortdaten: Dieses Event feuert bei jedem Ping, also auch im Leerlauf ohne aktiven Einsatz. Damit kommt es deutlich häufiger als die Einsatz-Events — bedenken Sie das, wenn Ihr Listener pro Ping Arbeit verrichtet; halten Sie die Reaktion entsprechend schlank.
Wichtig zur Datenhaltung: Leerlauf-Pings (ohne aktiven Einsatz) werden vom Core nicht
gespeichert — die GpsPoint-Ablage bleibt einsatzgebunden. Dieses Event ist also die
einzige Stelle, an der Leerlauf-Pings überhaupt sichtbar werden.
Der Hook existiert, damit ein Geofencing-Modul die Live-Position eines Fahrers schon
beobachten kann, bevor ein Einsatz existiert, und bei Bedarf selbst einen Einsatz startet
(etwa über den JobLifecycleService). Ein solches Geofencing-Modul ist geplant und derzeit
noch nicht verfügbar — das Event selbst steht jedoch bereits zur Verfügung.
$this->app['events']->listen(GpsPointReceived::class, function (GpsPointReceived $event) {
if ($event->activeJob !== null) {
return; // Fahrer arbeitet bereits an einem Einsatz
}
// Prüfen, ob ($event->lat, $event->lon) einen Geofence betreten hat, und ggf. Einsatz starten ...
});
Gängige Muster
Alle abgeschlossenen Einsätze protokollieren
Ein häufiger Anwendungsfall: ein Modul, das jeden abgeschlossenen Einsatz in einem eigenen
Protokoll festhält. Über $event->job greifen Sie auf den vollständigen Einsatz und seine
Beziehungen zu:
$this->app['events']->listen(JobCompleted::class, function (JobCompleted $event) {
ModuleLogger::make()->info('my-module', 'Job completed', [
'job_id' => $event->job->id,
'customer' => $event->job->customer?->name,
'type' => $event->job->type->value,
'weather' => $event->weatherAvailable,
]);
});
Benachrichtigung bei Schichtbeginn
$this->app['events']->listen(WorkShiftStarted::class, function (WorkShiftStarted $event) {
// Notify admin that a driver has started their shift
$this->sendShiftAlert($event->user, $event->workShift);
});
Auf den Modul-Lebenszyklus reagieren
Wenn Ihr Modul beim Aktivieren einen einmaligen Einrichtungsschritt braucht, prüfen Sie im Listener den Slug — so reagiert das Modul nur auf seine eigene Aktivierung und nicht auf die anderer Module:
$this->app['events']->listen(ModuleEnabled::class, function (ModuleEnabled $event) {
if ($event->module->slug === 'my-module') {
// Perform post-activation setup
$this->runSetup();
}
});