Laravel中如何合并父分类与子分类的文章?
我来给你几个实用的方案,帮你搞定父分类及其所有子分类的文章合并问题~
方案一:递归获取所有分类ID,一次性查询文章(适合中小型数据量)
首先在Category模型里加一个辅助方法,递归获取当前分类及其所有子分类的ID集合:
public function getAllDescendantIds() { // 先把当前分类ID加入数组 $ids = [$this->id]; // 递归遍历所有子分类,合并它们的ID foreach ($this->children as $child) { $ids = array_merge($ids, $child->getAllDescendantIds()); } return $ids; }
然后在控制器里,用这些ID一次性查询所有关联的文章,记得去重(避免同一篇文章属于多个分类时重复展示):
public function show_category($slug){ $category = $this->category->findBySlug($slug); // 获取当前分类+所有子分类的ID集合 $allCategoryIds = $category->getAllDescendantIds(); // 通过Post模型的分类关联查询所有匹配的文章,去重 $posts = Post::whereHas('categories', function ($query) use ($allCategoryIds) { $query->whereIn('category_id', $allCategoryIds); })->distinct()->get(); return view('post.category.show',compact('posts','category')); }
注意:要确保
Post模型里有对应的分类关联:public function categories() { return $this->belongsToMany(Category::class, 'post__post_category_relations', 'post_id', 'category_id'); }
方案二:使用嵌套集合优化(适合分类层级深、数据量大的场景)
如果你的分类层级复杂、数据量较多,递归查询会有性能问题,推荐用laravel-nestedset包来快速获取所有后代分类:
- 先安装包:
composer require kalnoy/nestedset
- 在
Category模型里引入嵌套集合特性:
use Kalnoy\Nestedset\NodeTrait; class Category extends Model { use NodeTrait; // ... 你的其他模型代码 }
- 控制器里直接获取所有相关分类ID并查询文章:
public function show_category($slug){ $category = $this->category->findBySlug($slug); // 获取当前分类+所有子分类的ID(嵌套集合提供的快捷方法) $allCategoryIds = $category->descendantsAndSelf()->pluck('id'); $posts = Post::whereHas('categories', function ($query) use ($allCategoryIds) { $query->whereIn('category_id', $allCategoryIds); })->distinct()->get(); return view('post.category.show',compact('posts','category')); }
这个方案的查询效率比递归高很多,因为嵌套集合用数据库的范围查询替代了递归遍历。
方案三:递归合并文章(适合小数据量场景)
如果你的分类数据很少,也可以直接在模型里递归合并所有关联文章:
public function allPosts() { // 先获取当前分类的文章 $posts = $this->posts; // 递归合并所有子分类的文章 foreach ($this->children as $child) { $posts = $posts->merge($child->allPosts()); } // 去重后返回 return $posts->unique('id'); }
控制器里直接调用:
public function show_category($slug){ $category = $this->category->findBySlug($slug); $posts = $category->allPosts(); return view('post.category.show',compact('posts','category')); }
注意:这个方案会产生多次数据库查询(每个子分类一次),数据量大时不推荐使用。
关键注意点
- 文章去重:一定要用
distinct()(数据库层去重)或unique('id')(集合层去重),避免同一篇文章因属于多个分类而重复展示。 - 预加载优化:如果用方案一,记得预加载子分类避免N+1查询:
$category = $this->category->with('children')->findBySlug($slug);
内容的提问来源于stack exchange,提问作者justcntt
相关产品推荐
相关产品推荐

