Search code examples
laravelmany-to-manye-commerce

Laravel ecommerce dynamic product filtering


I have this huge problem that I can't find a workaround. I'm building an e-commerce app with Laravel and it's essential to build a good filtering system for products. I have products and attributes table with a many-to-many relation. Pivot table product_attribute has additional value (if the attribute is color, value would be red for example) column. When entered to the shop page there is a sidebar with the filtering options. The only option I can show there is brand since it's a one-to-many relation with the product table. What's the right way to show these attributes. As I mentioned attributes should be dynamic to the products on the page. Different products in different categories (bikes, clothes, balls, pool tables) may have different attributes.

public function show($slug){

  // Get products with attributes for specic 
  $category = Category::with('products')->where('slug',$slug)->firstOrFail();

  // Collect brand ids
  $b_ids = array_filter($category->products->pluck('brand_id')->toArray());

  //Get brands to show in the filtering options
  $brands = Brand::whereIn('id',$b_ids)->get();

  $attributes = ?

  return view('front.shop', compact('category','brands', 'attributes'));
  }

Solution

  • Eager load attributes with products - Category::with('products.attributes')

    public function show($slug){
    
      // Get products with attributes for specic 
      $category = Category::with('products.attributes')->where('slug',$slug)->firstOrFail();
    
      // Collect brand ids
      $b_ids = array_filter($category->products->pluck('brand_id')->toArray());
    
      //Get brands to show in the filtering options
      $brands = Brand::whereIn('id',$b_ids)->get();
    
     
    
      return view('front.shop', compact('category','brands'));
    }
    

    Then in the view attributes can be accessed via the product

    that's not what I want. It just gives me the value. I want attribute name to be displayed at the top (for example COLOR) and values (Red, Blue, etc) at the bottom next to a checkbox for the user to filter

    Assuming you are storing all possible values for an attributes as options

    // ...
    
    @foreach($categories as $category)
       @foreach($category->products as $product)
            @include('attribute-options', ['attributes' => $product->attributes])
        @endforeach
    @endforeach
    
    // ... 
    

    Extract attributes display for a product to a partial - attribute-options.blade.php

    @foreach($attributes as $attribute)
        <h3>{{ $attribute->name }}
        <ul>
            @foreach($attribute->options as $option)
                <li>
                    <label for="{{$attribute->name}}-{{$option}}">{{ $option }}
                        <input 
                          type="radio" 
                          value="{{$option}}"
                          id="{{$attribute->name}}-{{$option}}"
                          name="{{$attribute->name}}-{{$option}}"
                        />
                    </label>
                </li>
            @endforeach
        </ul>
    @endforeach