Выбрать товары с несколькими параметрами одновременно (фильтр EAV)

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Ответить
Аватара пользователя
CyanoFresh
Сообщения: 68
Зарегистрирован: 2015.02.05, 23:50
Контактная информация:

Выбрать товары с несколькими параметрами одновременно (фильтр EAV)

Сообщение CyanoFresh »

Здравствуйте. Нужно выбрать товары, где, например, параметр "Цвет" = "Красный" или "Зеленый" (WHERE ... IN (...)) и "Размер" = "24" или "45" (тоже IN).
Сейчас получилось сделать только выборку по отдельным параметрам, то есть если указать еще один параметр, то все товары с ним добавляются в выборку, независимо от других параметров (плюсуются). А нужно товары, которые имеют эти параметры одновременно.
структура:

Изображение

кусок кода из searchModel: (костыль, но работает)

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

public function search($data, $query)
    {
        $query->select(['product.id', 'product.price'])
            ->groupBy('id');

        $this->load($data);

        if ($this->parameters) {
            $query->joinWith(['parameterValues'], false);
        }

        $dataProvider = new ActiveDataProvider([
            'query' => $query,
        ]);
        
        ...
        
        if ($this->parameters) {
            $queryString = '';

            $i = 0;
            $count = count($this->parameters);

            foreach ($this->parameters as $parameterId => $parameterValueIDs) {
                $parameterId = (int)$parameterId;

                if ($parameterId < 1) {
                    return $dataProvider;
                }

                if (is_array($parameterValueIDs)) {
                    $resultParameterValueIDs = 'IN (';

                    $countOfValues = count($parameterValueIDs);
                    $lastValueIndex = $countOfValues - 1;

                    for ($k = 0; $k < $countOfValues; $k++) {
                        $resultParameterValueIDs .= (int)$parameterValueIDs[$k];

                        if ($parameterValueIDs[$k] < 1) {
                            return $dataProvider;
                        }

                        if ($k < $lastValueIndex) {
                            $resultParameterValueIDs .= ',';
                        }
                    }

                    $resultParameterValueIDs .= ')';
                } else {
                    $parameterValueIDs = (int)$parameterValueIDs;

                    $resultParameterValueIDs = '= ' . $parameterValueIDs;
                }

                $queryString .= '((`parameter_value`.`id` ' . $resultParameterValueIDs . ') AND (`parameter_value`.`parameter_id` = ' . $parameterId . '))';

                if ($i < $count - 1) {
                    $queryString .= ' OR ';
                }

                $i++;
            }

            $query->andWhere($queryString);
        }
в frontend все работает, массив параметров передает.
Вот такие запросы получаются при фильтрации:

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

SELECT `product`.`id`, `product`.`price` 
FROM `product`
 LEFT JOIN `product_parameter_value` ON `product`.`id` = `product_parameter_value`.`product_id` 
 LEFT JOIN `parameter_value` ON `product_parameter_value`.`parameter_value_id` = `parameter_value`.`id` 
 WHERE (`product`.`status`=10) 
 AND (((`parameter_value`.`id` IN (132,130)) AND (`parameter_value`.`parameter_id` = 14)) OR ((`parameter_value`.`id` = 129) AND (`parameter_value`.`parameter_id` = 15))) 
 AND (`product`.`category_id`=9) 
 GROUP BY `id` 
 LIMIT 20
у параметра с ID 14 выбрано 2 значения с ID 132 и 130 - это работает нормально.
А вот когда у параметра 15 выбраны значения - выбирается отдельные товары с этим параметром и им все равно на параметр 14.

Как выбрать товары у которых значения параметра 14 совпадают с значениями 132 или 130, а так же значения параметра 15 с значением 129?
Фух, не знаю как еще обьяснить. Надеюсь понятно, если нет - напишите, пожалуйста

UPD: как правильно (некостыльно) выбрать (EAV) товары с несколькими параметрами средствами yii2?
Последний раз редактировалось CyanoFresh 2017.08.02, 22:18, всего редактировалось 1 раз.
Nerf
Сообщения: 780
Зарегистрирован: 2015.01.29, 00:37

Re: Выбрать товары с определенными параметрами одновременно (фильтр)

Сообщение Nerf »

У вас EAV...
Чтобы фильтровать по n атрибутам, вам нужно n раз для каждого атрибута сделать join (лучше inner), чтобы привести к "линейному" виду.
https://www.slideshare.net/billkarwin/s ... onstruct_a
По аналогии с этим, только все усложниться тем, что вам нужна информация из product_parameter_value.
Аватара пользователя
CyanoFresh
Сообщения: 68
Зарегистрирован: 2015.02.05, 23:50
Контактная информация:

Re: Выбрать товары с определенными параметрами одновременно (фильтр)

Сообщение CyanoFresh »

Nerf писал(а): 2017.08.02, 21:37 У вас EAV...
Чтобы фильтровать по n атрибутам, вам нужно n раз для каждого атрибута сделать join (лучше inner), чтобы привести к "линейному" виду.
https://www.slideshare.net/billkarwin/s ... onstruct_a
По аналогии с этим, только все усложниться тем, что вам нужна информация из product_parameter_value.
точно! EAV. забыл что это так называется. Спасибо за пример. Подскажите, пожалуйста, как это сделать некостыльно, желательно методами yii2?
Nerf
Сообщения: 780
Зарегистрирован: 2015.01.29, 00:37

Re: Выбрать товары с несколькими параметрами одновременно (фильтр EAV)

Сообщение Nerf »

Будет сложно и долго. Рассмотрите вариант с использованием поискового движка.
Код писать лень, но если все же силами текущей БД, то сделайте метод который добавляет в ActiveQuery нужные части запроса.

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

$alias = 'f' . ++$this->aliasCounter;
$query->innerJoinWith(['parameterValues ' . $alias => function (ActiveQuery $query) use ($code, $alias, $values) {
    return $query->andOnCondition([
        $alias . '.parameter_id' => $code,
        $alias . '.value' => $values,
    ]);
}], false);
Но код выдернут из контекста, вам не подходит, но что-то аналогичное можно сделать.
Аватара пользователя
CyanoFresh
Сообщения: 68
Зарегистрирован: 2015.02.05, 23:50
Контактная информация:

Re: Выбрать товары с несколькими параметрами одновременно (фильтр EAV)

Сообщение CyanoFresh »

Nerf писал(а): 2017.08.02, 22:34 Будет сложно и долго. Рассмотрите вариант с использованием поискового движка.
Код писать лень, но если все же силами текущей БД, то сделайте метод который добавляет в ActiveQuery нужные части запроса.

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

$alias = 'f' . ++$this->aliasCounter;
$query->innerJoinWith(['parameterValues ' . $alias => function (ActiveQuery $query) use ($code, $alias, $values) {
    return $query->andOnCondition([
        $alias . '.parameter_id' => $code,
        $alias . '.value' => $values,
    ]);
}], false);
Но код выдернут из контекста, вам не подходит, но что-то аналогичное можно сделать.
спасибо.
Как я понимаю, мне для каждого параметра нужно делать 2 inner joina. Получилось это сделать в чистых sql запросах, спасибо за наводку.
Насчет yii кода: $query->innerJoinWith позволяет сделать алиас только для второго inner join запроса (INNER JOIN `parameter_value` ...), а первый (INNER JOIN `product_parameter_value` ON `product`.`id` = `product_parameter_value`.`product_id`) остается без алиаса... Можно ли в yii2 указать алиас для первого join?
Или нужно вызывать 2 inner join запроса вручную?
Аватара пользователя
CyanoFresh
Сообщения: 68
Зарегистрирован: 2015.02.05, 23:50
Контактная информация:

Re: Выбрать товары с несколькими параметрами одновременно (фильтр EAV)

Сообщение CyanoFresh »

Тема закрыта. Сделал на двух innerJoin(). Спасибо @Nerf
Nerf
Сообщения: 780
Зарегистрирован: 2015.01.29, 00:37

Re: Выбрать товары с несколькими параметрами одновременно (фильтр EAV)

Сообщение Nerf »

innerJoinWith() хорошо работает с алиасами. Например при innerJoinWith(['relation1 r1', 'relation1.relation2 r2']) будет все ок. Причем, используя 3 параметр простого joinWith(), можно конкретно указывать какой именно join(left, inner) использовать.
Ответить