SQL-запрос

Общие вопросы по использованию второй версии фреймворка. Если не знаете как что-то сделать и это про Yii 2, вам сюда.
Ответить
Павелrr
Сообщения: 11
Зарегистрирован: 2019.02.16, 16:08

SQL-запрос

Сообщение Павелrr »

Доброго времени суток! У меня есть небольшой вопрос. Имею две таблицы, filters и products, которые связаны между собой через промежуточную таблицу по id(связь, разумеется, многие ко многим), подскажите пожалуйста, как в yii2 составить запрос, где на вход функции поступает массив из выбранных на данный момент фильтров, а на выходе бы я получал товары, которые соответствуют выбранным фильтрам.

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

public static function findByFilterParam($filterparamname){
        return self::find()->joinWith('filters')->where(['IN','filters.name', $filterparamname])->all();
}
Пока-что составил такую функцию, но она работает не так как надо
unknownby
Сообщения: 749
Зарегистрирован: 2019.11.05, 16:34
Контактная информация:

Re: SQL-запрос

Сообщение unknownby »

Что если так?

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

public static function findByFilterParam($filterparamname){
        return self::find()->where(['IN','filters.filters_id', $filterparamname])->all();
}
А в эту переменную $filterparamname должен поступать массив идентификаторов фильтров
yiiliveext
Сообщения: 910
Зарегистрирован: 2019.08.13, 01:49

Re: SQL-запрос

Сообщение yiiliveext »

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

public static function findByFilterParam($filterparamname){
        $i = 0;
        $query = self::find();
        foreach ($filterparamname as $filtername) {
            $query->innerJoin("filters fl$i", "products.id = fl$i.product_id AND fl$i.name = $filtername");
            $i++;
        }
        
        return $query->all();
}
или так

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

public static function findByFilterParam($filterparamname){
        $i = 0;
        $query = self::find();
        foreach ($filterparamname as $filtername) {   
            $query->innerJoinWith(["filters fl$i" => function ($q) {
                $q->onCondition(["fl$i.name" => $filtername]);
            }])
            $i++;
        }
        
        return $query->all();
}
Павелrr
Сообщения: 11
Зарегистрирован: 2019.02.16, 16:08

Re: SQL-запрос

Сообщение Павелrr »

unknownby писал(а): 2019.11.21, 08:50 Что если так?

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

public static function findByFilterParam($filterparamname){
        return self::find()->where(['IN','filters.filters_id', $filterparamname])->all();
}
А в эту переменную $filterparamname должен поступать массив идентификаторов фильтров
без join пишет Unknown column 'filters.filters_id' in 'where clause'

С ним возникает та же проблема, что и когда передаёшь name
Павелrr
Сообщения: 11
Зарегистрирован: 2019.02.16, 16:08

Re: SQL-запрос

Сообщение Павелrr »

yiiliveext писал(а): 2019.11.21, 10:07

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

public static function findByFilterParam($filterparamname){
        $i = 0;
        $query = self::find();
        foreach ($filterparamname as $filtername) {
            $query->innerJoin("filters fl$i", "products.id = fl$i.product_id AND fl$i.name = $filtername");
            $i++;
        }
        
        return $query->all();
}
или так

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

public static function findByFilterParam($filterparamname){
        $i = 0;
        $query = self::find();
        foreach ($filterparamname as $filtername) {   
            $query->innerJoinWith(["filters fl$i" => function ($q) {
                $q->onCondition(["fl$i.name" => $filtername]);
            }])
            $i++;
        }
        
        return $query->all();
}
в первом случае не работает, потому что у меня в таблице filters не содержится product_id ("products.id = fl$i.product_id ), как я и писал они связаны через промежуточную таблицу где есть product_id и filters_id.
Во втором случае, пишет про undefined variable $i и $filtername в fuction($q)
Павелrr
Сообщения: 11
Зарегистрирован: 2019.02.16, 16:08

Re: SQL-запрос

Сообщение Павелrr »

Еще раз напишу, на всякий случай, есть таблица filters в ней поля id и name.
Есть таблица products, допустим в ней тоже поля id и name.
Они связаны связью многие ко многим через таблицу filtersproducts, в которой есть поля product_id и filter_id.
Помогите мне пожалуйста разобраться, как по переданному массиву фильтров( не так важно что это за массив, с id или с name фильтров), выбрать только те товары, которые связаны со всем перечисленным списком фильтров.
Для примера.
Когда пользователь нажимает на фильтр с названием royal canin, мы выбираем те товары, у которых есть связь с этим фильтром.
Когда пользователь нажимает еще и на фильтр щенки, мы должны выбрать те товары, которые связаны и с фильтром, который называется royal canin, И с фильтром, который называется щенки(в коде который я приложил выше выполняется условие ИЛИ, а это не то что требуется)
Помогите пожалуйста разобраться, как это сделать!
yiiliveext
Сообщения: 910
Зарегистрирован: 2019.08.13, 01:49

Re: SQL-запрос

Сообщение yiiliveext »

Павелrr писал(а): 2019.11.22, 22:12 Во втором случае, пишет про undefined variable $i и $filtername в fuction($q)

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

public static function findByFilterParam($filterparamname){
        $i = 0;
        $query = self::find();
        foreach ($filterparamname as $filtername) {   
            $query->innerJoinWith(["filters fl$i" => function ($q) use ($i, $filtername) {
                $q->onCondition(["fl$i.name" => $filtername]);
            }])
            $i++;
        }
        
        return $query->all();
Павелrr
Сообщения: 11
Зарегистрирован: 2019.02.16, 16:08

Re: SQL-запрос

Сообщение Павелrr »

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

public static function findByFilterParam($filterparamname){
        $i = 0;
        $query = self::find();
        foreach ($filterparamname as $filtername) {   
            $query->innerJoinWith(["filters fl$i" => function ($q) use ($i, $filtername) {
                $q->onCondition(["fl$i.name" => $filtername]);
            }])
            $i++;
        }
        
        return $query->all();
[/quote]

Не работает к сожалению, как только выбираю второй фильтр, итоговая выборка становится пустой(
skynin
Сообщения: 400
Зарегистрирован: 2017.12.12, 10:09

Re: SQL-запрос

Сообщение skynin »

1. составьте правильный запрос руками, на SQL
2. добейтесь чтобы queryBuilder выдавал такой же SQL код
Не желайте странного, и не будет у вас головной боли чтобы достичь этого странного.
Тем более что окажется что оно вам и не нужно было, странное это.
Павелrr
Сообщения: 11
Зарегистрирован: 2019.02.16, 16:08

Re: SQL-запрос

Сообщение Павелrr »

skynin писал(а): 2019.11.23, 07:58 1. составьте правильный запрос руками, на SQL
2. добейтесь чтобы queryBuilder выдавал такой же SQL код

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

select * from products p
left join filtersproducts fp1 on fp1.product_id = p.id
left join filters fp2 on fp2.id = fp1.filter_id
where fp2.name IN ('марка1', 'марка2') 
group by p.id having count(*) > 1
Cоставил, не подскажете как его перевести на activequery? Я знаю как перевести всё, кроме последней строки с count(*)
Павелrr
Сообщения: 11
Зарегистрирован: 2019.02.16, 16:08

Re: SQL-запрос

Сообщение Павелrr »

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

public static function findByFilterParam($filterparamname){
	return self::find()
            ->select(['p.*', 'cnt' => 'COUNT(p.id)'])
            ->from(['p' =>'products'])
            ->joinWith('filters')
            ->where(['IN','filters.name', $filterparamname])
            ->groupBy('p.id')
            ->having(['>', 'cnt', count($filterparamname)-1])
            ->all();
}
Фух, сделал, оставлю тут, вдруг кому-нибудь пригодится. Всем огромное спасибо за помощь!
yiiliveext
Сообщения: 910
Зарегистрирован: 2019.08.13, 01:49

Re: SQL-запрос

Сообщение yiiliveext »

В этом способе точное условие такое

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

->having(['=', 'cnt', count($filterparamname)])
Ну и чтобы в точности повторить запрос, который вы написали на SQL

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

self::Products::find()
            ->select(['p.*'])
            ->from(['p' =>'products'])
            ->joinWith('filters')
            ->where(['IN','filters.name', $filterparamname])
            ->groupBy('p.id')
            ->having(['=', new \yii\db\Expression('COUNT(p.id)'), count($filterparamname)]);

Вариант с innerJoin в вашем случае будет выглядеть так

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

public static function findByFilterParam($filterparamname){
        $i = 0;
        $query = self::find();
        foreach ($filterparamname as $filtername) {
            $query->innerJoin("filtersproducts fp$i", "products.id = fp$i.product_id")
            ->innerJoin("filters fl$i", "fp$i.filter_id = fl$i.id AND fl$i.name = '$filtername'");
            $i++;
        }
        
        return $query->all();
}
Но это я использую в сложных мультиязычных мультифильтрах, где с having были какие-то проблемы, которых сейчас уже не упомню.
В вашем случае способ с having проще и быстрее.
Павелrr
Сообщения: 11
Зарегистрирован: 2019.02.16, 16:08

Re: SQL-запрос

Сообщение Павелrr »

Спасибо! ;)
Ответить