Laravel: отфильтровать много для многих по нескольким значениям

Я создаю многоуровневый навигационный модуль для моего приложения laravel. Очень похоже на Magento или WooCommerce. Это идея: продуктам могут быть назначены один или несколько атрибутов, а пользователи должны иметь возможность фильтровать продукты с помощью этих атрибутов . Подобно атрибуту «материал», где продуктам может быть присвоено одно или несколько значений, таких как железо , дерево и пластик . Моя проблема в том, что я не могу понять, как это сделать должным образом.

Мой datamodel выглядит так:

products table: id | name | other info... example: 42 | Chair | ... example: 14 | Bike | ... attributes table: id | name | description example: 02 | Material | the material that the... attribute_options table: id | attribute_id | value example: 11 | 02 | Wood example: 12 | 02 | Iron pivot table: id | product_id | attribute_option_id example: 12 | 42 | 11 example: 12 | 42 | 12 example: 12 | 14 | 12 

Я установил много-много отношений между моделями продуктов и продуктов (следовательно, сводная таблица).

В моем представлении отображается форма, которая перебирает все разные атрибуты, а затем перебирает их параметры и создает флажок для каждого из параметров со значением их идентификаторов . Значения идеально группируются в одном массиве и называются так: filter [attribute_id] [attribute_option_id] . Пример:

 <input type="checkbox" name="filter[02][11]" value="id"> Wood <input type="checkbox" name="filter[02][12]" value="id"> Iron <input type="checkbox" name="filter[xx][xx]" value="id"> Etc.. 

При отправке формы все выбранные значения атрибута атрибута отправляются на сервер по маршруту, на котором эта информация должна обрабатываться, и должны возвращаться только продукты, соответствующие всем различным критериям.

Поэтому, если будут опубликованы фильтры [02] [11] и [02] [12], должны быть возвращены только продукты, которые имеют атрибутные опции «Дерево» и «Железо».

Сначала я подумал, что это будет довольно просто, но я думаю, что я не настолько квалифицирован, как я думал, что буду … Так как это вопрос Laravel, я бы хотел, чтобы это было красноречивое решение!

PS Если я испортил (думая за моей) datamodel, скажите, пожалуйста! Я все еще изучаю много вещей о веб-разработке и, возможно, есть намного лучшее / более чистое решение / подход к моей проблеме

——– РЕДАКТИРОВАТЬ / ДОПОЛНИТЕЛЬНАЯ ИНФОРМАЦИЯ ——–

С помощью следующего кода я могу отфильтровать один атрибут

 $products = $products->whereHas('attributeOptions', function($query) { $query->where(function($query) { $query->where('value', 'iron'); }); }); 

Но поскольку продукты могут иметь несколько атрибутов разных типов (например, цвет и материал или несколько материалов), мне нужно иметь возможность устанавливать несколько типов:

 $products = $products->whereHas('attributeOptions', function($query) { $query->where(function($query) { $query->where('value', 'iron'); $query->where('value', 'wood'); $query->where('value', 'yellow'); }); }); 

Но этот код не работает, поскольку он проверяет, что одна строка имеет несколько значений вместо проверки значений в нескольких строках с одним и тем же product_id.

Отношение между продуктами и атрибутами :

 public function attributeOptions() { return $this->belongsToMany('AttributeOption', 'products_attribute_options', 'product_id', 'attribute_option_id'); } 

Возможно ли это в Eloquent / Laravel или мне нужно другое решение

Заранее спасибо, я бы хотел учиться у вас!

Я полностью согласен с @astro и @Dave в комментариях. Вы должны пересмотреть дизайн своей базы данных. Я отправлю две ссылки из @Dave, чтобы они стали более заметными для других:

Как реализовать систему фильтрации в SQL?

Наилучшая производительность для получения результатов EAV MySQL как реляционная таблица


Хотя я не думаю, что это хорошо работает с точки зрения производительности (со многими атрибутами и продуктами), вот решение, которое должно работать с вашей текущей настройкой:

Измените свои флажки следующим образом:

 <input type="checkbox" name="filter[02][]" value="11"> Wood <input type="checkbox" name="filter[02][]" value="12"> Iron <input type="checkbox" name="filter[xx][]" value="xx"> Etc.. 

Таким образом, несколько значений для того же фильтра будут добавлены в числовой массив для filter[02][]

 $query = Product::query(); foreach(Input::get('filter') as $attributeId => $optionIds){ foreach($optionIds as $optionId){ $query->whereHas('attributeOptions', function($q) use ($attributeId, $optionId){ $q->where('attributeId', $attributeId) ->where('attribute_option_id', $optionId); } } } $products = $query->get(); 

Я бы подошел к этому, используя метод whereHas, а затем используя whereIn, чтобы изолировать записи, которые содержат нужные атрибуты, а затем отфильтровать их только на те записи, которые имеют правильное количество атрибутов.

 // Assuming you have the IDs of the attributes, rather than the value $desiredAttributes = [11,12]; $products = $products->whereHas('attributeOptions', function($query) use($desiredAttributes) { $query->whereIn('attribute_id', $desiredAttributes); }, "=", count($desiredAttributes))->get(); 

Поэтому сначала нужно получить все записи, которые имеют атрибуты 11 или 12, а затем фильтруют только записи, которые имеют count (attribute_option_id) == 2