Yii2 DataFilter создает неправильное условие - PullRequest
0 голосов
/ 24 января 2020

Я должен сделать фильтрацию по дате в запросе. Я знаю о существующем yii\data\DataFilter классе, поэтому я использовал его для решения проблемы.

Фактический URL-адрес запроса (пример): https://api.site.com/module/post?filter[from_date][>=]=100 Не беспокойтесь о значении 100 , мы используем UNIX в нашей политике.

Я использую yii\rest\ActiveController для выполнения действий, поэтому я определил dataFilter свойство в [[actions()]]:

public function actions()
{
    $actions = parent::actions();
    $actions['index']['dataFilter'] = [
        'class' => DataFilter::class,
        'attributeMap' => [
            'from_date' => 'date_success',
            'to_date' => 'date_success',
        ],
        'searchModel' => function () {
            return (new DynamicModel(['from_date', 'to_date']))
                ->addRule(['from_date', 'to_date'], 'integer', ['min' => 0]);
        },
    ];
    return $actions;
}

После завершения запроса, возвращается пустой массив []. Я проследил SQL запросов: enter image description here

Как вы можете видеть, вместо ">" есть операнд "EQUALS", который определен в строке запроса ?filter[from_date][>]=100.

По умолчанию в yii\rest\IndexAction переменная $query вызывает метод where($filter):

/*
After successful filter build $filter variable becomes:
    $filter = [
        'date_success' => [
            '>' => 100,
        ],
    ]
*/

$query = $modelClass::find();
if (!empty($filter)) {
    $query->andWhere($filter);
}

Почему это происходит? Я отладил код и нашел одну интересную особенность! В yii\db\conditions\HashConditionBuilder:

public function build(ExpressionInterface $expression, array &$params = [])
{
    $hash = $expression->getHash();
    $parts = [];
    foreach ($hash as $column => $value) {
        if (ArrayHelper::isTraversable($value) || $value instanceof Query) {
            // IN condition
            // Executing will be here.
            // Yii2 thinks thats 'IN' condition, and builds as 'IN'.
            $parts[] = $this->queryBuilder->buildCondition(new InCondition($column, 'IN', $value), $params);
        } else {
            if (strpos($column, '(') === false) {
                $column = $this->queryBuilder->db->quoteColumnName($column);
            }
            if ($value === null) {
                $parts[] = "$column IS NULL";
            } elseif ($value instanceof ExpressionInterface) {
                $parts[] = "$column=" . $this->queryBuilder->buildExpression($value, $params);
            } else {
                $phName = $this->queryBuilder->bindParam($value, $params);
                $parts[] = "$column=$phName";
            }
        }
    }
    return count($parts) === 1 ? $parts[0] : '(' . implode(') AND (', $parts) . ')';
}

Эта проблема повторяется с различными «conditionOperators», результат абсолютно одинаковый.

filter[from_date][=]=100
filter[from_date][<]=100
filter[from_date][gt]=100
filter[from_date][gte]=100

1 Ответ

0 голосов
/ 24 января 2020

Было одно существенное различие между ActiveDataFilter и DataFilter. Он заключает в методе [[buildInternal()]].

DataFilter метод:

protected function buildInternal()
{
    return $this->normalize(false);
}

ActiveDataFilter метод:

protected function buildInternal()
{
    $filter = $this->normalize(false);
    if (empty($filter)) {
        return [];
    }
    return $this->buildCondition($filter);
}

Вызывая $this->buildCondition($filter) ActiveDataFilter передает переменную $filter и затем применяет QueryBilders, поэтому переменная $filter становится:

$filter = ['>', 'date_success', 100];
...