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

# Artisan-Befehle in Paketen registrieren und Abhängigkeiten auflösen

> Verfolgen Sie in Laravel 13 den Weg von commands() bis zum Start von Artisan und erfahren Sie, wie Sie Erzeugung und Ausführung von Befehlen trennen, Abhängigkeiten injizieren und was bei deferred Providern und langfristiger Wartung zu beachten ist.

Wenn Sie Ihrem Paket Artisan-Befehle für Diagnose, Synchronisierung oder Installation hinzufügen möchten, betrachten Sie die Registrierung des Befehls, die Erzeugung der Instanz und die Ausführung der Verarbeitung getrennt voneinander. Wenn Sie Services, die nur zur Laufzeit benötigt werden, im Konstruktor auflösen, kann das sogar den Start unbeteiligter Artisan-Befehle beeinträchtigen.

Diese Seite untersucht die Implementierung in Laravel Framework `v13.35.0` und vertieft das Registrierungsbeispiel mit `commands()` aus [Grundlagen der Paketentwicklung](/de/advanced/package-development). Zur Implementierung der Dateigenerierung siehe [GeneratorCommand](/de/advanced/generator-command), zur Teilnahme an Optimierungsaufgaben siehe [Integration in optimize](/de/advanced/package-optimization).

## Befehle explizit im Provider registrieren

Befehle in einem Paket werden in der Methode `boot()` des Service Providers über ihren Klassennamen registriert. Die automatische Erkennung von `app/Console/Commands` der Anwendung und die Registrierung von Klassen, die ein Paket bereitstellt, sind getrennte Einstiegspunkte.

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

namespace Acme\Courier;

use Acme\Courier\Console\StatusCommand;
use Illuminate\Support\ServiceProvider;

class CourierServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        if ($this->app->runningInConsole()) {
            $this->commands([
                StatusCommand::class,
            ]);
        }
    }
}
```

Dieses Beispiel setzt voraus, dass der Provider über die [Paketerkennung](/de/advanced/package-discovery) oder durch explizite Registrierung in der Anwendung geladen wird. Allein durch das Ablegen einer Befehlsklasse wird der Provider des Pakets nicht automatisch registriert.

`runningInConsole()` prüft, ob die Anwendung in einer Konsolenumgebung läuft, nicht ob ein bestimmter Befehl ausgeführt wird. Wenn Sie auch den Aufruf über `Artisan::call()` innerhalb von HTTP-Requests unterstützen, berücksichtigen Sie, dass diese Bedingung die Registrierung verhindern kann, und legen Sie den Nutzungsvertrag fest, einschließlich einer möglichen bedingungslosen Registrierung. Wie Sie Befehle programmgesteuert ausführen, erfahren Sie unter [Artisan](/de/artisan).

## commands() führt den Befehl nicht sofort aus

`ServiceProvider::commands()` registriert bei `Illuminate\Console\Application::starting()` eine Closure, die die übergebenen Klassennamen verwendet. Zu diesem Zeitpunkt wird `handle()` des Befehls nicht aufgerufen.

Beim regulären Console Kernel werden die registrierten Start-Callbacks später ausgeführt, wenn die Artisan Application erzeugt wird. Dort übergibt `resolveCommands()` jede Klasse an `resolve()` und setzt die Registrierung der Befehle fort.

```mermaid theme={null}
flowchart TD
    A["boot() des Providers"] --> B["commands() registriert einen Start-Callback"]
    B --> C["Console Kernel erzeugt die Artisan Application"]
    C --> D["resolveCommands() registriert die Klassen"]
    D --> E["Reguläre Klassen werden vom Container instanziiert"]
    E --> F["Der ausgewählte Befehl wird ausgeführt"]
    F --> G["Abhängigkeiten von handle() werden aufgelöst und es wird aufgerufen"]
```

Entscheidend ist hier: **Die Instanz eines Befehls wird nicht unbedingt zum selben Zeitpunkt erzeugt, zu dem der Befehl ausgeführt wird.** Bei regulären Klassen, die nicht den später beschriebenen speziellen Pfad für verzögerte Registrierung nutzen, ruft `resolve()` das `make()` des Containers auf, sodass der Konstruktor und seine Abhängigkeiten bereits bei der Registrierung aufgelöst werden.

Daher kann der Konstruktor eines Paketbefehls auch dann laufen, wenn Sie nur `php artisan list` oder einen anderen Befehl ausführen. Dass `commands()` in `boot()` steht, garantiert für sich genommen nicht, dass die Instanziierung bis zur Ausführung verzögert wird.

## Laufzeitabhängigkeiten in handle() injizieren

Als Beispiel erstellen wir einen Befehl, der nur anzeigt, ob die Paketkonfiguration aktiviert oder deaktiviert ist. Wie in der offiziellen Artisan-Dokumentation von Laravel 13 werden Name und Beschreibung mit den Attributen `Signature` und `Description` definiert.

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

namespace Acme\Courier\Console;

use Illuminate\Console\Attributes\Description;
use Illuminate\Console\Attributes\Signature;
use Illuminate\Console\Command;
use Illuminate\Contracts\Config\Repository;

#[Signature('courier:status')]
#[Description('Display whether the Courier integration is enabled')]
class StatusCommand extends Command
{
    public function handle(Repository $config): int
    {
        $enabled = (bool) $config->get('courier.enabled', false);

        $this->line($enabled ? 'Courier: enabled' : 'Courier: disabled');

        return self::SUCCESS;
    }
}
```

Dieses Beispiel fügt keinen Konstruktor hinzu, sondern erhält das Konfigurations-Repository als Argument von `handle()`. Da `Command::execute()` im Ausführungspfad des Befehls das `call()` des Containers verwendet, werden typisierte Abhängigkeiten injiziert.

Auch externe API-Clients oder Synchronisierungsservices können Sie an derselben Stelle injizieren, wenn sie nur zur Laufzeit benötigt werden. Hat jedoch die Erzeugung des Services selbst oder das `boot()` eines anderen Providers Seiteneffekte, werden diese dadurch nicht automatisch verhindert.

| Ort | Auswertungszeitpunkt bei einem regulären Befehl | Geeignet für |
| - | - | - |
| `boot()` des Providers | Beim Booten des Providers | Registrierung von Befehlsklassen |
| Konstruktor des Befehls | Bei der Instanziierung, nicht unbedingt unmittelbar vor der Ausführung | Leichtgewichtige Initialisierung |
| Typisierte Argumente von `handle()` | Bei der Ausführung des Befehls | Auflösen der für die Ausführung benötigten Services |
| Rumpf von `handle()` | Bei der Ausführung des Befehls | Eingabevalidierung, Geschäftslogik, Ergebnisanzeige |

<Warning>
  Stellen Sie im Konstruktor keine Verbindungen zu externen APIs her, aktualisieren Sie keine Datenbank und überschreiben Sie keine veröffentlichten Dateien. Nutzer Ihres Pakets müssen `list` und `help` direkt nach der Installation verwenden können, auch wenn die Verbindungseinstellungen noch nicht vollständig sind.
</Warning>

Wenn Sie einen eigenen Konstruktor definieren, rufen Sie auch `parent::__construct()` auf. Der übergeordnete Konstruktor von Laravel konfiguriert den Befehl anhand von Attributen, Signature, Description und Ähnlichem.

## Signature und verzögerte Registrierung sind getrennte Mechanismen

Das untersuchte `Application::resolve()` enthält einen Zweig, der den `name` des Symfony-Attributs `AsCommand` liest und die Zuordnung von Name und Klasse in einer Command Map speichert. In diesem Zweig wird das `make()` des Containers nicht sofort aufgerufen. Der Console Kernel ruft später `setContainerCommandLoader()` auf und richtet einen Loader ein, der diese Map verwendet.

Das im obigen Beispiel verwendete Laravel-Attribut `Signature` ist dagegen unabhängig von dieser `AsCommand`-Prüfung. **Allein durch das Hinzufügen von `Signature` gelangt eine Klasse nicht auf diesen Pfad der verzögerten Registrierung.**

Behandeln Sie diesen Unterschied als Hinweis beim Lesen der internen Implementierung und halten Sie Erzeugungskosten und Seiteneffekte zunächst mit der offiziellen Befehlsdefinition und der Abhängigkeitsinjektion in `handle()` gering. Auch wenn ein verzögerter Loader vorhanden ist, können Befehlsinformationen etwa für Auflistungen oder die Hilfe benötigt werden. Gehen Sie beim Design daher nicht davon aus, dass „der Befehl ausschließlich dann erzeugt wird, wenn er ausgewählt und ausgeführt wird“.

## Die Registrierung von Befehlen nicht verzögern

Das verzögerte Laden von Service-Bindings und das Bekanntmachen eines Befehls bei Artisan sind zwei verschiedene Dinge. Wenn ein Provider `DeferrableProvider` implementiert, wird sein `boot()` unter Umständen gar nicht ausgeführt, bis der betreffende Service benötigt wird.

Wenn Sie nur die Befehlsregistrierung in dieses `boot()` legen und der Provider bis zum Start von Artisan nicht geladen wird, wird auch der Start-Callback, der den Befehl bekannt macht, nicht registriert. Selbst wenn ein in die Abhängigkeitsinjektion von `handle()` verlagerter Service den deferred Provider laden würde, löst das nicht das Problem, das auftritt, bevor Artisan den Befehl findet.

Damit Befehle zuverlässig verfügbar sind, können Sie die Registrierung in einem sofort geladenen Provider unterbringen und nur die schwergewichtigen Service-Bindings in einen separaten deferred Provider auslagern. Die konkreten Anforderungen finden Sie unter [Deferred Service Provider](/de/advanced/deferred-provider).

## Was bei Updates zu prüfen ist

Befehlsnamen, Argumente, Optionen, Ausgaben und Exit-Codes sind öffentliche Schnittstellen, die von Shell-Skripten und CI Ihrer Nutzer verwendet werden. Ändern Sie sie nicht so unbekümmert wie klasseninterne Details, sondern prüfen Sie Kompatibilität und Migrationsschritte.

| Fall | Was zu prüfen ist |
| - | - |
| Installation in eine saubere Anwendung | Der Provider wird geladen und der Befehlsname wird gefunden |
| `php artisan list` | Die Ausführungslogik des Pakets und externe Verbindungen starten nicht |
| `php artisan help courier:status` | Beschreibung und Eingabespezifikation sind einsehbar |
| Tatsächliche Ausführung des Befehls | Die Abhängigkeiten von `handle()` werden aufgelöst und der beabsichtigte Exit-Code wird zurückgegeben |
| Konfiguration nicht veröffentlicht oder nicht gesetzt | Auflistung und Hilfe funktionieren weiterhin, fehlende Konfiguration wird zur Laufzeit angemessen behandelt |
| Update des Pakets oder von Laravel | Bestehende Befehlsnamen, Argumente und Optionen funktionieren weiterhin |

Für den obigen `StatusCommand` können Sie Ausgabe und Exit-Code in einer Testumgebung, in der der Provider geladen ist, wie folgt überprüfen. Zur Testinfrastruktur siehe [Pakete testen](/de/advanced/package-testing), Details zu Assertions finden Sie unter [Console Tests](/de/console-tests).

```php theme={null}
public function test_status_command_displays_enabled_state(): void
{
    config()->set('courier.enabled', true);

    $this->artisan('courier:status')
        ->expectsOutput('Courier: enabled')
        ->assertExitCode(0);
}
```

Prüfen Sie darüber hinaus bei Befehlen, die schwergewichtige Services verwenden, dass die Geschäftsmethoden des Services beim Ausführen von Auflistung oder Hilfe nicht aufgerufen werden. Wenn Sie nur prüfen, ob sich ein Befehl registrieren lässt, erkennen Sie keine Auswirkungen auf unbeteiligte Befehle.

## Herangezogene Primärquellen

* [Offizielle Laravel-Dokumentation: Befehle aus Paketen registrieren](https://github.com/laravel/docs/blob/13.x/packages.md#commands)
* [Offizielle Laravel-Dokumentation: Befehlsstruktur und Abhängigkeitsinjektion](https://github.com/laravel/docs/blob/13.x/artisan.md#command-structure)
* [ServiceProvider: commands()](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Support/ServiceProvider.php)
* [Console Application: starting(), bootstrap() und resolve()](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Console/Application.php)
* [Console Kernel: getArtisan() und Registrierung des Loaders](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Foundation/Console/Kernel.php)
* [Command: Konstruktor und execute()](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Console/Command.php)
* [Offizielle Tests: Auflösung von Befehlen und Alias-Registrierung mit AsCommand](https://github.com/laravel/framework/blob/v13.35.0/tests/Console/ConsoleApplicationTest.php)


## Related topics

- [Paket-Caches in optimize integrieren](/de/advanced/package-optimization.md)
- [Paketentwicklung mit Testbench Workbench](/de/advanced/package-workbench.md)
- [Events und Listener](/de/events.md)
- [Verzeichnisstruktur](/de/directory-structure.md)
- [Versionskompatibilität von Paketen verwalten](/de/advanced/package-versioning.md)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.