Zum Hauptinhalt springen
Schneespur
Dokumentation durchblättern

Module entwickeln

Das ServiceProvider-Muster

Der ServiceProvider ist der einzige Einstiegspunkt eines Moduls. Register- gegen Boot-Phase, Views und Übersetzungen laden, Einstellungen registrieren — mit Beispielen.

Jedes Modul hat genau einen ServiceProvider, der Illuminate\Support\ServiceProvider erweitert. Er ist der einzige Einstiegspunkt, an dem das Modul alle seine Fähigkeiten anmeldet. Alles, was ein Modul kann, beginnt hier.

Aufbau eines Modul-ServiceProviders

<?php

namespace Schneespur\Module\MyModule;

use Illuminate\Support\ServiceProvider;

class MyModuleServiceProvider extends ServiceProvider
{
    public function register(): void
    {
        // Phase 1: Services in den Container binden.
        // Andere Module/Provider sind evtl. noch nicht gebootet.
        // Hier KEINE anderen Services auflösen.
    }

    public function boot(): void
    {
        // Phase 2: Alle Provider sind registriert.
        // Jetzt ist es sicher, Registries aufzulösen und einzutragen.

        $this->loadViewsFrom(__DIR__ . '/../resources/views', 'my-module');

        $this->registerNavigation();
        $this->registerWidget();
        $this->registerRoutes();
        // … weitere Registrierungen
    }
}

Register gegen Boot

Der Unterschied zwischen den beiden Phasen ist nicht kosmetisch — er entscheidet, ob Ihr Modul zuverlässig lädt:

Phaseregister()boot()
Wannbevor irgendein Provider gebootet istnachdem alle Provider registriert sind
Erlaubtin den Container binden, Config mergenServices auflösen, in Registries eintragen, Views/Routen laden
Nicht tunandere Services oder Registries auflösenin den Container binden (geht, verletzt aber die Konvention)

Der Grund: In register() steht noch nicht fest, dass jeder andere Provider schon registriert wurde. Wer dort eine Registry auflöst, riskiert, dass sie noch gar nicht existiert. In boot() ist diese Garantie gegeben — deshalb gehören alle Registry-Einträge dorthin.

Views laden

$this->loadViewsFrom(__DIR__ . '/../resources/views', 'my-module');

Views werden danach als my-module::view-name referenziert:

return view('my-module::settings', ['data' => $data]);

Übersetzungen laden

Übersetzungen lädt der ModuleManager automatisch aus modules/{slug}/lang/. Sie sind als {slug}::file.key namespaced:

// In lang/de/messages.php: return ['hello' => 'Hallo'];
__('example::messages.hello'); // → 'Hallo'

Sie müssen $this->loadTranslationsFrom() nicht selbst aufrufen — das erledigt der ModuleManager.

Einstellungen registrieren

Mit ModuleManager::registerSettings() definieren Sie Standard-Einstellungen. Sie landen in der Tabelle settings, jeweils mit dem Modul-Slug als Präfix:

protected function registerSettings(): void
{
    app(ModuleManager::class)->registerSettings('my-module', [
        'api_key' => '',
        'enabled' => true,
        'max_retries' => 3,
    ]);
}

Gespeichert wird als my-module.api_key, my-module.enabled usw. Der Typ wird automatisch erkannt:

PHP-Typgespeichert als
string'string'
bool'bool'
int'int'
array'json'

Einstellungen lesen:

use App\Models\Setting;

$apiKey = Setting::get('my-module.api_key');
$enabled = Setting::get('my-module.enabled', false);

Einstellungen schreiben:

Setting::set('my-module.api_key', 'new-value');
Setting::set('my-module.enabled', true, 'bool');

Das Slug-Präfix ist auch der Grund, warum sich Einstellungen beim Entfernen eines Moduls sauber aufräumen lassen — der ModuleManager löscht einfach alle Schlüssel, die mit {slug}. beginnen.

Ein vollständiges Beispiel

Einen kompletten, lauffähigen ServiceProvider — mit Navigation, Dashboard-Widget, Routen, Einstellungen und Übersetzungen — bauen Sie Schritt für Schritt im Schnelleinstieg auf. Außerdem liegt der Anwendung ein Referenzmodul unter modules/example/ bei, das die Erweiterungspunkte demonstriert — Abschnitt für Abschnitt erklärt im Kapitel Beispielmodul.

Gängiges Muster: bedingtes Booten

Das Referenzmodul nutzt einen shouldBoot()-Wächter, um sich nur in der Entwicklung zu laden:

protected function shouldBoot(): bool
{
    return (bool) env('EXAMPLE_MODULE_ENABLED', false);
}

Produktive Module sollten dieses Muster nicht verwenden — Aktivieren und Deaktivieren läuft über die Modul-Verwaltung in der Oberfläche. Der shouldBoot()-Wächter ist ausschließlich für das mitgelieferte Entwicklungs-Referenzmodul gedacht.

ServiceProvider-Checkliste

Ein Modul-ServiceProvider registriert typischerweise einige oder alle dieser Punkte (in boot()):

  • Views: $this->loadViewsFrom()
  • Einstellungen: ModuleManager::registerSettings()
  • Navigationspunkte: NavigationRegistry::addItem()
  • Dashboard-Widgets: DashboardWidgetRegistry::registerWidget()
  • Filter: FilterRegistry::register()
  • Template-Slots: SlotRegistry::append() / SlotRegistry::replace()
  • Event-Listener: $this->app['events']->listen()
  • Benachrichtigungs-Kanäle: NotificationChannelRegistry::register()
  • 2FA-Verfahren: TwoFactorMethodRegistry::registerMethod()
  • Dispatch-Strategien: DispatchStrategyRegistry::register()
  • Web-Routen: Route::middleware(...)->group()
  • API-Routen: ModuleApiRegistrar::routes()
  • Berechtigungen: PermissionRegistry::registerPermission()
  • Rollen-Vorlagen: RoleTemplateRegistry::registerTemplate()
  • geplante Aufgaben: ScheduledTaskRegistry::register()
  • Backup-Ziele: BackupTargetRegistry::register()
  • Speicher-Backends: StorageBackendRegistry::register()
  • Diagnose-Reporter: DiagnosticReporterRegistry::register()

Die genaue API jeder dieser Registries steht unter Registries.