Completed
Push — aria-required-and-aria-invalid... ( 0b9feb...e1b0cb )
by Alexander
12:52 queued 09:30
created

QueryTrait::emulateExecution()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 5
ccs 3
cts 3
cp 1
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 3
nc 1
nop 1
crap 1
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\db;
9
10
use yii\base\NotSupportedException;
11
12
/**
13
 * The BaseQuery trait represents the minimum method set of a database Query.
14
 *
15
 * It is supposed to be used in a class that implements the [[QueryInterface]].
16
 *
17
 * @author Qiang Xue <[email protected]>
18
 * @author Carsten Brandt <[email protected]>
19
 * @since 2.0
20
 */
21
trait QueryTrait
22
{
23
    /**
24
     * @var string|array query condition. This refers to the WHERE clause in a SQL statement.
25
     * For example, `['age' => 31, 'team' => 1]`.
26
     * @see where() for valid syntax on specifying this value.
27
     */
28
    public $where;
29
    /**
30
     * @var int maximum number of records to be returned. If not set or less than 0, it means no limit.
31
     */
32
    public $limit;
33
    /**
34
     * @var int zero-based offset from where the records are to be returned. If not set or
35
     * less than 0, it means starting from the beginning.
36
     */
37
    public $offset;
38
    /**
39
     * @var array how to sort the query results. This is used to construct the ORDER BY clause in a SQL statement.
40
     * The array keys are the columns to be sorted by, and the array values are the corresponding sort directions which
41
     * can be either [SORT_ASC](http://php.net/manual/en/array.constants.php#constant.sort-asc)
42
     * or [SORT_DESC](http://php.net/manual/en/array.constants.php#constant.sort-desc).
43
     * The array may also contain [[Expression]] objects. If that is the case, the expressions
44
     * will be converted into strings without any change.
45
     */
46
    public $orderBy;
47
    /**
48
     * @var string|callable $column the name of the column by which the query results should be indexed by.
49
     * This can also be a callable (e.g. anonymous function) that returns the index value based on the given
50
     * row data. For more details, see [[indexBy()]]. This property is only used by [[QueryInterface::all()|all()]].
51
     */
52
    public $indexBy;
53
    /**
54
     * @var boolean whether to emulate the actual query execution, returning empty or false results.
55
     * @see emulateExecution()
56
     * @since 2.0.11
57
     */
58
    public $emulateExecution = false;
59
60
61
    /**
62
     * Sets the [[indexBy]] property.
63
     * @param string|callable $column the name of the column by which the query results should be indexed by.
64
     * This can also be a callable (e.g. anonymous function) that returns the index value based on the given
65
     * row data. The signature of the callable should be:
66
     *
67
     * ```php
68
     * function ($row)
69
     * {
70
     *     // return the index value corresponding to $row
71
     * }
72
     * ```
73
     *
74
     * @return $this the query object itself
75
     */
76 36
    public function indexBy($column)
77
    {
78 36
        $this->indexBy = $column;
79 36
        return $this;
80
    }
81
82
    /**
83
     * Sets the WHERE part of the query.
84
     *
85
     * See [[QueryInterface::where()]] for detailed documentation.
86
     *
87
     * @param string|array $condition the conditions that should be put in the WHERE part.
88
     * @return $this the query object itself
89
     * @see andWhere()
90
     * @see orWhere()
91
     */
92
    public function where($condition)
93
    {
94
        $this->where = $condition;
95
        return $this;
96
    }
97
98
    /**
99
     * Adds an additional WHERE condition to the existing one.
100
     * The new condition and the existing one will be joined using the 'AND' operator.
101
     * @param string|array $condition the new WHERE condition. Please refer to [[where()]]
102
     * on how to specify this parameter.
103
     * @return $this the query object itself
104
     * @see where()
105
     * @see orWhere()
106
     */
107
    public function andWhere($condition)
108
    {
109
        if ($this->where === null) {
110
            $this->where = $condition;
111
        } else {
112
            $this->where = ['and', $this->where, $condition];
113
        }
114
        return $this;
115
    }
116
117
    /**
118
     * Adds an additional WHERE condition to the existing one.
119
     * The new condition and the existing one will be joined using the 'OR' operator.
120
     * @param string|array $condition the new WHERE condition. Please refer to [[where()]]
121
     * on how to specify this parameter.
122
     * @return $this the query object itself
123
     * @see where()
124
     * @see andWhere()
125
     */
126
    public function orWhere($condition)
127
    {
128
        if ($this->where === null) {
129
            $this->where = $condition;
130
        } else {
131
            $this->where = ['or', $this->where, $condition];
132
        }
133
        return $this;
134
    }
135
136
    /**
137
     * Sets the WHERE part of the query but ignores [[isEmpty()|empty operands]].
138
     *
139
     * This method is similar to [[where()]]. The main difference is that this method will
140
     * remove [[isEmpty()|empty query operands]]. As a result, this method is best suited
141
     * for building query conditions based on filter values entered by users.
142
     *
143
     * The following code shows the difference between this method and [[where()]]:
144
     *
145
     * ```php
146
     * // WHERE `age`=:age
147
     * $query->filterWhere(['name' => null, 'age' => 20]);
148
     * // WHERE `age`=:age
149
     * $query->where(['age' => 20]);
150
     * // WHERE `name` IS NULL AND `age`=:age
151
     * $query->where(['name' => null, 'age' => 20]);
152
     * ```
153
     *
154
     * Note that unlike [[where()]], you cannot pass binding parameters to this method.
155
     *
156
     * @param array $condition the conditions that should be put in the WHERE part.
157
     * See [[where()]] on how to specify this parameter.
158
     * @return $this the query object itself
159
     * @see where()
160
     * @see andFilterWhere()
161
     * @see orFilterWhere()
162
     */
163 72
    public function filterWhere(array $condition)
164
    {
165 72
        $condition = $this->filterCondition($condition);
166 72
        if ($condition !== []) {
167 21
            $this->where($condition);
168 21
        }
169 72
        return $this;
170
    }
171
172
    /**
173
     * Adds an additional WHERE condition to the existing one but ignores [[isEmpty()|empty operands]].
174
     * The new condition and the existing one will be joined using the 'AND' operator.
175
     *
176
     * This method is similar to [[andWhere()]]. The main difference is that this method will
177
     * remove [[isEmpty()|empty query operands]]. As a result, this method is best suited
178
     * for building query conditions based on filter values entered by users.
179
     *
180
     * @param array $condition the new WHERE condition. Please refer to [[where()]]
181
     * on how to specify this parameter.
182
     * @return $this the query object itself
183
     * @see filterWhere()
184
     * @see orFilterWhere()
185
     */
186 6
    public function andFilterWhere(array $condition)
187
    {
188 6
        $condition = $this->filterCondition($condition);
189 6
        if ($condition !== []) {
190 3
            $this->andWhere($condition);
191 3
        }
192 6
        return $this;
193
    }
194
195
    /**
196
     * Adds an additional WHERE condition to the existing one but ignores [[isEmpty()|empty operands]].
197
     * The new condition and the existing one will be joined using the 'OR' operator.
198
     *
199
     * This method is similar to [[orWhere()]]. The main difference is that this method will
200
     * remove [[isEmpty()|empty query operands]]. As a result, this method is best suited
201
     * for building query conditions based on filter values entered by users.
202
     *
203
     * @param array $condition the new WHERE condition. Please refer to [[where()]]
204
     * on how to specify this parameter.
205
     * @return $this the query object itself
206
     * @see filterWhere()
207
     * @see andFilterWhere()
208
     */
209 3
    public function orFilterWhere(array $condition)
210
    {
211 3
        $condition = $this->filterCondition($condition);
212 3
        if ($condition !== []) {
213
            $this->orWhere($condition);
214
        }
215 3
        return $this;
216
    }
217
218
    /**
219
     * Removes [[isEmpty()|empty operands]] from the given query condition.
220
     *
221
     * @param array $condition the original condition
222
     * @return array the condition with [[isEmpty()|empty operands]] removed.
223
     * @throws NotSupportedException if the condition operator is not supported
224
     */
225 75
    protected function filterCondition($condition)
226
    {
227 75
        if (!is_array($condition)) {
228 21
            return $condition;
229
        }
230
231 75
        if (!isset($condition[0])) {
232
            // hash format: 'column1' => 'value1', 'column2' => 'value2', ...
233 6
            foreach ($condition as $name => $value) {
234 6
                if ($this->isEmpty($value)) {
235 3
                    unset($condition[$name]);
236 3
                }
237 6
            }
238 6
            return $condition;
239
        }
240
241
        // operator format: operator, operand 1, operand 2, ...
242
243 75
        $operator = array_shift($condition);
244
245 75
        switch (strtoupper($operator)) {
246 75
            case 'NOT':
247 75
            case 'AND':
248 75
            case 'OR':
249 27
                foreach ($condition as $i => $operand) {
250 27
                    $subCondition = $this->filterCondition($operand);
251 27
                    if ($this->isEmpty($subCondition)) {
252 27
                        unset($condition[$i]);
253 27
                    } else {
254 18
                        $condition[$i] = $subCondition;
255
                    }
256 27
                }
257
258 27
                if (empty($condition)) {
259 12
                    return [];
260
                }
261 18
                break;
262 54
            case 'BETWEEN':
263 54
            case 'NOT BETWEEN':
264 9
                if (array_key_exists(1, $condition) && array_key_exists(2, $condition)) {
265 9
                    if ($this->isEmpty($condition[1]) || $this->isEmpty($condition[2])) {
266 9
                        return [];
267
                    }
268
                }
269
                break;
270 48
            default:
271 48
                if (array_key_exists(1, $condition) && $this->isEmpty($condition[1])) {
272 48
                    return [];
273
                }
274 63
        }
275
276 24
        array_unshift($condition, $operator);
277
278 24
        return $condition;
279
    }
280
281
    /**
282
     * Returns a value indicating whether the give value is "empty".
283
     *
284
     * The value is considered "empty", if one of the following conditions is satisfied:
285
     *
286
     * - it is `null`,
287
     * - an empty string (`''`),
288
     * - a string containing only whitespace characters,
289
     * - or an empty array.
290
     *
291
     * @param mixed $value
292
     * @return bool if the value is empty
293
     */
294 75
    protected function isEmpty($value)
295
    {
296 75
        return $value === '' || $value === [] || $value === null || is_string($value) && trim($value) === '';
297
    }
298
299
    /**
300
     * Sets the ORDER BY part of the query.
301
     * @param string|array|Expression $columns the columns (and the directions) to be ordered by.
302
     * Columns can be specified in either a string (e.g. `"id ASC, name DESC"`) or an array
303
     * (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`).
304
     *
305
     * The method will automatically quote the column names unless a column contains some parenthesis
306
     * (which means the column contains a DB expression).
307
     *
308
     * Note that if your order-by is an expression containing commas, you should always use an array
309
     * to represent the order-by information. Otherwise, the method will not be able to correctly determine
310
     * the order-by columns.
311
     *
312
     * Since version 2.0.7, an [[Expression]] object can be passed to specify the ORDER BY part explicitly in plain SQL.
313
     * @return $this the query object itself
314
     * @see addOrderBy()
315
     */
316 142
    public function orderBy($columns)
317
    {
318 142
        $this->orderBy = $this->normalizeOrderBy($columns);
319 142
        return $this;
320
    }
321
322
    /**
323
     * Adds additional ORDER BY columns to the query.
324
     * @param string|array|Expression $columns the columns (and the directions) to be ordered by.
325
     * Columns can be specified in either a string (e.g. "id ASC, name DESC") or an array
326
     * (e.g. `['id' => SORT_ASC, 'name' => SORT_DESC]`).
327
     *
328
     * The method will automatically quote the column names unless a column contains some parenthesis
329
     * (which means the column contains a DB expression).
330
     *
331
     * Note that if your order-by is an expression containing commas, you should always use an array
332
     * to represent the order-by information. Otherwise, the method will not be able to correctly determine
333
     * the order-by columns.
334
     *
335
     * Since version 2.0.7, an [[Expression]] object can be passed to specify the ORDER BY part explicitly in plain SQL.
336
     * @return $this the query object itself
337
     * @see orderBy()
338
     */
339 36
    public function addOrderBy($columns)
340
    {
341 36
        $columns = $this->normalizeOrderBy($columns);
342 36
        if ($this->orderBy === null) {
343 15
            $this->orderBy = $columns;
344 15
        } else {
345 33
            $this->orderBy = array_merge($this->orderBy, $columns);
346
        }
347 36
        return $this;
348
    }
349
350
    /**
351
     * Normalizes format of ORDER BY data
352
     *
353
     * @param array|string|Expression $columns the columns value to normalize. See [[orderBy]] and [[addOrderBy]].
354
     * @return array
355
     */
356 142
    protected function normalizeOrderBy($columns)
357
    {
358 142
        if ($columns instanceof Expression) {
359 6
            return [$columns];
360 142
        } elseif (is_array($columns)) {
361 70
            return $columns;
362
        } else {
363 114
            $columns = preg_split('/\s*,\s*/', trim($columns), -1, PREG_SPLIT_NO_EMPTY);
364 114
            $result = [];
365 114
            foreach ($columns as $column) {
366 114
                if (preg_match('/^(.*?)\s+(asc|desc)$/i', $column, $matches)) {
367 15
                    $result[$matches[1]] = strcasecmp($matches[2], 'desc') ? SORT_ASC : SORT_DESC;
368 15
                } else {
369 111
                    $result[$column] = SORT_ASC;
370
                }
371 114
            }
372 114
            return $result;
373
        }
374
    }
375
376
    /**
377
     * Sets the LIMIT part of the query.
378
     * @param int $limit the limit. Use null or negative value to disable limit.
379
     * @return $this the query object itself
380
     */
381 56
    public function limit($limit)
382
    {
383 56
        $this->limit = $limit;
384 56
        return $this;
385
    }
386
387
    /**
388
     * Sets the OFFSET part of the query.
389
     * @param int $offset the offset. Use null or negative value to disable offset.
390
     * @return $this the query object itself
391
     */
392 33
    public function offset($offset)
393
    {
394 33
        $this->offset = $offset;
395 33
        return $this;
396
    }
397
398
    /**
399
     * Sets whether to emulate query execution, preventing any interaction with data storage.
400
     * After this mode is enabled, methods, returning query results like [[one()]], [[all()]], [[exists()]]
401
     * and so on, will return empty or false values.
402
     * You should use this method in case your program logic indicates query should not return any results, like
403
     * in case you set false where condition like `0=1`.
404
     * @param boolean $value whether to prevent query execution.
405
     * @return $this the query object itself.
406
     * @since 2.0.11
407
     */
408 21
    public function emulateExecution($value = true)
409
    {
410 21
        $this->emulateExecution = $value;
411 21
        return $this;
412
    }
413
}
414