0

Arguments, options, and interactive input — ask(), choice()

Intermediate5 min read·lv-25-002

Concept

Command output methods provide structured, colored, formatted output to the terminal. Good command output is important for both human operators and CI/CD logs.

Basic output:

  • $this->info(string $message): Green text — success/general info.
  • $this->line(string $message): Default terminal color — neutral info.
  • $this->comment(string $message): Yellow text — notices, warnings.
  • $this->warn(string $message): Yellow text with "WARNING" prefix.
  • $this->error(string $message): Red text — errors.
  • $this->alert(string $message): Highlighted alert box.

Formatted output:

  • $this->table(array $headers, array $rows): Render a table with columns.
  • $this->newLine(int $count = 1): Print blank lines.
  • $this->listing(array $elements): Bulleted list.

Progress bar:

php
$bar = $this->output->createProgressBar($total);
$bar->start();
foreach ($items as $item) { processItem($item); $bar->advance(); }
$bar->finish();
$this->newLine();

Verbosity: -v = verbose, -vv = very verbose, -vvv = debug. Check with $this->output->isVerbose(), isVeryVerbose(), isDebug(). Write verbose output: $this->line('Debug info', null, 'vvv') or use if ($this->output->isVerbose()) { ... }.

$this->components: Laravel 9+ styled component output. $this->components->info(), $this->components->error(), $this->components->task('Name', fn() => ...).

Code Example

php
<?php
namespace App\Console\Commands;

use Illuminate\Console\Command;

class ProcessOrders extends Command
{
    protected $signature = 'orders:process {--dry-run}';
    protected $description = 'Process all pending orders';

    public function handle(): int
    {
        // Styled output (Laravel 9+)
        $this->components->info('Starting order processing...');

        $orders = \App\Models\Order::where('status', 'pending')->get();

        if ($orders->isEmpty()) {
            $this->comment('No pending orders found.');
            return self::SUCCESS;
        }

        // Table output
        $this->table(
            ['ID', 'Customer', 'Total', 'Created'],
            $orders->map(fn($o) => [
                $o->id, $o->customer->name, "\${$o->total}", $o->created_at->diffForHumans()
            ])->toArray()
        );

        if (!$this->confirm("Process {$orders->count()} orders?")) {
            return self::SUCCESS;
        }

        // Progress bar
        $bar = $this->output->createProgressBar($orders->count());
        $bar->setFormat(' %current%/%max% [%bar%] %percent:3s%% %elapsed:6s%/%estimated:-6s%');
        $bar->start();

        $succeeded = 0;
        $failed    = 0;

        foreach ($orders as $order) {
            try {
                if (!$this->option('dry-run')) {
                    $order->process();
                }
                $succeeded++;
            } catch (\Exception $e) {
                $failed++;
                if ($this->output->isVerbose()) {
                    $this->error("Order {$order->id} failed: {$e->getMessage()}");
                }
            }
            $bar->advance();
        }

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

        $this->table(
            ['Result', 'Count'],
            [['Succeeded', $succeeded], ['Failed', $failed]]
        );

        return $failed > 0 ? self::FAILURE : self::SUCCESS;
    }
}