运行 php artisan make:command SendWeeklyReport 生成命令类并自动注册;在 configure() 中用 signature 定义参数与选项,在 handle() 中通过 argument()、option() 获取值,用 ask() 等交互方法;推荐用 line() 替代 info() 保证兼容性;Eloquent 和队列可用,但需注意事务、分块查询与环境隔离。

如何生成并注册一个自定义 Artisan 命令
运行 php artisan make:command SendWeeklyReport 即可生成命令类,它会自动放在 app/Console/Commands/SendWeeklyReport.php。Laravel 8+ 默认已启用命令自动发现(ConsoleServiceProvider 中的 commands() 方法扫描 app/Console/Commands 目录),无需手动在 app/Console/Kernel.php 的 $commands 数组中注册——除非你把命令放到了非标准路径。
若需手动注册,确保 Kernel.php 中包含该类引用,并将其添加到 $commands 数组:
protected $commands = [
\App\Console\Commands\SendWeeklyReport::class,
];
如何定义参数、选项及交互式输入
在命令类的 configure() 方法中用 signature 定义命令结构;参数用花括号 {},选项用两短横 --,布尔型选项默认不带值,带值选项加等号 =。
-
name是必填位置参数:{name : The name of the user} -
--force是布尔开关:--force : Force the operation to run -
--limit是带值选项:--limit=5 : Maximum number of items to process -
--env支持多选值:--env=production|staging : Target environment
在 handle() 中通过 $this->argument('name') 和 $this->option('limit') 获取值;交互式提问用 $this->ask()、$this->confirm()、$this->choice() 等方法。
为什么 handle() 中不能直接用 $this->info() 但能用 $this->line()
$this->info()、$this->error()、$this->comment() 等方法依赖于 Symfony 的 OutputInterface 实现,它们在 Laravel 9+ 中默认可用,但在某些低版本或自定义输出对象场景下可能未被正确代理。更稳妥的方式是使用底层的 $this->line() 或 $this->writeln(),它们直接调用 OutputInterface 的原生方法,兼容性更强。
- 推荐统一用
$this->line('Done.', 'fg=green')控制颜色 - 避免在
handle()外部(如构造函数)调用输出方法,此时输出对象尚未初始化 - 批量输出建议用
$this->withProgressBar($items, function ($item) { ... }),它自动处理进度与换行
如何在命令中安全调用 Eloquent 或队列任务
Artisan 命令默认运行在 console 环境下,Eloquent 模型和队列驱动都可用,但要注意两点:事务边界和连接复用。
- 数据库操作建议包裹在
DB::transaction()中,尤其涉及多表写入时 - 队列任务请用
dispatch(new ProcessReport($data))->onQueue('reports')显式指定队列名,避免混入 web 请求队列 - 避免在循环中频繁 new Model(),改用
User::query()->where(...)->chunkById(100, function ($users) { ... })防止内存溢出 - 如果命令需长时间运行(如导出百万条记录),记得在循环中调用
gc_collect_cycles()或unset($var)主动释放变量
真正容易被忽略的是环境隔离:APP_ENV=local php artisan send:report 不会自动切换数据库配置,必须靠 .env 或 config/database.php 中的 env() 判断逻辑来区分连接——别假设命令一定走测试库。









