Entwicklung von Laravel-Paketen rund um Service Provider — vom Veröffentlichen von Konfiguration, Views, Migrationen und Facades bis hin zu Auto-Discovery und Lazy Loading.
Ein Paket in Laravel ist ein Composer-Paket, das die Anwendung um Funktionalität erweitert. Es gibt zwei Arten:
Standalone-Pakete — universelle PHP-Bibliotheken ohne Laravel-Abhängigkeit (z. B. Carbon, Pest).
Laravel-Pakete — Pakete mit Laravel-integrierten Bausteinen wie Routen, Controllern, Views oder Konfiguration.
Diese Anleitung behandelt Letztere: die Entwicklung Laravel-spezifischer Pakete. Sie erfordert ein solides Verständnis interner Laravel-Konzepte wie Service Provider, Facades und der Veröffentlichung von Konfigurationsdateien.
Für Pakettests nutzen Sie Orchestra Testbench. Damit lassen sich Pakettests genauso schreiben wie in einer normalen Laravel-Anwendung.
Der Service Provider ist der Einstiegspunkt Ihres Pakets. Hier bündeln Sie die Registrierung von Views, Konfiguration, Migrationen, Routen und weiteren Ressourcen.Ein Service Provider erbt von Illuminate\Support\ServiceProvider und hat zwei Methoden: register und boot.
<?phpnamespace Acme\Courier;use Illuminate\Support\ServiceProvider;class CourierServiceProvider extends ServiceProvider{ /** * Services des Pakets registrieren */ public function register(): void { // Bindings im Service Container hier vornehmen $this->mergeConfigFrom( __DIR__.'/../config/courier.php', 'courier' ); $this->app->singleton(CourierManager::class, function ($app) { return new CourierManager($app['config']['courier']); }); } /** * Services des Pakets bootstrappen */ public function boot(): void { // Ressourcen hier registrieren $this->loadRoutesFrom(__DIR__.'/../routes/web.php'); $this->loadViewsFrom(__DIR__.'/../resources/views', 'courier'); $this->loadTranslationsFrom(__DIR__.'/../lang', 'courier'); $this->publishesMigrations([ __DIR__.'/../database/migrations' => database_path('migrations'), ]); $this->publishes([ __DIR__.'/../config/courier.php' => config_path('courier.php'), ], 'courier-config'); $this->publishes([ __DIR__.'/../resources/views' => resource_path('views/vendor/courier'), ], 'courier-views'); }}
Registrieren Sie im register-Methodenkörper keine Event-Listener, Routen oder Views. Sie könnten Services eines noch nicht geladenen Providers unabsichtlich aufrufen. Alles außer Bindings gehört in boot.
Mit mergeConfigFrom() in register gelten Paket-Defaults auch dann, wenn der Nutzer die Datei nicht veröffentlicht hat.
public function register(): void{ $this->mergeConfigFrom( __DIR__.'/../config/courier.php', 'courier' );}
mergeConfigFrom() merged nicht tief in verschachtelte Arrays. Bei mehrdimensionalen Konfigurationen können vom Nutzer nur teilweise definierte Bereiche fehlende Optionen zurücklassen.
Mit dem zweiten Argument von publishes() können Nutzer gezielt einzelne Ressourcengruppen veröffentlichen.
public function boot(): void{ $this->publishes([ __DIR__.'/../config/courier.php' => config_path('courier.php'), ], 'courier-config'); $this->publishesMigrations([ __DIR__.'/../database/migrations/' => database_path('migrations'), ], 'courier-migrations');}
# Nur die Konfigurationsdatei veröffentlichenphp artisan vendor:publish --tag=courier-config# Alle Ressourcen des Providers veröffentlichenphp artisan vendor:publish --provider="Acme\Courier\CourierServiceProvider"
Registrieren Sie das View-Verzeichnis mit loadViewsFrom(). Über den Namensraum im zweiten Argument nutzen Sie Views wie package::view.
public function boot(): void{ $this->loadViewsFrom(__DIR__.'/../resources/views', 'courier');}
Nach der Registrierung greifen Sie über den Paket-Namensraum darauf zu:
Route::get('/dashboard', function () { return view('courier::dashboard');});
Laravel sucht Views an zwei Orten: zuerst in resources/views/vendor/courier der Anwendung, ansonsten im Paketverzeichnis. So können Nutzer Views anpassen.
Bringt Ihr Paket Komponenten mit, registrieren Sie sie in boot.
use Illuminate\Support\Facades\Blade;use Acme\Courier\View\Components\AlertComponent;public function boot(): void{ Blade::component('courier-alert', AlertComponent::class);}
Mit einem Komponenten-Namensraum registrieren Sie mehrere Komponenten auf einmal.
use Illuminate\Support\Facades\Blade;public function boot(): void{ Blade::componentNamespace('Acme\\Courier\\View\\Components', 'courier');}
{{-- Bei Einzelregistrierung --}}<x-courier-alert />{{-- Bei Namespace-Registrierung --}}<x-courier::alert />
Artisan-Commands Ihres Pakets registrieren Sie über commands() — üblicherweise nur im Console-Kontext.
use Acme\Courier\Console\Commands\InstallCommand;use Acme\Courier\Console\Commands\SyncCommand;public function boot(): void{ if ($this->app->runningInConsole()) { $this->commands([ InstallCommand::class, SyncCommand::class, ]); }}
public function register(): void{ $this->app->singleton(\Acme\Courier\CourierManager::class, function ($app) { return new \Acme\Courier\CourierManager($app['config']['courier']); });}
// Aufruf über die Facadeuse Acme\Courier\Facades\Courier;Courier::send('[email protected]', 'Paket ist zugestellt');$status = Courier::track('ABC-123');
Provider, die nur Bindings vornehmen, können durch DeferrableProvider lazy geladen werden. Der Provider wird erst geladen, wenn der Service wirklich benötigt wird — das verbessert die Performance.
<?phpnamespace Acme\Courier;use Illuminate\Contracts\Support\DeferrableProvider;use Illuminate\Support\ServiceProvider;class CourierServiceProvider extends ServiceProvider implements DeferrableProvider{ public function register(): void { $this->app->singleton(CourierManager::class, function ($app) { return new CourierManager($app['config']['courier']); }); } /** * Liste der von diesem Provider bereitgestellten Services * * @return array<int, string> */ public function provides(): array { return [CourierManager::class]; }}
Laravel kompiliert und speichert die von Deferred Providern bereitgestellten Services. Nur wenn ein in provides() gelisteter Service aufgelöst wird, lädt Laravel den Provider.
Nutzen Sie DeferrableProvider nicht für Provider, die Ressourcen (Views, Routen, Event-Listener usw.) registrieren. Beim Deferring werden diese Ressourcen nicht registriert.
Mit einer Abhängigkeit zu illuminate/support (statt zum kompletten illuminate/framework) binden Sie nur die benötigten Komponenten ein. Halten Sie den Abhängigkeitsbaum Ihres Pakets klein.