Eloquent 模型使用详解 Has One Through 远程一对一

发布时间 2024-01-13 18:18:17作者: 我听不见

远程一对一也好,经过型,穿过型一对一也好,都能表示这种模型的关联方式:一种非直接的关系定义
这里使用官方的例子:?‍?修理工?车?‍?车主来说明

上下文解释

?‍?修理工 mechanics 负责维修 ?‍?车主 owners 的 ?车 cars,这里假设一个车主只有一张车,每一次由一个修理工负责维修

ER 图

![[Pasted image 20240113172412.png|400]]
按 SQL 标准关系定义来说,所有集合关系最终都是三种:一对一一对多多对多,其中 多对多 为所有关系的超集,一对多一对一 的超集,一对一 是特殊情况
这里之所以出现 has-one-through 远程一对一的原因在于

我们不想让机械师和车主产生直接联系,因为不想在 mechanics 表当中维护额外的 owners 外键,但是我们又想建立一种查询,可以通过查询某位修理工的姓名,从而得到他当前正在服务的车主

所以在查询的时候,需要借助一个中间实体 ?车 来完成这种操作,下面是完整的 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

另外一种方法

![[Pasted image 20240113172923.png|400]]
其实我们可以通过改动 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...
        );
	}
}