За последние 24 часа нас посетили 17392 программиста и 1650 роботов. Сейчас ищут 837 программистов ...

Релятивный фильтр товаров

Тема в разделе "PHP и базы данных", создана пользователем Игонь, 28 сен 2016.

Метки:
  1. Игонь

    Игонь Новичок

    С нами с:
    28 сен 2016
    Сообщения:
    6
    Симпатии:
    0
    Добрый день, пишу для сайта умный фильтр по аналогии Яндекс.Маркета.
    Собственно уже который день озадачился вопросом реализация релятивного фильтра.
    Что имеем:
    Динамическую форму: (Форма генерируется автоматически, но для наглядности вставил HTML)
    Код (Text):
    1. <form method="get" action="">
    2. <div>
    3. <label>Asus</label>
    4. <input type="checkbox" name="filter[]" value="Asus">
    5. <label>Acer</label>
    6. <input type="checkbox" name="filter[]" value="Acer">
    7. <label>Lenovo</label>
    8. <input type="checkbox" name="filter[]" value="Lenovo">
    9. </div>
    10. <div>
    11. <label>2015</label>
    12. <input type="checkbox" name="filter[]" value="2015">
    13. <label>2014</label>
    14. <input type="checkbox" name="filter[]" value="2014">
    15. <label>2013</label>
    16. <input type="checkbox" name="filter[]" value="2013">
    17. </div>
    18. <button>Искать</button>
    19. </form>
    После сабмита формы имеется url следующего вида:
    Код (Text):
    1. site.com?filter=Asus&filter=2015
    Так же имеем таблицы в базе данных:
    Код (Text):
    1. products (id, title)
    2. products_to_filter (id, product_id, filter_id)
    Сам обработчик выглядит следующим образом (P.S фреймворк Laravel)
    Код (Text):
    1.  
    2. Выборка соответствующих фильтров.
    3. $filter[] = explode(',', $request->input('filter');
    4. $filters = DB::table('products_to_filter')->whereIn('id', $filter)
    5. ->get()
    6. ->all();
    7. foreach($filters as $row) {$filterArray[] = $row->product_id;}
    8. Выборка продуктов:
    9. $products = DB::table('products')->whereIn('id', $filterArray)->get()->all();
    Собственно моя проблема заключается в следующем, фильтр работает, но не совсем так как мне нужно, тобишь при фильтрации, он должен результаты уменьшать, а он же наоборот ищет более углубленно, и как следствие увеличивает кол-во результатов. Как решить данную проблему?
     
  2. [vs]

    [vs] Суперстар
    Команда форума Модератор

    С нами с:
    27 сен 2007
    Сообщения:
    10.559
    Симпатии:
    632
    Я тебя в пол шестого могу спросить - вот как, ты думаешь, выглядит SQL запрос? Посмотреть запрос - это первое, что надо сделать. В SQL оператор IN объединяет перечисленные значения через OR. Ясень пень, чем больше "или", тем больше вариантов. Тебе надо строить WHERE через AND. В Lavarel, по-ходу, вместо одного whereIn делать where на каждый параметр со знаком "=".
     
    Игонь нравится это.
  3. Игонь

    Игонь Новичок

    С нами с:
    28 сен 2016
    Сообщения:
    6
    Симпатии:
    0
    Здравствуйте, а как произвести постройку запроса WHERE если данные в нём каждый раз меняются динамически?
    То есть я конечно могу составить запрос единожды, но проблема заключается в том, что необходимо данный запрос сделать максимально динамическим.
    Код (Text):
    1. SELECT * FROM products_to_filter WHERE filter_id = 'Asus' AND filter_id = '2015'
    Ведь данные каждый раз могут быть абсолютно разными.
     
  4. [vs]

    [vs] Суперстар
    Команда форума Модератор

    С нами с:
    27 сен 2007
    Сообщения:
    10.559
    Симпатии:
    632
    @Игонь ну у тебя же конечное число параметров в фильтре. Зафигачь if-else, или строй запрос строкой SQL а не через методы.
    Глянул код внимательнее, по-ходу надо так
    Код (Text):
    1. DB::table('products')->whereIn(...)->whereIn(...)->whereIn(...)->whereIn(...)
    но я о Lavarel сужу по докам.
    Короче итог в логике, для сужения поиска нужно объединять условия.
     
    Игонь нравится это.
  5. Игонь

    Игонь Новичок

    С нами с:
    28 сен 2016
    Сообщения:
    6
    Симпатии:
    0
    Очень признателен за то, что в 6 утра помогаете. В итоге пытаюсь сделать что-то вроде этого:
    Код (Text):
    1. foreach($filter as $key => $row) {
    2.             if($key == 0) {
    3.                 $sqlArray[] = 'filter_id = ' . $row;
    4.             } else {
    5.                 $sqlArray[] = 'and filter_id = ' . $row;
    6.             }
    7.         }
    8.         $sql = implode(' ', $sqlArray);
    9.         $test = DB::table('filters_to_products')
    10.             ->where('category_id', $category)
    11.             ->where($sql)
    12.             ->get();
    Но возможно где-то могу ошибиться, скажите пожалуйста, на чистом SQL правильный вид запроса как должен выглядеть?
     
  6. [vs]

    [vs] Суперстар
    Команда форума Модератор

    С нами с:
    27 сен 2007
    Сообщения:
    10.559
    Симпатии:
    632
    @Игонь в общем пораскинув мозгами, я понял что всё немного сложнее, и задача решается таким запросом
    Код (Text):
    1. SELECT `product_id`, COUNT(DISTINCT `filter_id`) as `filters` FROM `products_to_filter` WHERE `filter_id` IN (0,1,2) GROUP BY `filter_id` HAVING `filters` = 3;
    Где 3 - количество используемых фильтров. Поле filters будет = 3 только у тех продуктов, которые соответствовали всем трем 0,1,2.
     
  7. Игонь

    Игонь Новичок

    С нами с:
    28 сен 2016
    Сообщения:
    6
    Симпатии:
    0
    Решил проблему следующим образом:
    Модель
    Код (Text):
    1. public static function getProductByFilter($category, $filter)
    2.     {
    3.         foreach($filter as $key => $row) {
    4.             $sqlArray[] = 'and filter_id = ' . $row;
    5.         }
    6.         $sql = implode(' ', $sqlArray);
    7.         $filterProductsArray = DB::select('SELECT * FROM filters_to_products WHERE category_id = ' . $category . ' ' . $sql);
    8.         if($filterProductsArray) {
    9.             foreach($filterProductsArray as $product) $productsArray[] = $product->product_id;
    10.             $products = Product::whereIn('id', $productsArray)
    11.                 ->orderBy('id', 'desc');
    12.             return $products;
    13.         }
    14.     }
    Контроллер:
    Код (Text):
    1. if($request->input('filter')) {
    2.             foreach($request->input('filter') as $row) $filter[] = $row;
    3.             $products = Product::getProductByFilter($page->id, $filter);
    4.             if($products) $products = $products->paginate(10);
    5.         } else {
    6.             $products = Product::getProductsForCategory($page->id);
    7.             if($products) $products = $products->paginate(10);
    8.         }
    Что скажете о данной реализации?
     
  8. [vs]

    [vs] Суперстар
    Команда форума Модератор

    С нами с:
    27 сен 2007
    Сообщения:
    10.559
    Симпатии:
    632
    @Игонь мне кажется, что эта идея была ошибкой и запрос, построенный таким образом, всегда будет давать пустой результат.)
    --- Добавлено ---
    Запрос с подсчетом совпавших фильтров точно сработает