J
J
Justique2019-02-20 13:14:25
MySQL
Justique, 2019-02-20 13:14:25

Yii2 filter select only having a set of options?

Good afternoon, when implementing an object filter, I encountered such a problem.
I took yii2-filter
as a basis. The essence of the problem: There is a filter / option type checkbox

spoiler
5c6d268e34bc9734918172.png

It works on the principle of an object filter if at least one of the options is present in it.
It all looks like this (where item_id = id of the filtered object)
ActiveRecord
$filtered = FilterValue::find()->select('item_id')->groupBy('item_id')->andHaving("COUNT(DISTINCT `filter_id`) = $variantCount")->andFilterWhere($condition);

RawSql
SELECT `item_id` FROM `filter_value` WHERE (`filter_id`=2) AND (`variant_id` IN ('8', '12')) GROUP BY `item_id` HAVING COUNT(DISTINCT `filter_id`) = 1


It turns out that if an object has at least one option from this filter, then it will be included in the selection result.
It is necessary that the results of the item_id selection be given only to those who have all the selected options, in this case 8, 12.
From what comes to mind, this is only such an option, but I don’t like it.
RawSql
SELECT *, GROUP_CONCAT(variant_id) AS variant_ids FROM filter_value GROUP BY item_id HAVING LOCATE('8', variant_ids) AND LOCATE('12', variant_ids)

filtered function called via the model's behavior
public function filtered($filterIds = false, $mode = 0)
    {
        if(!$filterIds) {
            $filterIds = Yii::$app->request->get($this->fieldName);
        }

        if(empty($filterIds)) {
            return $this->owner;
        }

        $condition = ['OR'];
        $variantCount = 0;
        $filterCount = count($filterIds);

        foreach($filterIds as $filterId => $value) {
            $filter = Filter::findOne($filterId);
            if($filter->type == 'range' && is_string($value)) {
                $value = explode(';', $value);
                if($value[0] != $value[1]) {
                    $variants = FilterVariant::find()->where('filter_id = :filterId AND (numeric_value >= :min AND numeric_value <= :max)', [':filterId' => $filterId, ':min' => $value[0], ':max' => $value[1]])->select('id')->all();
                } else {
                    $variants = FilterVariant::find()->where('filter_id = :filterId AND numeric_value = :value', [':filterId' => $filterId, ':value' => $value[0]])->select('id')->all();
                }
                $variantIds = ArrayHelper::map($variants, 'id', 'id');
            } else {
                $variantIds = $value;
            }
            
            $condition[] = ['filter_id' => $filterId, 'variant_id' => $variantIds];

            if($mode == 1) {
                $variantCount += count($variantIds);
            } else {
                $variantCount++;
            }
            
        }
        $filtered = FilterValue::find()->select('item_id')->groupBy('item_id')->andHaving("COUNT(DISTINCT `filter_id`) = $variantCount")->andFilterWhere($condition);
       
        $modelClass = $this->owner->modelClass;
        $tableName = $modelClass::tableName();

        if ($filtered->count() > 0) {
            $this->owner->andWhere([$tableName . '.id' => $filtered]);
        } else {
            $this->owner->andWhere([$tableName . '.id' => 0]);
        }

        return $this->owner;
    }

Answer the question

In order to leave comments, you need to log in

Didn't find what you were looking for?

Ask your question

Ask a Question

731 491 924 answers to any question