Как настроить фильтр по связанным таблицам?

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Ответить
Gyry
Сообщения: 87
Зарегистрирован: 2017.09.11, 15:23

Как настроить фильтр по связанным таблицам?

Сообщение Gyry »

Такая проблема возникла с фильтрами из связанных таблиц.

Объяснение ситуации/предыстория:
Вот моя структура связанной таблицы.
Изображение
Имеется таблица object(товары). Необходимо было сохранять возможность добавлять разные атрибуты(то есть характеристики) разным объектам.
Имеется отдельная таблица "attribute" в которой сохраняются названия группа путктов
В таблице "group" сохраняются пункты для каждого атрибута(их может быть несколько штук.
Соответственно при сохранение объекта мы сохраняем в отдельную таблицу выбранных параметров (рис. выше)

Вопрос/проблема:
Мне нужно, что бы в фильтр фильтровал группу атрибутов как "OR" (это например аргумент 1.2 и аргумент 1.3)
И что бы когда выбираешь например аргумент 1.2 и аргумент 2.2 фильтр фильтровал их как "AND".
Вывожу я объекты. У меня получается выводить их все только с "OR", но мне так не нужно, и в этом проблема.
Как мне сделать так, чтобы при выборе различных атрибутов фильтровалось по типу "AND", а внутри атрибутов фильтровалось типом "OR"? Как в обычных фильтрах.

Описание своих действий:
Сгенерировал код с помощью GRUD.
Код в Search модели:

Код: Выделить всё

 $query = Object::find()
    ->andWhere(['status' => 1])
    ->joinWith('objectCheckboxs')          
...            
...            

 $query->andFilterWhere([         
    ...
    ...
    'object_attribute_checkbox.group_id' => $this->getArr($params['GroupCheckboxes'][$this->type_id]),
]);

 private function getArr($list){
    $arr = [];
    if (!is_null($list)){
        foreach ($list as $item){
            foreach ($item as $oneElem){
                $arr[] = $oneElem;
            }
        }
    }
    return $arr;
}
$this->getArr($params['GroupCheckboxes'][$this->type_id] - это я получаю массив айдишников полей "group_id" (по итогу он выглядит следующим образом [9, 12]
Функция getArr() нужна для перебора полученного массива, в й массив типа [9, 12].
Также пробовал настроить фильтр таким способом, но ни чего не вышло:

Код: Выделить всё

$query->andFilterWhere([
            'and',
                ['object_attribute_radio.group_id' => 9],
                ['object_attribute_radio.group_id' => 12]
            ,
        ]);
andku83
Сообщения: 988
Зарегистрирован: 2016.07.01, 10:24
Откуда: Харьков

Re: Как настроить фильтр по связанным таблицам?

Сообщение andku83 »

Направление решения верное, вы не написали какой результат необходим и какой получается.
По рекомендациям: я бы посоветовал использовать innerJoinWith() вместо joinWith(), не забывать про groupBy() или distinct() (можно получить непонятное поведение на выходе из ActiveDataProvider).
Смотрите результирующий SQL запрос в дебагере и думайте что в нем не так.
Gyry
Сообщения: 87
Зарегистрирован: 2017.09.11, 15:23

Re: Как настроить фильтр по связанным таблицам?

Сообщение Gyry »

andku83 писал(а): 2018.06.02, 03:10 Направление решения верное, вы не написали какой результат необходим и какой получается.
По рекомендациям: я бы посоветовал использовать innerJoinWith() вместо joinWith(), не забывать про groupBy() или distinct() (можно получить непонятное поведение на выходе из ActiveDataProvider).
Смотрите результирующий SQL запрос в дебагере и думайте что в нем не так.
Сейчас объясню на примере что я хочу получить:
Вот мой запрос:
Изображение
если вкратце я хочу вывести все объекты у которые умеют group_id 9 и 12, но в результате мне выдаёт, что нету этих объектов. Хотя по факту (красный овал) они имеются. Вот я и не понимаю что я делаю не так.
Для пояснения: зелёным овалом обведён название четбоксов(в этом примере у меня гадио, но сути не меняет)
синим цветом - это уже сохранённый атрибут(то есть четбокс элемент или выбранный радио элемент)

То есть у меня фильтруется тут всё окей, но у меня фильтруется неправильно, мне нужно фильтровать по типу "AND", чтобы у одного объекта были всё выбранные атрибуты
andku83
Сообщения: 988
Зарегистрирован: 2016.07.01, 10:24
Откуда: Харьков

Re: Как настроить фильтр по связанным таблицам?

Сообщение andku83 »

в случае с выборкой в БД вы не имеете объектов и в каждой строке у вас имеется только одно свойство (вы же только один раз сделали JOIN?), посмотрите тему по ссылке выше, вам нужно сделать столько JOIN по скольки свойствам вы хотите отфильтровать
Gyry
Сообщения: 87
Зарегистрирован: 2017.09.11, 15:23

Re: Как настроить фильтр по связанным таблицам?

Сообщение Gyry »

andku83 писал(а): 2018.06.04, 12:05 в случае с выборкой в БД вы не имеете объектов и в каждой строке у вас имеется только одно свойство (вы же только один раз сделали JOIN?), посмотрите тему по ссылке выше, вам нужно сделать столько JOIN по скольки свойствам вы хотите отфильтровать
Вроде получилось и вроде как работает:

Код: Выделить всё

SELECT o.* FROM object o 
LEFT JOIN object_attribute_radio oar1 ON o.id = oar1.object_id  
LEFT JOIN object_attribute_radio oar2 ON o.id = oar2.object_id  
WHERE (oar1.group_id = 11 OR oar1.group_id = 10) AND (oar2.group_id = 12)  
GROUP BY o.id;
Но есть какой нибудь вариант без этих LEFT JOIN, что бы бд лишний раз не дёргать?
Gyry
Сообщения: 87
Зарегистрирован: 2017.09.11, 15:23

Re: Как настроить фильтр по связанным таблицам?

Сообщение Gyry »

Потому что в последствии их может быть как минимум 10 свойств и и делать однотипный 10 джойнов будет как то не очень
Аватара пользователя
ElisDN
Сообщения: 5845
Зарегистрирован: 2012.10.07, 10:24
Контактная информация:

Re: Как настроить фильтр по связанным таблицам?

Сообщение ElisDN »

В SQL либо десять LEFT JOIN ... WHERE, либо десять WHERE EXISTS (SELECT oar1.id ...) AND EXISTS (...), либо WHERE o.id IN (SELECT oar1.object_id FROM ...) AND o.id IN (...).

А так для скорости и лёгкости поиска уже берут ElasticSearch.
Loveorigami
Сообщения: 977
Зарегистрирован: 2014.08.27, 21:54

Re: Как настроить фильтр по связанным таблицам?

Сообщение Loveorigami »

Gyry писал(а): 2018.06.04, 11:12
andku83 писал(а): 2018.06.02, 03:10 Направление решения верное, вы не написали какой результат необходим и какой получается.
По рекомендациям: я бы посоветовал использовать innerJoinWith() вместо joinWith(), не забывать про groupBy() или distinct() (можно получить непонятное поведение на выходе из ActiveDataProvider).
Смотрите результирующий SQL запрос в дебагере и думайте что в нем не так.
Сейчас объясню на примере что я хочу получить:
Вот мой запрос:
Изображение
Offtop
Подскажите, при помощи чего вы получили разбор запроса, как на картинке. Спасибо
Gyry
Сообщения: 87
Зарегистрирован: 2017.09.11, 15:23

Re: Как настроить фильтр по связанным таблицам?

Сообщение Gyry »

Loveorigami писал(а): 2018.06.04, 20:05
Gyry писал(а): 2018.06.04, 11:12
andku83 писал(а): 2018.06.02, 03:10 Направление решения верное, вы не написали какой результат необходим и какой получается.
По рекомендациям: я бы посоветовал использовать innerJoinWith() вместо joinWith(), не забывать про groupBy() или distinct() (можно получить непонятное поведение на выходе из ActiveDataProvider).
Смотрите результирующий SQL запрос в дебагере и думайте что в нем не так.
Сейчас объясню на примере что я хочу получить:
Вот мой запрос:
Изображение
Offtop
Подскажите, при помощи чего вы получили разбор запроса, как на картинке. Спасибо
xdebug phpstorm. Очень годная вещь, рекомендую. Кучу время экономит при нахождении ошибок.
Gyry
Сообщения: 87
Зарегистрирован: 2017.09.11, 15:23

Re: Как настроить фильтр по связанным таблицам?

Сообщение Gyry »

В общем на данный момент я выбрал путь LEFT JOIN, по другому это невозможно сделать :)
Пример запроса:
SELECT o.* FROM object o
LEFT JOIN object_attribute_radio oar1 ON o.id = oar1.object_id
LEFT JOIN object_attribute_radio oar2 ON o.id = oar2.object_id
WHERE (oar1.group_id = 11 OR oar1.group_id = 10) AND (oar2.group_id = 12)
GROUP BY o.id;
andku83
Сообщения: 988
Зарегистрирован: 2016.07.01, 10:24
Откуда: Харьков

Re: Как настроить фильтр по связанным таблицам?

Сообщение andku83 »

Gyry писал(а): 2018.06.05, 12:42 В общем на данный момент я выбрал путь LEFT JOIN, по другому это невозможно сделать :)
Вариантов на само деле множество, выше Дмитрий вам показал вариант с EXISTS, еще вариант c subQuery без join таблицы атрибутов:

Код: Выделить всё

->andWhere(['id' => ObjectAttributeRadio::find()->select('object_id')->andWhere(['group_id' => $ids1])]
->andWhere(['id' => ObjectAttributeRadio::find()->select('object_id')->andWhere(['group_id' => $ids2])]
...
Ответить