Eloquent 模型使用详解 Has One Through 远程一对一
远程一对一也好,经过型,穿过型一对一也好,都能表示这种模型的关联方式:一种非直接关系的关系定义
这里使用官方的例子:👩🔧修理工
,🚗车
,👨💼车主
来说明
上下文解释
👩🔧修理工
mechanics 负责维修 👨💼车主
owners 的 🚗车
cars,这里假设一个车主只有一张车,每一次由一个修理工负责维修
ER 图
按 SQL 标准关系定义来说,所有集合关系最终都是三种:一对一
,一对多
,多对多
,其中 多对多
为所有关系的超集,一对多
是 一对一
的超集,一对一
是特殊情况
这里之所以出现 has-one-through
远程一对一的原因在于
我们不想让修理工和车主产生直接联系,因为不想在 mechanics 表当中维护额外的 owners 外键,但是我们又想建立一种查询,可以通过查询某位修理工的姓名,从而得到他当前正在服务的车主
所以在查询的时候,需要借助一个中间表 cars 来完成这种操作,下面是完整的 SQL
-- 根据修理工名字查询他正在服务的车主(实践中如果数据量大,会单独查询出 id 再进行 in 查询或者 exists 来提高性能,而不是直接用 join 处理,这里只是简单的举个例子)
SELECT o.id, o.name FROM owners o
INNER JOIN cars c ON o.car_id = c.id
INNER JOIN mechanics m ON m.id = c.mechanic_id
WHERE m.name = ?
这里的 has-one-through
远程一对一当中的 through
指的就是因为中间额外的链接了 cars
表
另一种实现
其实我们可以通过改动 mechanics 表结构的 DDL,来减少额外的操作,让他变成一个普通的一对一查询
也就是去掉中间表 cars 之后的查询
-- 根据修理工名字查询他正在服务的车主
SELECT o.id, o.name FROM owners o
INNER JOIN mechanics m ON m.owner_id = o.id
WHERE m.name = ?
这样带来的问题是
当我们的修理工维修完当前车主的车之后,下一次再来一个新的车主,我们需要对修理工的 owner_id
进行额外的 update,需要时刻保持这个字段的状态同步于现实世界中发生的变化
这是一个典型的计算机当中的利用空间换时间的问题:通过增加存储空间冗余,来提升查询速度,代价是需要在应用代码中维护额外的字段状态
Eloquent 代码的实现
namespace App;
use Illuminate\Database\Eloquent\Model;
// 修理工实体模型
class Mechanic extends Model
{
/**
* Get the car's owner by implicat keys.
* 获取车主
*/
public function carOwnerByImplicatKeys()
{
// 通过 App\Car 来查询 App\Owner
return $this->hasOneThrough('App\Owner', 'App\Car');
}
/**
* Get the car's owner by explict keys.
* 获取车主,但是使用显式指定的键名
*/
public function carOwnerByExplictKeys()
{
// 通过 App\Car 来查询 App\Owner
return $this->hasOneThrough(
'App\Owner',
'App\Car',
'mechanic_id', // cars 表上的外键名 Foreign key on cars table...
'car_id', // owners 表上的外键名 Foreign key on owners table...
'id', // mechanics 表自身的键名 Local key on mechanics table...
'id' // cars 表自身的键名 Local key on cars table...
);
}
}