Laravel Artisan 命令行工具完全指南:自定义命令与任务调度
Orion K Lv6

Laravel Artisan 命令行工具完全指南

Laravel Artisan 是 Laravel 框架内置的强大命令行工具,它为开发者提供了丰富的命令来简化开发流程。本文将深入探讨 Artisan 的各个方面,从基础使用到高级自定义命令和任务调度。

1. Artisan 简介

什么是 Artisan

Artisan 是 Laravel 的命令行接口,提供了许多有用的命令来帮助开发者构建应用程序。它基于强大的 Symfony Console 组件构建。

查看可用命令

1
2
3
4
5
# 查看所有可用命令
php artisan list

# 查看特定命令的帮助信息
php artisan help migrate

2. 常用内置命令

应用程序管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 生成应用密钥
php artisan key:generate

# 清除应用缓存
php artisan cache:clear

# 清除配置缓存
php artisan config:clear

# 清除路由缓存
php artisan route:clear

# 清除视图缓存
php artisan view:clear

数据库相关

1
2
3
4
5
6
7
8
9
10
11
# 运行数据库迁移
php artisan migrate

# 回滚迁移
php artisan migrate:rollback

# 刷新数据库
php artisan migrate:refresh

# 运行数据填充
php artisan db:seed

代码生成

1
2
3
4
5
6
7
8
9
10
11
# 生成控制器
php artisan make:controller UserController

# 生成模型
php artisan make:model User

# 生成中间件
php artisan make:middleware CheckAge

# 生成迁移文件
php artisan make:migration create_users_table

3. 创建自定义 Artisan 命令

生成命令类

1
2
# 创建自定义命令
php artisan make:command SendEmails

命令类结构

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
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Mail;
use App\Models\User;

class SendEmails extends Command
{
/**
* 命令签名和参数定义
*
* @var string
*/
protected $signature = 'emails:send
{user : 用户ID}
{--queue : 是否使用队列发送}';

/**
* 命令描述
*
* @var string
*/
protected $description = '发送邮件给指定用户';

/**
* 创建新的命令实例
*
* @return void
*/
public function __construct()
{
parent::__construct();
}

/**
* 执行命令逻辑
*
* @return int 返回状态码,0表示成功
*/
public function handle()
{
$userId = $this->argument('user');
$useQueue = $this->option('queue');

$user = User::find($userId);

if (!$user) {
$this->error('用户不存在!');
return 1;
}

$this->info("开始发送邮件给用户: {$user->name}");

// 显示进度条
$bar = $this->output->createProgressBar(100);
$bar->start();

for ($i = 0; $i < 100; $i++) {
// 模拟邮件发送过程
usleep(50000); // 50ms
$bar->advance();
}

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

if ($useQueue) {
$this->info('邮件已加入队列发送');
} else {
$this->info('邮件发送完成');
}

return 0;
}
}

命令参数和选项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
// 必需参数
protected $signature = 'emails:send {user}';

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

// 带默认值的参数
protected $signature = 'emails:send {user=1}';

// 数组参数
protected $signature = 'emails:send {user*}';

// 选项
protected $signature = 'emails:send {user} {--queue}';

// 带值的选项
protected $signature = 'emails:send {user} {--queue=default}';

// 选项简写
protected $signature = 'emails:send {user} {--Q|queue}';

用户交互

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
public function handle()
{
// 询问用户输入
$name = $this->ask('请输入您的姓名');

// 隐藏输入(如密码)
$password = $this->secret('请输入密码');

// 确认操作
if ($this->confirm('确定要继续吗?')) {
$this->info('继续执行...');
}

// 选择选项
$type = $this->choice(
'请选择邮件类型',
['welcome', 'reminder', 'invoice'],
0
);

// 输出信息
$this->line('普通信息');
$this->info('成功信息');
$this->comment('注释信息');
$this->question('问题信息');
$this->error('错误信息');
$this->warn('警告信息');
}

4. 任务调度系统

调度器简介

Laravel 的任务调度器允许你在 Laravel 应用程序内部流畅地定义命令调度,只需要在服务器上设置一个 Cron 条目。4

定义调度任务

routes/console.php 文件中定义调度任务:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<?php

use Illuminate\Support\Facades\Schedule;
use Illuminate\Support\Facades\DB;
use App\Jobs\SendNewsletterJob;
use App\Console\Commands\SendEmails;

// 调度闭包
Schedule::call(function () {
DB::table('recent_users')->delete();
})->daily();

// 调度 Artisan 命令
Schedule::command('emails:send 1 --queue')->daily();

// 调度队列任务
Schedule::job(new SendNewsletterJob)->everyFiveMinutes();

// 调度系统命令
Schedule::exec('node /home/forge/script.js')->daily();

调度频率选项

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// 基础频率
Schedule::command('emails:send')->everyMinute(); // 每分钟
Schedule::command('emails:send')->everyFiveMinutes(); // 每5分钟
Schedule::command('emails:send')->everyTenMinutes(); // 每10分钟
Schedule::command('emails:send')->everyThirtyMinutes(); // 每30分钟
Schedule::command('emails:send')->hourly(); // 每小时
Schedule::command('emails:send')->daily(); // 每天
Schedule::command('emails:send')->weekly(); // 每周
Schedule::command('emails:send')->monthly(); // 每月

// 特定时间
Schedule::command('emails:send')->dailyAt('13:00'); // 每天13:00
Schedule::command('emails:send')->twiceDaily(1, 13); // 每天1:00和13:00
Schedule::command('emails:send')->weeklyOn(1, '8:00'); // 每周一8:00
Schedule::command('emails:send')->monthlyOn(4, '15:00'); // 每月4号15:00

// 自定义 Cron 表达式
Schedule::command('emails:send')->cron('0 15 * * *'); // 每天15:00

条件约束

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
// 环境约束
Schedule::command('emails:send')
->daily()
->environments(['staging', 'production']);

// 时间约束
Schedule::command('emails:send')
->hourly()
->between('7:00', '22:00');

// 工作日约束
Schedule::command('emails:send')
->weekdays()
->daily();

// 自定义约束
Schedule::command('emails:send')
->daily()
->when(function () {
return date('d') <= 14; // 只在月份前半部分执行
});

// 跳过约束
Schedule::command('emails:send')
->daily()
->skip(function () {
return date('D') === 'Sun'; // 跳过周日
});

防止任务重叠

1
2
3
4
5
6
7
8
9
// 防止任务重叠
Schedule::command('emails:send')
->everyMinute()
->withoutOverlapping();

// 设置超时时间
Schedule::command('emails:send')
->everyMinute()
->withoutOverlapping(10); // 10分钟后释放锁

任务输出

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
// 输出到文件
Schedule::command('emails:send')
->daily()
->sendOutputTo('/path/to/file.log');

// 追加到文件
Schedule::command('emails:send')
->daily()
->appendOutputTo('/path/to/file.log');

// 邮件输出
Schedule::command('emails:send')
->daily()
->emailOutputTo('admin@example.com');

// 只在失败时邮件
Schedule::command('emails:send')
->daily()
->emailOutputOnFailure('admin@example.com');

任务钩子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Schedule::command('emails:send')
->daily()
->before(function () {
// 任务执行前
Log::info('开始发送邮件任务');
})
->after(function () {
// 任务执行后
Log::info('邮件任务执行完成');
})
->onSuccess(function () {
// 任务成功时
Log::info('邮件发送成功');
})
->onFailure(function () {
// 任务失败时
Log::error('邮件发送失败');
});

5. 启动调度器

服务器配置

在服务器上添加以下 Cron 条目:1

1
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1

本地开发

在本地开发环境中,可以使用以下命令:

1
2
3
4
5
6
7
8
9
10
11
# 在前台运行调度器
php artisan schedule:work

# 手动运行一次调度
php artisan schedule:run

# 查看调度任务列表
php artisan schedule:list

# 清除调度缓存锁
php artisan schedule:clear-cache

6. 高级特性

单服务器任务

1
2
3
4
// 确保任务只在一台服务器上运行
Schedule::command('emails:send')
->daily()
->onOneServer();

后台任务

1
2
3
4
// 在后台运行任务
Schedule::command('emails:send')
->daily()
->runInBackground();

维护模式

1
2
3
4
// 即使在维护模式下也运行
Schedule::command('emails:send')
->daily()
->evenInMaintenanceMode();

任务分组

1
2
3
4
5
6
7
8
9
10
11
// 创建任务组
Schedule::command('backup:database')
->daily()
->group('backups');

Schedule::command('backup:files')
->daily()
->group('backups');

// 运行特定组的任务
// php artisan schedule:run --group=backups

7. 实际应用示例

数据备份命令

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
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
use Illuminate\Support\Facades\DB;

class BackupDatabase extends Command
{
protected $signature = 'backup:database {--compress : 是否压缩备份文件}';
protected $description = '备份数据库';

public function handle()
{
$this->info('开始备份数据库...');

$filename = 'backup_' . date('Y_m_d_H_i_s') . '.sql';
$compress = $this->option('compress');

// 获取数据库配置
$database = config('database.connections.mysql.database');
$username = config('database.connections.mysql.username');
$password = config('database.connections.mysql.password');
$host = config('database.connections.mysql.host');

// 构建 mysqldump 命令
$command = sprintf(
'mysqldump -h%s -u%s -p%s %s > %s',
$host,
$username,
$password,
$database,
storage_path('app/backups/' . $filename)
);

// 执行备份
$result = null;
$output = [];
exec($command, $output, $result);

if ($result === 0) {
$this->info("数据库备份成功: {$filename}");

if ($compress) {
$this->info('正在压缩备份文件...');
// 压缩逻辑
}
} else {
$this->error('数据库备份失败');
return 1;
}

return 0;
}
}

清理过期文件命令

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
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Illuminate\Support\Facades\Storage;
use Carbon\Carbon;

class CleanupExpiredFiles extends Command
{
protected $signature = 'cleanup:expired-files
{--days=30 : 保留天数}
{--dry-run : 仅显示将要删除的文件}';

protected $description = '清理过期的临时文件';

public function handle()
{
$days = $this->option('days');
$dryRun = $this->option('dry-run');

$this->info("查找 {$days} 天前的过期文件...");

$expiredDate = Carbon::now()->subDays($days);
$files = Storage::disk('temp')->allFiles();
$expiredFiles = [];

foreach ($files as $file) {
$lastModified = Carbon::createFromTimestamp(
Storage::disk('temp')->lastModified($file)
);

if ($lastModified->lt($expiredDate)) {
$expiredFiles[] = $file;
}
}

if (empty($expiredFiles)) {
$this->info('没有找到过期文件');
return 0;
}

$this->info("找到 " . count($expiredFiles) . " 个过期文件");

if ($dryRun) {
$this->table(['文件路径', '最后修改时间'],
array_map(function ($file) {
return [
$file,
Carbon::createFromTimestamp(
Storage::disk('temp')->lastModified($file)
)->format('Y-m-d H:i:s')
];
}, $expiredFiles)
);

$this->warn('这是预览模式,没有实际删除文件');
return 0;
}

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

$deletedCount = 0;
foreach ($expiredFiles as $file) {
if (Storage::disk('temp')->delete($file)) {
$deletedCount++;
}
$bar->advance();
}

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

$this->info("成功删除 {$deletedCount} 个过期文件");

return 0;
}
}

8. 最佳实践

命令设计原则

  1. 单一职责:每个命令只做一件事
  2. 幂等性:多次执行相同命令应该产生相同结果
  3. 错误处理:适当的错误处理和状态码返回
  4. 用户友好:清晰的输出和进度指示

性能优化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 使用队列处理大量数据
Schedule::job(new ProcessLargeDataset)->everyFiveMinutes();

// 分批处理
public function handle()
{
User::chunk(1000, function ($users) {
foreach ($users as $user) {
// 处理用户
}
});
}

// 内存优化
public function handle()
{
// 使用游标避免内存溢出
foreach (User::cursor() as $user) {
// 处理用户
}
}

监控和日志

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
public function handle()
{
$startTime = microtime(true);

try {
// 执行任务逻辑
$this->processData();

$duration = microtime(true) - $startTime;
Log::info('任务执行成功', [
'command' => $this->signature,
'duration' => $duration,
'memory_usage' => memory_get_peak_usage(true)
]);

} catch (\Exception $e) {
Log::error('任务执行失败', [
'command' => $this->signature,
'error' => $e->getMessage(),
'trace' => $e->getTraceAsString()
]);

return 1;
}

return 0;
}

9. 测试 Artisan 命令

单元测试

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
<?php

namespace Tests\Feature;

use Tests\TestCase;
use Illuminate\Foundation\Testing\RefreshDatabase;
use App\Models\User;

class SendEmailsCommandTest extends TestCase
{
use RefreshDatabase;

public function test_send_emails_command_success()
{
$user = User::factory()->create();

$this->artisan('emails:send', ['user' => $user->id])
->expectsOutput('邮件发送完成')
->assertExitCode(0);
}

public function test_send_emails_command_with_invalid_user()
{
$this->artisan('emails:send', ['user' => 999])
->expectsOutput('用户不存在!')
->assertExitCode(1);
}

public function test_send_emails_command_with_queue_option()
{
$user = User::factory()->create();

$this->artisan('emails:send', [
'user' => $user->id,
'--queue' => true
])
->expectsOutput('邮件已加入队列发送')
->assertExitCode(0);
}
}

调度测试

1
2
3
4
5
6
7
8
9
10
public function test_scheduled_commands()
{
// 测试命令是否正确调度
$this->artisan('schedule:list')
->expectsOutput('emails:send');

// 模拟调度运行
$this->artisan('schedule:run')
->assertExitCode(0);
}

总结

Laravel Artisan 是一个功能强大的命令行工具,它不仅提供了丰富的内置命令来简化开发流程,还允许开发者创建自定义命令来自动化各种任务。结合任务调度系统,Artisan 可以帮助开发者构建强大的自动化工作流。3

掌握 Artisan 的使用不仅能提高开发效率,还能帮助构建更加健壮和可维护的 Laravel 应用程序。通过合理的命令设计、适当的错误处理和完善的测试,可以确保自动化任务的可靠性和稳定性。

本站由 提供部署服务