Vai al contenuto principale

Cos’è GeneratorCommand

Illuminate\Console\GeneratorCommand è la classe astratta alla base di tutti i comandi di generazione di codice di Laravel. make:model, make:controller, make:request ne sono tutte estensioni. Fornendo un proprio comando make:xxx nel pacchetto, l’utente non crea più le classi a mano: basta eseguire php artisan make:handler OrderHandler per generare il file con il namespace corretto.
GeneratorCommand è quasi assente dalla documentazione ufficiale: si comprende solo leggendo il codice sorgente del framework. È un tipico argomento avanzato.

Implementazione minima

L’unico metodo obbligatorio nella classe che estende GeneratorCommand è getStub(). Le altre proprietà sono opzionali, ma nella pratica sono presenti.
MembroTipoRuolo
$nameProprietàNome del comando (es: make:handler)
$descriptionProprietàDescrizione del comando
$typeProprietàTipo del risultato, usato nei messaggi di successo (es: Handler)
getStub()Metodo (obbligatorio)Restituisce il path del file stub
getDefaultNamespace()MetodoControlla il namespace di default della generazione
Esempio di implementazione 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';
    }
}
Con getDefaultNamespace() impostato, eseguendo php artisan make:handler OrderHandler viene generata la classe App\Handlers\OrderHandler in app/Handlers/OrderHandler.php.

Creare il file stub

Colloca il file stub nel percorso restituito da getStub(). Lo stub è un file PHP template i cui placeholder vengono sostituiti con namespace e nome classe.
src
Console
Commands
HandlerMakeCommand.php
stubs
handler.stub
Esempio di file stub:
handler.stub
<?php

namespace {{ namespace }};

class {{ class }}
{
    public function handle(): void
    {
        //
    }
}
GeneratorCommand sostituisce automaticamente i placeholder seguenti.
PlaceholderValore sostituitoForma alternativa
{{ namespace }}Namespace della classe generataDummyNamespace
{{ class }}Nome della classe generataDummyClass
{{ rootNamespace }}Namespace root dell’appDummyRootNamespace
{{ namespace }} e DummyNamespace hanno lo stesso risultato. Gli stub integrati di Laravel usavano entrambe le forme; per nuovi progetti consigliamo {{ namespace }}.

Permettere agli utenti di personalizzare lo stub

Per consentire all’utente di sovrascrivere lo stub, usa il pattern resolveStubPath(). Prima pubblichi gli stub dal service provider in boot().
public function boot(): void
{
    if ($this->app->runningInConsole()) {
        $this->publishes([
            __DIR__.'/../Console/Commands/stubs' => base_path('stubs'),
        ], 'stubs');
    }
}
Poi in getStub() prediligi lo stub personalizzato dall’utente.
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;
}
L’utente che esegue php artisan vendor:publish --tag=stubs può modificare il template editando stubs/handler.stub nella root del progetto.
resolveStubPath() è un pattern usato anche in RequestMakeCommand integrato in Laravel. Se pubblichi un pacchetto, adotta questo pattern.

Registrazione nel service provider

Il comando si registra nel metodo boot() del service provider. Verificando runningInConsole() eviti caricamenti inutili in fase di richiesta 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');
        }
    }
}
Impostando extra.laravel nel composer.json del pacchetto, l’utente non deve registrare a mano il service provider.
"extra": {
    "laravel": {
        "providers": [
            "Vendor\\Package\\PackageServiceProvider"
        ]
    }
}

Casi d’uso

Alcuni scenari tipici in cui GeneratorCommand brilla.
Un comando che genera Request che estendono una tua classe base, con logica di autorizzazione e regole di validazione personalizzate. In getDefaultNamespace() restituisci App\Http\Requests.
Un comando che genera il boilerplate dei Data Transfer Object. Prepara uno stub con proprietà readonly e un metodo factory from().
Un comando che genera classi Action a singola responsabilità. Genera una classe nel namespace App\Actions con il metodo execute().
Il comando make:livewire è implementato dalla classe MakeCommand che estende GeneratorCommand. Fa override di handle() per generare in una sola volta due file: la classe componente e la view Blade.

Test

Con Orchestra Testbench verifichi che il comando generi correttamente il file.
<?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();
    }
}
I comandi generator scrivono nel filesystem reale. Fai sempre pulizia in tearDown(), così l’operazione avviene anche se un test fallisce a metà.

Pagine correlate

Sviluppo di pacchetti Laravel

Rivedi le basi dello sviluppo di pacchetti tramite service provider.

Testare pacchetti Laravel con Orchestra Testbench

Rivedi come costruire l’infrastruttura di test per un pacchetto.
Ultima modifica il 13 luglio 2026