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

# Enregistrement des commandes Artisan d'un package et résolution des dépendances

> Analysez le parcours de commands() jusqu'au démarrage d'Artisan dans Laravel 13 : séparation entre la création et l'exécution des commandes, injection de dépendances, providers différés et points d'attention pour la maintenance à long terme.

Lorsque vous souhaitez ajouter à votre package des commandes Artisan de diagnostic, de synchronisation ou d'installation, considérez séparément l'enregistrement de la commande, la création de son instance et l'exécution du traitement. Si vous résolvez dans le constructeur des services qui ne sont nécessaires qu'à l'exécution, vous risquez d'impacter jusqu'au démarrage de commandes Artisan sans rapport.

Cette page examine l'implémentation de Laravel Framework `v13.35.0` et approfondit l'exemple d'enregistrement avec `commands()` présenté dans [Développement de packages Laravel](/fr/advanced/package-development). Pour l'implémentation de la génération de fichiers, consultez [GeneratorCommand](/fr/advanced/generator-command), et pour la participation aux tâches d'optimisation, consultez [Intégration à optimize](/fr/advanced/package-optimization).

## Enregistrer explicitement depuis le provider

Les commandes d'un package sont enregistrées par leur nom de classe dans la méthode `boot()` du service provider. La découverte automatique du répertoire `app/Console/Commands` de l'application et l'enregistrement des classes fournies par un package sont deux points d'entrée distincts.

```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,
            ]);
        }
    }
}
```

Cet exemple suppose que le provider est chargé via la [découverte automatique des packages](/fr/advanced/package-discovery) ou par un enregistrement explicite côté application. Il ne suffit pas de placer une classe de commande pour que le provider du package soit automatiquement enregistré.

`runningInConsole()` détermine si l'on se trouve dans un environnement console, et non si une commande particulière est en cours d'exécution. Si vous prévoyez aussi un usage via `Artisan::call()` depuis une requête HTTP, tenez compte du fait que cette condition peut empêcher l'enregistrement, et définissez le contrat d'utilisation en envisageant éventuellement un enregistrement inconditionnel. Pour l'exécution programmatique, consultez [Artisan](/fr/artisan).

## commands() n'exécute pas la commande sur-le-champ

`ServiceProvider::commands()` enregistre auprès de `Illuminate\Console\Application::starting()` une closure qui utilise les noms de classes reçus. À ce stade, la méthode `handle()` de la commande n'est pas appelée.

Avec le Console Kernel habituel, les callbacks de démarrage enregistrés sont exécutés plus tard, lorsque l'Artisan Application est créée. C'est alors que `resolveCommands()` transmet chaque classe à `resolve()` et procède à l'enregistrement des commandes.

```mermaid theme={null}
flowchart TD
    A["boot() du provider"] --> B["Enregistrement du callback de démarrage via commands()"]
    B --> C["Le Console Kernel crée l'Artisan Application"]
    C --> D["Enregistrement des classes via resolveCommands()"]
    D --> E["Les classes ordinaires sont instanciées par le conteneur"]
    E --> F["Exécution de la commande sélectionnée"]
    F --> G["Résolution des dépendances de handle() puis appel"]
```

Le point essentiel est que **l'instanciation d'une commande ne coïncide pas nécessairement avec son exécution**. Pour les classes ordinaires qui n'empruntent pas le chemin d'enregistrement différé dédié décrit plus bas, `resolve()` appelle `make()` du conteneur : le constructeur et ses dépendances sont donc résolus au moment de l'enregistrement.

Par conséquent, le simple fait d'exécuter `php artisan list` ou une autre commande peut déclencher le constructeur de la commande du package. Placer `commands()` dans `boot()` ne garantit pas en soi que l'instanciation sera reportée jusqu'à l'exécution.

## Injecter les dépendances d'exécution dans handle()

À titre d'exemple, créons une commande qui affiche uniquement si la configuration du package est activée ou désactivée. Comme dans la documentation officielle d'Artisan pour Laravel 13, le nom et la description sont définis avec les attributs `Signature` et `Description`.

```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;
    }
}
```

Cet exemple n'ajoute pas de constructeur et reçoit le Repository de configuration en argument de `handle()`. Comme `Command::execute()` utilise `call()` du conteneur dans le chemin d'exécution de la commande, les dépendances typées sont injectées.

Si vous utilisez un client d'API externe ou un service de synchronisation, vous pouvez également l'injecter au même endroit s'il n'est nécessaire qu'à l'exécution. Cependant, si la création du service lui-même ou le `boot()` d'un autre provider produit des effets de bord, ceux-ci ne sont pas évités automatiquement.

| Emplacement | Moment d'évaluation pour une commande ordinaire | Traitement adapté |
| - | - | - |
| `boot()` du provider | Au démarrage du provider | Enregistrement des classes de commande |
| Constructeur de la commande | À l'instanciation, pas nécessairement juste avant l'exécution | Initialisation légère |
| Arguments typés de `handle()` | À l'exécution de la commande | Résolution des services nécessaires à l'exécution |
| Corps de `handle()` | À l'exécution de la commande | Validation des entrées, logique métier, affichage des résultats |

<Warning>
  N'effectuez pas dans le constructeur de connexion à une API externe, de mise à jour de la base de données ni d'écrasement de fichiers publiés. Les utilisateurs du package doivent pouvoir utiliser `list` et `help` même juste après l'installation ou lorsque la configuration de connexion n'est pas encore finalisée.
</Warning>

Si vous définissez votre propre constructeur, appelez également `parent::__construct()`. En effet, le constructeur parent de Laravel configure la commande à partir des attributs, de la signature, de la description, etc.

## Signature et l'enregistrement différé sont deux mécanismes distincts

La méthode `Application::resolve()` examinée contient une branche qui lit le `name` de l'attribut Symfony `AsCommand` et enregistre la correspondance entre le nom et la classe dans une command map. Dans cette branche, `make()` du conteneur n'est pas appelé immédiatement. Le Console Kernel appelle ensuite `setContainerCommandLoader()` pour configurer un loader qui utilise cette map.

En revanche, l'attribut Laravel `Signature` utilisé dans l'exemple ci-dessus est distinct de cette détection d'`AsCommand`. **Ajouter `Signature` ne suffit pas à faire passer la classe par ce chemin d'enregistrement différé.**

Considérez cette différence comme un point d'attention lors de la lecture de l'implémentation interne, et commencez par réduire le coût de création et les effets de bord grâce à la définition officielle des commandes et à l'injection de dépendances dans `handle()`. Même avec un loader différé, les informations de la commande peuvent être nécessaires pour la liste, l'aide, etc. : ne concevez donc pas votre code en supposant que la commande « ne sera jamais créée en dehors de son exécution ».

## Ne pas différer l'enregistrement des commandes

Différer le chargement des liaisons de services et faire connaître une commande à Artisan sont deux choses différentes. Lorsqu'un provider implémente `DeferrableProvider`, sa méthode `boot()` elle-même peut ne pas être exécutée tant que le service concerné n'est pas nécessaire.

Si l'enregistrement des commandes se trouve uniquement dans ce `boot()` et que le provider n'est pas chargé avant le démarrage d'Artisan, le callback de démarrage qui signale l'existence de la commande n'est pas enregistré non plus. Même si un service déplacé vers l'injection de dépendances de `handle()` charge le provider différé, cela ne résout pas le problème qui survient avant qu'Artisan ne trouve la commande.

Pour garantir l'exposition des commandes, vous pouvez placer leur enregistrement dans un provider chargé immédiatement et isoler uniquement les liaisons des services lourds dans un provider différé distinct. Pour les exigences précises, consultez [Service providers différés](/fr/advanced/deferred-provider).

## Points à vérifier lors des mises à jour

Le nom de la commande, ses arguments, ses options, sa sortie et son code de sortie constituent une interface publique utilisée par les scripts shell et la CI des utilisateurs. Ne les modifiez pas avec la même légèreté que l'implémentation interne d'une classe ; vérifiez la compatibilité et la procédure de migration.

| Cas | Points à vérifier |
| - | - |
| Installation dans une application vierge | Le provider est chargé et le nom de la commande est trouvé |
| `php artisan list` | Le traitement d'exécution du package et les connexions externes ne démarrent pas |
| `php artisan help courier:status` | La description et la spécification des entrées sont consultables |
| Exécution réelle de la commande | Les dépendances de `handle()` sont résolues et le code de sortie est celui attendu |
| Configuration non publiée ou non définie | La liste et l'aide fonctionnent, et la configuration manquante est correctement gérée à l'exécution |
| Mise à jour du package ou de Laravel | Les noms, arguments et options de commande existants restent utilisables |

Pour le `StatusCommand` ci-dessus, vous pouvez vérifier la sortie et le code de sortie comme suit dans un environnement de test qui charge le provider. Pour l'infrastructure de test, consultez [Tester des packages Laravel avec Orchestra Testbench](/fr/advanced/package-testing), et pour le détail des assertions, consultez [Tests de console](/fr/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);
}
```

En complément, pour les commandes qui utilisent des services lourds, vérifiez aussi que les méthodes métier du service ne sont pas appelées lors de l'exécution de la liste ou de l'aide. Vérifier uniquement que l'enregistrement fonctionne ne permet pas de détecter l'impact sur des commandes sans rapport.

## Sources primaires consultées

* [Documentation officielle de Laravel : enregistrement des commandes d'un package](https://github.com/laravel/docs/blob/13.x/packages.md#commands)
* [Documentation officielle de Laravel : structure des commandes et injection de dépendances](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(), resolve()](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Console/Application.php)
* [Console Kernel : getArtisan() et enregistrement du loader](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Foundation/Console/Kernel.php)
* [Command : constructeur et execute()](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Console/Command.php)
* [Tests officiels : résolution des commandes et enregistrement d'alias via AsCommand](https://github.com/laravel/framework/blob/v13.35.0/tests/Console/ConsoleApplicationTest.php)


## Related topics

- [Hooks de résolution des services d'un package](/fr/advanced/package-service-resolution.md)
- [Enregistrement et cache des routes d'un package](/fr/advanced/package-routes.md)
- [Surcharge et mise à jour des traductions d'un package](/fr/advanced/package-translations.md)
- [Cache d'un package et intégration à optimize](/fr/advanced/package-optimization.md)
- [Développement de packages Laravel](/fr/advanced/package-development.md)


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