与Laravel的hasOne雄辩的关系相混淆

时间:2019-07-09 12:24:25

标签: laravel laravel-5 eloquent eloquent--relationship

我有一个新的Laravel 5.8应用程序。我开始研究雄辩的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   |
----------------------

我想要获得属于实际登录名的IP。

因此,我向具有hasOne外键的Login table添加了Data table关系:

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

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

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

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

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

但是我得到这个错误:

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

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

3 个答案:

答案 0 :(得分:2)

使用您的数据库结构:

登录belongsTo数据

数据hasOne登录

数据belongsTo IP

IP hasOne数据

修正方法后,您可以使用这种关系

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

答案 1 :(得分:2)

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

Laravel关系是双向的。在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的关系,在某些情况下非常有用,因为它可以预先加载您的关系,并减少查询量。

在您描述的情况下(加载单个Login模型),它没有任何性能上的改进,但也没有降低速度。

在加载许多模型时很有用,因此可以将数据库查询次数从N+1减少到2

想象一下,您正在加载100个Login模型,而没有急于加载,您将执行1个查询以获取Login模型,进行100个查询以获取Data模型,以及另外100个查询以获取您的Ip模型。

急于加载,它将仅执行3个查询,从而大大提高了性能。

答案 2 :(得分:1)

您可以尝试这样:

登录

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