> ## Documentation Index
> Fetch the complete documentation index at: https://kawax.biz/llms.txt
> Use this file to discover all available pages before exploring further.

# Laravel-Paketentwicklung

> 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.

## Was ist ein Paket?

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.

<Info>
  Für Pakettests nutzen Sie [Orchestra Testbench](https://github.com/orchestral/testbench). Damit lassen sich Pakettests genauso schreiben wie in einer normalen Laravel-Anwendung.
</Info>

## Auto-Discovery für Pakete

Bei der Installation liest Laravel den Abschnitt `extra.laravel` aus der `composer.json` und registriert Service Provider und Facades automatisch.

```json theme={null}
"extra": {
    "laravel": {
        "providers": [
            "Acme\\Courier\\CourierServiceProvider"
        ],
        "aliases": {
            "Courier": "Acme\\Courier\\Facades\\Courier"
        }
    }
}
```

Mit dieser Einstellung müssen Nutzer die `bootstrap/providers.php` nicht anpassen — das Paket wird automatisch geladen.

### Auto-Discovery deaktivieren

Wollen Nutzer die Erkennung für ein bestimmtes Paket deaktivieren, tragen sie es in der `composer.json` ihrer Anwendung ein.

```json theme={null}
"extra": {
    "laravel": {
        "dont-discover": [
            "acme/courier"
        ]
    }
}
```

## Rolle des Service Providers

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`.

```php theme={null}
<?php

namespace 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');
    }
}
```

<Warning>
  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`.
</Warning>

## Konfigurationsdateien veröffentlichen

### publishes() — Dateien veröffentlichen

Ein Aufruf von `publishes()` in `boot` erlaubt Nutzern, Dateien über `vendor:publish` in die eigene Anwendung zu kopieren.

```php theme={null}
public function boot(): void
{
    $this->publishes([
        __DIR__.'/../config/courier.php' => config_path('courier.php'),
    ]);
}
```

Auf veröffentlichte Werte greifen Sie ganz normal per `config`-Helper zu.

```php theme={null}
$value = config('courier.option');
```

### mergeConfigFrom() — mit Defaults mergen

Mit `mergeConfigFrom()` in `register` gelten Paket-Defaults auch dann, wenn der Nutzer die Datei nicht veröffentlicht hat.

```php theme={null}
public function register(): void
{
    $this->mergeConfigFrom(
        __DIR__.'/../config/courier.php', 'courier'
    );
}
```

<Warning>
  `mergeConfigFrom()` merged nicht tief in verschachtelte Arrays. Bei mehrdimensionalen Konfigurationen können vom Nutzer nur teilweise definierte Bereiche fehlende Optionen zurücklassen.
</Warning>

### Veröffentlichungsgruppen per Tag

Mit dem zweiten Argument von `publishes()` können Nutzer gezielt einzelne Ressourcengruppen veröffentlichen.

```php theme={null}
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');
}
```

```shell theme={null}
# Nur die Konfigurationsdatei veröffentlichen
php artisan vendor:publish --tag=courier-config

# Alle Ressourcen des Providers veröffentlichen
php artisan vendor:publish --provider="Acme\Courier\CourierServiceProvider"
```

## Routen registrieren

`loadRoutesFrom()` lädt eine Routen-Datei. Bei aktiver Routen-Cache-Kompilierung der Anwendung wird der Aufruf automatisch übersprungen.

```php theme={null}
public function boot(): void
{
    $this->loadRoutesFrom(__DIR__.'/../routes/web.php');
}
```

In der Routen-Datei referenzieren Sie Ihre Paket-Controller.

```php theme={null}
// routes/web.php
use Acme\Courier\Http\Controllers\TrackingController;
use Illuminate\Support\Facades\Route;

Route::prefix('courier')->group(function () {
    Route::get('/track/{id}', [TrackingController::class, 'show'])
        ->name('courier.track');
});
```

## Migrationen veröffentlichen

Mit `publishesMigrations()` veröffentlichen Sie Migrationen. Laravel aktualisiert dabei die Zeitstempel automatisch.

```php theme={null}
public function boot(): void
{
    $this->publishesMigrations([
        __DIR__.'/../database/migrations' => database_path('migrations'),
    ]);
}
```

## Views veröffentlichen

### loadViewsFrom() — Views registrieren

Registrieren Sie das View-Verzeichnis mit `loadViewsFrom()`. Über den Namensraum im zweiten Argument nutzen Sie Views wie `package::view`.

```php theme={null}
public function boot(): void
{
    $this->loadViewsFrom(__DIR__.'/../resources/views', 'courier');
}
```

Nach der Registrierung greifen Sie über den Paket-Namensraum darauf zu:

```php theme={null}
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.

### Views veröffentlichen

```php theme={null}
public function boot(): void
{
    $this->loadViewsFrom(__DIR__.'/../resources/views', 'courier');

    $this->publishes([
        __DIR__.'/../resources/views' => resource_path('views/vendor/courier'),
    ], 'courier-views');
}
```

### Blade-Komponenten registrieren

Bringt Ihr Paket Komponenten mit, registrieren Sie sie in `boot`.

```php theme={null}
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.

```php theme={null}
use Illuminate\Support\Facades\Blade;

public function boot(): void
{
    Blade::componentNamespace('Acme\\Courier\\View\\Components', 'courier');
}
```

```blade theme={null}
{{-- Bei Einzelregistrierung --}}
<x-courier-alert />

{{-- Bei Namespace-Registrierung --}}
<x-courier::alert />
```

## Übersetzungen veröffentlichen

Mit `loadTranslationsFrom()` registrieren Sie Übersetzungsdateien. Übersetzungen referenzieren Sie als `package::file.key`.

```php theme={null}
public function boot(): void
{
    $this->loadTranslationsFrom(__DIR__.'/../lang', 'courier');

    $this->publishes([
        __DIR__.'/../lang' => $this->app->langPath('vendor/courier'),
    ]);
}
```

```php theme={null}
// Verwendung
echo trans('courier::messages.welcome');
```

Für JSON-Übersetzungen verwenden Sie `loadJsonTranslationsFrom()`.

```php theme={null}
public function boot(): void
{
    $this->loadJsonTranslationsFrom(__DIR__.'/../lang');
}
```

## Commands registrieren

Artisan-Commands Ihres Pakets registrieren Sie über `commands()` — üblicherweise nur im Console-Kontext.

```php theme={null}
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,
        ]);
    }
}
```

### In `optimize` einklinken

Hat Ihr Paket eigene Caches, integrieren Sie sich per `optimizes()` in `php artisan optimize` und `php artisan optimize:clear`.

```php theme={null}
public function boot(): void
{
    if ($this->app->runningInConsole()) {
        $this->optimizes(
            optimize: 'courier:cache',
            clear: 'courier:clear-cache',
        );
    }
}
```

### Informationen im `about`-Command ergänzen

Mit `AboutCommand::add()` erweitern Sie die Ausgabe von `php artisan about`.

```php theme={null}
use Illuminate\Foundation\Console\AboutCommand;

public function boot(): void
{
    AboutCommand::add('Courier Package', fn () => ['Version' => '1.0.0']);
}
```

## Facades erstellen

Mit einer Facade rufen Sie Container-Bindings wie statische Methoden auf.

<Steps>
  <Step title="Service-Klasse erstellen">
    ```php theme={null}
    <?php

    namespace Acme\Courier;

    class CourierManager
    {
        public function __construct(
            protected array $config,
        ) {}

        public function send(string $to, string $message): bool
        {
            // Nachricht senden
            return true;
        }

        public function track(string $id): array
        {
            // Sendungsinformationen abrufen
            return ['status' => 'delivered'];
        }
    }
    ```
  </Step>

  <Step title="Facade-Klasse erstellen">
    Erben Sie von `Illuminate\Support\Facades\Facade` und geben Sie in `getFacadeAccessor()` den Container-Schlüssel zurück.

    ```php theme={null}
    <?php

    namespace Acme\Courier\Facades;

    use Illuminate\Support\Facades\Facade;

    /**
     * @method static bool send(string $to, string $message)
     * @method static array track(string $id)
     *
     * @see \Acme\Courier\CourierManager
     */
    class Courier extends Facade
    {
        protected static function getFacadeAccessor(): string
        {
            return \Acme\Courier\CourierManager::class;
        }
    }
    ```
  </Step>

  <Step title="Im Service Provider binden">
    ```php theme={null}
    public function register(): void
    {
        $this->app->singleton(\Acme\Courier\CourierManager::class, function ($app) {
            return new \Acme\Courier\CourierManager($app['config']['courier']);
        });
    }
    ```
  </Step>

  <Step title="In composer.json registrieren">
    ```json theme={null}
    "extra": {
        "laravel": {
            "providers": [
                "Acme\\Courier\\CourierServiceProvider"
            ],
            "aliases": {
                "Courier": "Acme\\Courier\\Facades\\Courier"
            }
        }
    }
    ```
  </Step>
</Steps>

PHPDoc-`@method`-Annotationen ermöglichen IDE-Vervollständigung.

```php theme={null}
// Aufruf über die Facade
use Acme\Courier\Facades\Courier;

Courier::send('user@example.com', 'Paket ist zugestellt');
$status = Courier::track('ABC-123');
```

## DeferrableProvider — Lazy Loading umsetzen

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.

```php theme={null}
<?php

namespace 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.

<Warning>
  Nutzen Sie `DeferrableProvider` nicht für Provider, die Ressourcen (Views, Routen, Event-Listener usw.) registrieren. Beim Deferring werden diese Ressourcen nicht registriert.
</Warning>

## Pakete testen

Für den Test eines Pakets nutzen Sie [Orchestra Testbench](https://github.com/orchestral/testbench). Sie schreiben die Tests, als säßen Sie in einer normalen Laravel-Anwendung.

```shell theme={null}
composer require --dev orchestra/testbench
```

Registrieren Sie den Service Provider Ihres Pakets, indem Sie `getPackageProviders()` überschreiben.

```php theme={null}
<?php

namespace Acme\Courier\Tests;

use Acme\Courier\CourierServiceProvider;
use Orchestra\Testbench\TestCase as BaseTestCase;

class TestCase extends BaseTestCase
{
    /**
     * Service Provider des Pakets registrieren
     */
    protected function getPackageProviders($app): array
    {
        return [
            CourierServiceProvider::class,
        ];
    }

    /**
     * Facade-Aliase des Pakets registrieren
     */
    protected function getPackageAliases($app): array
    {
        return [
            'Courier' => \Acme\Courier\Facades\Courier::class,
        ];
    }

    /**
     * Testumgebung konfigurieren
     */
    protected function defineEnvironment($app): void
    {
        $app['config']->set('courier.api_key', 'test-key');
    }
}
```

```php theme={null}
<?php

namespace Acme\Courier\Tests\Feature;

use Acme\Courier\Facades\Courier;
use Acme\Courier\Tests\TestCase;

class CourierTest extends TestCase
{
    public function test_can_send_message(): void
    {
        $result = Courier::send('user@example.com', 'Testnachricht');

        $this->assertTrue($result);
    }
}
```

## Veröffentlichung auf Composer

Best Practices für die Veröffentlichung auf [Packagist](https://packagist.org/).

**Grundkonfiguration der `composer.json`**

```json theme={null}
{
    "name": "acme/courier",
    "description": "A Laravel courier package",
    "type": "library",
    "license": "MIT",
    "require": {
        "php": "^8.2",
        "illuminate/support": "^11.0||^12.0||^13.0"
    },
    "require-dev": {
        "orchestra/testbench": "^9.0||^10.0",
        "phpunit/phpunit": "^11.0"
    },
    "autoload": {
        "psr-4": {
            "Acme\\Courier\\": "src/"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "Acme\\Courier\\Tests\\": "tests/"
        }
    },
    "extra": {
        "laravel": {
            "providers": [
                "Acme\\Courier\\CourierServiceProvider"
            ],
            "aliases": {
                "Courier": "Acme\\Courier\\Facades\\Courier"
            }
        }
    },
    "minimum-stability": "stable",
    "prefer-stable": true
}
```

<Tip>
  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.
</Tip>

**Beispiel für die Verzeichnisstruktur**

```
acme/courier/
├── config/
│   └── courier.php
├── database/
│   └── migrations/
│       └── 2024_01_01_000000_create_courier_logs_table.php
├── lang/
│   └── de/
│       └── messages.php
├── resources/
│   └── views/
│       └── dashboard.blade.php
├── routes/
│   └── web.php
├── src/
│   ├── Console/
│   │   └── Commands/
│   │       └── InstallCommand.php
│   ├── Facades/
│   │   └── Courier.php
│   ├── Http/
│   │   └── Controllers/
│   │       └── TrackingController.php
│   ├── CourierManager.php
│   └── CourierServiceProvider.php
├── tests/
│   ├── Feature/
│   └── TestCase.php
├── composer.json
└── README.md
```

## Verwandte Seiten

<Columns cols={2}>
  <Card title="Service Provider" icon="plug" href="/de/service-providers">
    Details zu `register`/`boot` sowie zu Deferred Providern.
  </Card>

  <Card title="Versionskompatibilität verwalten" icon="git-branch" href="/de/advanced/package-versioning">
    Strategien für Laravel-/PHP-Major-Upgrades und die Testmatrix in GitHub Actions.
  </Card>
</Columns>


## Related topics

- [GeneratorCommand — Eigene make:-Befehle implementieren](/de/advanced/generator-command.md)
- [CHANGELOG und Release-Management für Pakete](/de/advanced/package-changelog.md)
- [Statische Analyse für Pakete (PHPStan / Larastan)](/de/advanced/package-static-analysis.md)
- [Laravel Package Skeleton – Starter-Template für offizielle Pakete](/de/blog/package-skeleton-introduction.md)
- [Paketentwicklung mit Testbench Workbench](/de/advanced/package-workbench.md)
