Laravel Eloquent关系查询结果限制

10

我有一个简单的相册和图片设置,每个相册有很多图片。我可以正常获取所有数据,但是我想限制返回的图片数量为3。我尝试传递一个闭包来实现:

Album::with(['images' =>  function($query) { $query->take(3);}])->get();

这会将图片数量限制为3,但我想把每个相册的图片数量都限制为3。第一个相册将展示3张照片,但所有其他相册都没有照片。
我尝试向模型中添加新的方法,像这样:
public function limitImages()
{
    return $this->hasMany('App\Image')->limit(3);
}

我在控制器中调用了以下代码:

Album::with('limitImages')->get();

但这并不限制返回的图像数量。

1
你尝试在你的第一个闭包尝试中使用$query->limit(3),而不是take(3)了吗? - GiamPy
是的,我已经尝试在控制器和模型中交换limit()和take(),但它仍然以相同的方式工作。对于第一个专辑返回2张图片,而对于其余的专辑则没有返回任何图片。 - twigg
1
同样的事情发生了。这是一个有趣的问题!! - DevK
假设您执行了 Album::with('images')->get(),并且数据库中只有3个相册(具有id:1、2和3)。Laravel在后台执行下面的两个查询:首先是 select * from albums,这将返回相册,并将id放入数组中([1, 2, 3]),然后它执行 select * from images where in alubm_id [1, 2, 3]。如果您能告诉我如何编写SQL以将第二个查询限制为每个相册的3张图片,我将把它放入查询构建器中(我擅长Laravel - 特别是Eloquent方面,但不太擅长SQL)。但我很确定没有简单的方法可以做到这一点。 - DevK
奇怪...这正是我在所有项目中的做法,而且它按预期运行;每个父级(Album)都有一个子级(Image)的关系,限制为X条目。唯一的例外是我在->with([...])函数中使用limit()而不是take()。也许是你使用的DB类型有问题?我正在使用MySQL,但我没有看到你指定你正在使用什么。 - Tim Lewis
显示剩余5条评论
4个回答

20

我觉得如果尝试实现这个功能,你很快就会遇到N+1问题。 直接在返回的集合中完成它:

Album::with('images')->get()->map(function($album) {
    $album->setRelation('images', $album->images->take(3));
    return $album;
});

原来如果有20个专辑,那么就会有20个额外的查询。如何只用一个查询呢? - Ilya Degtyarenko
with('images') 会预加载相册中的图片,这将在两个查询中获取所有内容(一个用于相册,一个用于所有图片)。 - Eric Tucker
使用 with('images') 实际上会加载所有的模型,可以在不使用 with('images') 的情况下实现。 - Haseeb Zulfiqar

3

对于那些不介意N+1问题,甚至可能更喜欢它的人。

在专辑模型上,添加一个自定义属性,返回有限的关系,即:

class Album extends Model
{
    public function images(): hasMany
    {
        return $this->hasMany('App\Image');
    }

    public function getLimitedImagesAttribute()
    {
        return $this->images()->take(3)->get();
    }
}

在您的控制器中:
class MainController extends Controller
{
    return Album::select('name')->get()->each->append('limited_images')
}

注意:此方法将运行N + 1个查询,其中N是数据库中专辑数量。如果Album表中的行数不太大,但相关的图像表过大,则可能更喜欢此方法。


1

被接受的答案非常低效,因为它先获取所有相关项,然后再进行过滤。最好在数据库查询中进行限制,像这样:

Album::with(['images' => function ($query) {
    $query->limit(3);
}])->get();

确保您的方括号使用正确!


它不起作用。 - Haseeb Zulfiqar
这只会获取3张图片。我们希望每个相册获取3张图片。 - Connor Leech

0

take()高级方法只是在查询结尾添加了'limit' 关键字。这种类型的查询更加复杂,原始的Eloquent 不支持。

幸运的是,有一个名为 eloquent-eager-limit的额外包可以解决此问题。为了让它正常工作,使用composer require staudenmeir/eloquent-eager-limit 命令安装该包,并在父模型和子模型类中分别添加 use \Staudenmeir\EloquentEagerLimit\HasEagerLimit; 语句。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接