Zum Hauptinhalt springen
Schneespur
Dokumentation durchblättern

Module entwickeln

Template-Slots

Über Template-Slots fügt ein Modul Blade-Inhalte an festen Punkten der Layouts ein, ohne Core-Views zu verändern — mit append, replace und Berechtigungsprüfung.

Template-Slots sind vordefinierte Einfügepunkte in den Layouts der Anwendung. Ein Modul kann an diesen Punkten eigenen Blade-Inhalt ausgeben, ohne die Core-Views anzufassen. Genau darin liegt der Zweck: Sie erweitern die Oberfläche, ohne Dateien zu bearbeiten, die beim nächsten Update überschrieben werden — so bleiben Aktualisierungen konfliktfrei.

Wie Slots funktionieren

Ein Slot besteht aus zwei Hälften: Das Core-Layout markiert die Stelle, und Ihr Modul trägt Inhalt dafür ein.

Im Blade-Layout (Core)

Das Layout enthält an der vorgesehenen Stelle eine Direktive:

@extensionSlot('admin.content.after')

Diese Direktive ruft intern SlotRegistry::render('admin.content.after', $user) auf und gibt das gesammelte HTML aus. Sie selbst schreiben diese Direktive nicht — sie steht bereits in den Core-Layouts. Ihre Aufgabe ist nur, Inhalt für den passenden Slot-Namen anzumelden.

Im Modul (Registrierung)

Die Anmeldung läuft über die SlotRegistry, die Sie im boot() Ihres ServiceProviders auflösen:

$slots = app(SlotRegistry::class);

// Inhalt anhängen — mehrere Module dürfen denselben Slot bestücken
$slots->append('admin.content.after', 'my-module::my-partial', ['key' => 'value']);

// Oder den Slot vollständig ersetzen (last-wins)
$slots->replace('admin.content.before', 'my-module::replacement');

Der Unterschied zwischen append und replace ist bewusst gewählt: append ist additiv und verträglich — beliebig viele Module können nebeneinander an denselben Slot anbauen. replace ersetzt den gesamten Slot-Inhalt und ist deshalb dosiert einzusetzen.

@extensionSlot gegen @filterSlot — die richtige Wahl treffen

Zwei Direktiven sehen für „HTML an einer festen Stelle eines Layouts einfügen“ austauschbar aus und sind es nicht.

@extensionSlot (diese Seite)@filterSlot
RegistrySlotRegistryFilterRegistry
Dateneingefroren beim Aufruf von append()/replace() — also zur Registrierung, meist in Ihrer ServiceProvider::boot()bei jedem Rendern frisch übergeben — zur Anfragezeit
Der Konsument siehtgenau das $data, das Ihr Modul bei der Registrierung übergeben hat, nicht mehrdie Laufzeit-Argumente, die der Core dem Hook mitgibt (eine Entität, einen Einsatz, einen Kunden …)
Typischer Einsatzstatischer Inhalt, optional je Benutzer über ein permission-Gate ein- oder ausgeblendet: ein Banner, ein Navigationspunkt, ein Seitenleisten-FeldInhalt, der davon abhängt, welcher Datensatz gerade auf dem Bildschirm ist

SlotRegistry::render(string $slotName, ?Authenticatable $user = null): string verwendet $user ausschließlich, um ein optionales permission-Gate auszuwerten. Gerendert wird View::make($entry['view'], $entry['data']) mit exakt den Daten, die Ihr Modul bei append() oder replace() übergeben hat.

$user selbst wird dabei nie in diese Daten hineingemischt. Ein Slot-Partial, das {{ $user->name }} schreibt, scheitert deshalb mit einem Fehler wegen einer undefinierten Variable — es sei denn, Ihr Modul hat selbst einen user-Schlüssel in $data gelegt.

Der tiefere Grund: Als Ihr Modul in boot() append() aufrief, existierten der Kunde, der Einsatz oder der Formular-Datensatz der heutigen Anfrage noch gar nicht. Ein Slot kann sie also gar nicht tragen. FilterRegistry::apply(string $hook, mixed $value, mixed ...$context) kennt diese Beschränkung nicht: Der Core übergibt den Kontext, den er an der Stelle des apply()-Aufrufs hat — und genau dort ist der Datensatz bekannt.

Das ist der Grund, warum die beiden Formular-Hooks unter Filter-Hooks — schneespur.admin.customer_object.form.after und schneespur.admin.driver.form.after — @filterSlot-Hooks sind und keine @extensionSlot-Slots, obwohl „ein Feld an ein Formular anhängen“ wie der Lehrbuchfall für einen Slot klingt.

Als Faustregel: Braucht Ihr Modul den gerade bearbeiteten Datensatz als Kontext, greifen Sie zu @filterSlot. Zu @extensionSlot greifen Sie nur, wenn eine beim Boot eingefrorene Nutzlast genügt, optional je Benutzer abgesichert.

Verfügbare Slots

Welche Slots es gibt, hängt vom Layout ab. Drei Layouts stellen Einfügepunkte bereit: das Admin-, das Fahrer- und das Portal-Layout.

Admin-Layout (layouts/admin.blade.php)

Slot-NameOrtEinsatzzweck
admin.head.afterim <head>, nach dem Core-CSSeigene Stylesheets, Meta-Tags
admin.sidebar.before-navüber der Navigation in der SidebarModul-Branding, Hinweise
admin.sidebar.after-navunter der Navigation in der Sidebarweitere Links, Status-Infos
admin.content.beforevor dem HauptinhaltBanner, Warnungen
admin.content.afternach dem HauptinhaltFooter-Widgets, Debug-Infos

Fahrer-Layout (layouts/driver.blade.php)

Slot-NameOrtEinsatzzweck
driver.head.afterim <head>, nach dem Core-CSSeigene Stylesheets
driver.topbar.actionsin der Top-Leiste, AktionsbereichSchnellaktions-Buttons
driver.content.beforevor dem HauptinhaltHinweise, Warnmeldungen
driver.content.afternach dem Hauptinhaltzusätzliche Funktionen
driver.bottom-nav.afternach der unteren Navigationweitere Navigationspunkte

Portal-Layout (layouts/portal.blade.php)

Slot-NameOrtEinsatzzweck
portal.head.afterim <head>, nach dem Core-CSSeigene Stylesheets
portal.nav.afternach den Portal-Navigationspunktenweitere Navigationslinks
portal.content.beforevor dem HauptinhaltHinweise, saisonale Infos
portal.content.afternach dem Hauptinhaltzusätzliche Abschnitte
portal.footer.beforevor dem Footerrechtliche Hinweise, Links

Die Slot-API

Anhängen mit append

Mehrere Module dürfen an denselben Slot anhängen. Der Inhalt wird der Reihe nach gerendert, gesteuert über order:

$slots->append(
    slotName: 'admin.content.after',
    viewPath: 'my-module::partials.info-panel',
    data: ['message' => 'Hello from module'],
    order: 100,           // Sortierreihenfolge (kleiner = früher)
    permission: 'settings.view', // optional: nur rendern, wenn der Nutzer diese Berechtigung hat
);

Über order legen Sie fest, an welcher Position Ihr Inhalt im Slot erscheint, wenn dort auch andere Module anbauen — ein kleinerer Wert rückt nach vorn. So bleibt die Reihenfolge vorhersagbar, auch wenn Sie nicht wissen, welche Module sonst noch aktiv sind.

Ersetzen mit replace

replace ersetzt den gesamten Slot-Inhalt. Wenn mehrere Module denselben Slot ersetzen, gewinnt das zuletzt registrierte (mit einem Warneintrag im Log). Setzen Sie replace deshalb sparsam ein — der Warnhinweis ist Ihr Signal, dass sich zwei Module in die Quere kommen.

$slots->replace(
    slotName: 'admin.sidebar.before-nav',
    viewPath: 'my-module::sidebar-header',
    data: [],
    permission: null,
);

Berechtigungsprüfung

Geben Sie eine permission an, wird der Slot-Inhalt nur für Nutzer gerendert, die diese Berechtigung besitzen. So lässt sich Inhalt gezielt auf bestimmte Rollen beschränken, ohne die Prüfung in der View selbst zu wiederholen:

$slots->append('admin.content.after', 'my-module::admin-only', [], 100, 'settings.edit');
// Nur sichtbar für Nutzer mit der Berechtigung 'settings.edit'

Welche Berechtigungen es gibt und wie ein Modul eigene anmeldet, steht unter Berechtigungen und Rollen.

Beispiel: ein Banner einfügen

Im boot() des ServiceProviders melden Sie das Partial an …

// Im boot() des ServiceProviders
$slots = app(SlotRegistry::class);
$slots->append('admin.content.before', 'my-module::banners.update-available', [
    'version' => '2.0.0',
], 50);

… und die zugehörige Blade-View rendert den eigentlichen Inhalt:

{{-- resources/views/banners/update-available.blade.php --}}
<div class="bg-blue-50 border border-blue-200 rounded-lg p-4 mb-4">
    <p class="text-blue-800">
        Module-Update {{ $version }} verfügbar.
    </p>
</div>

Die per data übergebenen Werte stehen in der View als Variablen zur Verfügung — hier $version.

Beispiel: einen Tab im Portal ergänzen

Oft gehören zwei Slots zusammen: einer für den Navigationspunkt, einer für den Inhalt, den er zeigt. So fügen Sie dem Portal einen eigenen Bereich hinzu:

// Navigationslink anhängen
$slots->append('portal.nav.after', 'my-module::nav.documents-link');

// Inhaltsabschnitt anhängen
$slots->append('portal.content.after', 'my-module::portal.documents-section');

Slots sind nur einer von mehreren Erweiterungspunkten, die ein ServiceProvider anmeldet. Wie die SlotRegistry neben den übrigen Registries in den Boot-Ablauf eines Moduls passt, zeigt Das ServiceProvider-Muster; die vollständige Liste der Registries finden Sie unter Registries.