Laravel - 渴望加载多对多,仅获取一条记录(不是集合)

Posted

技术标签:

【中文标题】Laravel - 渴望加载多对多,仅获取一条记录(不是集合)【英文标题】:Laravel - Eager load many-to-many, get one record only (not a collection) 【发布时间】:2016-02-24 18:43:12 【问题描述】:

我的帖子有图片(多对多,因为图片也可以有其他关系)。在我的数据透视表中,我有一个名为“特色”的布尔字段,用于指定该帖子的主图像。我想在帖子索引页面中显示与当前用户关联的所有帖子。我只想从数据库中获取一张图片,那应该是特色图片。目前我只能将精选图像作为一个集合来获取。这样做的原因是,如果用户有很多帖子,我不想继续检索他们所有帖子 (N+1) 的特色图片,而是使用即时加载仅用 2 个查询获取特色图片。

\\Post Model
public function images() 
    return $this->belongsToMany(Image::class);


public function image()
    return $this->images()->where('featured', '=', true)->first();


public function featured_image()
    return $this->images()->where('featured', '=', true);



\\Controller

$user = Auth::user();

$posts = $user->posts()->with('image')->get();

// throws error
//Call to undefined method Illuminate\Database\Query\Builder::addEagerConstraints()


// if I do this
$posts = $user->posts()->with('featured_image')->get();

// I get all the user's posts, I get the featured image but as a collection even if I only have one record there

我该怎么做?

【问题讨论】:

->get() 将始终返回 collection 无论实际输出是什么,而 ->first() 将返回第一个输出作为 model。那么问题是,如何constraint eagerload 为每个给定的posts 仅加载单个关系。另外,请注意关系($this->hasMany$this->belongsToMany 等)实际上是一个 QueryBuilder 实例,因此,如果您将模型放入 eagerload with(),它将失败。附言。 laravel 中的每个查询都由 first()get() 完成,这会将其转换为模型/集合。 最后我不得不允许在没有任何 get 或 first 关系的情况下进行预加载。而我所做的是使用 Collection 的 first() 方法并从该列表中获取唯一的项目。 是的,它可以这样做,但不知何故,我们不得不检索所有东西,只是为了取一个东西......这......相当......效率低下。 【参考方案1】:

我认为这可能是您想要的解决方案:

\\Post Model

public function images() 
    return $this->belongsToMany(Image::class);


public function getFeaturedImageAttribute() 
    return $this->images->where('featured', true)->first();



\\Controller    

$user = Auth::user();

$posts = $user->posts()->with('images')->get();

在生成的帖子集合中,每个帖子都有一个“featured_image”属性,可以这样访问:

foreach ( $posts as $post )

    $featured_image = $post->featured_image;

    // do something with the image...

重要提示:因为访问器方法使用 '$this->images' 而不是 '$this->images()',所以它将使用预先加载的 'images' 集合的 where() 和 first() 方法而不是查询生成器。这会导致大量的 php 处理,但没有新的查询。

【讨论】:

【参考方案2】:

这可能不是最佳选择,但如果您仅限于两个查询,则可以执行以下操作:

$posts = $user->posts;
$idOfPosts = $posts->pluck('id');
$featuredImages = Image::whereIn('post_id', $idOfPosts)->where('featured', true)->get();

不是 Eager Load 方法,而是解决 (N+1) 问题。

【讨论】:

以上是关于Laravel - 渴望加载多对多,仅获取一条记录(不是集合)的主要内容,如果未能解决你的问题,请参考以下文章

Laravel 与渴望的多对多关系

渴望加载多对多 - EF Core [关闭]

从所有记录中获取所有相关的多对多,而不是从每个记录 laravel

使用 Laravel 限制多态多对多关系中的相关记录

Laravel:无法添加记录多对多关系

如何从多对多关系中只加载 1 个结果 Laravel