Saltar al contenido principal

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. 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.
GeneratorCommand apenas está documentado oficialmente y no se puede entender sin leer el código fuente del framework. Es un tema avanzado típico.

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.
MiembroTipoRol
$namePropiedadNombre del comando (por ejemplo, make:handler).
$descriptionPropiedadDescripción del comando.
$typePropiedadNombre 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étodoControla el namespace por defecto del destino.
Ejemplo de implementación del comando make:handler.
<?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.
src
Console
Commands
HandlerMakeCommand.php
stubs
handler.stub
Ejemplo de archivo stub:
handler.stub
<?php

namespace {{ namespace }};

class {{ class }}
{
    public function handle(): void
    {
        //
    }
}
GeneratorCommand sustituye automáticamente los siguientes placeholders dentro del stub.
PlaceholderValor sustituidoNotació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.
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.
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.
resolveStubPath() es un patrón que también utiliza el RequestMakeCommand integrado de Laravel. Si publicas un paquete, adopta este patrón.

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

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.
"extra": {
    "laravel": {
        "providers": [
            "Vendor\\Package\\PackageServiceProvider"
        ]
    }
}

Ejemplos de aplicación

Algunos casos de uso donde GeneratorCommand brilla.
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.
Un comando que produce el boilerplate de un Data Transfer Object. Prepara un stub con propiedades readonly y un método factory from().
Un comando que genera clases Action con responsabilidad única. Genera una clase con método execute() en el namespace App\Actions.
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.

Pruebas

Con Orchestra Testbench, prueba que tu comando genera correctamente los archivos.
<?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();
    }
}
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.

Páginas relacionadas

Desarrollo de paquetes Laravel

Repasa los fundamentos del desarrollo de paquetes con service providers.

Probar paquetes Laravel con Orchestra Testbench

Aprende a montar la base de las pruebas de tu paquete.
Última modificación el 13 de julio de 2026