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

Laravel多对多关联下嵌套分类商品查询及排序控制器改造

Solution for Showing Category + Subcategory Products with Sorting

Hey there! Let's get this sorted out so clicking a category shows products from that category and all its subcategories, plus working sorting functionality. Here's what you need to change:


First, let's add a static method to your Category model that fetches the ID of the selected category plus all its nested subcategories. This iterative approach is more efficient than recursive calls for larger category trees:

// app/Category.php
class Category extends Model {
    // ... existing methods ...

    public static function getAllRelatedCategoryIds($slug) {
        // Get the main category (throws 404 if not found)
        $mainCategory = self::where('slug', $slug)->firstOrFail();
        
        $categoryIds = [$mainCategory->id];
        $childIds = self::where('parent_id', $mainCategory->id)->pluck('id')->toArray();
        
        // Iterate to get all nested subcategories
        while (!empty($childIds)) {
            $categoryIds = array_merge($categoryIds, $childIds);
            $childIds = self::whereIn('parent_id', $childIds)->pluck('id')->toArray();
        }
        
        return $categoryIds;
    }
}

Step 2: Rewrite the Product Controller's Index Method

Your current controller only fetches products from the single selected category. Let's refactor it to use our new category ID method, clean up the sorting logic, and fetch all relevant products:

// app/Http/Controllers/ProductController.php
class ProductController extends Controller {
    public function index($slug, Request $request) {
        // Get all IDs for the category and its subcategories
        $categoryIds = Category::getAllRelatedCategoryIds($slug);
        
        // Start building the product query
        $productsQuery = Product::whereHas('categories', function ($query) use ($categoryIds) {
            $query->whereIn('categories.id', $categoryIds);
        });
        
        // Apply sorting (default to "latest" if no sortBy parameter is present)
        $sortOption = $request->get('sortBy', 'latest');
        switch ($sortOption) {
            case 'asc':
                $productsQuery->orderBy('price', 'asc');
                break;
            case 'desc':
                $productsQuery->orderBy('price', 'desc');
                break;
            case 'latest':
            default:
                $productsQuery->orderBy('created_at', 'desc');
                break;
        }
        
        // Fetch the products (use paginate() instead of get() if you want pagination)
        $products = $productsQuery->get();
        
        // Get the main category for your view (for breadcrumbs, title, etc.)
        $mainCategory = Category::where('slug', $slug)->firstOrFail();
        
        return view('products', compact('mainCategory', 'products'));
    }
}

Key Improvements Here:

  • Cleaner logic: We removed the messy if/else block around the sort parameter and handle everything in one flow.
  • Subcategory inclusion: The whereHas clause checks if a product belongs to any of the relevant categories (main + subcategories).
  • Automatic 404 handling: firstOrFail() will throw a 404 error if the category slug doesn't exist, so you don't need manual checks.

Step 3: Update Your View

Since we're now passing a direct $products collection to the view (instead of relying on $category->products), you'll need to adjust the loop and tweak the sort dropdown:

<!-- resources/views/products.blade.php -->
<!-- Remove the csrf_field() - it's not needed for GET requests! -->
<form id="sortProducts" class="form-inline" method="get">
    <label for="sortBy">Sort By:</label>
    <select name="sortBy" id="sortBy">
        <option value="latest" {{ request()->sortBy == 'latest' ? 'selected' : '' }}>Latest</option>
        <option value="asc" {{ request()->sortBy == 'asc' ? 'selected' : '' }}>Price Low to High</option>
        <option value="desc" {{ request()->sortBy == 'desc' ? 'selected' : '' }}>Price High to Low</option>
    </select>
</form>

<!-- Loop through the $products collection instead of $category->products -->
@foreach($products as $product)
<div class="product">
    <img border="0" src="{{ Voyager::image($product->image) }}" alt="{{ $product->name }}">
    <div class="product-name">{{ $product->name }}</div>
    <div class="product-price">${{ $product->price }}</div>
</div>
@endforeach

View Tweaks:

  • Removed {{ csrf_field() }}: CSRF tokens aren't required for GET requests, so this was unnecessary.
  • Added selected attributes: This keeps the user's chosen sort option visible after the page reloads.

Optional: Pagination

If you have lots of products, replace $productsQuery->get() with $productsQuery->paginate(12) (adjust the number to fit your needs) and add pagination links in your view:

{{ $products->links() }}

That's it! Now when a user clicks "Phone", they'll see all products from Phone, Samsung, and Nokia categories, and can sort them by price or date.

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.06 18:38:14