Laravel 10如何扩展现有Artisan命令?添加自定义选项
如何扩展现有的Laravel Artisan migrate命令?
我希望为php artisan migrate添加两个自定义选项:
php artisan migrate --core php artisan migrate --project=0f9ebA2
需求是不修改默认migrate命令的任何行为,仅新增这两个选项,通过一个包装类预先设置配置变量或路径参数,之后把所有其他参数正常传递给原Artisan migrate命令。
预期的大致实现逻辑:
- 当使用
--core选项时:
public function handle() { $this->call('migrate', ['--path' => '/database/migrations/core', ...$this->arguments()]); }
- 当使用
--project=xxx选项时:
public function handle() { config(['tenant.key' => $this->argument('--project')]); $this->call('migrate', ['--path' => '/database/migrations/project', ...$this->arguments()]); }
Laravel官方文档主要聚焦于编写全新自定义命令,偶尔提到用$this->call()调用其他命令,但没有覆盖扩展现有命令或传递通配符参数的场景。
已尝试但无效的方案
- 创建独立自定义命令(如
php artisan migrate:core):虽然能实现功能,但无法保留原migrate命令的所有必要选项,没法为核心迁移设置上下文。 - 自定义命令手动传递参数:可以通过
$this->arguments()获取参数并附加到内部调用,但所有参数都必须在自定义命令的签名里声明,否则会被拒绝,没有通配符传递的方式;复制原migrate的参数定义又缺乏灵活性,无法适配Laravel核心后续变更或第三方包的扩展。 - 直接继承原MigrateCommand创建新命令:尝试做原migrate的别名,代码如下:
namespace App\Console\Commands; use Illuminate\Database\Console\Migrations\MigrateCommand; class MigrateCore extends MigrateCommand { protected $signature = 'migrate:core'; public function handle() { parent::handle(); } }
运行时抛出Laravel核心级别的BindingResolutionException异常:
Target [Illuminate\Database\Migrations\MigrationRepositoryInterface] is not instantiable while building [App\Console\Commands\MigrateCore, Illuminate\Database\Migrations\Migrator]
显然这不是Laravel推荐的实现方式。
正确的实现方案
方法1:扩展原生migrate命令(推荐)
Laravel允许通过命令启动钩子修改现有命令的定义,添加自定义选项并注入逻辑,完全保留原命令的所有功能:
- 在
app/Providers/AppServiceProvider.php的boot方法中添加以下代码:
use Illuminate\Support\Facades\Artisan; use Illuminate\Database\Console\Migrations\MigrateCommand; use Symfony\Component\Console\Input\InputOption; public function boot() { Artisan::starting(function ($artisan) { // 找到原生migrate命令 $command = $artisan->find('migrate'); // 添加--core选项(无值) $command->getDefinition()->addOption( new InputOption( 'core', null, InputOption::VALUE_NONE, '执行核心模块迁移' ) ); // 添加--project选项(需要传值) $command->getDefinition()->addOption( new InputOption( 'project', null, InputOption::VALUE_REQUIRED, '执行指定项目的租户迁移' ) ); // 保存原命令的handle方法 $originalHandle = $command->handle; // 覆盖handle方法注入自定义逻辑 $command->handle = function () use ($command, $originalHandle) { $input = $command->input; // 处理--core选项 if ($input->getOption('core')) { $input->setOption('path', database_path('migrations/core')); } // 处理--project选项 if ($projectKey = $input->getOption('project')) { config(['tenant.key' => $projectKey]); $input->setOption('path', database_path('migrations/project')); } // 执行原命令的逻辑 return $originalHandle->call($command); }; }); }
- 使用方式:
直接运行目标命令,原migrate的所有参数(如--force、--seed等)都能正常使用:
# 执行核心迁移,同时强制跳过环境检查 php artisan migrate --core --force # 执行指定项目的迁移并自动填充种子数据 php artisan migrate --project=0f9ebA2 --seed
方法2:自定义包装命令(兼容旧版本)
如果不想修改原命令,也可以创建一个自定义命令,通过通配符接收所有参数再传递给原migrate:
namespace App\Console\Commands; use Illuminate\Console\Command; use Illuminate\Support\Facades\Artisan; class CustomMigrate extends Command { protected $signature = 'migrate {--core : 执行核心模块迁移} {--project= : 执行指定项目的租户迁移} {--*=* : 传递原migrate命令的所有选项}'; protected $description = '扩展版migrate命令,支持核心/项目迁移切换'; public function handle() { $arguments = []; // 处理自定义选项 if ($this->option('core')) { $arguments['--path'] = database_path('migrations/core'); } if ($projectKey = $this->option('project')) { config(['tenant.key' => $projectKey]); $arguments['--path'] = database_path('migrations/project'); } // 收集所有其他非自定义选项 foreach ($this->options() as $key => $value) { $excludeOptions = ['core', 'project', 'help', 'quiet', 'verbose', 'version', 'ansi', 'no-ansi', 'no-interaction', 'env']; if (!in_array($key, $excludeOptions)) { $arguments["--{$key}"] = $value !== false ? $value : true; } } // 调用原migrate命令 return Artisan::call('migrate', $arguments); } }
内容的提问来源于stack exchange,提问作者CodeMoose
相关产品推荐
相关产品推荐

