0

Creating Artisan commands — make:command, $signature, $description

Beginner5 min read·lv-25-001

Concept

Arguments, options, and interactive input make Artisan commands usable by both humans and scheduled processes.

Arguments: Positional values required (or optional) from the command line. {name} = required. {name?} = optional. {name=default} = with default. {names*} = variadic (array of values).

Options: Named flags. {--force} = boolean (present or not). {--count=} = option with required value. {--count=10} = option with default value. {--C|count=} = with short alias -C.

Accessing in handle(): $this->argument('name'), $this->arguments() (all), $this->option('force'), $this->options() (all).

Interactive input:

  • $this->ask(string $question, $default = null): Free-text prompt.
  • $this->secret(string $question): Hidden input (for passwords).
  • $this->confirm(string $question, bool $default = false): Yes/no prompt. Returns bool.
  • $this->choice(string $question, array $choices, $default = null): Selection from list.
  • $this->anticipate(string $question, array $choices): Auto-completing prompt.

$this->runningInConsole(): Check if running in console context (vs HTTP). Actually use app()->runningInConsole().

Production safeguard: Use $this->confirm('Are you sure? This affects production!') before destructive operations.

Code Example

php
<?php
namespace App\Console\Commands;

use Illuminate\Console\Command;

class ImportUsers extends Command
{
    protected $signature = 'users:import
                            {file : Path to the CSV file}
                            {--skip-header : Skip the first row (header)}
                            {--batch=100 : Batch size for processing}
                            {--dry-run : Validate without actually importing}
                            {--email=* : Only import users with these emails}';

    protected $description = 'Import users from a CSV file';

    public function handle(): int
    {
        $file    = $this->argument('file');
        $batch   = (int) $this->option('batch');
        $dryRun  = $this->option('dry-run');
        $emails  = $this->option('email'); // array

        // Validation
        if (!file_exists($file)) {
            $this->error("File not found: {$file}");
            return self::FAILURE;
        }

        // Interactive confirmation in production
        if (!$this->option('dry-run') && app()->isProduction()) {
            if (!$this->confirm('You are running this in PRODUCTION. Continue?')) {
                $this->line('Aborted.');
                return self::SUCCESS;
            }
        }

        // Dynamic option asking
        $sendWelcome = $this->confirm('Send welcome emails to imported users?', false);

        // Reading with access to all option types
        $this->info("Config:");
        $this->table(
            ['Setting', 'Value'],
            [
                ['File',         $file],
                ['Batch size',   $batch],
                ['Dry run',      $dryRun ? 'Yes' : 'No'],
                ['Welcome mail', $sendWelcome ? 'Yes' : 'No'],
                ['Email filter', $emails ? implode(', ', $emails) : 'None'],
            ]
        );

        // Process
        // ...

        return self::SUCCESS;
    }
}

// Calling with options
// php artisan users:import users.csv
// php artisan users:import users.csv --skip-header --batch=500
// php artisan users:import users.csv --dry-run
// php artisan users:import users.csv --email=a@b.com --email=c@d.com
// php artisan users:import users.csv -vvv  (verbose output)