Laravel 5.6 下,在数据库迁移中通过 connection 属性配置新的数据库连接无效。
最近,公司老大准备将项目不同模块单独拆分开,包括数据库,这样就涉及到将不同模块的数据持久化到不同的数据库中,也就是不同模块的迁移文件有不同的连接。
在配置的时候,发现 Migration 抽象类中有这样一个属性
abstract class Migration
{
/**
* The name of the database connection to use.
*
* @var string
*/
protected $connection;
上面的英文注释意思是:配置使用的数据库连接的名称。
于是我在迁移文件中重写了这个属性:
class CreateTestTable extends Migration
{
protected $connection = 'mysql_test1';
问题来了:
我运行迁移后新建的表始终是在默认的数据库中而不是通过 connection 配置的数据库中。
这样我就很纳闷了,因为我觉得既然在抽象类 Migration 中存在这个属性,并且注释也表明可以通过配置该属性来配置将表迁移到配置的库中,但是为什么没有效果。
后面通过阅读源码,有了一些发现,同时也又产生了一些疑问:
在 Migrator.php 这个文件中,我找到了执行迁移中 up 的方法:
/**
* Run a migration inside a transaction if the database supports it.
*
* @param object $migration
* @param string $method
* @return void
*/
protected function runMigration($migration, $method)
{
//通过在 Migration 中的 connection 配置生成对应的连接
$connection = $this->resolveConnection(
$migration->getConnection()
);
//将迁移中的 UP 方法以匿名函数的形式赋值给 $callback
$callback = function () use ($migration, $method) {
if (method_exists($migration, $method)) {
$migration->{$method}();
}
};
//检查连接是否支持事务,并且是否开启事务,如果支持并且开启事务,
//则在连接的事务中去执行迁移中的 up 方法。
$this->getSchemaGrammar($connection)->supportsSchemaTransactions()
&& $migration->withinTransaction
? $connection->transaction($callback)
: $callback();
}
上面的方法中,我将关键的逻辑都注释了出来。
这里确实是通过配置生成了一个新连接,但是在这里只是用到了该连接的的事务。
最终还是执行的迁移文件中的 up 方法,这个 up 方法中的数据模型如果没有配置连接的话,就是默认的连接。
具体迁移文件中 up 方法中的逻辑是怎样的,就不在此说明,有兴趣的可以看一下。
到此,这个困扰我的问题在我心中有了一个答案,就是 Migration 中配置的 connection 属性并没有它注释上的效果,这应该是一个 bug (可能我有见解不对的地方,请大家指出)。
在这里,我还发现了一个有趣的事:
/**
* Run a migration inside a transaction if the database supports it.
*
* @param object $migration
* @param string $method
* @return void
*/
protected function runMigration($migration, $method)
{
$connection = $this->resolveConnection(
$migration->getConnection()
);
//该匿名函数没有参数
$callback = function () use ($migration, $method) {
if (method_exists($migration, $method)) {
$migration->{$method}();
}
};
$this->getSchemaGrammar($connection)->supportsSchemaTransactions()
&& $migration->withinTransaction
? $connection->transaction($callback)
: $callback();
}
上面的 $callback
匿名函数是没有参数的,
继续看上面的代码,$connection->transaction($callback)
, 我们追到 transaction()
方法中:
public function transaction(Closure $callback, $attempts = 1)
{
for ($currentAttempt = 1; $currentAttempt <= $attempts; $currentAttempt++) {
$this->beginTransaction();
// We'll simply execute the given callback within a try / catch block and if we
// catch any exception we can rollback this transaction so that none of this
// gets actually persisted to a database or stored in a permanent fashion.
try {
//这里调用 callback 竟然添加了参数 $this
return tap($callback($this), function ($result) {
$this->commit();
});
}
看上面的代码,tap 竟然给 $callback
传了一个参数, 实际上 $callback
这个匿名函数是没有参数的。
我又到本地去写了测试代码去测试了,后面得出了一个结论:
php 中匿名函数的参数是一个可变参数。
上面针对在 Migration 中配置属性 connection 无效的事件做了一个自己的见解,可能有误,如果大家有什么更好的看法,请说出来大家一起讨论讨论。
2018-8-28 9:00 跟新:
老大将上述迁移问题提到githab,链接在此,得到的回复确实是一个bug,证明我的猜想是正确的。
推荐文章: