1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* |
4
|
|
|
* This file is part of the moss-storage package |
5
|
|
|
* |
6
|
|
|
* (c) Michal Wachowski <[email protected]> |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the LICENSE |
9
|
|
|
* file that was distributed with this source code. |
10
|
|
|
*/ |
11
|
|
|
|
12
|
|
|
namespace Moss\Storage\Query; |
13
|
|
|
|
14
|
|
|
use Doctrine\DBAL\Connection; |
15
|
|
|
use Doctrine\DBAL\Driver\Statement; |
16
|
|
|
use Doctrine\DBAL\Types\Type; |
17
|
|
|
use Moss\Storage\GetTypeTrait; |
18
|
|
|
use Moss\Storage\Model\Definition\FieldInterface; |
19
|
|
|
use Moss\Storage\Model\ModelInterface; |
20
|
|
|
use Moss\Storage\Query\Accessor\AccessorInterface; |
21
|
|
|
use Moss\Storage\Query\EventDispatcher\EventDispatcherInterface; |
22
|
|
|
use Moss\Storage\Query\Relation\RelationFactoryInterface; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* Query used to read data from table |
26
|
|
|
* |
27
|
|
|
* @author Michal Wachowski <[email protected]> |
28
|
|
|
* @package Moss\Storage |
29
|
|
|
*/ |
30
|
|
|
class ReadQuery extends AbstractQuery implements ReadQueryInterface |
31
|
|
|
{ |
32
|
|
|
const EVENT_BEFORE = 'read.before'; |
33
|
|
|
const EVENT_AFTER = 'read.after'; |
34
|
|
|
|
35
|
|
|
use GetTypeTrait; |
36
|
|
|
|
37
|
|
|
protected $queryString; |
38
|
|
|
protected $queryParams = []; |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* @var array |
42
|
|
|
*/ |
43
|
|
|
protected $casts = []; |
44
|
|
|
|
45
|
|
|
/** |
46
|
|
|
* Constructor |
47
|
|
|
* |
48
|
|
|
* @param Connection $connection |
49
|
|
|
* @param ModelInterface $model |
50
|
|
|
* @param RelationFactoryInterface $factory |
51
|
|
|
* @param AccessorInterface $accessor |
52
|
|
|
* @param EventDispatcherInterface $dispatcher |
53
|
|
|
*/ |
54
|
|
View Code Duplication |
public function __construct(Connection $connection, ModelInterface $model, RelationFactoryInterface $factory, AccessorInterface $accessor, EventDispatcherInterface $dispatcher) |
|
|
|
|
55
|
|
|
{ |
56
|
|
|
parent::__construct($connection, $model, $factory, $accessor, $dispatcher); |
57
|
|
|
|
58
|
|
|
$this->setQuery(); |
59
|
|
|
$this->fields(); |
60
|
|
|
} |
61
|
|
|
|
62
|
|
|
/** |
63
|
|
|
* Sets query instance with delete operation and table |
64
|
|
|
*/ |
65
|
|
|
protected function setQuery() |
66
|
|
|
{ |
67
|
|
|
$this->builder = $this->connection->createQueryBuilder(); |
68
|
|
|
$this->builder->select([]); |
69
|
|
|
$this->builder->from($this->connection->quoteIdentifier($this->model->table())); |
70
|
|
|
} |
71
|
|
|
|
72
|
|
|
/** |
73
|
|
|
* Sets field names which will be read |
74
|
|
|
* |
75
|
|
|
* @param array $fields |
76
|
|
|
* |
77
|
|
|
* @return $this |
78
|
|
|
*/ |
79
|
|
View Code Duplication |
public function fields($fields = []) |
|
|
|
|
80
|
|
|
{ |
81
|
|
|
$this->builder->select([]); |
82
|
|
|
$this->casts = []; |
83
|
|
|
|
84
|
|
|
if (empty($fields)) { |
85
|
|
|
foreach ($this->model->fields() as $field) { |
86
|
|
|
$this->assignField($field); |
87
|
|
|
} |
88
|
|
|
|
89
|
|
|
return $this; |
90
|
|
|
} |
91
|
|
|
|
92
|
|
|
foreach ($fields as $field) { |
93
|
|
|
$this->assignField($this->model->field($field)); |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
return $this; |
97
|
|
|
} |
98
|
|
|
|
99
|
|
|
/** |
100
|
|
|
* Adds field to query |
101
|
|
|
* |
102
|
|
|
* @param string $field |
103
|
|
|
* |
104
|
|
|
* @return $this |
105
|
|
|
*/ |
106
|
|
|
public function field($field) |
107
|
|
|
{ |
108
|
|
|
$this->assignField($this->model->field($field)); |
109
|
|
|
|
110
|
|
|
return $this; |
111
|
|
|
} |
112
|
|
|
|
113
|
|
|
/** |
114
|
|
|
* Assigns field to query |
115
|
|
|
* |
116
|
|
|
* @param FieldInterface $field |
117
|
|
|
*/ |
118
|
|
|
protected function assignField(FieldInterface $field) |
119
|
|
|
{ |
120
|
|
|
if ($field->mapping() !== null) { |
121
|
|
|
$this->builder->addSelect( |
122
|
|
|
sprintf( |
123
|
|
|
'%s AS %s', |
124
|
|
|
$this->connection->quoteIdentifier($field->mapping()), |
125
|
|
|
$this->connection->quoteIdentifier($field->name()) |
126
|
|
|
) |
127
|
|
|
); |
128
|
|
|
} else { |
129
|
|
|
$this->builder->addSelect($this->connection->quoteIdentifier($field->name())); |
130
|
|
|
} |
131
|
|
|
|
132
|
|
|
$this->casts[$field->name()] = $field->type(); |
133
|
|
|
} |
134
|
|
|
|
135
|
|
|
/** |
136
|
|
|
* Adds where condition to query |
137
|
|
|
* |
138
|
|
|
* @param string|array $field |
139
|
|
|
* @param mixed $value |
140
|
|
|
* @param string $comparison |
141
|
|
|
* @param string $logical |
142
|
|
|
* |
143
|
|
|
* @return $this |
144
|
|
|
* @throws QueryException |
145
|
|
|
*/ |
146
|
|
|
public function where($field, $value, $comparison = '=', $logical = 'and') |
147
|
|
|
{ |
148
|
|
|
$condition = $this->condition($field, $value, $comparison, $logical); |
149
|
|
|
|
150
|
|
|
if ($this->normalizeLogical($logical) === 'or') { |
151
|
|
|
$this->builder()->orWhere($condition); |
152
|
|
|
|
153
|
|
|
return $this; |
154
|
|
|
} |
155
|
|
|
|
156
|
|
|
$this->builder()->andWhere($condition); |
157
|
|
|
|
158
|
|
|
return $this; |
159
|
|
|
} |
160
|
|
|
|
161
|
|
|
/** |
162
|
|
|
* Adds where condition to query |
163
|
|
|
* |
164
|
|
|
* @param string|array $field |
165
|
|
|
* @param mixed $value |
166
|
|
|
* @param string $comparison |
167
|
|
|
* @param string $logical |
168
|
|
|
* |
169
|
|
|
* @return string |
170
|
|
|
* @throws QueryException |
171
|
|
|
*/ |
172
|
|
|
public function condition($field, $value, $comparison, $logical) |
173
|
|
|
{ |
174
|
|
|
$comparison = $this->normalizeComparison($comparison); |
175
|
|
|
$logical = $this->normalizeLogical($logical); |
176
|
|
|
|
177
|
|
|
if (!is_array($field)) { |
178
|
|
|
return $this->buildSingularFieldCondition($field, $value, $comparison); |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
return $this->buildMultipleFieldsCondition($field, $value, $comparison, $logical); |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
/** |
185
|
|
|
* Builds condition for singular field |
186
|
|
|
* |
187
|
|
|
* @param string $field |
188
|
|
|
* @param mixed $value |
189
|
|
|
* @param string $comparison |
190
|
|
|
* |
191
|
|
|
* @return array |
192
|
|
|
*/ |
193
|
|
|
protected function buildSingularFieldCondition($field, $value, $comparison) |
194
|
|
|
{ |
195
|
|
|
$field = $this->model()->field($field); |
196
|
|
|
|
197
|
|
|
return $this->buildConditionString( |
198
|
|
|
$this->connection()->quoteIdentifier($field->mappedName()), |
199
|
|
|
$value === null ? null : $this->bindValues($field->mappedName(), $field->type(), $value), |
200
|
|
|
$comparison |
201
|
|
|
); |
202
|
|
|
} |
203
|
|
|
|
204
|
|
|
/** |
205
|
|
|
* Builds conditions for multiple fields |
206
|
|
|
* |
207
|
|
|
* @param array $fields |
208
|
|
|
* @param mixed $value |
209
|
|
|
* @param string $comparison |
210
|
|
|
* @param string $logical |
211
|
|
|
* |
212
|
|
|
* @return array |
213
|
|
|
*/ |
214
|
|
|
protected function buildMultipleFieldsCondition($fields, $value, $comparison, $logical) |
215
|
|
|
{ |
216
|
|
|
$conditions = []; |
217
|
|
|
foreach ((array) $fields as $field) { |
218
|
|
|
$field = $this->model()->field($field); |
219
|
|
|
|
220
|
|
|
$fieldName = $field->mappedName(); |
221
|
|
|
$conditions[] = $this->buildConditionString( |
222
|
|
|
$this->connection()->quoteIdentifier($fieldName), |
223
|
|
|
$value === null ? null : $this->bindValues($fieldName, $field->type(), $value), |
224
|
|
|
$comparison |
225
|
|
|
); |
226
|
|
|
|
227
|
|
|
$conditions[] = $logical; |
228
|
|
|
} |
229
|
|
|
|
230
|
|
|
array_pop($conditions); |
231
|
|
|
|
232
|
|
|
return '(' . implode(' ', $conditions) . ')'; |
233
|
|
|
} |
234
|
|
|
|
235
|
|
|
/** |
236
|
|
|
* Builds condition string |
237
|
|
|
* |
238
|
|
|
* @param string $field |
239
|
|
|
* @param string|array $bind |
240
|
|
|
* @param string $operator |
241
|
|
|
* |
242
|
|
|
* @return string |
243
|
|
|
*/ |
244
|
|
|
protected function buildConditionString($field, $bind, $operator) |
245
|
|
|
{ |
246
|
|
|
if (is_array($bind)) { |
247
|
|
|
foreach ($bind as &$val) { |
248
|
|
|
$val = $this->buildConditionString($field, $val, $operator); |
249
|
|
|
unset($val); |
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
$logical = $operator === '!=' ? ' and ' : ' or '; |
253
|
|
|
|
254
|
|
|
return '(' . implode($logical, $bind) . ')'; |
255
|
|
|
} |
256
|
|
|
|
257
|
|
|
if ($bind === null) { |
258
|
|
|
return $field . ' ' . ($operator == '!=' ? 'IS NOT NULL' : 'IS NULL'); |
259
|
|
|
} |
260
|
|
|
|
261
|
|
|
if ($operator === 'regexp') { |
262
|
|
|
return sprintf('%s regexp %s', $field, $bind); |
263
|
|
|
} |
264
|
|
|
|
265
|
|
|
return $field . ' ' . $operator . ' ' . $bind; |
266
|
|
|
} |
267
|
|
|
|
268
|
|
|
/** |
269
|
|
|
* Asserts correct comparison operator |
270
|
|
|
* |
271
|
|
|
* @param string $operator |
272
|
|
|
* |
273
|
|
|
* @return string |
274
|
|
|
* @throws QueryException |
275
|
|
|
*/ |
276
|
|
|
protected function normalizeComparison($operator) |
277
|
|
|
{ |
278
|
|
|
switch (strtolower($operator)) { |
279
|
|
|
case '<': |
280
|
|
|
case 'lt': |
281
|
|
|
return '<'; |
282
|
|
|
case '<=': |
283
|
|
|
case 'lte': |
284
|
|
|
return '<='; |
285
|
|
|
case '>': |
286
|
|
|
case 'gt': |
287
|
|
|
return '>'; |
288
|
|
|
case '>=': |
289
|
|
|
case 'gte': |
290
|
|
|
return '>='; |
291
|
|
|
case '~': |
292
|
|
|
case '~=': |
293
|
|
|
case '=~': |
294
|
|
|
case 'regex': |
295
|
|
|
case 'regexp': |
296
|
|
|
return "regexp"; |
297
|
|
|
// LIKE |
298
|
|
|
case 'like': |
299
|
|
|
return "like"; |
300
|
|
|
case '||': |
301
|
|
|
case 'fulltext': |
302
|
|
|
case 'fulltext_boolean': |
303
|
|
|
return 'fulltext'; |
304
|
|
|
case '<>': |
305
|
|
|
case '!=': |
306
|
|
|
case 'ne': |
307
|
|
|
case 'not': |
308
|
|
|
return '!='; |
309
|
|
|
case '=': |
310
|
|
|
case 'eq': |
311
|
|
|
return '='; |
312
|
|
|
default: |
313
|
|
|
throw new QueryException(sprintf('Query does not supports comparison operator "%s" in query "%s"', $operator, $this->model()->entity())); |
314
|
|
|
} |
315
|
|
|
} |
316
|
|
|
|
317
|
|
|
/** |
318
|
|
|
* Asserts correct logical operation |
319
|
|
|
* |
320
|
|
|
* @param string $operator |
321
|
|
|
* |
322
|
|
|
* @return string |
323
|
|
|
* @throws QueryException |
324
|
|
|
*/ |
325
|
|
|
protected function normalizeLogical($operator) |
326
|
|
|
{ |
327
|
|
|
switch (strtolower($operator)) { |
328
|
|
|
case '&&': |
329
|
|
|
case 'and': |
330
|
|
|
return 'and'; |
331
|
|
|
case '||': |
332
|
|
|
case 'or': |
333
|
|
|
return 'or'; |
334
|
|
|
default: |
335
|
|
|
throw new QueryException(sprintf('Query does not supports logical operator "%s" in query "%s"', $operator, $this->model()->entity())); |
336
|
|
|
} |
337
|
|
|
} |
338
|
|
|
|
339
|
|
|
/** |
340
|
|
|
* Binds condition value to key |
341
|
|
|
* |
342
|
|
|
* @param string $name |
343
|
|
|
* @param string $type |
344
|
|
|
* @param mixed $values |
345
|
|
|
* |
346
|
|
|
* @return string |
347
|
|
|
*/ |
348
|
|
|
protected function bindValues($name, $type, $values) |
349
|
|
|
{ |
350
|
|
|
if (!is_array($values)) { |
351
|
|
|
return $this->builder->createNamedParameter($values, $type); |
352
|
|
|
} |
353
|
|
|
|
354
|
|
|
foreach ($values as $key => $value) { |
355
|
|
|
$values[$key] = $this->bindValues($name, $type, $value); |
356
|
|
|
} |
357
|
|
|
|
358
|
|
|
return $values; |
359
|
|
|
} |
360
|
|
|
|
361
|
|
|
/** |
362
|
|
|
* Adds sorting to query |
363
|
|
|
* |
364
|
|
|
* @param string $field |
365
|
|
|
* @param string $order |
366
|
|
|
* |
367
|
|
|
* @return $this |
368
|
|
|
*/ |
369
|
|
|
public function order($field, $order = 'desc') |
370
|
|
|
{ |
371
|
|
|
$field = $this->model->field($field); |
372
|
|
|
$order = $this->normalizeOrder($order); |
373
|
|
|
|
374
|
|
|
$this->builder->addOrderBy($this->connection->quoteIdentifier($field->mappedName()), $order); |
375
|
|
|
|
376
|
|
|
return $this; |
377
|
|
|
} |
378
|
|
|
|
379
|
|
|
/** |
380
|
|
|
* Asserts correct order |
381
|
|
|
* |
382
|
|
|
* @param string $order |
383
|
|
|
* |
384
|
|
|
* @return string |
385
|
|
|
* @throws QueryException |
386
|
|
|
*/ |
387
|
|
|
protected function normalizeOrder($order) |
388
|
|
|
{ |
389
|
|
|
switch (strtolower($order)) { |
390
|
|
|
case 'asc': |
391
|
|
|
return 'asc'; |
392
|
|
|
case 'desc': |
393
|
|
|
return 'desc'; |
394
|
|
|
default: |
395
|
|
|
throw new QueryException(sprintf('Unsupported sorting method "%s" in query "%s"', $this->getType($order), $this->model->entity())); |
396
|
|
|
|
397
|
|
|
} |
398
|
|
|
} |
399
|
|
|
|
400
|
|
|
/** |
401
|
|
|
* Sets limits to query |
402
|
|
|
* |
403
|
|
|
* @param int $limit |
404
|
|
|
* @param null|int $offset |
405
|
|
|
* |
406
|
|
|
* @return $this |
407
|
|
|
*/ |
408
|
|
|
public function limit($limit, $offset = null) |
409
|
|
|
{ |
410
|
|
|
if ($offset !== null) { |
411
|
|
|
$this->builder->setFirstResult((int) $offset); |
412
|
|
|
} |
413
|
|
|
|
414
|
|
|
$this->builder->setMaxResults((int) $limit); |
415
|
|
|
|
416
|
|
|
return $this; |
417
|
|
|
} |
418
|
|
|
|
419
|
|
|
/** |
420
|
|
|
* Returns number of entities that will be read |
421
|
|
|
* |
422
|
|
|
* @return int |
423
|
|
|
*/ |
424
|
|
|
public function count() |
425
|
|
|
{ |
426
|
|
|
if (empty($this->queryString)) { |
427
|
|
|
$builder = clone $this->builder; |
428
|
|
|
$builder->resetQueryPart('orderBy'); |
429
|
|
|
$stmt = $builder->execute(); |
430
|
|
|
} else { |
431
|
|
|
$stmt = $this->connection->executeQuery($this->queryString, $this->queryParams); |
432
|
|
|
} |
433
|
|
|
|
434
|
|
|
return (int) $stmt->rowCount(); |
435
|
|
|
} |
436
|
|
|
|
437
|
|
|
/** |
438
|
|
|
* Sets custom query to be executed instead of one based on entity structure |
439
|
|
|
* |
440
|
|
|
* @param string $query |
441
|
|
|
* @param array $params |
442
|
|
|
* |
443
|
|
|
* @return $this |
444
|
|
|
*/ |
445
|
|
|
public function query($query, array $params = []) |
446
|
|
|
{ |
447
|
|
|
$this->queryString = $query; |
448
|
|
|
$this->queryParams = $params; |
449
|
|
|
|
450
|
|
|
return $this; |
451
|
|
|
} |
452
|
|
|
|
453
|
|
|
/** |
454
|
|
|
* Executes query |
455
|
|
|
* After execution query is reset |
456
|
|
|
* |
457
|
|
|
* @return mixed |
458
|
|
|
*/ |
459
|
|
|
public function execute() |
460
|
|
|
{ |
461
|
|
|
$this->dispatcher->fire(self::EVENT_BEFORE); |
462
|
|
|
|
463
|
|
|
$stmt = $this->executeQuery(); |
464
|
|
|
$result = $this->model->entity() ? $this->fetchAsObject($stmt) : $this->fetchAsAssoc($stmt); |
465
|
|
|
|
466
|
|
|
$this->dispatcher->fire(self::EVENT_AFTER, $result); |
467
|
|
|
|
468
|
|
|
foreach ($this->relations as $relation) { |
469
|
|
|
$result = $relation->read($result); |
470
|
|
|
} |
471
|
|
|
|
472
|
|
|
|
473
|
|
|
return $result; |
474
|
|
|
} |
475
|
|
|
|
476
|
|
|
/** |
477
|
|
|
* Executes query - from builder or custom |
478
|
|
|
* |
479
|
|
|
* @return Statement |
480
|
|
|
* @throws \Doctrine\DBAL\DBALException |
481
|
|
|
*/ |
482
|
|
|
protected function executeQuery() |
483
|
|
|
{ |
484
|
|
|
if (empty($this->queryString)) { |
485
|
|
|
return $this->builder->execute(); |
|
|
|
|
486
|
|
|
} |
487
|
|
|
|
488
|
|
|
return $this->connection->executeQuery($this->queryString, $this->queryParams); |
489
|
|
|
} |
490
|
|
|
|
491
|
|
|
/** |
492
|
|
|
* Fetches result as associative array, mostly for pivot tables |
493
|
|
|
* |
494
|
|
|
* @param Statement $stmt |
495
|
|
|
* |
496
|
|
|
* @return array |
497
|
|
|
*/ |
498
|
|
|
protected function fetchAsAssoc(Statement $stmt) |
499
|
|
|
{ |
500
|
|
|
$stmt->setFetchMode(\PDO::FETCH_ASSOC); |
501
|
|
|
|
502
|
|
|
return $stmt->fetchAll(\PDO::FETCH_ASSOC); |
503
|
|
|
} |
504
|
|
|
|
505
|
|
|
/** |
506
|
|
|
* Fetches result as entity object |
507
|
|
|
* |
508
|
|
|
* @param Statement $stmt |
509
|
|
|
* |
510
|
|
|
* @return array |
511
|
|
|
*/ |
512
|
|
|
protected function fetchAsObject(Statement $stmt) |
513
|
|
|
{ |
514
|
|
|
$stmt->setFetchMode(\PDO::FETCH_CLASS, $this->model->entity()); |
515
|
|
|
$result = $stmt->fetchAll(); |
516
|
|
|
|
517
|
|
|
$ref = new \ReflectionClass($this->model->entity()); |
518
|
|
|
foreach ($result as $entity) { |
519
|
|
|
$this->restoreObject($entity, $this->casts, $ref); |
520
|
|
|
} |
521
|
|
|
|
522
|
|
|
return $result; |
523
|
|
|
} |
524
|
|
|
|
525
|
|
|
/** |
526
|
|
|
* Restores entity values from their stored representation |
527
|
|
|
* |
528
|
|
|
* @param object $entity |
529
|
|
|
* @param array $restore |
530
|
|
|
* @param \ReflectionClass $ref |
531
|
|
|
* |
532
|
|
|
* @return mixed |
533
|
|
|
*/ |
534
|
|
|
protected function restoreObject($entity, array $restore, \ReflectionClass $ref) |
535
|
|
|
{ |
536
|
|
|
foreach ($restore as $field => $type) { |
537
|
|
|
if (is_array($entity)) { |
538
|
|
|
if (!isset($entity[$field])) { |
539
|
|
|
continue; |
540
|
|
|
} |
541
|
|
|
|
542
|
|
|
$entity[$field] = $this->convertToPHPValue($entity[$field], $type); |
543
|
|
|
continue; |
544
|
|
|
} |
545
|
|
|
|
546
|
|
|
if (!$ref->hasProperty($field)) { |
547
|
|
|
if (!isset($entity->$field)) { |
548
|
|
|
continue; |
549
|
|
|
} |
550
|
|
|
|
551
|
|
|
$entity->$field = $this->convertToPHPValue($entity->$field, $type); |
552
|
|
|
continue; |
553
|
|
|
} |
554
|
|
|
|
555
|
|
|
$prop = $ref->getProperty($field); |
556
|
|
|
$prop->setAccessible(true); |
557
|
|
|
|
558
|
|
|
$value = $prop->getValue($entity); |
559
|
|
|
$value = $this->convertToPHPValue($value, $type); |
560
|
|
|
$prop->setValue($entity, $value); |
561
|
|
|
} |
562
|
|
|
|
563
|
|
|
return $entity; |
564
|
|
|
} |
565
|
|
|
|
566
|
|
|
/** |
567
|
|
|
* Converts read value to its php representation |
568
|
|
|
* |
569
|
|
|
* @param mixed $value |
570
|
|
|
* @param string $type |
571
|
|
|
* |
572
|
|
|
* @return mixed |
573
|
|
|
* @throws \Doctrine\DBAL\DBALException |
574
|
|
|
*/ |
575
|
|
|
protected function convertToPHPValue($value, $type) |
576
|
|
|
{ |
577
|
|
|
return Type::getType($type)->convertToPHPValue($value, $this->connection->getDatabasePlatform()); |
578
|
|
|
} |
579
|
|
|
|
580
|
|
|
/** |
581
|
|
|
* Resets adapter |
582
|
|
|
* |
583
|
|
|
* @return $this |
584
|
|
|
*/ |
585
|
|
|
public function reset() |
586
|
|
|
{ |
587
|
|
|
$this->builder->resetQueryParts(); |
588
|
|
|
$this->relations = []; |
589
|
|
|
$this->queryString = null; |
590
|
|
|
$this->queryParams = []; |
591
|
|
|
$this->casts = []; |
592
|
|
|
$this->resetBinds(); |
593
|
|
|
|
594
|
|
|
$this->setQuery(); |
595
|
|
|
$this->fields(); |
596
|
|
|
|
597
|
|
|
return $this; |
598
|
|
|
} |
599
|
|
|
} |
600
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.