对 Laravel 的 hasOne Eloquent 关系感到困惑

Confused with Laravel's hasOne Eloquent Relationships

我有一个新的 Laravel 5.8 应用程序。我开始研究 Eloquent ORM 及其关系。

马上就遇到了问题

我有以下 table。 (这只是一个例子,出于测试原因,不会成为实际应用)

Login table:
--------------------------
| id | user    | data_id |
--------------------------
| 1  | admin   | 1       |
| 2  | admin   | 2       |
| 3  | admin   | 3       |
--------------------------

Data table:
--------------
| id | ip_id |
--------------
| 1  | 1     |
| 2  | 2     |
| 3  | 3     |
--------------

IP table:
----------------------
| id | ip            |
----------------------
| 1  | 192.168.1.1   |
| 2  | 192.168.1.2   |
| 3  | 192.168.1.3   |
----------------------

我想要的是获取属于实际登录的IP

所以我向 Login table 添加了一个 hasOne 关系,该关系具有 Data table 的外键:

public function data()
{
    return $this->hasOne('App\Models\Data');
}

然后我向 Data table 添加了一个 hasOne 关系,该关系具有 IP table 的外键:

public function ip()
{
    return $this->hasOne('App\Models\Ip');
}

完成后,我想检索登录的第一条记录的 IP 地址 table:

Login::find(1)->data()->ip()->get();

但是我得到这个错误:

Call to undefined method Illuminate\Database\Eloquent\Relations\HasOne::ip()

我在这里遗漏了什么以及如何以正确的方式获取该登录的 IP?我在某处需要 belongsTo 吗?

你可以这样试试:

登录

public function data()
{
    return $this->belongsTo('App\Models\Data', 'data_id');
}

数据

public function ip()
{
    return $this->belongsTo('App\Models\Ip', 'ip_id');
}
$login = Login::with(['data.ip'])->find(1);

data 里面你会有 ip 比如 $login->data->ip.

你的数据库结构:

登录belongsTo数据

数据hasOne登录

数据belongsToIP

IPhasOne数据

修复你的方法后,你可以像这样使用你的关系

$login = Login::with(['data.ip'])->find(1);

第一个错误:错误的关系定义

Laravel 关系是 bi-directional。在 one-to-one 关系上,您可以定义直接关系 (HasOne) 和反向关系 (BelongsTo)

直接关系应该是:

             HasOne                HasOne
[ Login ] <----------- [ Data ] <----------- [ IP ]

反比关系应该是:

           BelongsTo             BelongsTo
[ Login ] -----------> [ Data ] -----------> [ IP ]

有关如何定义它的详细信息,请参阅 Eloquent: Relationships - One-to-One 文档。

注意除非需要,否则不需要为关系定义两个方向。在你的情况下,我认为你只需要定义 belongsTo 方向。

第二个错误:您正在调用关系方法,而不是关系本身

当你这样做时:

Login::find(1)->data()->ip()->get();

您正在调用定义关系的方法 data,而不是相关模型。这在某些情况下很有用,但对您来说不是。

正确的是调用关系魔法 属性 而不是:

Login::find(1)->data->ip;

请注意,我们不使用 (),这里也不需要 get()。 Laravel 负责为我们加载它。

使用预先加载

Laravel Eloquent 有一个 Eager Loading 关系,这在某些情况下非常有用,因为它 pre-loads 您的关系,并减少您执行的查询数量。

在您描述的情况下(加载单个 Login 模型)它不会提高任何性能,但也不会减慢速度。

当您加载许多模型时它很有用,因此它将您的数据库查询计数从 N+1 减少到 2

假设您要加载 100 个 Login 模型,如果不预先加载,您将执行 1 个查询来获取 Login 模型,执行 100 个查询来获取 Data 模型,并且更多 100 个查询以获得您的 Ip 个模型。

使用预先加载,它只会执行 3 个查询,从而大大提高性能。