Laravel Artisan Commands Reference

A reference for Laravel's Artisan command-line tool: built-in commands and writing your own.

Useful built-in commands

CommandDoes
php artisan serveRuns the built-in dev server.
php artisan tinkerAn interactive REPL with the app already booted — great for poking at models/queries.
php artisan migrateRuns pending migrations. Add --force to run without confirmation in production.
php artisan migrate:rollbackReverts the last migration batch.
php artisan make:model Post -mfcGenerates a model plus, in one go, a migration (-m), factory (-f), and controller (-c).
php artisan route:listLists every registered route.
php artisan config:cacheCompiles all config files into one cached file for production performance.
php artisan config:clear / cache:clear / route:clear / view:clearClear the respective cache — reach for these first when changes don't seem to take effect.
php artisan queue:workProcesses queued jobs.
php artisan schedule:workRuns the task scheduler in the foreground, for local development.
php artisan aboutPrints a summary of the app's environment and configuration.

Writing a custom command

php artisan make:command SendWeeklyDigest
class SendWeeklyDigest extends Command
{
    protected $signature = 'digest:send {--dry-run}';

    protected $description = 'Email the weekly digest to subscribed users';

    public function handle(): int
    {
        $users = User::where('subscribed', true)->get();

        $this->info("Sending to {$users->count()} users...");

        if (! $this->option('dry-run')) {
            $users->each(fn ($user) => Mail::to($user)->send(new WeeklyDigest($user)));
        }

        return self::SUCCESS;
    }
}

See the mail and notifications guide for building a Mailable like WeeklyDigest.

php artisan digest:send
php artisan digest:send --dry-run

Signature syntax

SyntaxMeaning
{user}Required argument.
{user?}Optional argument.
{user=1}Optional argument with a default.
{user : The ID of the user}A trailing description, shown by php artisan help digest:send — makes the command self-documenting without a separate README entry.
{users*}Array argument — accepts multiple values, e.g. digest:send 1 2 3.
{--queue}Boolean flag/option, e.g. --queue.
{--queue=default}Option that takes a value, with a default.
{--id=*}Array option — repeatable, e.g. --id=1 --id=2.

Retrieve any of these inside handle() with $this->argument('user') / $this->option('queue') (no name returns all of them as an array).

User interaction and output

$this->info('All good.');
$this->warn('Careful.');
$this->error('Something failed.');

$name = $this->ask('What is your name?');
$confirmed = $this->confirm('Do you wish to continue?');
$choice = $this->choice('Pick an environment', ['local', 'staging', 'production']);

$this->table(['Name', 'Email'], $users->map->only(['name', 'email']));

$this->withProgressBar($users, function ($user) {
    // ...
});

Newer Laravel versions also expose a styled output helper via $this->components, matching the look of Laravel's own first-party commands more closely than the plain methods above:

$this->components->info('All good.');
$this->components->error('Something failed.');
$this->components->task('Sending digest', fn () => $this->sendDigest());
$this->components->twoColumnDetail('User', 'jane@example.com');

Running a command from code

use Illuminate\Support\Facades\Artisan;

Artisan::call('digest:send', ['--dry-run' => true]);

// Also how the scheduler runs commands
Schedule::command('digest:send')->weekly();

Artisan::call() runs the command synchronously in the current process and returns its exit code, which is handy for triggering one command from inside another. For anything that should run independently of the request lifecycle — a nightly digest, a report — scheduling it via Schedule::command() is almost always the better fit than calling it manually; see the task scheduling guide for the full range of frequency options.

Tinker

php artisan tinker drops into a REPL with the full application already booted, so Eloquent models, facades, and helper functions are all available immediately — no bootstrapping required. It's the fastest way to check a query, inspect a model's relationships, or try out a snippet before committing it to a controller:

>>> User::where('subscribed', true)->count()
>>> $user = User::find(1);
>>> $user->posts()->latest()->first()
>>> event(new OrderShipped($order)); // fire an event manually
>>> Mail::to($user)->send(new WeeklyDigest($user));

Because Tinker holds the app in memory across calls, changes made to model files while a session is open aren't picked up until it's restarted — a common source of confusion when a fix "doesn't seem to work" in Tinker after an edit.

Command dependencies and testing

Like controllers, a command's handle() method supports method injection — Laravel resolves type-hinted arguments out of the container automatically, on top of whatever the signature captured:

public function handle(MailerService $mailer): int
{
    $mailer->sendDigest();

    return self::SUCCESS;
}

Commands are also straightforward to test without actually running them from the terminal, using the same testing helpers as HTTP tests:

$this->artisan('digest:send', ['--dry-run' => true])
    ->expectsOutput('Sending to 12 users...')
    ->assertExitCode(0);

Preventing overlapping runs

If the same command could be triggered twice at once — a long-running import kicked off manually while yesterday's scheduled run is still going — mark it isolatable so Laravel refuses to start a second instance while one is already running:

class SendWeeklyDigest extends Command implements Isolatable
{
    // ...
}
php artisan digest:send --isolated

This uses the same underlying cache lock mechanism as withoutOverlapping() on a scheduled task, just addressable directly from the command line.