Laravel фильтровать несколько массивов WhereIn

Я пытаюсь создать фильтр для интернет-магазина. Пользователь может фильтровать продукты по тегам, каждый тег аромата добавляется к массиву: $tags_array

т. е. показать мне все продукты, содержащие тег "киви" И "ментол".

дд($tags_array);

array:2 [
  0 => "kiwi"
  1 => "menthol"
]

Раздел фильтра запроса

$query = $tags_array ? $query
 ->join('product_product_tag', 'products.id', '=', 'product_product_tag.product_id')
 ->join('product_tags', 'product_tags.id', '=', 'product_product_tag.tag_id')
 ->whereIn('product_tags.value', $tags_array) : $query;

-Один товар имеет много тегов

-Один тег принадлежит многим продуктам

product_tags

id | value 
------------------
1  | kiwi 
2  | apple
3  | menthol 
4  | strawberry 

-Сводная таблица: product_product_tag

product_product_tag

id | product_id | tag_id 
----------------------------
1  |    1       |    1
2  |    1       |    3
----------------------------
3  |    2       |    1
4  |    2       |    2
5  |    2       |    3
----------------------------
6  |    3       |    1
7  |    3       |    4
----------------------------
8  |    4       |    3

ПРОБЛЕМА

Текущий запрос показывает все продукты с тегом киви ИЛИ ментол.

фактический результат: Продукты: 1, 2, 3, 4

ожидаемый результат: Продукты: 1, 2

Этот запрос работает правильно, когда применяется только один тег. Как включить в этот фильтр несколько примененных тегов (продукт имеет тег киви И тег ментола). Пользователь может фильтровать по тегам 0,1,2,3,...

Есть ли наилучшая практика для обработки динамического количества тегов?

изменить: я использую Laravel DB Query Builder

2-е редактирование: попытка ответа btl

  $query = $tags_array ? $query
      ->join('product_product_tag', 'products.id', '=', 'product_product_tag.product_id')
      ->join('product_tags', 'product_tags.id', '=', 'product_product_tag.tag_id') : $query;
  if($tags_array) {
      foreach ($tags_array as $tag) {
          $query->where('product_tags.value', '=', $tag);
      }
  }

3-е редактирование: вернемся к следующему:

$query = $tags_array ? $query
 ->join('product_product_tag', 'products.id', '=', 'product_product_tag.product_id')
 ->join('product_tags', 'product_tags.id', '=', 'product_product_tag.tag_id')
 ->where('product_tags.value', $tags_array) : $query;

Это все равно неправильно. Текущий фильтр возвращает результаты для первого найденного тега. Буду продолжать работать над более динамичным решением


person ryank    schedule 06.02.2018    source источник


Ответы (1)


whereIn('product_tags.value', $tags_array) будет соответствовать всему, что имеет хотя бы один из тегов в массиве, поэтому он ведет себя как OR. Если вам нужно сопоставить только продукты со всеми выбранными тегами, вам нужно настроить несколько предложений where, что-то вроде:

$query = Product::with('tags');
foreach ($tags_array as $tag) {
     $query->whereHas('tags', function($q) use ($tag) {
         $q->where('id', $tag);
     });
 }
 $query->get();

Изменить

$query = $tags_array ? $query
 ->join('product_product_tag', 'products.id', '=', 'product_product_tag.product_id')
 ->join('product_tags', 'product_tags.id', '=', 'product_product_tag.tag_id');

foreach ($tags_array as $tag) {
    $query->where('product_tags.id', '=', $tag);
}
person Community    schedule 06.02.2018
comment
Тай, я понимаю логику здесь, но мой запрос написан с использованием Laravel db builder. Любая идея о том, как преобразовать это? Я удалил красноречивый из тегов, извините за путаницу - person ryank; 06.02.2018
comment
Я не думаю, что это слишком сильно изменится, вы все равно будете использовать псевдоним для запроса, а затем перебирать теги. Посмотрите на мое редактирование и посмотрите, работает ли это. - person ; 06.02.2018
comment
Смотрите мое второе редактирование, где я реализовал ваш ответ. Это работает для одного тега аромата, но как только добавляется второй, результатов нет. - person ryank; 06.02.2018