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

# GeneratorCommand — implementación de comandos make: personalizados

> Explica cómo crear comandos make: propios de tu paquete extendiendo Illuminate\Console\GeneratorCommand. Presenta desde la creación del archivo stub hasta el control del namespace, de forma práctica.

## Qué es GeneratorCommand

`Illuminate\Console\GeneratorCommand` es la clase abstracta base para todos los comandos de generación de código de Laravel. `make:model`, `make:controller`, `make:request`, etc. la extienden.

```mermaid theme={null}
flowchart TD
    A["GeneratorCommand<br>(clase abstracta)"] --> B["make:model<br>ModelMakeCommand"]
    A --> C["make:request<br>RequestMakeCommand"]
    A --> D["make:controller<br>ControllerMakeCommand"]
    A --> E["make:handler<br>Tu comando"]
```

Si un paquete ofrece su propio comando `make:xxx`, en lugar de crear las clases a mano el usuario ejecuta `php artisan make:handler OrderHandler` y obtiene el archivo generado con el namespace correcto.

<Info>
  `GeneratorCommand` apenas está documentado oficialmente y no se puede entender sin leer el código fuente del framework. Es un tema avanzado típico.
</Info>

## Implementación mínima

Lo único obligatorio al extender `GeneratorCommand` es implementar el método `getStub()`. El resto de propiedades es opcional, pero en la práctica se declaran las siguientes.

| Miembro                 | Tipo                 | Rol                                                                               |
| ----------------------- | -------------------- | --------------------------------------------------------------------------------- |
| `$name`                 | Propiedad            | Nombre del comando (por ejemplo, `make:handler`).                                 |
| `$description`          | Propiedad            | Descripción del comando.                                                          |
| `$type`                 | Propiedad            | Nombre del tipo generado. Se usa en el mensaje de éxito (por ejemplo, `Handler`). |
| `getStub()`             | Método (obligatorio) | Devuelve la ruta del archivo stub.                                                |
| `getDefaultNamespace()` | Método               | Controla el namespace por defecto del destino.                                    |

Ejemplo de implementación del comando `make:handler`.

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

namespace Vendor\Package\Console\Commands;

use Illuminate\Console\GeneratorCommand;

class HandlerMakeCommand extends GeneratorCommand
{
    protected $name = 'make:handler';

    protected $description = 'Create a new handler class';

    protected $type = 'Handler';

    protected function getStub(): string
    {
        return __DIR__.'/stubs/handler.stub';
    }

    protected function getDefaultNamespace($rootNamespace): string
    {
        return $rootNamespace.'\Handlers';
    }
}
```

Al configurar `getDefaultNamespace()`, al ejecutar `php artisan make:handler OrderHandler` se genera la clase `App\Handlers\OrderHandler` en `app/Handlers/OrderHandler.php`.

## Creación del archivo stub

Coloca el archivo stub en la ruta devuelta por `getStub()`. Un stub es un archivo PHP plantilla en el que ciertos placeholders se sustituyen por el namespace y el nombre de clase.

<Tree>
  <Tree.Folder name="src" defaultOpen>
    <Tree.Folder name="Console" defaultOpen>
      <Tree.Folder name="Commands" defaultOpen>
        <Tree.File name="HandlerMakeCommand.php" />

        <Tree.Folder name="stubs" defaultOpen>
          <Tree.File name="handler.stub" />
        </Tree.Folder>
      </Tree.Folder>
    </Tree.Folder>
  </Tree.Folder>
</Tree>

Ejemplo de archivo stub:

```php handler.stub theme={null}
<?php

namespace {{ namespace }};

class {{ class }}
{
    public function handle(): void
    {
        //
    }
}
```

`GeneratorCommand` sustituye automáticamente los siguientes placeholders dentro del stub.

| Placeholder           | Valor sustituido                 | Notación alternativa |
| --------------------- | -------------------------------- | -------------------- |
| `{{ namespace }}`     | Namespace de la clase generada.  | `DummyNamespace`     |
| `{{ class }}`         | Nombre de la clase generada.     | `DummyClass`         |
| `{{ rootNamespace }}` | Namespace raíz de la aplicación. | `DummyRootNamespace` |

`{{ namespace }}` y `DummyNamespace` producen el mismo resultado. Los stubs integrados de Laravel usaban ambas formas, pero para stubs nuevos se recomienda `{{ namespace }}`.

## Permitir la personalización del stub

Para permitir que los usuarios sobrescriban el stub, utiliza el patrón `resolveStubPath()`. Primero, publica el stub en el `boot()` del service provider.

```php theme={null}
public function boot(): void
{
    if ($this->app->runningInConsole()) {
        $this->publishes([
            __DIR__.'/../Console/Commands/stubs' => base_path('stubs'),
        ], 'stubs');
    }
}
```

Después, en `getStub()`, usa preferentemente el stub personalizado por el usuario.

```php theme={null}
protected function getStub(): string
{
    return $this->resolveStubPath('/stubs/handler.stub');
}

protected function resolveStubPath(string $stub): string
{
    return file_exists($customPath = $this->laravel->basePath(trim($stub, '/')))
        ? $customPath
        : __DIR__.$stub;
}
```

Los usuarios que ejecuten `php artisan vendor:publish --tag=stubs` podrán editar `stubs/handler.stub` en la raíz del proyecto para modificar la plantilla.

<Tip>
  `resolveStubPath()` es un patrón que también utiliza el `RequestMakeCommand` integrado de Laravel. Si publicas un paquete, adopta este patrón.
</Tip>

## Registro en el service provider

Los comandos se registran en el método `boot()` del service provider. Al comprobar `runningInConsole()`, evitas cargas innecesarias durante las peticiones web.

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

namespace Vendor\Package;

use Illuminate\Support\ServiceProvider;
use Vendor\Package\Console\Commands\HandlerMakeCommand;

class PackageServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        if ($this->app->runningInConsole()) {
            $this->commands([
                HandlerMakeCommand::class,
            ]);

            $this->publishes([
                __DIR__.'/../Console/Commands/stubs' => base_path('stubs'),
            ], 'stubs');
        }
    }
}
```

Configurando `extra.laravel` en el `composer.json` del paquete, los usuarios no tienen que registrar manualmente el service provider.

```json theme={null}
"extra": {
    "laravel": {
        "providers": [
            "Vendor\\Package\\PackageServiceProvider"
        ]
    }
}
```

## Ejemplos de aplicación

Algunos casos de uso donde `GeneratorCommand` brilla.

<AccordionGroup>
  <Accordion title="Extensión de FormRequest">
    Un comando que genera Requests que heredan de una clase base propia con lógica de autenticación o validaciones personalizadas. En `getDefaultNamespace()` devuelves `App\Http\Requests`.
  </Accordion>

  <Accordion title="Generador de DTO">
    Un comando que produce el boilerplate de un Data Transfer Object. Prepara un stub con propiedades readonly y un método factory `from()`.
  </Accordion>

  <Accordion title="Clase Action">
    Un comando que genera clases Action con responsabilidad única. Genera una clase con método `execute()` en el namespace `App\Actions`.
  </Accordion>

  <Accordion title="Livewire usa el mismo mecanismo">
    El comando `make:livewire` está implementado por la clase `MakeCommand`, que extiende `GeneratorCommand`. Sobrescribe `handle()` para generar simultáneamente el archivo de la clase del componente y la vista Blade.
  </Accordion>
</AccordionGroup>

## Pruebas

Con Orchestra Testbench, prueba que tu comando genera correctamente los archivos.

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

namespace Tests\Feature\Console;

use Illuminate\Support\Facades\File;
use Orchestra\Testbench\TestCase;
use Vendor\Package\PackageServiceProvider;

class HandlerMakeCommandTest extends TestCase
{
    protected function getPackageProviders($app): array
    {
        return [PackageServiceProvider::class];
    }

    protected function tearDown(): void
    {
        File::deleteDirectory(app_path('Handlers'));

        parent::tearDown();
    }

    public function test_make_handler_creates_file(): void
    {
        $path = app_path('Handlers/OrderHandler.php');

        $this->artisan('make:handler', ['name' => 'OrderHandler'])
            ->assertSuccessful();

        $this->assertFileExists($path);
        $this->assertStringContainsString('namespace App\Handlers;', File::get($path));
        $this->assertStringContainsString('class OrderHandler', File::get($path));
    }

    public function test_make_handler_does_not_overwrite_existing_file(): void
    {
        $path = app_path('Handlers/OrderHandler.php');
        File::ensureDirectoryExists(dirname($path));
        File::put($path, '<?php // existing');

        $this->artisan('make:handler', ['name' => 'OrderHandler'])
            ->assertFailed();
    }
}
```

<Warning>
  Los comandos generadores escriben en el sistema de archivos real. Limpia siempre en `tearDown()` para que se ejecute incluso si el test falla a mitad.
</Warning>

## Páginas relacionadas

<Columns cols={2}>
  <Card title="Desarrollo de paquetes Laravel" icon="package" href="/es/advanced/package-development">
    Repasa los fundamentos del desarrollo de paquetes con service providers.
  </Card>

  <Card title="Probar paquetes Laravel con Orchestra Testbench" icon="flask-conical" href="/es/advanced/package-testing">
    Aprende a montar la base de las pruebas de tu paquete.
  </Card>
</Columns>

<Info>
  Fuente: [Illuminate\Console\GeneratorCommand](https://github.com/laravel/framework/blob/master/src/Illuminate/Console/GeneratorCommand.php), [Illuminate\Foundation\Console\RequestMakeCommand](https://github.com/laravel/framework/blob/master/src/Illuminate/Foundation/Console/RequestMakeCommand.php)
</Info>


## Related topics

- [Casts personalizados de Eloquent](/es/advanced/eloquent-casts.md)
- [Feed Generator](/es/packages/laravel-bluesky/feed-generator.md)
- [Estructura de directorios](/es/directory-structure.md)
- [Implementación de un guard de autenticación personalizado](/es/advanced/custom-auth-guard.md)
- [Crear un agente personalizado de Boost](/es/advanced/boost-custom-agent.md)
