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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.27 02:18:18