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

# Artisan console

> Build custom CLI commands with Artisan to automate repetitive tasks, import data, run maintenance operations, and interact with your application from the terminal.

## What is Artisan?

Artisan is the command-line interface bundled with Laravel. It lives at the project root as the `artisan` script and provides dozens of commands to help you build and maintain your application.

List all available commands:

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

Get help for a specific command:

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

<Info>
  If you use Laravel Sail, replace `php artisan` with `sail artisan`. Commands run inside the Docker container.

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

## Tinker (REPL)

[Laravel Tinker](https://github.com/laravel/tinker) is an interactive REPL that lets you interact with your application directly from the terminal—query Eloquent models, dispatch jobs, fire events, and more.

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

Once inside Tinker you can run any PHP code:

```php theme={null}
>>> App\Models\User::find(1)
>>> App\Models\User::factory()->create()
>>> app(App\Services\OrderService::class)->process(1)
```

<Tip>
  Tinker writes to your real database. Limit its use to local or staging environments, and be careful on production.
</Tip>

## Writing commands

### Generating a command

Use `make:command` to scaffold a new command class in `app/Console/Commands`:

```shell theme={null}
php artisan make:command SendWeeklyReport
```

### Command structure

In Laravel 13, define the command signature and description with PHP attributes. The `handle()` method contains the logic:

```php theme={null}
<?php

namespace App\Console\Commands;

use App\Models\User;
use App\Support\DripEmailer;
use Illuminate\Console\Attributes\Description;
use Illuminate\Console\Attributes\Signature;
use Illuminate\Console\Command;

#[Signature('mail:send {user : The ID of the user to email}')]
#[Description('Send a marketing email to a user')]
class SendEmails extends Command
{
    /**
     * Execute the console command.
     */
    public function handle(DripEmailer $drip): void
    {
        $drip->send(User::find($this->argument('user')));
    }
}
```

<Info>
  You can also use the classic `$signature` and `$description` properties instead of attributes—both work in Laravel 13.

  ```php theme={null}
  protected $signature = 'mail:send {user}';
  protected $description = 'Send a marketing email to a user';
  ```
</Info>

<Tip>
  Keep commands thin. Delegate the actual work to service classes so your logic stays reusable and testable.
</Tip>

### Exit codes

A command exits with `0` (success) by default. Return an integer from `handle()` to set a custom exit code:

```php theme={null}
public function handle(): int
{
    $this->error('Something went wrong.');
    return 1;
}
```

Or call `fail()` to immediately terminate with exit code `1`:

```php theme={null}
$this->fail('Something went wrong.');
```

### Closure commands

Define lightweight commands directly in `routes/console.php` without a full class:

```php theme={null}
use Illuminate\Support\Facades\Artisan;

Artisan::command('inspire', function () {
    $this->info(Inspiring::quote());
})->purpose('Display an inspiring quote');
```

## Defining input

### Arguments and options

Define arguments and options in the `$signature` string (or `#[Signature]` attribute):

```php theme={null}
protected $signature = 'import:products
                        {file : Path to the CSV file}
                        {--limit= : Maximum number of rows to import}
                        {--dry-run : Preview without saving to the database}';
```

| Syntax                | Type                  | Notes                                 |
| --------------------- | --------------------- | ------------------------------------- |
| `{file}`              | Required argument     | Missing value causes an error         |
| `{file?}`             | Optional argument     | Defaults to `null` when omitted       |
| `{file=products.csv}` | Argument with default | Uses the default when omitted         |
| `{--queue}`           | Flag option           | `true` when passed, `false` otherwise |
| `{--limit=}`          | Value option          | Passed as `--limit=100`               |
| `{--limit=50}`        | Option with default   | Uses `50` when omitted                |
| `{--Q\|queue}`        | Option with shortcut  | `-Q` is equivalent to `--queue`       |

### Retrieving input

```php theme={null}
public function handle(): void
{
    $file   = $this->argument('file');
    $limit  = $this->option('limit');
    $dryRun = $this->option('dry-run');

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

    if ($dryRun) {
        $this->warn('Dry run — no data will be saved.');
    }
}
```

### `input()` — typed accessors

The `input()` method lets you retrieve command arguments and options through the same typed accessors available on HTTP requests.

```php theme={null}
use App\Enums\ReportType;

public function handle(): void
{
    $from = $this->input()->date('from');
    $type = $this->input()->enum('type', ReportType::class);
    $limit = $this->input()->integer('limit');
}
```

Pass a name directly to retrieve a specific value from either arguments or options:

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

<Tip>
  While `argument()` and `option()` return strings, the typed accessors on `input()` convert values to the appropriate type. Use them for type-safe dates, numbers, enums, and similar values.
</Tip>

## Interacting with the user

### Output methods

```php theme={null}
$this->info('Operation completed successfully.');  // Green
$this->warn('This cannot be undone.');             // Yellow
$this->error('An error occurred.');                // Red
$this->line('Plain text output.');                 // No color
$this->comment('Additional context here.');        // Grey
```

### Prompting for input

```php theme={null}
// Free-text input
$name = $this->ask('Enter the operator name');

// Input with a default
$env = $this->ask('Select environment', 'production');

// Hidden input for secrets
$apiKey = $this->secret('Enter your API key');

// Yes / No confirmation
if (! $this->confirm('Import to the production database?')) {
    $this->info('Cancelled.');
    return;
}

// Select from a list
$format = $this->choice('Choose an output format', ['csv', 'json', 'xml'], 0);
```

### Progress bars

Display progress for long-running operations:

```php theme={null}
use App\Models\Product;

// Automatic progress bar over a collection
$this->withProgressBar(Product::cursor(), function (Product $product) {
    $this->processProduct($product);
});
```

Manual control for more flexibility:

```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();
```

## Practical example: data import command

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

  <Step title="Implement the command">
    ```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 to the CSV file}
                                {--limit= : Maximum rows to import}
                                {--dry-run : Preview without saving}';

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

        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 not found: {$filePath}");
                return;
            }

            $handle  = fopen($filePath, 'r');
            $headers = fgetcsv($handle);
            $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} rows found (dry run — nothing was saved).");
            } else {
                $this->info("Imported {$count} products successfully.");
            }
        }
    }
    ```
  </Step>

  <Step title="Run the command">
    ```shell theme={null}
    # Standard import
    php artisan import:products storage/products.csv

    # Limit the number of rows
    php artisan import:products storage/products.csv --limit=100

    # Preview without saving
    php artisan import:products storage/products.csv --dry-run
    ```
  </Step>
</Steps>

## Scheduling commands

Register commands in `routes/console.php` to run them on a schedule. See the [Task scheduling](/en/scheduling) guide for details.

```php theme={null}
use Illuminate\Support\Facades\Schedule;

// Every night at 02:00
Schedule::command('orders:prune --days=90')->dailyAt('2:00');

// Every Monday at 09:00
Schedule::command('import:products storage/weekly.csv')->weeklyOn(1, '9:00');
```

## Testing commands

Use the `artisan()` testing helper to simulate running a command:

```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
    {
        Order::factory()->create([
            'status'     => 'completed',
            'created_at' => now()->subDays(100),
        ]);

        Order::factory()->create([
            'status'     => 'completed',
            'created_at' => now()->subDays(10),
        ]);

        $this->artisan('orders:prune', ['--days' => 90])
            ->expectsConfirmation('Delete completed orders older than 90 days?', 'yes')
            ->expectsOutput('Deleted 1 order(s).')
            ->assertExitCode(0);

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

| Assertion method                  | Purpose                                |
| --------------------------------- | -------------------------------------- |
| `expectsOutput('...')`            | Assert specific text appears in output |
| `expectsQuestion('?', 'answer')`  | Simulate `ask()` input                 |
| `expectsConfirmation('?', 'yes')` | Simulate `confirm()` input             |
| `expectsChoice('?', 'option')`    | Simulate `choice()` selection          |
| `assertExitCode(0)`               | Assert the exit code (0 = success)     |
| `assertFailed()`                  | Assert a non-zero exit code            |

## Common commands

```shell theme={null}
php artisan make:command CommandName  # Scaffold a new command class
php artisan list                       # List all available commands
php artisan help command:name          # Show usage for a command
php artisan tinker                     # Open the interactive REPL
```


## Related topics

- [Laravel Prompts](/en/prompts.md)
- [Processes](/en/processes.md)
- [Task scheduling](/en/scheduling.md)
- [Deferred Service Providers](/en/advanced/deferred-provider.md)
- [Console Tests](/en/console-tests.md)
