最近把版本库里 .env.exampleAPP_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]);

谨以此记录,愿能帮到后来人。