最近把版本库里 .env.example 的 APP_ENV 字段值从原来的 local 改为了 production,原意是为了更好的区分开发和生产环境。
然而今天在主机壳的虚拟主机上测试我的程序的时候(准备把演示站搬过去),却出现了奇怪的问题——数据库 Migration 不管用了。
我是在控制器里通过调用 Artisan::call('migrate') 来执行数据库迁移操作的(毕竟虚拟主机哪来的 shell 访问),但是这次这条命令竟然没有执行任何事务。
通过 Artisan 执行 Command 的简化流程大概是像这样的:
Illuminate\Foundation\Console\Kernel
↓
Illuminate\Console\Application
↓
Illuminate\Console\Command
↓
Illuminate\Container\Container
↓
// 这个就是执行 Artisan::call('migrate') 时调用的类
Illuminate\Database\Console\Migrations\MigrateCommand
↓
……
经过一段时间的排查,最后锁定了是在 MigrateCommand 这个类停止继续往下执行的,即接下来的脚本都没有被执行到,也就是说问题就出在 MigrateCommand 这里。
在这个类的 fire 方法里,我们可以看到:
if (! $this->confirmToProceed()) {
return;
}
就是因为这个 $this->confirmToProceed() 返回了一个 bool(false),才导致脚本在这里停止执行了。
这个 confirmToProceed 方法是定义在 Illuminate\Console\ConfirmableTrait 里的,通过阅读代码我们可以知道这个方法可以让我们传进去一个回调(或者让它在 shell 里要求用户输入 Y 确认)来确认是否要执行这个 action。
不巧的是,在 MigrateCommand 里并没有传 callback 给这个方法,所以它就使用了默认的回调:
/**
* Get the default confirmation callback.
*
* @return \Closure
*/
protected function getDefaultConfirmCallback()
{
return function () {
return $this->getLaravel()->environment() == 'production';
};
}
罪魁祸首出现了!没错,就是因为我把 APP_ENV 给改成了 production 造成了这个情况。
而我是通过 Artisan::call('migrate') 来调用命令的,自然不可能执行什么确认操作,也就导致了 migration 不干事。
解决方法就是在调用 Artisan::call 命令时加上一个 --force 参数来让它忽略确认操作强制执行:
Artisan::call('migrate', ['--force' => true]);
谨以此记录,愿能帮到后来人。