Skip to main content

Documentation Index

Fetch the complete documentation index at: https://kawax.biz/llms.txt

Use this file to discover all available pages before exploring further.

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:
php artisan list
Get help for a specific command:
php artisan help migrate
If you use Laravel Sail, replace php artisan with sail artisan. Commands run inside the Docker container.
./vendor/bin/sail artisan list

Tinker (REPL)

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.
php artisan tinker
Once inside Tinker you can run any PHP code:
>>> App\Models\User::find(1)
>>> App\Models\User::factory()->create()
>>> app(App\Services\OrderService::class)->process(1)
Tinker writes to your real database. Limit its use to local or staging environments, and be careful on production.

Writing commands

Generating a command

Use make:command to scaffold a new command class in app/Console/Commands:
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

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')));
    }
}
You can also use the classic $signature and $description properties instead of attributes—both work in Laravel 13.
protected $signature = 'mail:send {user}';
protected $description = 'Send a marketing email to a user';
Keep commands thin. Delegate the actual work to service classes so your logic stays reusable and testable.

Exit codes

A command exits with 0 (success) by default. Return an integer from handle() to set a custom exit code:
public function handle(): int
{
    $this->error('Something went wrong.');
    return 1;
}
Or call fail() to immediately terminate with exit code 1:
$this->fail('Something went wrong.');

Closure commands

Define lightweight commands directly in routes/console.php without a full class:
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):
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}';
SyntaxTypeNotes
{file}Required argumentMissing value causes an error
{file?}Optional argumentDefaults to null when omitted
{file=products.csv}Argument with defaultUses the default when omitted
{--queue}Flag optiontrue when passed, false otherwise
{--limit=}Value optionPassed as --limit=100
{--limit=50}Option with defaultUses 50 when omitted
{--Q|queue}Option with shortcut-Q is equivalent to --queue

Retrieving input

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.');
    }
}

Interacting with the user

Output methods

$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

// 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:
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:
$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

1

Generate the command

php artisan make:command ImportProducts
2

Implement the command

<?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.");
        }
    }
}
3

Run the command

# 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

Scheduling commands

Register commands in routes/console.php to run them on a schedule. See the Task scheduling guide for details.
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

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 methodPurpose
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

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
Last modified on March 29, 2026