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

Laravel Eloquent:获取指定分类及所有嵌套子分类的文章

解决分类及其嵌套子分类ID重复问题,同时优化文章查询方式

嘿,看起来你在递归获取分类ID的时候碰到了重复值的问题,我来给你几个解决方案,从修复现有代码到更高效的数据库查询都有!

一、先修复你的递归方法(快速解决重复问题)

你原来的递归逻辑里,getChildren方法的参数传递和返回逻辑有点混乱,导致同一个ID被多次添加到数组里。我们可以简化递归逻辑,让每个分类只收集自己和子分类的ID,最后统一去重:

修改后的getSubcategoriesIds方法(可以删掉原来的getChildren):

// PostController.php
public function getSubcategoriesIds($category) {
    // 先把当前分类ID加入数组
    $ids = [$category->id];
    
    // 递归遍历每个子分类,合并它们的ID数组
    foreach ($category->subcategories as $subcategory) {
        $ids = array_merge($ids, $this->getSubcategoriesIds($subcategory));
    }
    
    // 去重并重置数组索引,避免重复值和混乱的键名
    return array_values(array_unique($ids));
}

这样修改后,每个分类ID只会被添加一次,返回的数组就不会有重复了。

二、更高效的方案:用数据库递归查询(推荐)

上面的方法虽然能解决问题,但如果分类层级很深,会依赖Laravel的集合递归处理,效率不如直接让数据库一次性返回所有需要的ID。我们可以用数据库的递归CTE查询(适合MySQL 8.0+、PostgreSQL等支持的数据库):

在Category模型里添加一个静态方法,专门获取分类及其所有子分类的ID:

// Category.php
public static function getDescendantIds($categoryId) {
    // 用原生SQL的递归查询,一次性拿到所有后代ID(包括自身)
    $recursiveQuery = "WITH RECURSIVE category_tree AS (
        SELECT id, parent_id FROM categories WHERE id = ?
        UNION ALL
        SELECT c.id, c.parent_id FROM categories c
        JOIN category_tree ct ON c.parent_id = ct.id
    )
    SELECT id FROM category_tree";
    
    // 执行查询并提取ID数组
    return collect(DB::select($recursiveQuery, [$categoryId]))->pluck('id')->toArray();
}

然后在控制器里直接调用这个方法:

// PostController.php
public function show($id) {
    $categoryIds = Category::getDescendantIds($id);
    $posts = Post::whereIn('category_id', $categoryIds)->get();
    return view('post.index', compact('posts'));
}

这种方式只需要一次数据库查询,就能拿到所有需要的分类ID,性能更好,而且天然不会有重复值。

三、终极优化:直接关联查询文章,不用单独拿ID

其实你还可以跳过“先拿分类ID再查文章”的步骤,直接利用Laravel的关联关系,一次性获取目标分类及其所有子分类的文章:

在Category模型里添加一个关联方法:

// Category.php
public function allDescendantPosts() {
    return $this->hasMany(Post::class)
                ->orWhereIn('category_id', function ($query) {
                    // 子查询获取所有子分类ID
                    $query->select('id')
                          ->from('categories')
                          ->withRecursive(['descendants' => function ($q) {
                              $q->select('id', 'parent_id')
                                ->from('categories')
                                ->where('parent_id', '=', DB::raw('categories.id'));
                          }])
                          ->where('id', $this->id);
                });
}

然后控制器里直接调用这个关联:

// PostController.php
public function show($id) {
    $category = Category::findOrFail($id);
    $posts = $category->allDescendantPosts;
    return view('post.index', compact('posts'));
}

这样一步到位,代码更简洁,也减少了中间步骤的性能损耗。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 17:27:38