Laravel 13 命令行进阶指南

Artisan 是 Laravel 的命令行接口,提供了丰富的命令来帮助开发。本文将深入探讨 Laravel 13 Artisan 命令行的高级用法。

创建命令

生成命令类

1
2
php artisan make:command SendEmails
php artisan make:command ProcessOrders --command=orders:process

命令结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use App\Services\OrderService;

class ProcessOrders extends Command
{
protected $signature = 'orders:process
{--limit=100 : Maximum number of orders to process}
{--force : Force processing without confirmation}';

protected $description = 'Process pending orders';

public function handle(OrderService $service): int
{
$limit = $this->option('limit');
$force = $this->option('force');

if (! $force && ! $this->confirm('Do you want to process orders?')) {
$this->info('Operation cancelled.');
return self::SUCCESS;
}

$processed = $service->process($limit);

$this->info("Processed {$processed} orders.");

return self::SUCCESS;
}
}

命令签名

参数

1
2
3
4
5
6
7
8
9
10
11
// 必填参数
protected $signature = 'user:create {name}';

// 可选参数
protected $signature = 'user:create {name?}';

// 默认值
protected $signature = 'user:create {name=Guest}';

// 数组参数
protected $signature = 'user:create {names*}';

选项

1
2
3
4
5
6
7
8
9
10
11
// 布尔选项
protected $signature = 'orders:process {--force}';

// 带值选项
protected $signature = 'orders:process {--limit=100}';

// 选项简写
protected $signature = 'orders:process {--L|limit=100}';

// 数组选项
protected $signature = 'orders:process {--id=*}';

完整签名示例

1
2
3
4
5
6
7
protected $signature = 'report:generate
{type : Report type (daily|weekly|monthly)}
{--date= : Specific date for the report}
{--format=pdf : Output format (pdf|csv|excel)}
{--email=* : Email addresses to send report}
{--notify : Send notification when done}
{--force : Overwrite existing report}';

获取输入

获取参数

1
2
$name = $this->argument('name');
$arguments = $this->arguments();

获取选项

1
2
$limit = $this->option('limit');
$options = $this->options();

输出

基本输出

1
2
3
4
$this->info('Operation completed successfully.');
$this->error('An error occurred.');
$this->warning('This action cannot be undone.');
$this->line('Plain text output');

表格输出

1
2
3
4
$this->table(
['ID', 'Name', 'Email'],
User::all(['id', 'name', 'email'])->toArray()
);

进度条

1
2
3
4
5
6
7
8
9
10
11
$users = User::all();
$bar = $this->output->createProgressBar(count($users));

$bar->start();

foreach ($users as $user) {
$user->process();
$bar->advance();
}

$bar->finish();

列表输出

1
2
3
4
5
6
7
$this->newLine();
$this->line('Users:');
$this->newLine();

foreach ($users as $user) {
$this->line(" - {$user->name}");
}

交互

确认

1
2
3
4
5
if ($this->confirm('Do you want to continue?')) {
}

if ($this->confirm('Do you want to continue?', true)) {
}

询问

1
2
3
$name = $this->ask('What is your name?');
$password = $this->secret('Enter your password');
$email = $this->askForEmail('What is your email?');

选择

1
2
3
4
5
$role = $this->choice(
'Select user role',
['admin', 'editor', 'user'],
'user'
);

预期输入

1
2
3
$this->anticipate('Search user', function ($input) {
return User::where('name', 'like', "%{$input}%")->pluck('name');
});

注册命令

自动发现

命令放在 app/Console/Commands 目录下会自动被发现。

手动注册

1
2
3
4
// app/Console/Kernel.php
protected $commands = [
Commands\ProcessOrders::class,
];

命令调度

定义调度

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// app/Console/Kernel.php
protected function schedule(Schedule $schedule): void
{
$schedule->command('orders:process')
->daily()
->at('02:00');

$schedule->command('report:generate daily')
->daily()
->emailOutputTo('admin@example.com');

$schedule->command('cache:clear')
->hourly()
->withoutOverlapping();

$schedule->command('backup:run')
->daily()
->onSuccess(function () {
Log::info('Backup completed');
})
->onFailure(function () {
Log::error('Backup failed');
});
}

调度频率

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$schedule->command('task')->everyMinute();
$schedule->command('task')->everyFiveMinutes();
$schedule->command('task')->everyTenMinutes();
$schedule->command('task')->everyFifteenMinutes();
$schedule->command('task')->everyThirtyMinutes();
$schedule->command('task')->hourly();
$schedule->command('task')->hourlyAt(17);
$schedule->command('task')->daily();
$schedule->command('task')->dailyAt('13:00');
$schedule->command('task')->twiceDaily(1, 13);
$schedule->command('task')->weekly();
$schedule->command('task')->weeklyOn(1, '08:00');
$schedule->command('task')->monthly();
$schedule->command('task')->monthlyOn(4, '15:00');
$schedule->command('task')->quarterly();
$schedule->command('task')->yearly();
$schedule->command('task')->cron('* * * * *');

调度约束

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$schedule->command('task')
->weekdays()
->weekends()
->mondays()
->tuesdays()
->wednesdays()
->thursdays()
->fridays()
->saturdays()
->sundays()
->days([1, 15])
->between('08:00', '17:00')
->unlessBetween('12:00', '13:00')
->when(function () {
return date('N') <= 5;
})
->skip(function () {
return Holiday::isToday();
});

调度选项

1
2
3
4
5
6
7
8
9
$schedule->command('task')
->withoutOverlapping(10)
->runInBackground()
->onOneServer()
->evenInMaintenanceMode()
->environments(['production'])
->when(function () {
return true;
});

队列命令

队列工作进程

1
2
3
4
5
6
php artisan queue:work
php artisan queue:work --queue=high,default
php artisan queue:work --daemon
php artisan queue:work --tries=3
php artisan queue:work --timeout=60
php artisan queue:work --memory=128

队列监听

1
2
php artisan queue:listen
php artisan queue:listen --queue=high,default,low

队列重启

1
2
3
php artisan queue:restart
php artisan queue:retry all
php artisan queue:retry 5

内置命令

应用命令

1
2
3
4
5
6
7
php artisan down
php artisan down --message="Upgrading database"
php artisan down --retry=60
php artisan up
php artisan serve
php artisan serve --port=8080
php artisan serve --host=0.0.0.0

缓存命令

1
2
3
4
5
6
7
8
9
10
php artisan cache:clear
php artisan cache:forget key
php artisan config:cache
php artisan config:clear
php artisan route:cache
php artisan route:clear
php artisan view:cache
php artisan view:clear
php artisan event:cache
php artisan event:clear

数据库命令

1
2
3
4
5
6
7
8
php artisan migrate
php artisan migrate:rollback
php artisan migrate:reset
php artisan migrate:fresh
php artisan migrate:refresh
php artisan migrate:status
php artisan db:seed
php artisan db:wipe

生成命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
php artisan make:model User
php artisan make:model User --migration
php artisan make:controller UserController
php artisan make:controller UserController --resource
php artisan make:middleware CheckRole
php artisan make:request StoreUserRequest
php artisan make:resource UserResource
php artisan make:factory UserFactory
php artisan make:seeder UserSeeder
php artisan make:policy UserPolicy
php artisan make:rule ValidEmail
php artisan make:job ProcessPodcast
php artisan make:event OrderShipped
php artisan make:listener SendNotification --event=OrderShipped
php artisan make:mail WelcomeEmail
php artisan make:notification InvoicePaid
php artisan make:command ProcessOrders

自定义命令示例

数据导出命令

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use App\Models\User;
use League\Csv\Writer;

class ExportUsers extends Command
{
protected $signature = 'users:export
{--format=csv : Export format (csv|json)}
{--output= : Output file path}
{--active : Export only active users}';

protected $description = 'Export users to file';

public function handle(): int
{
$query = User::query();

if ($this->option('active')) {
$query->where('active', true);
}

$users = $query->get();

if ($users->isEmpty()) {
$this->warn('No users found.');
return self::SUCCESS;
}

$format = $this->option('format');
$output = $this->option('output') ?: storage_path("users-export.{$format}");

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

$bar = $this->output->createProgressBar($users->count());
$bar->start();

match ($format) {
'csv' => $this->exportCsv($users, $output, $bar),
'json' => $this->exportJson($users, $output, $bar),
default => $this->error("Unsupported format: {$format}")
};

$bar->finish();
$this->newLine();
$this->info("Export completed: {$output}");

return self::SUCCESS;
}

private function exportCsv($users, $output, $bar): void
{
$csv = Writer::createFromPath($output, 'w+');
$csv->insertOne(['ID', 'Name', 'Email', 'Created At']);

foreach ($users as $user) {
$csv->insertOne([
$user->id,
$user->name,
$user->email,
$user->created_at->toDateTimeString(),
]);
$bar->advance();
}
}

private function exportJson($users, $output, $bar): void
{
$data = [];

foreach ($users as $user) {
$data[] = [
'id' => $user->id,
'name' => $user->name,
'email' => $user->email,
'created_at' => $user->created_at->toDateTimeString(),
];
$bar->advance();
}

file_put_contents($output, json_encode($data, JSON_PRETTY_PRINT));
}
}

最佳实践

1. 使用描述性签名

1
2
3
protected $signature = 'orders:process 
{--limit=100 : Maximum orders to process}
{--force : Skip confirmation}';

2. 返回正确的退出码

1
2
3
4
5
6
7
8
public function handle(): int
{
if ($error) {
return self::FAILURE;
}

return self::SUCCESS;
}

3. 使用进度条

1
2
3
4
5
6
7
8
9
$bar = $this->output->createProgressBar(count($items));
$bar->start();

foreach ($items as $item) {
$this->process($item);
$bar->advance();
}

$bar->finish();

总结

Laravel 13 的 Artisan 命令行提供了强大的命令创建和管理能力。通过合理使用命令参数、选项、输出格式和交互功能,可以构建出功能丰富、用户友好的命令行工具。记住使用描述性的命令签名、返回正确的退出码、使用进度条提供反馈,并充分利用调度系统来自动化任务执行。Artisan 是 Laravel 开发的重要工具,掌握它对于构建高效的工作流程至关重要。