预加载 hasMany & belongsTo(循环 reference/infinite 循环)

Eager load hasMany & belongsTo (circular reference/infinite loop)

更新(解决方案)

原创

我 运行 我认为这是一个简单的问题:

我的问题是我想同时加载 User 模型上的 images()Image 模型上的 user()。为此,我只是按照文档中的说明设置了 $with 属性。

我的User模特:

class User extends EloquentModel {
    protected $with = ['images'];

    public function images()
    {
        return $this->hasMany(Image::class);
    }
}

我的Image模特:

class Image extends EloquentModel {
    protected $with = ['user'];

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

但是表演时:

$user = User::find(203);

这会导致无限循环(php 分段错误)。一定有某种我找不到的循环引用:

[1]    85728 segmentation fault

编辑 2016/02

这是我发现的最简单的"Workaround":

// User.php
public function setRelation($relation, $value)
{
    if ($relation === 'images') {
        foreach ($value as $image) {
            $image->setUser($this);
        }
    }
    return parent::setRelation($relation, $value);
}

当您尝试查找 属性 时,那个 属性 eager 加载它拥有的所有图像,并且每个 Image eager 加载它所属的 属性,即 属性 你试图找到,它将再次开始急切加载它拥有的所有图像。等...

我解决这个问题的方法是不在模型内部进行预加载,而是在调用模型时进行预加载。

所以使用以下内容:

$prop = Property::with('images')->find(203);

在 属性 模型中删除此行时:

protected $with = ['images'];

图像模型中的这一行:

protected $with = ['property'];

我希望这个解决方案对你有用。

有一个without()方法:https://laravel.com/api/5.8/Illuminate/Database/Eloquent/Builder.html#method_without

without() 置于关系的双方都有效。

class Property extends EloquentModel {
    protected $with = ['images'];

    public function images()
    {
        return $this->hasMany(Image::class)->without('property');
    }
}
class Image extends EloquentModel {
    protected $with = ['property'];

    public function property()
    {
        return $this->belongsTo(Property::class)->without('images');
    }

    public function getAlt()
    {
        return $this->property->title;
    }
}

更新:

尽管使用 without() 很容易避免无限循环问题,但通过多年使用 Laravel 的经验,我意识到在模型中设置 $with 是不好的做法,因为它会导致关系总是加载。因此导致循环reference/infinite循环

相反,始终使用with() 明确指定 需要预先加载的必要关系,无论多么深(关系的关系)

例如:

$user = User::with('images' => function ($query) {
            $query->with('property' => function ($query) {
                $query->with('deeperifneeded' => function ($query) {
                    //...
                });
            });
        ]);

注意:可能需要删除 without()