对 Laravel 的 hasOne Eloquent 关系感到困惑

Rad*_*ity 0 laravel eloquent laravel-5 eloquent-relationship

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

我马上就遇到了一个问题。

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

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   |
----------------------
Run Code Online (Sandbox Code Playgroud)

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

所以我添加了一个hasOneLogin table具有外键的关系Data table

public function data()
{
    return $this->hasOne('App\Models\Data');
}
Run Code Online (Sandbox Code Playgroud)

然后我添加了一个hasOneData table具有外键的关系IP table

public function ip()
{
    return $this->hasOne('App\Models\Ip');
}
Run Code Online (Sandbox Code Playgroud)

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

Login::find(1)->data()->ip()->get();
Run Code Online (Sandbox Code Playgroud)

但我收到此错误:

Call to undefined method Illuminate\Database\Eloquent\Relations\HasOne::ip()
Run Code Online (Sandbox Code Playgroud)

我在这里缺少什么以及如何以正确的方式获取该登录的 IP?我需要一个belongsTo地方吗?

Eli*_*res 5

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

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

直接关系应该是:

             HasOne                HasOne
[ Login ] <----------- [ Data ] <----------- [ IP ]
Run Code Online (Sandbox Code Playgroud)

逆关系应该是:

           BelongsTo             BelongsTo
[ Login ] -----------> [ Data ] -----------> [ IP ]
Run Code Online (Sandbox Code Playgroud)

有关如何定义它的详细信息,请参阅Eloquent:关系 - 一对一文档。

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

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

当你这样做时:

Login::find(1)->data()->ip()->get();
Run Code Online (Sandbox Code Playgroud)

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

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

Login::find(1)->data->ip;
Run Code Online (Sandbox Code Playgroud)

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

使用 Eager Loading

Laravel Eloquent 有一个Eager Loading关系,这在某些情况下非常有用,因为它预先加载你的关系,并减少你执行的查询数量。

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

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

想象一下,您正在加载 100 个Login模型,如果没有预先加载,您将执行 1 次查询以获取Login模型,执行 100 次查询以获取模型,再执行 100 次查询以获取您的Data模型Ip模型。

使用预先加载,它只会执行 3 次查询,从而导致性能大幅提升。

  • 太棒了,感谢您的描述性解释,我现在明白了。我认为我的思考问题是我认为“登录”表是“主”表,其余部分是登录表的一部分。这是事实,但需要思考如何扭转这种情况。不管怎样,非常感谢你,你救了我的命!:) (2认同)