sunel / eav

Entity–attribute–value model (EAV) for Laravel Artisan
https://sunel.github.io/eav/
143 stars 39 forks source link

How to develop a products filter with multiple attributes and tags #21

Closed joaopmmartins closed 5 years ago

joaopmmartins commented 5 years ago

I am developing a products listing page, which has a filter sidebar for attributes and tags. The filter is based on checkboxes and users can select multiple filters.

Filter by Width:

30cm (12 products) 20 cm (3 products)

Filter by Weight:

2 Kg (3 products) 12 Kg (6 products)

Filter by Tags:

Outdoor Seating ( 4 products) Long Handled Equipment ( 12 counts)

my product model has the following relationship:

public function attributes() { return $this->hasMany('Eav\Attribute', 'entity_id'); }

public function categories() { return $this->belongsToMany('App\Category', 'category_product', 'product_id', 'category_id'); }

public function tags() { return $this->belongsToMany('App\Tag', 'product_tag'); }

I have the following function in my product model, to filter the category products on attributes and tags:

public function scopeFilter($q) {

    // filter on EAV attributes 
    $filter = request('filter');
    foreach ($filter as $filter_code => $values) :
                $q->whereInAttribute($filter_code, $values);
    endforeach;

   // filter on tags
   $tags = request('tag');
    $q->whereHas('tags', function ($query) use ($tags) {
                $query->whereIn('slug', $tags);
    });
   ......

    return $q;

}

Can you advise me how to develop such a function to get the eloquent query to return selected EAV attributes OR (not AND) selected tags?

Many Thanks.

sunel commented 5 years ago

Ref https://github.com/sunel/eav/issues/22#issuecomment-445520605

For the start and i will walk you based on our need

joaopmmartins commented 5 years ago

The width and weight are EAV attributes but the tags are stored in a separated table. How can I select all the products with attributes weight=2Kg OR tagged with 'Outdoor Seating'?

I tried to create the OR filter like this:

$products->where(function($q) { $tags = array('outdoor-seating'); $q->whereHas('tags', function ($query) use ($tags) { $query->whereIn('slug', $tags); }); $q->orWhereAttribute('weight','=','2Kg'); });

but the orWhereAttribute is missing from the output query. see below:

select ..... ...... ...... where... ....... and ( exists (select * from [tags] inner join [product_tag] on [tags].[id] = [product_tag].[tag_id] where [product].[id] = [product_tag].[product_id] and [slug] in ('outdoor-seating') )

How can I combine the EAV orWhereAttribute and whereHas to create a filter with OR multiple conditions?

sunel commented 5 years ago

Let me put my understanding

You have a EAV model as below

class Products extends  \Eav\Model
{
    const ENTITY  = 'product';

    public function tags() {
          return $this->belongsToMany(Tag::class);
    }
}

class Tag extends \Illuminate\Database\Eloquent\Model
{
    public function posts()
    {
        return $this->belongsToMany(Products::class);
    }
}

now you want to filter it tags and attributes

$products = Products::whereHas('tags', function ($query) {
    $query->whereIn('slug', ['outdoor-seating']);
})->orWhereAttribute('weight','=','2Kg')->get();

Remember

 \Eav\Model extends \Illuminate\Database\Eloquent\Model
joaopmmartins commented 5 years ago

Hi Sunel,

The following query:

$products = Product::with('categories')->orWhereHas('categories', function ($query) { $query->where('slug', request()->category); })->where('product.frontend_status', 1)->select(['attr.*']) ->whereAttribute('garden_tool_type','=','cultivator') ->whereAttribute(function ($query) { $query->orWhereAttribute('garden_tool_material', '=', 'wooden'); $query->orWhereAttribute('garden_tool_material', '=', 'metal'); });
// ->orWhereAttribute('garden_tool_type','=','fork')

gives me the error: 'Illegal offset type'.

I just want to create a query with grouped AND and OR that does:

SELECT the products belonging to category of slug 'garden-tools' AND ( attribute 'garden_tool_type' = 'cultivator' OR garden_tool_material = 'wooden' OR garden_tool_material='metal' OR tag='css' OR tag ='js' ...... )?

sunel commented 5 years ago

@joaopmmartins

Can you mention which line this error occurs.

i am sure this code your refering request()->category should be request()->query('category')

sunel commented 5 years ago

Closing this issue as we have #25 to track the bug