对 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
登录
数据belongsTo
IP
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 个查询,从而大大提高性能。
我有一个新的 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
登录
数据belongsTo
IP
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 个查询,从而大大提高性能。