Laravel实现主分类下所有子分类商品展示方案问询
问题:主分类页面展示所有子分类的全部商品
我有一个主分类,其下包含多个子分类。进入该主分类页面时,希望展示该主分类下所有子分类的全部商品。目前我的代码仅能展示当前分类的商品,不知道如何递归遍历所有子分类,恳请提供解决方案。
模型(Model)
class Category extends Model { public function children() { return $this->hasMany(Category::class, 'parent_id', 'id'); } public function parent() { return $this->belongsTo(Category::class, 'parent_id', 'id'); } public function products() { return $this->belongsToMany(Product::class, 'product_category', 'category_id'); } }
控制器(Controller)
class FrontedController extends EcommerceController { public function index() { $categories = Category::where('parent_id', 1)->where('id', '<>', 1)->get(); return view('ecommerce::fronted.pages.category',['categories' => $categories]); } }
解决方案
下面提供几种可行的实现方式,按需选择:
方法一:递归获取所有子分类ID后查询商品
步骤1:在Category模型添加递归获取后代ID的方法
public function getAllDescendantIds() { $descendantIds = []; foreach ($this->children as $child) { $descendantIds[] = $child->id; // 递归获取子分类的后代ID $descendantIds = array_merge($descendantIds, $child->getAllDescendantIds()); } return $descendantIds; }
步骤2:修改控制器逻辑,查询所有关联商品
public function index() { $mainCategoryId = 1; $mainCategory = Category::find($mainCategoryId); // 收集主分类ID + 所有子分类ID $categoryIds = array_merge([$mainCategoryId], $mainCategory->getAllDescendantIds()); // 查询所有目标分类下的商品,用distinct避免重复 $products = Product::whereHas('categories', function ($query) use ($categoryIds) { $query->whereIn('category_id', $categoryIds); })->distinct()->get(); $categories = $mainCategory->children; return view('ecommerce::fronted.pages.category', [ 'categories' => $categories, 'products' => $products ]); }
方法二:使用数据库递归CTE(性能更优)
如果分类层级较深,递归PHP方法可能有性能瓶颈,推荐用数据库层面的递归查询:
public function index() { $mainCategoryId = 1; // 用递归CTE获取所有子分类ID(含主分类) $categoryIds = Category::withRecursive(['descendants' => function ($query) { $query->select('id', 'parent_id'); }])->where('id', $mainCategoryId) ->get() ->pluck('id') ->merge( Category::withRecursive(['descendants' => function ($query) { $query->select('id', 'parent_id'); }])->where('id', $mainCategoryId) ->first() ->descendants ->pluck('id') ) ->unique(); $products = Product::whereHas('categories', function ($query) use ($categoryIds) { $query->whereIn('category_id', $categoryIds); })->distinct()->get(); $categories = Category::where('parent_id', $mainCategoryId)->get(); return view('ecommerce::fronted.pages.category', [ 'categories' => $categories, 'products' => $products ]); }
方法三:模型关联直接获取所有后代商品
在Category模型定义关联方法,直接获取当前分类及所有子分类的商品:
public function allProducts() { // 合并当前分类商品与所有子分类商品 return $this->products()->union( $this->children->flatMap(function ($child) { return $child->allProducts(); }) ); }
控制器中调用:
public function index() { $mainCategory = Category::find(1); $products = $mainCategory->allProducts()->distinct()->get(); $categories = $mainCategory->children; return view('ecommerce::fronted.pages.category', [ 'categories' => $categories, 'products' => $products ]); }
注意事项
- 确保Product模型中定义了与Category的关联:
class Product extends Model { public function categories() { return $this->belongsToMany(Category::class, 'product_category'); } } - 使用
distinct()是为了避免同一商品属于多个分类时重复展示 - 层级较深的分类结构,优先选择方法二(数据库递归CTE),性能更稳定
内容的提问来源于stack exchange,提问作者Ivan
相关产品推荐
相关产品推荐

