You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel关联查询中take()返回空集合问题排查

Laravel多对多关联:部分分类关联文章返回空集合问题

问题场景

我想获取4个关联有文章的分类,且每个分类仅返回4篇文章,用了这段代码:

$categoryArticles = Category::whereHas('articles')->with(['articles' => function($q){
    $q->select('id', 'title')->take(4);
}])->take(4)->get();

结果部分分类的articles关联是空集合;但去掉关联闭包里的take(4)后,所有分类都能正常返回关联文章:

$categoryArticles = Category::whereHas('articles')->with(['articles' => function($q){
    $q->select('id', 'title');
}])->take(4)->get();

相关代码

模型定义

Article.php:

public function categories() {
    return $this->belongsToMany(Category::class, 'article_category');
}

Category.php:

public function articles() {
    return $this->belongsToMany(Article::class, 'article_category');
}

中间表结构

Schema::create('article_category', function (Blueprint $table) {
    $table->unsignedBigInteger('article_id');
    $table->unsignedBigInteger('category_id');
    $table->foreign('article_id')->references('id')->on('articles')->onDelete('cascade');
    $table->foreign('category_id')->references('id')->on('categories')->onDelete('cascade');
});

问题原因

  1. 字段缺失导致关联匹配失败:关联查询的select只取了id和title,但多对多关联需要中间表的category_id来把文章和分类对应起来,没包含这个字段的话,Laravel无法完成匹配,自然返回空集合。
  2. take()的全局限制问题:即使加上了category_id,直接在关联闭包里用take(4)也不是按每个分类取4条,而是全局取前4条文章,再分配到对应的分类上——如果某个分类的关联文章不在这前4条里,该分类的articles就会是空的。

解决方案

方案1:子查询实现每个分类取4篇文章

通过子查询针对每个分类单独限制文章数量,同时确保查询包含中间表的关联字段:

$categoryArticles = Category::whereHas('articles')
    ->with(['articles' => function ($query) {
        $query->select('articles.id', 'articles.title', 'article_category.category_id')
            ->whereIn('articles.id', function ($subQuery) {
                $subQuery->select('article_id')
                    ->from('article_category')
                    ->whereColumn('category_id', 'categories.id')
                    ->limit(4);
            });
    }])
    ->take(4)
    ->get();

方案2:自定义关联方法

在Category模型里新增一个专门返回前4篇文章的关联:

// Category.php
public function topFourArticles() {
    return $this->belongsToMany(Article::class, 'article_category')
        ->select('articles.id', 'articles.title', 'article_category.category_id')
        ->limit(4);
}

然后调用这个关联即可:

$categoryArticles = Category::whereHas('articles')
    ->with('topFourArticles')
    ->take(4)
    ->get();

关键注意事项

  • 多对多关联的查询必须包含中间表的关联字段(比如这里的article_category.category_id),否则无法完成文章与分类的匹配。
  • 不要直接在关联闭包里用take()/limit()来实现“每个分类N条”的需求,必须通过子查询或自定义关联的方式,确保限制是针对单个分类而非全局结果。

内容的提问来源于stack exchange,提问作者K1-Aria

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.21 17:02:09