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

# Console Artisan

> Come creare comandi CLI personalizzati usando la console Artisan di Laravel.

## 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`.

```shell theme={null}
php artisan list
```

Per vedere come si usa un comando anteponi `help`.

```shell theme={null}
php artisan help migrate
```

<Info>
  Se usi Laravel Sail, sostituisci `php artisan` con `sail artisan`.
  I comandi verranno eseguiti nel container Docker.

  ```shell theme={null}
  ./vendor/bin/sail artisan list
  ```
</Info>

## Tinker

[Laravel Tinker](https://github.com/laravel/tinker) è un ambiente REPL che permette di interagire in modo interattivo con l'intera applicazione — modelli Eloquent, job, eventi — da riga di comando.

```shell theme={null}
php artisan tinker
```

Dopo l'avvio puoi eseguire direttamente codice PHP.

```php theme={null}
// 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)
```

<Tip>
  Tinker modifica realmente i dati. Fai molta attenzione a eseguirlo in produzione.
  È adatto a verifica e debug in ambiente locale o di staging.
</Tip>

## Creazione di comandi personalizzati

### Generare un comando

Con `make:command` generi lo scheletro di una classe comando.

```shell theme={null}
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 theme={null}
<?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.');
    }
}
```

<Info>
  In Laravel 13 puoi usare anche la sintassi con gli attribute PHP.

  ```php theme={null}
  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
      {
          // ...
      }
  }
  ```
</Info>

### Definizione di argomenti e opzioni

In `$signature` definisci in un'unica stringa nome, argomenti e opzioni del comando.

```php theme={null}
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)}';
```

| Sintassi              | Tipo                   | Descrizione                           |
| --------------------- | ---------------------- | ------------------------------------- |
| `{file}`              | Argomento obbligatorio | Omesso genera errore                  |
| `{file?}`             | Argomento opzionale    | Se omesso vale `null`                 |
| `{file=products.csv}` | Argomento con default  | Se omesso usa il valore predefinito   |
| `{--queue}`           | Opzione flag           | `true` se indicata, `false` se omessa |
| `{--limit=}`          | Opzione con valore     | Si passa come `--limit=100`           |
| `{--limit=50}`        | Opzione con default    | Se omessa vale `50`                   |
| `{--Q\|queue}`        | Con abbreviazione      | Puoi indicarla anche come `-Q`        |

### Lettura di argomenti e opzioni

All'interno di `handle()` usa `argument()` e `option()` per leggere i valori.

```php theme={null}
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.

```php theme={null}
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).

```php theme={null}
$queue = $this->input('queue', 'default');
```

<Tip>
  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.
</Tip>

## Interazione con l'utente

### Emissione di messaggi

Sono disponibili metodi per emettere messaggi colorati in console.

```php theme={null}
$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.

```php theme={null}
// 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.

```php theme={null}
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.

```php theme={null}
$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.

<Steps>
  <Step title="Genera il comando">
    ```shell theme={null}
    php artisan make:command ImportProducts
    ```
  </Step>

  <Step title="Implementa il comando">
    ```php theme={null}
    <?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.");
            }
        }
    }
    ```
  </Step>

  <Step title="Esegui il comando">
    ```shell theme={null}
    # 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
    ```
  </Step>
</Steps>

### Comando di manutenzione periodica

Esempio di comando di manutenzione periodica che elimina i dati vecchi.

```php theme={null}
<?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](/it/scheduling).

```php theme={null}
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 theme={null}
<?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);
    }
}
```

<Info>
  Esempi di metodi di asserzione utilizzabili con `artisan()`:

  | Metodo                             | Descrizione                                       |
  | ---------------------------------- | ------------------------------------------------- |
  | `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 |
</Info>

## Comandi frequenti

```shell theme={null}
# 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
```


## Related topics

- [Laravel Prompts](/it/prompts.md)
- [Processi](/it/processes.md)
- [Service provider differiti](/it/advanced/deferred-provider.md)
- [Laravel Console Starter](/it/packages/laravel-console-starter/index.md)
- [Tutorial - Laravel Console Starter](/it/packages/laravel-console-starter/tutorial.md)
