非常简单:
public function bs()
{
$database = $this->getConnection()->getDatabaseName();
return $this->belongsToMany('B', "$database.a_bs", 'a_id', 'b_id');
}
我正在动态获取数据库名称,因为我的连接是根据环境变量配置的。 Laravel 似乎假设数据透视表与目标关系存在于同一数据库中,因此这将迫使它查找与该方法所在模型(您的“A”领域)相对应的数据库。
如果您不担心 SQLite 数据库,即在单元测试范围内,那么这就是您所需要的。但如果你是,请继续阅读。
首先,前面的例子本身是不够的。 $database 的值最终将成为文件路径,因此您需要将其别名为不会破坏 SQL 语句的名称,并使其可供当前连接访问。"ATTACH DATABASE '$database' AS $name"
你就是这样做的:
public function bs()
{
$database = $this->getConnection()->getDatabaseName();
if (is_file($database)) {
$connection = app('B')->getConnection()->getName();
$name = $this->getConnection()->getName();
\Illuminate\Support\Facades\DB::connection($connection)->statement("ATTACH DATABASE '$database' AS $name");
$database = $name;
}
return $this->belongsToMany('B', "$database.a_bs", 'a_id', 'b_id');
}
警告:交易搞砸了:如果当前连接正在使用事务,则 ATTACH DATABASE 语句将失败。你can使用其上的交易after但执行该语句。
然而,如果related连接使用事务,生成的数据将默默地呈现为对当前数据不可见。这让我发疯的时间比我愿意承认的要长,因为我的查询运行没有错误,但总是空的。似乎只有真正写入附加数据库的数据才能真正被其附加数据库访问。
因此,在被迫写入附加数据库后,您可能仍然希望测试自行清理。一个简单的解决方案就是使用$this->artisan('migrate:rollback', ['--database' => $attachedConnectionName]);
。但是,如果您有多个测试需要相同的表,则这不是很有效,因为它迫使它们每次都必须重建它们。
更好的选择是截断表,但保持其结构不变:
//Get all tables within the attached database
collect(DB::connection($database)->select("SELECT name FROM sqlite_master WHERE type = 'table'"))->each(function ($table) use ($name) {
//Clear all entries for the table
DB::connection($database)->delete("DELETE FROM '$table->name'");
//Reset any auto-incremented index value
DB::connection($database)->delete("DELETE FROM sqlite_sequence WHERE name = '$table->name'");
});
}
这将擦除该连接中的所有数据,但是您没有理由不能对其应用某种您认为合适的过滤器。或者,您可以利用 SQLite DB 是易于访问的文件这一事实,只需将附件复制到临时文件,并在测试执行完成后使用它覆盖源代码。结果在功能上与交易相同。