自定义 Artisan 命令需在 configure() 中注册参数/选项(addArgument/addOption),handle() 中用 argument()/option() 获取;参数名限字母数字下划线,布尔选项勿加等号;数据库操作应使用 cursor()/chunkById()、DB::transaction() 及 --dry-run 模式预览影响。

如何创建一个带参数和选项的自定义 Artisan 命令
直接用 php artisan make:command 生成骨架,但关键在 configure() 和 handle() 里怎么定义输入。参数用 $this->argument('name') 取,选项用 $this->option('force') 取;别漏掉在 configure() 中调用 $this->addArgument() 或 $this->addOption(),否则运行时会报 Not enough arguments 或静默忽略选项。
常见错误:把布尔选项写成 --force=true —— Laravel 默认识别 --force 即为 true,加等号反而会被当字符串值处理;需要显式支持 =value 形式的话,得设 InputOption::VALUE_OPTIONAL 并手动解析。
- 参数名不支持中划线,只能是字母+数字+下划线(
user-id❌ →user_id✅) - 选项默认不接受值,要传值必须指定
InputOption::VALUE_REQUIRED或InputOption::VALUE_OPTIONAL - 命令名建议用冒号分隔层级(如
cache:clear-tenant),避免和核心命令冲突
在 Command 中安全访问数据库和 Eloquent 模型
Artisan 命令默认在 console 环境启动,但 Eloquent 可能因配置未加载或连接未初始化而抛 ConnectionException。确保在 handle() 开头加 $this->getApplication()->setBasePath(getcwd());(Laravel 9+ 通常不需要),更重要的是检查 config/database.php 中的 'default' 连接是否在当前环境可用。
批量操作时别直接用 Model::all(),容易内存溢出;改用 chunkById() 或 cursor():
User::where('active', true)->cursor()->each(function ($user) {
$user->update(['last_seen_at' => now()]);
});
- 若命令需长时间运行,记得在循环中调用
gc_collect_cycles()防止内存泄漏 - 事务内执行多条 DB 操作,务必用
DB::transaction()包裹,而不是靠模型自身的 save() 自动开启 - 不要在
__construct()中加载模型数据——构造阶段容器可能未完全启动,应推迟到handle()
让命令支持 --dry-run 模式并输出执行摘要
这是运维友好型命令的核心特征。--dry-run 不是 Laravel 内置功能,需手动实现:先加选项 $this->addOption('dry-run', 'd', InputOption::VALUE_NONE, 'Show what would be done'),再在逻辑分支中判断 $this->option('dry-run')。
关键不是“跳过执行”,而是“预估影响范围 + 清晰反馈”。比如清理日志前先 LogEntry::where('created_at', 'subDays(30))->count(),然后输出 $this->info("Would delete {$count} log entries");。
- 所有实际变更操作(
delete、update、sync)必须包裹在if (!$this->option('dry-run')) { ... }中 - 使用
$this->table()输出结构化预览比拼接字符串更易读 - 别在 dry-run 下触发事件(
event(new UserDeleted($user))),除非你明确想测试事件监听器
命令执行失败时如何正确退出并返回状态码
Artisan 命令的返回值决定 shell 脚本能否捕获错误:return 0; 表示成功,非零值(如 return 1;)表示失败。别依赖 die() 或未捕获异常——这会让父进程无法区分是 PHP 崩溃还是业务拒绝。
推荐做法:用 try/catch 捕获预期异常(如 ModelNotFoundException),用 $this->error() 输出提示,最后 return 1;;对不可恢复错误(如 DB 连接中断),让异常冒泡,Laravel 会自动转为 exit code 1。
- 不要在
handle()中调用exit()—— 它绕过 Laravel 的异常处理和事件调度 - 自定义退出码可提高脚本可维护性(如
return 128;表示配置缺失,return 129;表示权限不足) - CI/CD 流水线中,命令失败后立即停止后续步骤,所以状态码必须真实反映结果
最常被忽略的一点:命令类里的 $signature 属性如果写错格式(比如多空格、缺引号),会导致 php artisan list 不显示该命令,且无任何报错提示——只能靠 php artisan --help 或调试容器绑定来排查。









