You need to enable JavaScript to run this app. laravel-nestedset关联文章查询如何校验父级分类发布状态?
优惠活动
大模型
产品
解决方案
定价
更多

laravel-nestedset关联文章查询如何校验父级分类发布状态?

问题根因

原有查询仅通过whereHas('category')校验了文章直接关联的单级分类发布状态,完全没有覆盖嵌套集结构中分类的上级父链路校验,因此会出现上级分类未发布、但直接分类已发布的文章被错误返回的问题。
laravel-nestedset 基于嵌套集(Modified Preorder Tree Traversal)实现树结构,每个分类节点自带lft(左值)、rgt(右值)字段,节点的全链路祖先(含自身)可以通过左右值范围规则一次性匹配,无需递归查库。

可直接落地的实现方案

前置确认

先确保两个基础配置正确:

  • App\Models\Category 模型已引入 Kalnoy\Nestedset\NodeTrait trait(laravel-nestedset 5.x 的基础配置)
  • App\Models\Article 模型已正确定义分类关联:
    public function category()
    {
        return $this->belongsTo(\App\Models\Category::class);
    }
    

方案1:基于嵌套集左右值的查询(性能最优)

嵌套集结构的核心判定规则:一个节点的所有祖先节点(从顶级到当前节点自身),必然满足 祖先节点.lft <= 当前节点.lft 且 祖先节点.rgt >= 当前节点.rgt。
基于这个规则写的查询,单条SQL就能完成全链路校验,没有多层子查询嵌套,数据量大时性能最优:

use App\Models\Article;

$validArticles = Article::whereExists(function ($query) {
    // 关联匹配文章直接所属的分类
    $query->from('categories as direct_category')
        ->whereColumn('direct_category.id', 'articles.category_id');
})
->whereNotExists(function ($query) {
    // 校验:该分类全链路(含自身)不存在未发布的分类
    $query->from('categories as ancestor_category')
        ->whereColumn('ancestor_category.lft', '<=', 'direct_category.lft')
        ->whereColumn('ancestor_category.rgt', '>=', 'direct_category.rgt')
        ->where('ancestor_category.publish', false);
})
->get();

对应你举的测试场景验证:

结构为news > politics,news.publish=0、politics.publish=1
此时归属politics的文章,关联的direct_category会匹配到politics节点,对应的ancestor_category查询会命中上级news的未发布记录,直接被过滤,符合预期无结果返回。

方案2:基于模型关联的链式写法(可读性更高)

laravel-nestedset 的NodeTrait 自带ancestors关联(返回当前节点的所有上级祖先节点,不包含节点自身),可以直接通过关联校验实现需求,代码可读性更强:

use App\Models\Article;

$validArticles = Article::whereHas('category', function ($query) {
    // 校验直接所属分类自身是发布状态
    $query->where('publish', true);
    // 校验所有上级祖先节点,不存在未发布的分类
    $query->whereDoesntHave('ancestors', function ($subQuery) {
        $subQuery->where('publish', false);
    });
})->get();

这种实现的逻辑和方案1完全一致,缺点是生成的SQL嵌套层级比方案1稍多,适合分类树层级不深、数据量不大的场景。

适配调整说明
  • 你的articles表category_id允许为null,如果需要保留无分类的文章,去掉查询里的whereExists关联直接分类的逻辑即可;如果需要过滤无分类的文章,保留现有逻辑即可
  • 如果之前自定义过laravel-nestedset的默认左右值字段名,记得把代码里的lft、rgt替换成实际使用的字段名
  • 两种实现都不会出现N+1查询问题,不需要循环递归查询父级分类ID

内容的提问来源于stack exchange,提问作者user2726957

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.11.13 22:24:10