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

# Registro de comandos Artisan de paquetes y resolución de dependencias

> Recorre el camino desde commands() hasta el arranque de Artisan en Laravel 13 y aprende a separar la creación y la ejecución de comandos, la inyección de dependencias, los providers diferidos y las precauciones para el mantenimiento a largo plazo.

Cuando quieras añadir a tu paquete comandos Artisan de diagnóstico, sincronización o instalación, considera por separado el registro del comando, la creación de la instancia y la ejecución del proceso. Si resuelves en el constructor servicios que solo se necesitan durante la ejecución, puedes acabar afectando incluso al arranque de comandos Artisan que no tienen nada que ver.

Esta página revisa la implementación de Laravel Framework `v13.35.0` y profundiza en el ejemplo de registro con `commands()` de [Fundamentos del desarrollo de paquetes](/es/advanced/package-development). Para implementar la generación de archivos, consulta [GeneratorCommand](/es/advanced/generator-command); para participar en las tareas de optimización, consulta [Integración con optimize](/es/advanced/package-optimization).

## Registrar los comandos explícitamente desde el provider

Los comandos de un paquete se registran por nombre de clase en el método `boot()` del service provider. El descubrimiento automático de `app/Console/Commands` de la aplicación y el registro de las clases que proporciona un paquete son puntos de entrada distintos.

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

Este ejemplo supone que el provider se carga mediante el [descubrimiento de paquetes](/es/advanced/package-discovery) o mediante un registro explícito en la aplicación. Colocar simplemente una clase de comando no hace que el provider del paquete se registre automáticamente.

`runningInConsole()` comprueba si la aplicación se ejecuta en un entorno de consola, no si se está ejecutando un comando concreto. Si también ofreces la posibilidad de llamar a `Artisan::call()` desde una petición HTTP, ten en cuenta que esta condición puede impedir el registro y define el contrato de uso, incluida la opción de registrar sin condiciones. Para ejecutar comandos desde el código, consulta [Artisan](/es/artisan).

## commands() no ejecuta el comando en ese momento

`ServiceProvider::commands()` registra en `Illuminate\Console\Application::starting()` un closure que utiliza los nombres de clase recibidos. En este punto no se llama al `handle()` del comando.

Con el Console Kernel habitual, los callbacks de arranque registrados se ejecutan más tarde, cuando se crea la Artisan Application. Allí, `resolveCommands()` pasa cada clase a `resolve()` y continúa con el registro de los comandos.

```mermaid theme={null}
flowchart TD
    A["boot() del provider"] --> B["commands() registra un callback de arranque"]
    B --> C["El Console Kernel crea la Artisan Application"]
    C --> D["resolveCommands() registra las clases"]
    D --> E["Las clases normales se instancian con el contenedor"]
    E --> F["Se ejecuta el comando seleccionado"]
    F --> G["Se resuelven las dependencias de handle() y se llama"]
```

Lo importante aquí es que **la instancia de un comando no se crea necesariamente en el mismo momento en que se ejecuta**. En las clases normales que no usan la ruta específica de registro diferido que se describe más adelante, `resolve()` llama al `make()` del contenedor, por lo que el constructor y sus dependencias se resuelven en el momento del registro.

Por lo tanto, el constructor de un comando del paquete puede ejecutarse aunque solo ejecutes `php artisan list` u otro comando. Colocar `commands()` en `boot()` no garantiza por sí mismo que la creación de la instancia se retrase hasta la ejecución.

## Inyectar las dependencias de ejecución en handle()

Como ejemplo, crearemos un comando que solo muestra si la configuración del paquete está activada o desactivada. Al igual que en la documentación oficial de Artisan de Laravel 13, el nombre y la descripción se definen con los atributos `Signature` y `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;
    }
}
```

Este ejemplo no añade un constructor, sino que recibe el Repository de configuración como argumento de `handle()`. Como `Command::execute()` utiliza el `call()` del contenedor en la ruta de ejecución del comando, se inyectan las dependencias con tipo declarado.

Si usas clientes de API externas o servicios de sincronización, también puedes inyectarlos en el mismo lugar cuando solo se necesiten durante la ejecución. Sin embargo, si la creación del propio servicio o el `boot()` de otro provider tiene efectos secundarios, estos no se evitan automáticamente.

| Ubicación | Momento en que se evalúa en un comando normal | Adecuado para |
| - | - | - |
| `boot()` del provider | Al arrancar el provider | Registrar las clases de comando |
| Constructor del comando | Al crear la instancia, que no siempre es justo antes de la ejecución | Inicialización ligera |
| Argumentos con tipo de `handle()` | Al ejecutar el comando | Resolver los servicios necesarios para la ejecución |
| Cuerpo de `handle()` | Al ejecutar el comando | Validación de la entrada, lógica de negocio, visualización de resultados |

<Warning>
  No te conectes a API externas, no actualices la base de datos ni sobrescribas archivos publicados en el constructor. Los usuarios del paquete deben poder usar `list` y `help` justo después de la instalación, incluso cuando la configuración de conexión aún no está completa.
</Warning>

Si defines tu propio constructor, llama también a `parent::__construct()`. El constructor padre de Laravel configura el comando a partir de los atributos, la signature, la description, etc.

## Signature y el registro diferido son mecanismos distintos

El `Application::resolve()` que revisamos tiene una rama que lee el `name` del atributo `AsCommand` de Symfony y guarda la correspondencia entre el nombre y la clase en un command map. En esta rama no se llama al `make()` del contenedor en ese momento. Más tarde, el Console Kernel llama a `setContainerCommandLoader()` y configura un loader que utiliza este map.

Por otro lado, el atributo `Signature` de Laravel que usamos en el ejemplo anterior es independiente de esta comprobación de `AsCommand`. **Añadir solo `Signature` no hace que la clase entre en esta ruta de registro diferido.**

Trata esta diferencia como un punto de atención al leer la implementación interna y, en primer lugar, reduce el coste de creación y los efectos secundarios con la definición oficial de comandos y la inyección de dependencias en `handle()`. Aunque exista un loader diferido, la información del comando puede ser necesaria para el listado, la ayuda, etc., así que no diseñes partiendo de que «el comando nunca se crea salvo cuando se selecciona y se ejecuta».

## No diferir el registro de los comandos

Cargar de forma diferida los bindings de servicios y dar a conocer un comando a Artisan son cosas distintas. Si un provider implementa `DeferrableProvider`, es posible que su `boot()` ni siquiera se ejecute hasta que se necesite el servicio correspondiente.

Si colocas el registro de comandos únicamente en ese `boot()` y el provider no se carga antes de que arranque Artisan, tampoco se registra el callback de arranque que da a conocer el comando. Aunque un servicio que hayas trasladado a la inyección de dependencias de `handle()` cargue el provider diferido, eso no resuelve el problema que ocurre antes de que Artisan encuentre el comando.

Para garantizar que los comandos estén disponibles, puedes colocar el registro en un provider de carga inmediata y separar únicamente los bindings de servicios pesados en otro provider diferido. Para conocer los requisitos concretos, consulta [Service providers diferidos](/es/advanced/deferred-provider).

## Qué comprobar al actualizar

Los nombres de comandos, los argumentos, las opciones, la salida y los códigos de salida son interfaces públicas que utilizan los scripts de shell y la CI de los usuarios. No los cambies con la misma ligereza que los detalles internos de una clase; comprueba la compatibilidad y los pasos de migración.

| Caso | Qué comprobar |
| - | - |
| Instalación en una aplicación limpia | El provider se carga y se encuentra el nombre del comando |
| `php artisan list` | No se inicia la lógica de ejecución del paquete ni las conexiones externas |
| `php artisan help courier:status` | Se pueden consultar la descripción y la especificación de la entrada |
| Ejecución real del comando | Se resuelven las dependencias de `handle()` y se devuelve el código de salida previsto |
| Configuración no publicada o sin definir | El listado y la ayuda no fallan y la configuración que falta se gestiona adecuadamente durante la ejecución |
| Actualización del paquete o de Laravel | Los nombres de comandos, argumentos y opciones existentes siguen funcionando |

Con el `StatusCommand` anterior, puedes verificar la salida y el código de salida de la siguiente manera en un entorno de pruebas donde se haya cargado el provider. Para la infraestructura de pruebas, consulta [Pruebas de paquetes](/es/advanced/package-testing); para los detalles de las aserciones, consulta [Console Tests](/es/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);
}
```

Además, en los comandos que utilizan servicios pesados, verifica también que no se llame a los métodos de negocio del servicio al ejecutar el listado o la ayuda. Comprobar únicamente que el comando se puede registrar no detecta su impacto en comandos no relacionados.

## Fuentes primarias consultadas

* [Documentación oficial de Laravel: registro de comandos de paquetes](https://github.com/laravel/docs/blob/13.x/packages.md#commands)
* [Documentación oficial de Laravel: estructura de comandos e inyección de dependencias](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() y resolve()](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Console/Application.php)
* [Console Kernel: getArtisan() y registro del loader](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Foundation/Console/Kernel.php)
* [Command: constructor y execute()](https://github.com/laravel/framework/blob/v13.35.0/src/Illuminate/Console/Command.php)
* [Pruebas oficiales: resolución de comandos y registro de alias con AsCommand](https://github.com/laravel/framework/blob/v13.35.0/tests/Console/ConsoleApplicationTest.php)


## Related topics

- [Hooks de resolución de servicios en paquetes](/es/advanced/package-service-resolution.md)
- [Desarrollo de paquetes para Laravel](/es/advanced/package-development.md)
- [Caché de paquetes e integración con optimize](/es/advanced/package-optimization.md)
- [Service providers diferidos](/es/advanced/deferred-provider.md)
- [Registro de rutas y caché en paquetes](/es/advanced/package-routes.md)


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