Completed
Push — master ( b7a96a...376006 )
by Alexander
40:44 queued 34:50
created

ActiveDataFilter::buildAttributeCondition()   C

Complexity

Conditions 8
Paths 4

Size

Total Lines 30
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 17
CRAP Score 8

Importance

Changes 0
Metric Value
dl 0
loc 30
ccs 17
cts 17
cp 1
rs 5.3846
c 0
b 0
f 0
cc 8
eloc 19
nc 4
nop 2
crap 8
1
<?php
2
/**
3
 * @link http://www.yiiframework.com/
4
 * @copyright Copyright (c) 2008 Yii Software LLC
5
 * @license http://www.yiiframework.com/license/
6
 */
7
8
namespace yii\data;
9
10
/**
11
 * ActiveDataFilter allows composing a filtering condition in a format suitable for [[\yii\db\QueryInterface::where()]].
12
 *
13
 * @see DataFilter
14
 *
15
 * @author Paul Klimov <[email protected]>
16
 * @since 2.0.13
17
 */
18
class ActiveDataFilter extends DataFilter
19
{
20
    /**
21
     * @var array maps filtering condition keywords to build methods.
22
     * These methods are used by [[buildCondition()]] to build the actual filtering conditions.
23
     * Particular condition builder can be specified using a PHP callback. For example:
24
     *
25
     * ```php
26
     * [
27
     *     'XOR' => function (string $operator, mixed $condition) {
28
     *         //return array;
29
     *     },
30
     *     'LIKE' => function (string $operator, mixed $condition, string $attribute) {
31
     *         //return array;
32
     *     },
33
     * ]
34
     * ```
35
     */
36
    public $conditionBuilders = [
37
        'AND' => 'buildConjunctionCondition',
38
        'OR' => 'buildConjunctionCondition',
39
        'NOT' => 'buildBlockCondition',
40
        '<' => 'buildOperatorCondition',
41
        '>' => 'buildOperatorCondition',
42
        '<=' => 'buildOperatorCondition',
43
        '>=' => 'buildOperatorCondition',
44
        '=' => 'buildOperatorCondition',
45
        '!=' => 'buildOperatorCondition',
46
        'IN' => 'buildOperatorCondition',
47
        'NOT IN' => 'buildOperatorCondition',
48
        'LIKE' => 'buildOperatorCondition',
49
    ];
50
    /**
51
     * @var array map filtering operators to operators used in [[\yii\db\QueryInterface::where()]].
52
     * The format is: `[filterOperator => queryOperator]`.
53
     * If particular operator keyword does not appear in the map, it will be used as is.
54
     *
55
     * Usually the map can be left empty as filter operator names are consistent with the ones
56
     * used in [[\yii\db\QueryInterface::where()]]. However, you may want to adjust it in some special cases.
57
     * For example, when using PosgreSQL you may want to setup the following map:
58
     *
59
     * ```php
60
     * [
61
     *     'LIKE' => 'ILIKE'
62
     * ]
63
     * ```
64
     */
65
    public $queryOperatorMap = [];
66
67
68
    /**
69
     * @inheritdoc
70
     */
71 8
    protected function buildInternal()
72
    {
73 8
        $filter = $this->normalize(false);
74 8
        if (empty($filter)) {
75 1
            return [];
76
        }
77
78 7
        return $this->buildCondition($filter);
79
    }
80
81
    /**
82
     * @param array $condition
83
     * @return array built condition.
84
     */
85 7
    protected function buildCondition($condition)
86
    {
87 7
        $parts = [];
88 7
        foreach ($condition as $key => $value) {
89 7
            if (isset($this->conditionBuilders[$key])) {
90 4
                $method = $this->conditionBuilders[$key];
91 4
                if (is_string($method)) {
92 3
                    $callback = [$this, $method];
93
                } else {
94 4
                    $callback = $method;
95
                }
96
            } else {
97 7
                $callback = [$this, 'buildAttributeCondition'];
98
            }
99 7
            $parts[] = $callback($key, $value);
100
        }
101
102 7
        if (!empty($parts)) {
103 7
            if (count($parts) > 1) {
104 1
                $parts = array_merge(['AND'], $parts);
105
            } else {
106 6
                $parts = array_shift($parts);
107
            }
108
        }
109
110 7
        return $parts;
111
    }
112
113
    /**
114
     * Builds conjunction condition, which consists of multiple independent ones.
115
     * It covers such operators as `and` and `or`.
116
     * @param string $operator operator keyword.
117
     * @param mixed $condition raw condition.
118
     * @return array actual condition.
119
     */
120 2
    protected function buildConjunctionCondition($operator, $condition)
121
    {
122 2
        if (isset($this->queryOperatorMap[$operator])) {
123
            $operator = $this->queryOperatorMap[$operator];
124
        }
125 2
        $result = [$operator];
126
127 2
        foreach ($condition as $part) {
128 2
            $result[] = $this->buildCondition($part);
129
        }
130
131 2
        return $result;
132
    }
133
134
    /**
135
     * Builds block condition, which consists of a single condition.
136
     * It covers such operators as `not`.
137
     * @param string $operator operator keyword.
138
     * @param mixed $condition raw condition.
139
     * @return array actual condition.
140
     */
141 1
    protected function buildBlockCondition($operator, $condition)
142
    {
143 1
        if (isset($this->queryOperatorMap[$operator])) {
144
            $operator = $this->queryOperatorMap[$operator];
145
        }
146
        return [
147 1
            $operator,
148 1
            $this->buildCondition($condition)
149
        ];
150
    }
151
152
    /**
153
     * Builds search condition for a particular attribute.
154
     * @param string $attribute search attribute name.
155
     * @param mixed $condition search condition.
156
     * @return array actual condition.
157
     */
158 7
    protected function buildAttributeCondition($attribute, $condition)
159
    {
160 7
        if (is_array($condition)) {
161 3
            $parts = [];
162 3
            foreach ($condition as $operator => $value) {
163 3
                if (isset($this->operatorTypes[$operator])) {
164 3
                    if (isset($this->conditionBuilders[$operator])) {
165 3
                        $method = $this->conditionBuilders[$operator];
166 3
                        if (is_string($method)) {
167 2
                            $callback = [$this, $method];
168
                        } else {
169 1
                            $callback = $method;
170
                        }
171 3
                        $parts[] = $callback($operator, $value, $attribute);
172
                    } else {
173 3
                        $parts[] = $this->buildOperatorCondition($operator, $value, $attribute);
174
                    }
175
                }
176
            }
177
178 3
            if (!empty($parts)) {
179 3
                if (count($parts) > 1) {
180 1
                    return array_merge(['AND'], $parts);
181
                }
182 2
                return array_shift($parts);
183
            }
184
        }
185
186 5
        return [$attribute => $this->filterAttributeValue($attribute, $condition)];
187
    }
188
189
    /**
190
     * Builds an operator condition.
191
     * @param string $operator operator keyword.
192
     * @param mixed $condition attribute condition.
193
     * @param string $attribute attribute name.
194
     * @return array actual condition.
195
     */
196 2
    protected function buildOperatorCondition($operator, $condition, $attribute)
197
    {
198 2
        if (isset($this->queryOperatorMap[$operator])) {
199
            $operator = $this->queryOperatorMap[$operator];
200
        }
201 2
        return [$operator, $attribute, $this->filterAttributeValue($attribute, $condition)];
202
    }
203
}
204