Vai al contenuto principale

Cos’è Artisan

Artisan è lo strumento a riga di comando (CLI) incluso in Laravel. Esiste come script artisan nella root del progetto e offre molti comandi che rendono efficienti sviluppo e gestione operativa. Per vedere l’elenco dei comandi disponibili usa list.
php artisan list
Per vedere come si usa un comando anteponi help.
php artisan help migrate
Se usi Laravel Sail, sostituisci php artisan con sail artisan. I comandi verranno eseguiti nel container Docker.
./vendor/bin/sail artisan list

Tinker

Laravel Tinker è un ambiente REPL che permette di interagire in modo interattivo con l’intera applicazione — modelli Eloquent, job, eventi — da riga di comando.
php artisan tinker
Dopo l’avvio puoi eseguire direttamente codice PHP.
// Recupera un utente e verifica
>>> App\Models\User::find(1)
// Crea un record con una factory
>>> App\Models\User::factory()->create()
// Chiama direttamente una classe di servizio
>>> app(App\Services\OrderService::class)->process(1)
Tinker modifica realmente i dati. Fai molta attenzione a eseguirlo in produzione. È adatto a verifica e debug in ambiente locale o di staging.

Creazione di comandi personalizzati

Generare un comando

Con make:command generi lo scheletro di una classe comando.
php artisan make:command ImportProducts
Viene generato app/Console/Commands/ImportProducts.php.

Struttura base del comando

La classe comando generata ha tre elementi principali: $signature, $description e handle().
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;

class ImportProducts extends Command
{
    /**
     * Definizione del nome del comando, argomenti e opzioni
     */
    protected $signature = 'import:products';

    /**
     * Descrizione del comando (mostrata in php artisan list)
     */
    protected $description = 'Importa i dati dei prodotti da CSV';

    /**
     * Esecuzione del comando
     */
    public function handle(): void
    {
        $this->info('Avvio dell\'importazione...');
        // Scrivi qui la logica
        $this->info('Completato.');
    }
}
In Laravel 13 puoi usare anche la sintassi con gli attribute PHP.
use Illuminate\Console\Attributes\Description;
use Illuminate\Console\Attributes\Signature;

#[Signature('import:products')]
#[Description('Importa i dati dei prodotti da CSV')]
class ImportProducts extends Command
{
    public function handle(): void
    {
        // ...
    }
}

Definizione di argomenti e opzioni

In $signature definisci in un’unica stringa nome, argomenti e opzioni del comando.
protected $signature = 'import:products
                        {file : Path del file CSV da importare}
                        {--limit= : Numero massimo di elementi da importare}
                        {--dry-run : Non salva realmente sul DB (solo verifica)}';
SintassiTipoDescrizione
{file}Argomento obbligatorioOmesso genera errore
{file?}Argomento opzionaleSe omesso vale null
{file=products.csv}Argomento con defaultSe omesso usa il valore predefinito
{--queue}Opzione flagtrue se indicata, false se omessa
{--limit=}Opzione con valoreSi passa come --limit=100
{--limit=50}Opzione con defaultSe omessa vale 50
{--Q|queue}Con abbreviazionePuoi indicarla anche come -Q

Lettura di argomenti e opzioni

All’interno di handle() usa argument() e option() per leggere i valori.
public function handle(): void
{
    $file    = $this->argument('file');       // Lettura di un argomento
    $limit   = $this->option('limit');        // Lettura di un'opzione
    $dryRun  = $this->option('dry-run');      // Opzione flag (bool)

    $this->info("File: {$file}");

    if ($dryRun) {
        $this->warn('Modalità dry-run: il salvataggio sul DB verrà saltato');
    }
}

input() — accessor tipizzati

Con il metodo input() puoi leggere argomenti e opzioni con gli stessi accessor tipizzati delle richieste HTTP.
use App\Enums\ReportType;

public function handle(): void
{
    // Ottieni argomenti e opzioni come istanza CommandInput
    $from = $this->input()->date('from');
    $type = $this->input()->enum('type', ReportType::class);
    $limit = $this->input()->integer('limit');
}
Se vuoi leggere per nome, passalo direttamente (viene cercato sia negli argomenti sia nelle opzioni).
$queue = $this->input('queue', 'default');
Mentre argument() e option() restituiscono stringhe, gli accessor tipizzati di input() convertono nel tipo appropriato. Comodi per gestire in modo type-safe date, numeri ed enum.

Interazione con l’utente

Emissione di messaggi

Sono disponibili metodi per emettere messaggi colorati in console.
$this->info('Elaborazione completata con successo');    // Verde
$this->warn('Questa operazione non è reversibile');    // Giallo
$this->error('Si è verificato un errore');       // Rosso
$this->line('Testo normale');             // Senza colore
$this->comment('Informazione aggiuntiva');               // Grigio chiaro

Richiesta all’utente

Puoi accettare input interattivamente dall’utente.
// Input testuale
$name = $this->ask('Inserisci il nome del responsabile');

// Con valore predefinito
$env = $this->ask('Seleziona l\'ambiente di esecuzione', 'production');

// Informazioni sensibili come password (input non visibile a schermo)
$password = $this->secret('Inserisci la chiave API');

// Conferma Yes/No
if (! $this->confirm('Importare nel DB di produzione?')) {
    $this->info('Operazione annullata.');
    return;
}

// Scelta tra opzioni
$format = $this->choice('Seleziona il formato di output', ['csv', 'json', 'xml'], 0);

Barra di avanzamento

Mostra chiaramente il progresso durante l’elaborazione di grandi volumi di dati.
use App\Models\Product;

// Passando una collection, la progress bar viene mostrata automaticamente
$this->withProgressBar(Product::cursor(), function (Product $product) {
    $this->processProduct($product);
});
Per un controllo più granulare usa il seguente pattern.
$total = Product::count();
$bar = $this->output->createProgressBar($total);
$bar->start();

Product::cursor()->each(function (Product $product) use ($bar) {
    $this->processProduct($product);
    $bar->advance();
});

$bar->finish();
$this->newLine();

Casi d’uso pratici

Comando di importazione dati

Esempio pratico di comando che importa i dati dei prodotti da un CSV.
1

Genera il comando

php artisan make:command ImportProducts
2

Implementa il comando

<?php

namespace App\Console\Commands;

use App\Models\Product;
use Illuminate\Console\Command;

class ImportProducts extends Command
{
    protected $signature = 'import:products
                            {file : Path del file CSV}
                            {--limit= : Numero massimo di elementi da importare}
                            {--dry-run : Solo verifica (non salva sul DB)}';

    protected $description = 'Importa i dati dei prodotti da un file CSV';

    public function handle(): void
    {
        $filePath = $this->argument('file');
        $limit    = $this->option('limit') ? (int) $this->option('limit') : null;
        $dryRun   = $this->option('dry-run');

        if (! file_exists($filePath)) {
            $this->error("File non trovato: {$filePath}");
            return;
        }

        // Leggi il CSV con le funzioni built-in di PHP
        $handle = fopen($filePath, 'r');
        $headers = fgetcsv($handle); // Prima riga come header
        $rows = [];
        while (($row = fgetcsv($handle)) !== false) {
            $rows[] = array_combine($headers, $row);
            if ($limit !== null && count($rows) >= $limit) {
                break;
            }
        }
        fclose($handle);

        $count = 0;

        $this->withProgressBar($rows, function (array $row) use ($dryRun, &$count) {
            if (! $dryRun) {
                Product::updateOrCreate(
                    ['sku' => $row['sku']],
                    [
                        'name'  => $row['name'],
                        'price' => $row['price'],
                    ]
                );
            }
            $count++;
        });

        $this->newLine();

        if ($dryRun) {
            $this->warn("{$count} elementi identificati (dry-run: salvataggio saltato)");
        } else {
            $this->info("Importazione completata: {$count} elementi.");
        }
    }
}
3

Esegui il comando

# Importazione normale
php artisan import:products storage/products.csv

# Limita il numero di elementi
php artisan import:products storage/products.csv --limit=100

# Verifica in dry-run
php artisan import:products storage/products.csv --dry-run

Comando di manutenzione periodica

Esempio di comando di manutenzione periodica che elimina i dati vecchi.
<?php

namespace App\Console\Commands;

use App\Models\Order;
use Illuminate\Console\Command;

class PruneOldOrders extends Command
{
    protected $signature = 'orders:prune {--days=90 : Elimina i dati più vecchi di questi giorni}';

    protected $description = 'Elimina gli ordini completati più vecchi del numero di giorni indicato';

    public function handle(): void
    {
        $days = (int) $this->option('days');

        if (! $this->confirm("Eliminare gli ordini completati più vecchi di {$days} giorni?")) {
            $this->info('Operazione annullata.');
            return;
        }

        $deleted = Order::where('status', 'completed')
            ->where('created_at', '<', now()->subDays($days))
            ->delete();

        $this->info("Eliminati {$deleted} ordini.");
    }
}

Combinazione con l’esecuzione pianificata

Puoi pianificare i comandi creati in routes/console.php. Per i dettagli consulta Task scheduling.
use Illuminate\Support\Facades\Schedule;

// Esegui ogni giorno alle 2 del mattino
Schedule::command('orders:prune --days=90')->dailyAt('2:00');

// Esegui ogni lunedì alle 9
Schedule::command('import:products storage/weekly.csv')->weeklyOn(1, '9:00');

Test dei comandi

Con le funzionalità di test di Laravel puoi verificare il comportamento dei comandi Artisan.
<?php

namespace Tests\Feature\Commands;

use App\Models\Order;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;

class PruneOldOrdersTest extends TestCase
{
    use RefreshDatabase;

    public function test_it_deletes_old_completed_orders(): void
    {
        // Crea un ordine completato più vecchio di 90 giorni
        Order::factory()->create([
            'status'     => 'completed',
            'created_at' => now()->subDays(100),
        ]);

        // Ordine completato recente (non dovrebbe essere eliminato)
        Order::factory()->create([
            'status'     => 'completed',
            'created_at' => now()->subDays(10),
        ]);

        $this->artisan('orders:prune', ['--days' => 90])
            ->expectsConfirmation('Eliminare gli ordini completati più vecchi di 90 giorni?', 'yes')
            ->expectsOutput('Eliminati 1 ordini.')
            ->assertExitCode(0);

        $this->assertDatabaseCount('orders', 1);
    }

    public function test_it_cancels_when_user_declines(): void
    {
        Order::factory()->create(['status' => 'completed', 'created_at' => now()->subDays(100)]);

        $this->artisan('orders:prune', ['--days' => 90])
            ->expectsConfirmation('Eliminare gli ordini completati più vecchi di 90 giorni?', 'no')
            ->expectsOutput('Operazione annullata.')
            ->assertExitCode(0);

        $this->assertDatabaseCount('orders', 1);
    }
}
Esempi di metodi di asserzione utilizzabili con artisan():
MetodoDescrizione
expectsOutput('...')Verifica che il testo indicato sia emesso
expectsQuestion('?', 'risposta')Simula l’input di ask()
expectsConfirmation('?', 'yes')Simula la risposta di confirm()
expectsChoice('?', 'scelta')Simula la selezione di choice()
assertExitCode(0)Verifica il codice di uscita (0 = successo)
assertFailed()Verifica che il codice di uscita sia diverso da 0

Comandi frequenti

# Crea un nuovo comando
php artisan make:command CommandName

# Elenco dei comandi disponibili
php artisan list

# Verifica come si usa un comando
php artisan help command:name

# Avvia Tinker
php artisan tinker
Ultima modifica il 13 luglio 2026