GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Pull Request — 2.4 (#133)
by Alessandro
06:28 queued 03:16
created

QueryBuilderFactory   F

Complexity

Total Complexity 82

Size/Duplication

Total Lines 537
Duplicated Lines 0 %

Test Coverage

Coverage 96.8%

Importance

Changes 0
Metric Value
wmc 82
dl 0
loc 537
ccs 242
cts 250
cp 0.968
rs 1.5789
c 0
b 0
f 0

32 Methods

Rating   Name   Duplication   Size   Complexity  
A setPageLength() 0 5 1
A getPageLength() 0 3 1
A setPage() 0 5 1
A getPage() 0 3 1
A getPrinting() 0 3 1
A getValueAvailableFilters() 0 3 1
B join() 0 54 7
A getFields() 0 9 2
A setRelationEntityAlias() 0 3 1
A getSelect() 0 3 1
C sort() 0 39 8
A getAvailableFilters() 0 3 1
A storeJoin() 0 4 1
A getRel() 0 3 1
A setAndFilters() 0 5 1
A getAndFilters() 0 3 1
A noExistsJoin() 0 9 2
A getRelationEntityAlias() 0 3 1
C filter() 0 43 11
A setOrFilters() 0 5 1
A addRel() 0 3 1
A getEntityManager() 0 3 1
F applyFilterOr() 0 101 14
A setPrinting() 0 5 1
A getOrFilters() 0 3 1
C applyFilterAnd() 0 76 11
A ensureQueryBuilderIsDefined() 0 7 2
A setSorting() 0 5 1
A setRel() 0 5 1
A setSelect() 0 5 1
A getQueryBuilder() 0 7 2
A setFields() 0 5 1

How to fix   Complexity   

Complex Class

Complex classes like QueryBuilderFactory often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use QueryBuilderFactory, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace Mado\QueryBundle\Queries;
4
5
use Doctrine\ORM\EntityManager;
6
use Doctrine\ORM\QueryBuilder;
7
use Mado\QueryBundle\Component\Meta\Exceptions\UnInitializedQueryBuilderException;
8
use Mado\QueryBundle\Dictionary;
9
use Mado\QueryBundle\Exceptions;
10
use Mado\QueryBundle\Queries\Objects\FilterObject;
11
12
class QueryBuilderFactory extends AbstractQuery
13
{
14
    const DIRECTION_AZ = 'asc';
15
16
    const DIRECTION_ZA = 'desc';
17
18
    const DEFAULT_OPERATOR = 'eq';
19
20
    private const AND_OPERATOR_LOGIC = 'AND';
21
22
    private const OR_OPERATOR_LOGIC = 'OR';
23
24
    protected $qBuilder;
25
26
    protected $fields;
27
28
    protected $andFilters;
29
30
    protected $orFilters;
31
32
    private $relationEntityAlias;
33
34
    protected $sorting;
35
36
    private $joins;
37
38
    protected $rel;
39
40
    protected $printing;
41
42
    protected $page;
43
44
    protected $pageLength;
45
46
    protected $select;
47
48 1
    public function getAvailableFilters()
49
    {
50 1
        return array_keys($this->getValueAvailableFilters());
51
    }
52
53 2
    public function getValueAvailableFilters()
54
    {
55 2
        return Dictionary::getOperators();
56
    }
57
58 21
    public function setFields(array $fields = [])
59
    {
60 21
        $this->fields = $fields;
61
62 21
        return $this;
63
    }
64
65 2
    public function getFields()
66
    {
67 2
        if (null === $this->fields) {
68 1
            throw new \RuntimeException(
69 1
                'Oops! Fields are not defined'
70
            );
71
        }
72
73 1
        return $this->fields;
74
    }
75
76
    /** @since version 2.2 */
77 10
    public function setAndFilters(array $andFilters = [])
78
    {
79 10
        $this->andFilters = $andFilters;
80
81 10
        return $this;
82
    }
83
84 9
    public function setOrFilters(array $orFilters = [])
85
    {
86 9
        $this->orFilters = $orFilters;
87
88 9
        return $this;
89
    }
90
91 4
    public function setSorting(array $sorting = [])
92
    {
93 4
        $this->sorting = $sorting;
94
95 4
        return $this;
96
    }
97
98 1
    public function getAndFilters()
99
    {
100 1
        return $this->andFilters;
101
    }
102
103 1
    public function getOrFilters()
104
    {
105 1
        return $this->orFilters;
106
    }
107
108 7
    private function noExistsJoin($prevEntityAlias, $currentEntityAlias)
109
    {
110 7
        if (null === $this->joins) {
111 7
            $this->joins = [];
112
        }
113
114 7
        $needle = $prevEntityAlias . '_' . $currentEntityAlias;
115
116 7
        return !in_array($needle, $this->joins);
117
    }
118
119 7
    private function storeJoin($prevEntityAlias, $currentEntityAlias)
120
    {
121 7
        $needle = $prevEntityAlias . '_' . $currentEntityAlias;
122 7
        $this->joins[$needle] = $needle;
123 7
    }
124
125
    /**
126
     * @param String $relation Nome della relazione semplice (groups.name) o con embedded (_embedded.groups.name)
127
     * @return $this
128
     */
129 7
    public function join(String $relation, $logicOperator = self::AND_OPERATOR_LOGIC)
130
    {
131 7
        $relation = explode('|', $relation)[0];
132 7
        $relations = [$relation];
133
134
135 7
        if (strstr($relation, '_embedded.')) {
136 7
            $embeddedFields = explode('.', $relation);
137 7
            $this->parser->camelize($embeddedFields[1]);
138
139
            // elimino l'ultimo elemento che dovrebbe essere il nome del campo
140 7
            unset($embeddedFields[count($embeddedFields) - 1]);
141
142
            // elimino il primo elemento _embedded
143 7
            unset($embeddedFields[0]);
144
145 7
            $relations = $embeddedFields;
146
        }
147
148 7
        $entityName = $this->getEntityName();
149 7
        $entityAlias = $this->entityAlias;
150
151 7
        foreach ($relations as $relation) {
152 7
            $relation = $this->parser->camelize($relation);
153 7
            $relationEntityAlias = 'table_' . $relation;
154
155 7
            $metadata = $this->manager->getClassMetadata($entityName);
156
157 7
            if ($metadata->hasAssociation($relation)) {
158
159 7
                $association = $metadata->getAssociationMapping($relation);
160
161 7
                $fieldName = $this->parser->camelize($association['fieldName']);
162
163 7
                if ($this->noExistsJoin($relationEntityAlias, $relation)) {
164 7
                    if ($logicOperator === self::AND_OPERATOR_LOGIC) {
165 4
                        $this->qBuilder->innerJoin($entityAlias . "." . $fieldName, $relationEntityAlias);
166 3
                    } elseif ($logicOperator === self::OR_OPERATOR_LOGIC) {
167 3
                        $this->qBuilder->leftJoin($entityAlias . "." . $fieldName, $relationEntityAlias);
168
                    } else {
169
                        throw new Exceptions('Missing Logic operator');
170
                    }
171
172 7
                    $this->storeJoin($relationEntityAlias, $relation);
173
                }
174
175 7
                $entityName = $association['targetEntity'];
176 7
                $entityAlias = $relationEntityAlias;
177
            }
178
179 7
            $this->setRelationEntityAlias($relationEntityAlias);
180
        }
181
182 7
        return $this;
183
    }
184
185 17
    public function filter()
186
    {
187 17
        if (null === $this->andFilters && null === $this->orFilters) {
188 1
            throw new Exceptions\MissingFiltersException();
189
        }
190
191 16
        if (!$this->fields) {
192 1
            throw new Exceptions\MissingFieldsException();
193
        }
194
195 15
        if (null !== $this->andFilters) {
196 8
            foreach ($this->andFilters as $filter => $value) {
197 8
                $this->applyFilterAnd(
198 8
                    Objects\FilterObject::fromRawFilter($filter),
199 8
                    $value,
200 8
                    Objects\Value::fromFilter($value)
201
                );
202
            }
203
        }
204
205 15
        if (null !== $this->orFilters) {
206 7
            $orFilter = [];
207 7
            $orFilter['orCondition'] = null;
208 7
            $orFilter['parameters'] = [];
209
210 7
            foreach ($this->orFilters as $filter => $value) {
211 7
                $orFilter = $this->applyFilterOr(
212 7
                    Objects\FilterObject::fromRawFilter($filter),
213 7
                    $value,
214 7
                    $orFilter
215
                );
216
            }
217
218 7
            if ((count($orFilter) > 0) && (null != $orFilter['orCondition'])) {
219 7
                $this->qBuilder->andWhere($orFilter['orCondition']);
220
221 7
                foreach ($orFilter['parameters'] as $parameter) {
222 7
                    $this->qBuilder->setParameter($parameter['field'], $parameter['value']);
223
                }
224
            }
225
        }
226
227 15
        return $this;
228
    }
229
230 8
    private function applyFilterAnd(
231
        Objects\FilterObject $filterObject,
232
        $value,
233
        Objects\Value $filterValue
234
    ) {
235 8
        $whereCondition = $this->entityAlias . '.' . $filterObject->getFieldName() . ' '
236 8
            . $filterObject->getOperatorMeta();
237
238 8
        if (in_array($filterObject->getFieldName(), $this->fields)) {
239 5
            $salt = '_' . random_int(111, 999);
240
241 5
            if ($filterObject->isListType()) {
242 1
                $whereCondition .= ' (:field_' . $filterObject->getFieldName() . $salt . ')';
243 4
            } elseif ($filterObject->isFieldEqualityType()) {
244 1
                $whereCondition .= ' ' . $this->entityAlias . '.' . $value;
245
            } else {
246 3
                $whereCondition .= ' :field_' . $filterObject->getFieldName() . $salt;
247
            }
248
249 5
            $this->qBuilder->andWhere($whereCondition);
250
251 5
            if ($filterObject->haveOperatorSubstitutionPattern()) {
252 2
                if ($filterObject->isListType()) {
253 1
                    $value = explode(',', $value);
254
                } else {
255 1
                    $value = str_replace(
256 1
                        '{string}',
257 1
                        $value,
258 1
                        $filterObject->getOperatorsSubstitutionPattern()
259
                    );
260
                }
261
            }
262
263 5
            $this->qBuilder->setParameter('field_' . $filterObject->getFieldName() . $salt, $value);
264
        } else {
265 3
            if (strpos($filterObject->getFieldName(), 'Embedded.') === false) {
266
                $whereCondition .= ' ' . $this->entityAlias . '.' . $value;
267
                $this->qBuilder->andWhere($whereCondition);
268
            }
269
        }
270
271
        // controllo se il filtro si riferisce ad una relazione dell'entità quindi devo fare dei join
272
        // esempio per users: filtering[_embedded.groups.name|eq]=admin
273 8
        if (strstr($filterObject->getRawFilter(), '_embedded.')) {
274
275 3
            $this->join($filterObject->getRawFilter());
276 3
            $relationEntityAlias = $this->getRelationEntityAlias();
277
278 3
            $embeddedFields = explode('.', $filterObject->getFieldName());
279 3
            $embeddedFieldName = $this->parser->camelize($embeddedFields[count($embeddedFields) - 1]);
280
281 3
            $salt = '_' . random_int(111, 999);
282
283 3
            $whereCondition = $relationEntityAlias . '.' . $embeddedFieldName . ' '
284 3
                . $filterObject->getOperatorMeta();
285
286 3
            if ($filterObject->isListType()) {
287 1
                $whereCondition .= ' (:field_' . $embeddedFieldName . $salt . ')';
288
            } else {
289 2
                $whereCondition .= ' :field_' . $embeddedFieldName . $salt;
290
            }
291
292 3
            $this->qBuilder->andWhere($whereCondition);
293 3
            if ($filterObject->haveOperatorSubstitutionPattern()) {
294 2
                if ($filterObject->isListType()) {
295 1
                    $value = explode(',', $filterValue->getFilter());
296
                } else {
297 1
                    $value = str_replace(
298 1
                        '{string}',
299 1
                        $value,
300 1
                        $filterObject->getOperatorsSubstitutionPattern()
301
                    );
302
                }
303
            }
304
305 3
            $this->qBuilder->setParameter('field_' . $embeddedFieldName . $salt, $value);
306
        }
307 8
    }
308
309 7
    private function applyFilterOr(
310
        Objects\FilterObject $filterObject,
311
        $value,
312
        $orCondition
313
    ) {
314 7
        $whereCondition = $this->entityAlias . '.' . $filterObject->getFieldName() . ' '
315 7
            . $filterObject->getOperatorMeta();
316
317
        // controllo se il filtro che mi arriva dalla richiesta è una proprietà di questa entità
318
        // esempio per users: filtering[username|contains]=mado
319 7
        if (in_array($filterObject->getFieldName(), $this->fields)) {
320 4
            $salt = '_' . random_int(111, 999);
321
322 4
            if ($filterObject->isListType()) {
323 1
                $whereCondition .= ' (:field_' . $filterObject->getFieldName() . $salt . ')';
324 3
            } else if ($filterObject->isFieldEqualityType()) {
325 1
                $whereCondition .= $this->entityAlias . '.' . $value;
326
            } else {
327 2
                $whereCondition .= ' :field_' . $filterObject->getFieldName() . $salt;
328
            }
329
330 4
            if (null != $orCondition['orCondition']) {
331
                $orCondition['orCondition'] .= ' OR ' . $whereCondition;
332
            } else {
333 4
                $orCondition['orCondition'] = $whereCondition;
334
            }
335
336 4
            if ($filterObject->haveOperatorSubstitutionPattern()) {
337 2
                if ($filterObject->isListType()) {
338 1
                    $value = explode(',', $value);
339
                } else {
340 1
                    $value = str_replace(
341 1
                        '{string}',
342 1
                        $value,
343 1
                        $filterObject->getOperatorsSubstitutionPattern()
344
                    );
345
                }
346
            }
347
348 4
            $orCondition['parameters'][] = [
349 4
                'field' => 'field_' . $filterObject->getFieldName() . $salt,
350 4
                'value' => $value
351
            ];
352
        } else {
353 3
            $isNotARelation = 0 !== strpos($filterObject->getFieldName(), 'Embedded.');
354 3
            if ($isNotARelation) {
355
                    $whereCondition .= ' ' . $this->entityAlias . '.' . $value;
356
                if (null != $orCondition['orCondition']) {
357
                    $orCondition['orCondition'] .= ' OR ' . $whereCondition;
358
                } else {
359
                    $orCondition['orCondition'] = $whereCondition;
360
                }
361
            }
362
        }
363
364
        // controllo se il filtro si riferisce ad una relazione dell'entità quindi devo fare dei join
365
        // esempio per users: filtering[_embedded.groups.name|eq]=admin
366 7
        if (strstr($filterObject->getRawFilter(), '_embedded.')) {
367
368 3
            $this->join($filterObject->getRawFilter(), self::OR_OPERATOR_LOGIC);
369 3
            $relationEntityAlias = $this->getRelationEntityAlias();
370
371 3
            $embeddedFields = explode('.', $filterObject->getFieldName());
372 3
            $embeddableFieldName = $this->parser->camelize($embeddedFields[count($embeddedFields) - 1]);
373
374 3
            $salt = '_' . random_int(111, 999);
375
376 3
            $whereCondition = $relationEntityAlias . '.' . $embeddableFieldName . ' '
377 3
                . $filterObject->getOperatorMeta();
378
379 3
            if ($filterObject->isListType()) {
380 1
                $whereCondition .= ' (:field_' . $embeddableFieldName . $salt . ')';
381
            } else {
382 2
                $whereCondition .= ' :field_' . $embeddableFieldName . $salt;
383
            }
384
385 3
            if (null != $orCondition['orCondition']) {
386 1
                $orCondition['orCondition'] .= ' OR ' . $whereCondition;
387
            } else {
388 3
                $orCondition['orCondition'] = $whereCondition;
389
            }
390
391 3
            if ($filterObject->haveOperatorSubstitutionPattern()) {
392 2
                if ($filterObject->isListType()) {
393 1
                    $value = explode(',', $value);
394
                } else {
395 1
                    $value = str_replace(
396 1
                        '{string}',
397 1
                        $value,
398 1
                        $filterObject->getOperatorsSubstitutionPattern()
399
                    );
400
                }
401
            }
402
403 3
            $orCondition['parameters'][] = [
404 3
                'field' => 'field_' . $embeddableFieldName . $salt,
405 3
                'value' => $value
406
            ];
407
        }
408
409 7
        return $orCondition;
410
    }
411
412 5
    public function sort()
413
    {
414 5
        if (!$this->fields) {
415 1
            throw new \RuntimeException(
416 1
                'Oops! Fields are not defined'
417
            );
418
        }
419
420 4
        if (null === $this->sorting) {
421 1
            throw new \RuntimeException(
422 1
                'Oops! Sorting is not defined'
423
            );
424
        }
425
426 3
        foreach ($this->sorting as $sort => $val) {
427 3
            $val = strtolower($val);
428
429 3
            $fieldName = $this->parser->camelize($sort);
430
431 3
            if (in_array($fieldName, $this->fields)) {
432 2
                $direction = ($val === self::DIRECTION_AZ) ? self::DIRECTION_AZ : self::DIRECTION_ZA;
433 2
                $this->ensureQueryBuilderIsDefined();
434 1
                $this->qBuilder->addOrderBy($this->entityAlias . '.' . $fieldName, $direction);
435
            }
436
437 2
            if (strstr($sort, '_embedded.')) {
438 1
                $this->join($sort);
439 1
                $relationEntityAlias = $this->getRelationEntityAlias();
440
441 1
                $embeddedFields = explode('.', $sort);
442 1
                $fieldName = $this->parser->camelize($embeddedFields[2]);
443 1
                $direction = ($val === self::DIRECTION_AZ) ? self::DIRECTION_AZ : self::DIRECTION_ZA;
444
445 2
                $this->qBuilder->addOrderBy($relationEntityAlias . '.' . $fieldName, $direction);
446
            }
447
448
        }
449
450 2
        return $this;
451
    }
452
453 14
    public function getQueryBuilder() :QueryBuilder
454
    {
455 14
        if (!$this->qBuilder) {
456 1
            throw new UnInitializedQueryBuilderException();
457
        }
458
459 13
        return $this->qBuilder;
460
    }
461
462 7
    private function setRelationEntityAlias(string $relationEntityAlias)
463
    {
464 7
        $this->relationEntityAlias = $relationEntityAlias;
465 7
    }
466
467 7
    private function getRelationEntityAlias()
468
    {
469 7
        return $this->relationEntityAlias;
470
    }
471
472 6
    public function setRel(array $rel)
473
    {
474 6
        $this->rel = $rel;
475
476 6
        return $this;
477
    }
478
479 2
    public function getRel() : array
480
    {
481 2
        return $this->rel;
482
    }
483
484 1
    public function addRel($relation)
485
    {
486 1
        array_push($this->rel, $relation);
487 1
    }
488
489 2
    public function setPrinting($printing)
490
    {
491 2
        $this->printing = $printing;
492
493 2
        return $this;
494
    }
495
496 1
    public function getPrinting()
497
    {
498 1
        return $this->printing;
499
    }
500
501 2
    public function setPage(int $page)
502
    {
503 2
        $this->page = $page;
504
505 2
        return $this;
506
    }
507
508 1
    public function getPage() :int
509
    {
510 1
        return $this->page;
511
    }
512
513 2
    public function setPageLength($pageLength)
514
    {
515 2
        $this->pageLength = $pageLength;
516
517 2
        return $this;
518
    }
519
520 1
    public function getPageLength()
521
    {
522 1
        return $this->pageLength;
523
    }
524
525 2
    public function setSelect($select) : QueryBuilderFactory
526
    {
527 2
        $this->select = $select;
528
529 2
        return $this;
530
    }
531
532 1
    public function getSelect()
533
    {
534 1
        return $this->select;
535
    }
536
537 1
    public function getEntityManager() : EntityManager
538
    {
539 1
        return $this->manager;
540
    }
541
542 2
    public function ensureQueryBuilderIsDefined()
543
    {
544 2
        if (!$this->qBuilder) {
545 1
            throw new \RuntimeException(
546
                'Oops! QueryBuilder was never initialized. '
547
                . "\n" . 'QueryBuilderFactory::createQueryBuilder()'
548 1
                . "\n" . 'QueryBuilderFactory::createSelectAndGroupBy()'
549
            );
550
        }
551 1
    }
552
}
553