Как говорят лучше просить и 5 минут побыть дураком, чем не спросить и быть дураком всю жизнь :)
Пишу небольшой сайт с объявлениями, где у каждого объявления есть свои параметры (с разными типами и значениями).
Соотвественно были созданы несколько таблиц: - основная таблица с id обьявления и теми параметрами которые есть у каждого объявления - таблица с параметрами и их типами - таблица в которой пишутся значения параметров с привязкой к id объявлению, id параметру и значению параметра.
В принципе ничего сложного. Сложное начинается дальше:
Есть 3 основных задачи что нужно сделать: - вывести объявления по группам (id группы в основной таблице) - фильтровать объявлениям по параметрам в группе - добавить объявление в группу, где предопределены выбираемые параметры.
А дальше начинается лютая жесть. 1. Вывести объявления по группам - легко, сделано. 2. Фильтровать объявления по параметрам. То на чём я пока застрял:
Эта задача делиться на 2 части: 1. Сформировать страницу с фильтрами для выбранной группы. Пользователь нажимает на кнопку "фильтр" в нужной группе объявлений, посылается ajax запрос и начинается формироватся страница с фильтрами: - смотрим на все активные объявления в группе - смотрим какие параметры определены у каждого объявления в активных объявлениях и их значения - группируем параметры убирая дубли - забираем json массив и генериуем на php страницу с фильтрами. (крутость то что показываются только те фильтры и значения фильтров, которы присвоены активным объявлениям в группе, то есть не показываем лишние_
Пользователь, получив сгенерированную страницу с формой должен выбирать те компоненты фильтров из доступных и отмечать те значения в группах фильтров, которые ему нужны, и по идее показывается те объявления которые подпадают под этот запрос.
Вот тут я застрял.
В чём именно проблема. Я пытаюсь написать универсальный обработчик запрос-ответ. То есть посылаем выбранные параметры, отбираем MySql запросом нужные объявления и показываем.
Количество групп фильтров (цвет, цена, размер и т.д.) заранее не известны для будущего MySql запроса. Это будет известно когда прийдет запрос от обработчика фильтров.
Было бы не сложно, если бы способ фильтрации был одного типа (например чекбоксы), но фильтры бывают разные. Например типа integer, вес в кг, где пользователь выбирает на фронтэнде диапазон от и до.
Получается что на страницу обработчика перед выборкой SELECT мы получаем данные из формы, где будет известно:
То есть можно ли сделать один универсальный обработчик на водящие данные, что бы выбрать нужные объявления? Для наглядности пример двух входящий форм, что бы понять можно ли вообще написать универсальный обработчик фильтров.
пришедшая форма от группы объявлений № 1
пришедшая форма от группы объявлений № 2
Получается что у нас приходит номер параметра (где можно в базе увидеть его тип), приходит выбранные значения (одно значение, диапазон значений, выборка), и собственно нужно как-то это обработать заранее не зная какие типы параметров прийдут и какие значение из этих параметров нужно выбрать.
Самое тупое и простое решение которое на данном этапе я знаю как реализовать: сделать 30 обработчиков на 30 групп товаров. Но товарищи программисты, как я после этого в зеркало смотреть буду? :)
Кто знает как мне помочь?
У себя для поиска по списку пользователей использую такой подход (реализовано на данный момент частично, но подход должен быть понятен):
/**
* Получение списка id пользователей по условиям
*
* @param array $filters Фильтры
* @param array $order Порядок сортировки
*
* @throws InvalidArgumentException
*
* @return array
*/
public function filter(array $filters, array $order = [])
{
$fields = $this->c->dbMap->users; // список имен полей таблицы пользователей с их типом
$orderBy = [];
$where = ['u.id>1']; // нужное мне ограничение при любых фильрах
// проверка и построение порядка вывода результата
foreach ($order as $field => $dir) {
if (! isset($fields[$field])) {
throw new InvalidArgumentException('No sorting field found');
}
if ('ASC' !== $dir && 'DESC' !== $dir) {
throw new InvalidArgumentException('The sorting order is not clear');
}
$orderBy[] = "u.{$field} {$dir}";
}
if (empty($orderBy)) {
$orderBy = 'u.username ASC';
} else {
$orderBy = \implode(', ', $orderBy);
}
$vars = [];
// проверка и построение условий запроса
foreach ($filters as $field => $rule) {
if (! isset($fields[$field])) {
throw new InvalidArgumentException('No sorting field found');
}
switch ($rule[0]) {
case '=':
case '!=':
$where[] = "u.{$field}{$rule[0]}?{$fields[$field]}";
$vars[] = $rule[1];
break;
case 'LIKE':
$where[] = "u.{$field} LIKE ?{$fields[$field]}";
$vars[] = \str_replace(['*', '_'], ['%', '\\_'], $rule[1]);
break;
case 'BETWEEN':
... // пишем свои обработки
break;
case ... // пишем свои обработки
...
break;
default:
throw new InvalidArgumentException('The condition is not clear');
}
}
$where = \implode(' AND ', $where);
$sql = "SELECT u.id
FROM ::users AS u
WHERE {$where}
ORDER BY {$orderBy}";
$ids = $this->c->DB->query($sql, $vars)->fetchAll(PDO::FETCH_COLUMN);
return $ids;
}
На вход подаем массив $filters в виде
$filters = [
'username' => ['LIKE', 'Vi%'],
'group' => ['BETWEEN', 1, 10],
];
и массив $order в виде
$order = [
'username' => 'DESC',
];
На выходе получаем id'шки записей удовлетворяющие фильтрам в нужном порядке.
Я не буду кидать сюда код, а просто изложу некие мысли. Описанный ниже подход успешно реализован, но код показать не могу.
фильтр
и расширяя его для каждого из типов фильтров, получим набор классов, которые умеют принимать значения и генерировать SQL-условиеВ итоге имеем: классы фильтр
для всех типов, и все эти фильтры умеют делать SQL-условие из выбранных в себе значений.
В моей задаче была не одна таблица, и много больше, поэтому мне было легче сделать фильтры и общем виде и создавать их для каждой таблицы, просто указывая тип и связанное поле. В вашей задаче немного другой уклон, но подход в целом можно взять тот же.
Рисуем фильтры с выбранными значениями, ловим выбор пользователя, получаем набор экземпляров класса фильтр
, собираем со всех фильтров условие SQL, объединяем условия SQL через AND между собой и получаем большущий такой WHERE для SQL запроса. Сохраняем в сессии, лимитируем, выдаем постраничный результат, все довольны.
Плюсы:
Минусы:
Если текстом описание не понятно, я могу набросать небольшой пример с парой типов фильтров, но я надеюсь, что вы сами попробуете, это не трудно
Айфон мало держит заряд, разбираемся с проблемой вместе с AppLab
Перевод документов на английский язык: Важность и ключевые аспекты
Добрый деньМожет кто нибудь из гуру битрикса знает, как добавить drag'n'drop в редактирование формы iblock