【问题标题】:Eager load hasMany & belongsTo (circular reference/infinite loop)急切加载hasMany和belongsTo(循环引用/无限循环)
【发布时间】:2016-05-01 21:57:42
【问题描述】:

更新(解决方案)

  • 如果您需要来自$user->images 内的$image 之一的->user 关系,那么$user 变量已经可用,因为您从中加载了->images
  • 不要使用protected $with Eloquent 属性。这是一种反模式。
  • 而是显式地从需要的位置/时间按需加载关系(注意:它不应该阻止您保持干燥!)
  • 如果您真的需要/想要,请参阅@nicksonyap 的回答。它可以解决问题(我相信 - 未经测试)。

原创

我遇到了一个我认为很简单的问题:

  • 我有一个User 对象,有很多Images
  • Image 属于 User...(反比关系)

我的问题是我想同时加载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

这是我发现的最简单的“解决方法”:

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

【问题讨论】:

    标签: laravel-5 eloquent eager-loading


    【解决方案1】:

    有一个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 是一种不好的做法,因为它会导致关系始终加载。因此导致循环引用/无限循环

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

    例如:

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

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

    【讨论】:

    • 这可行。我接受了你的回答(大约 3 年后......),尽管我建议不要这样做,以支持明确(见我的更新)。
    【解决方案2】:

    当您尝试查找属性时,该属性会急切加载它拥有的所有图像,并且每个图像都会急切加载它所属的属性,这是您尝试查找的属性,它将再次开始急切加载所有图像它有。等等……

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

    所以使用以下内容:

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

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

    protected $with = ['images'];
    

    图像模型中的这一行:

    protected $with = ['property'];
    

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

    【讨论】:

    • 嗨,谢谢。我觉得你,我只是很惊讶在 Eloquent 中没有内置的方法来处理这种情况。据我了解,这就像Property 收到images 关系一样简单,然后对于每个图像->setRelation('property', $this)。而不是向 ORM 等重新询问相同的属性,等等......我很惊讶没有什么能阻止你避免无限循环(错误消息???)
    猜你喜欢
    • 1970-01-01
    • 2012-12-11
    • 1970-01-01
    • 1970-01-01
    • 2012-04-02
    • 2015-06-25
    • 1970-01-01
    • 1970-01-01
    • 2023-04-07
    相关资源
    最近更新 更多