Completed
Push — develop ( ec64f2...9148fa )
by Neomerx
02:26
created

ModelQueryBuilder::innerJoinOneTable()   C

Complexity

Conditions 8
Paths 4

Size

Total Lines 37
Code Lines 28

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 20
CRAP Score 8

Importance

Changes 0
Metric Value
dl 0
loc 37
ccs 20
cts 20
cp 1
rs 5.3846
c 0
b 0
f 0
cc 8
eloc 28
nc 4
nop 7
crap 8
1
<?php namespace Limoncello\Flute\Adapters;
2
3
/**
4
 * Copyright 2015-2017 [email protected]
5
 *
6
 * Licensed under the Apache License, Version 2.0 (the "License");
7
 * you may not use this file except in compliance with the License.
8
 * You may obtain a copy of the License at
9
 *
10
 * http://www.apache.org/licenses/LICENSE-2.0
11
 *
12
 * Unless required by applicable law or agreed to in writing, software
13
 * distributed under the License is distributed on an "AS IS" BASIS,
14
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
 * See the License for the specific language governing permissions and
16
 * limitations under the License.
17
 */
18
19
use Doctrine\DBAL\Connection;
20
use Doctrine\DBAL\Query\Expression\CompositeExpression;
21
use Doctrine\DBAL\Query\QueryBuilder;
22
use Doctrine\DBAL\Types\Type;
23
use Generator;
24
use Limoncello\Contracts\Data\ModelSchemeInfoInterface;
25
use Limoncello\Contracts\Data\RelationshipTypes;
26
use Limoncello\Flute\Contracts\Http\Query\FilterParameterInterface;
27
use Limoncello\Flute\Exceptions\InvalidArgumentException;
28
use PDO;
29
30
/**
31
 * @package Limoncello\Flute
32
 *
33
 * @SuppressWarnings(PHPMD.TooManyMethods)
34
 * @SuppressWarnings(PHPMD.TooManyPublicMethods)
35
 * @SuppressWarnings(PHPMD.ExcessiveClassComplexity)
36
 */
37
class ModelQueryBuilder extends QueryBuilder
38
{
39
    /**
40
     * @var string
41
     */
42
    private $modelClass;
43
44
    /**
45
     * @var string
46
     */
47
    private $mainTableName;
48
49
    /**
50
     * @var string
51
     */
52
    private $mainAlias;
53
54
    /**
55
     * @var ModelSchemeInfoInterface
56
     */
57
    private $modelSchemes;
58
59
    /**
60
     * @var int
61
     */
62
    private $aliasIdCounter = 0;
63
64
    /**
65
     * @var array
66
     */
67
    private $knownAliases = [];
68
69
    /**
70
     * @param Connection               $connection
71
     * @param string                   $modelClass
72
     * @param ModelSchemeInfoInterface $modelSchemes
73
     */
74 45
    public function __construct(Connection $connection, string $modelClass, ModelSchemeInfoInterface $modelSchemes)
75
    {
76 45
        assert(!empty($modelClass));
77
78 45
        parent::__construct($connection);
79
80 45
        $this->modelSchemes = $modelSchemes;
81 45
        $this->modelClass   = $modelClass;
82
83 45
        $this->mainTableName = $this->getModelSchemes()->getTable($this->getModelClass());
84 45
        $this->mainAlias     = $this->createAlias($this->getMainTableName());
85
    }
86
87
    /**
88
     * @return string
89
     */
90 45
    public function getModelClass(): string
91
    {
92 45
        return $this->modelClass;
93
    }
94
95
    /**
96
     * Select all fields associated with model.
97
     *
98
     * @return self
99
     */
100 42
    public function selectModelFields(): self
101
    {
102 42
        $columns = [];
103 42
        foreach ($this->getModelSchemes()->getAttributes($this->getModelClass()) as $column) {
104 42
            $columns[] = $this->buildColumnName($this->getMainAlias(), $column);
105
        }
106
107 42
        $this->select($columns);
108
109 42
        return $this;
110
    }
111
112
    /**
113
     * @return self
114
     */
115 43
    public function fromModelTable(): self
116
    {
117 43
        $this->from(
118 43
            $this->quoteTableName($this->getMainTableName()),
119 43
            $this->quoteTableName($this->getMainAlias())
120
        );
121
122 43
        return $this;
123
    }
124
125
    /**
126
     * @param iterable $attributes
127
     *
128
     * @return self
129
     *
130
     * @SuppressWarnings(PHPMD.StaticAccess)
131
     */
132 4
    public function createModel(iterable $attributes): self
133
    {
134 4
        $valuesAsParams = [];
135 4
        $dbPlatform     = $this->getConnection()->getDatabasePlatform();
136 4
        $types          = $this->getModelSchemes()->getAttributeTypes($this->getModelClass());
137
138 4
        foreach ($attributes as $column => $value) {
139 4
            assert(is_string($column) && $this->getModelSchemes()->hasAttributeType($this->getModelClass(), $column));
140
141 4
            $type                          = Type::getType($types[$column]);
142 4
            $pdoValue                      = $type->convertToDatabaseValue($value, $dbPlatform);
143 4
            $quotedColumn                  = $this->quoteColumnName($column);
144 4
            $valuesAsParams[$quotedColumn] = $this->createNamedParameter($pdoValue, $type->getBindingType());
145
        }
146
147
        $this
148 4
            ->insert($this->quoteTableName($this->getMainTableName()))
149 4
            ->values($valuesAsParams);
150
151 4
        return $this;
152
    }
153
154
    /**
155
     * @param iterable $attributes
156
     *
157
     * @return self
158
     *
159
     * @SuppressWarnings(PHPMD.StaticAccess)
160
     */
161 3
    public function updateModels(iterable $attributes): self
162
    {
163 3
        $dbPlatform = $this->getConnection()->getDatabasePlatform();
164 3
        $types      = $this->getModelSchemes()->getAttributeTypes($this->getModelClass());
165
166 3
        $this->update($this->quoteTableName($this->getMainTableName()));
167 3
        foreach ($attributes as $column => $value) {
168 3
            assert(is_string($column) && $this->getModelSchemes()->hasAttributeType($this->getModelClass(), $column));
169
170 3
            $type         = Type::getType($types[$column]);
171 3
            $pdoValue     = $type->convertToDatabaseValue($value, $dbPlatform);
172 3
            $quotedColumn = $this->quoteColumnName($column);
173 3
            $this->set($quotedColumn, $this->createNamedParameter($pdoValue, $type->getBindingType()));
174
        }
175
176 3
        return $this;
177
    }
178
179
    /**
180
     * @return self
181
     */
182 5
    public function deleteModels(): self
183
    {
184 5
        $this->delete($this->quoteTableName($this->getMainTableName()));
185
186 5
        return $this;
187
    }
188
189
    /**
190
     * @inheritdoc
191
     */
192 4
    public function prepareCreateInToManyRelationship(
193
        string $relationshipName,
194
        string $identity,
195
        string $secondaryIdBindName
196
    ): self {
197
        list ($intermediateTable, $primaryKey, $secondaryKey) =
198 4
            $this->getModelSchemes()->getBelongsToManyRelationship($this->getModelClass(), $relationshipName);
199
200
        $this
201 4
            ->insert($this->quoteTableName($intermediateTable))
202 4
            ->values([
203 4
                $this->quoteColumnName($primaryKey)   => $this->createNamedParameter($identity),
204 4
                $this->quoteColumnName($secondaryKey) => $secondaryIdBindName,
205
            ]);
206
207 4
        return $this;
208
    }
209
210
    /**
211
     * @inheritdoc
212
     */
213 2
    public function clearToManyRelationship(string $relationshipName, string $identity): self
214
    {
215
        list ($intermediateTable, $primaryKey) =
216 2
            $this->getModelSchemes()->getBelongsToManyRelationship($this->getModelClass(), $relationshipName);
217
218 2
        $filters = [$primaryKey => [FilterParameterInterface::OPERATION_EQUALS => [$identity]]];
219
        $this
220 2
            ->delete($this->quoteTableName($intermediateTable))
221 2
            ->addFilters($intermediateTable, $this->expr()->andX(), $filters);
0 ignored issues
show
Documentation introduced by
$filters is of type array<?,array<string|int...tring,{"0":"string"}>>>, but the function expects a object<Limoncello\Flute\Adapters\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
222
223 2
        return $this;
224
    }
225
226
    /**
227
     * @param iterable $filters
228
     *
229
     * @return self
230
     */
231 8
    public function addFiltersWithAndToTable(iterable $filters): self
232
    {
233 8
        return $this->addFilters($this->getMainTableName(), $this->expr()->andX(), $filters);
234
    }
235
236
    /**
237
     * @param iterable $filters
238
     *
239
     * @return self
240
     */
241
    public function addFiltersWithOrToTable(iterable $filters): self
242
    {
243
        return $this->addFilters($this->getMainTableName(), $this->expr()->orX(), $filters);
244
    }
245
246
    /**
247
     * @param iterable $filters
248
     *
249
     * @return self
250
     */
251 31
    public function addFiltersWithAndToAlias(iterable $filters): self
252
    {
253 31
        return $this->addFilters($this->getMainAlias(), $this->expr()->andX(), $filters);
254
    }
255
256
    /**
257
     * @param iterable $filters
258
     *
259
     * @return self
260
     */
261 2
    public function addFiltersWithOrToAlias(iterable $filters): self
262
    {
263 2
        return $this->addFilters($this->getMainAlias(), $this->expr()->orX(), $filters);
264
    }
265
266
    /**
267
     * @param string        $relationshipName
268
     * @param iterable      $relationshipFilters
269
     * @param iterable|null $relationshipSorts
270
     *
271
     * @return self
272
     */
273 23
    public function addRelationshipFiltersAndSortsWithAnd(
274
        string $relationshipName,
275
        iterable $relationshipFilters,
276
        ?iterable $relationshipSorts
277
    ): self {
278 23
        $joinWith = $this->expr()->andX();
279
280 23
        return $this->addRelationshipFiltersAndSorts(
281 23
            $relationshipName,
282 23
            $joinWith,
283 23
            $relationshipFilters,
284 23
            $relationshipSorts
285
        );
286
    }
287
288
    /**
289
     * @return self
290
     */
291 13
    public function distinct(): self
292
    {
293
        // emulate SELECT DISTINCT with grouping by primary key
294 13
        $primaryColumn = $this->getModelSchemes()->getPrimaryKey($this->getModelClass());
295 13
        $this->addGroupBy($this->buildColumnName($this->getMainAlias(), $primaryColumn));
296
297 13
        return $this;
298
    }
299
300
    /**
301
     * @param string        $relationshipName
302
     * @param iterable      $relationshipFilters
303
     * @param iterable|null $relationshipSorts
304
     *
305
     * @return self
306
     */
307 1
    public function addRelationshipFiltersAndSortsWithOr(
308
        string $relationshipName,
309
        iterable $relationshipFilters,
310
        ?iterable $relationshipSorts
311
    ): self {
312 1
        $joinWith = $this->expr()->orX();
313
314 1
        return $this->addRelationshipFiltersAndSorts(
315 1
            $relationshipName,
316 1
            $joinWith,
317 1
            $relationshipFilters,
318 1
            $relationshipSorts
319
        );
320
    }
321
322
    /**
323
     * @param iterable $sortParameters
324
     *
325
     * @return self
326
     */
327 4
    public function addSorts(iterable $sortParameters): self
328
    {
329 4
        foreach ($sortParameters as $columnName => $isAsc) {
330 4
            assert(is_string($columnName) === true && is_bool($isAsc) === true);
331 4
            $fullColumnName = $this->buildColumnName($this->getMainAlias(), $columnName);
332 4
            assert($this->getModelSchemes()->hasAttributeType($this->getModelClass(), $columnName));
333 4
            $this->addOrderBy($fullColumnName, $isAsc === true ? 'ASC' : 'DESC');
334
        }
335
336 4
        return $this;
337
    }
338
339
    /**
340
     * @param string              $tableOrAlias
341
     * @param CompositeExpression $filterLink
342
     * @param iterable            $filters
343
     *
344
     * @return self
345
     */
346 34
    private function addFilters(string $tableOrAlias, CompositeExpression $filterLink, iterable $filters): self
347
    {
348 34
        $added = false;
349 34
        foreach ($filters as $columnName => $operationsWithArgs) {
350 34
            $fullColumnName = $this->buildColumnName($tableOrAlias, $columnName);
351 34
            $this->applyFilter($filterLink, $fullColumnName, $operationsWithArgs);
352 33
            $added = true;
353
        }
354 33
        if ($added === true) {
355 33
            $this->andWhere($filterLink);
356
        }
357
358 33
        return $this;
359
    }
360
361
    /**
362
     * @param string              $relationshipName
363
     * @param CompositeExpression $filterLink
364
     * @param iterable            $relationshipFilters
365
     * @param iterable|null       $relationshipSorts
366
     *
367
     * @return self
368
     */
369 24
    private function addRelationshipFiltersAndSorts(
370
        string $relationshipName,
371
        CompositeExpression $filterLink,
372
        iterable $relationshipFilters,
373
        ?iterable $relationshipSorts
374
    ): self {
375 24
        $relationshipType = $this->getModelSchemes()->getRelationshipType($this->getModelClass(), $relationshipName);
376
        switch ($relationshipType) {
377 24
            case RelationshipTypes::BELONGS_TO:
378 12
                $builder = $this->addBelongsToFiltersAndSorts(
379 12
                    $relationshipName,
380 12
                    $filterLink,
381 12
                    $relationshipFilters,
382 12
                    $relationshipSorts
383
                );
384 12
                break;
385
386 16
            case RelationshipTypes::HAS_MANY:
387 11
                $builder = $this->addHasManyFiltersAndSorts(
388 11
                    $relationshipName,
389 11
                    $filterLink,
390 11
                    $relationshipFilters,
391 11
                    $relationshipSorts
392
                );
393 11
                break;
394
395 9
            case RelationshipTypes::BELONGS_TO_MANY:
396
            default:
397 9
                assert($relationshipType === RelationshipTypes::BELONGS_TO_MANY);
398 9
                $builder = $this->addBelongsToManyFiltersAndSorts(
399 9
                    $relationshipName,
400 9
                    $filterLink,
401 9
                    $relationshipFilters,
402 9
                    $relationshipSorts
403
                );
404 9
                break;
405
        }
406
407 24
        return $builder;
408
    }
409
410
    /**
411
     * @return string
412
     */
413 45
    private function getMainTableName(): string
414
    {
415 45
        return $this->mainTableName;
416
    }
417
418
    /**
419
     * @return ModelSchemeInfoInterface
420
     */
421 45
    private function getModelSchemes(): ModelSchemeInfoInterface
422
    {
423 45
        return $this->modelSchemes;
424
    }
425
426
    /**
427
     * @return string
428
     */
429 43
    private function getMainAlias(): string
430
    {
431 43
        return $this->mainAlias;
432
    }
433
434
    /**
435
     * @param string              $relationshipName
436
     * @param CompositeExpression $filterLink
437
     * @param iterable            $relationshipFilters
438
     * @param iterable|null       $relationshipSorts
439
     *
440
     * @return self
441
     */
442 12
    private function addBelongsToFiltersAndSorts(
443
        string $relationshipName,
444
        CompositeExpression $filterLink,
445
        iterable $relationshipFilters,
446
        ?iterable $relationshipSorts
447
    ): self {
448 12
        $foreignKey = $this->getModelSchemes()->getForeignKey($this->getModelClass(), $relationshipName);
449
        list($onePrimaryKey, $oneTable) =
450 12
            $this->getModelSchemes()->getReversePrimaryKey($this->getModelClass(), $relationshipName);
451
452 12
        $this->innerJoinOneTable(
453 12
            $this->getMainAlias(),
454 12
            $foreignKey,
455 12
            $oneTable,
456 12
            $onePrimaryKey,
457 12
            $filterLink,
458 12
            $relationshipFilters,
459 12
            $relationshipSorts
460
        );
461 12
        $this->andWhere($filterLink);
462
463 12
        return $this;
464
    }
465
466
    /**
467
     * @param string              $relationshipName
468
     * @param CompositeExpression $filterLink
469
     * @param iterable            $relationshipFilters
470
     * @param iterable|null       $relationshipSorts
471
     *
472
     * @return self
473
     */
474 11
    private function addHasManyFiltersAndSorts(
475
        string $relationshipName,
476
        CompositeExpression $filterLink,
477
        iterable $relationshipFilters,
478
        ?iterable $relationshipSorts
479
    ): self {
480 11
        $primaryKey = $this->getModelSchemes()->getPrimaryKey($this->getModelClass());
481
        list($manyForeignKey, $manyTable) =
482 11
            $this->getModelSchemes()->getReverseForeignKey($this->getModelClass(), $relationshipName);
483
484 11
        $this->innerJoinOneTable(
485 11
            $this->getMainAlias(),
486 11
            $primaryKey,
487 11
            $manyTable,
488 11
            $manyForeignKey,
489 11
            $filterLink,
490 11
            $relationshipFilters,
491 11
            $relationshipSorts
492
        );
493 11
        $this->andWhere($filterLink);
494
495 11
        return $this;
496
    }
497
498
    /**
499
     * @param string              $relationshipName
500
     * @param CompositeExpression $targetFilterLink
501
     * @param iterable            $relationshipFilters
502
     * @param iterable|null       $relationshipSorts
503
     *
504
     * @return self
505
     */
506 9
    private function addBelongsToManyFiltersAndSorts(
507
        string $relationshipName,
508
        CompositeExpression $targetFilterLink,
509
        iterable $relationshipFilters,
510
        ?iterable $relationshipSorts
511
    ): self {
512 9
        $primaryKey = $this->getModelSchemes()->getPrimaryKey($this->getModelClass());
513
        list ($intermediateTable, $intermediatePk, $intermediateFk) =
514 9
            $this->getModelSchemes()->getBelongsToManyRelationship($this->getModelClass(), $relationshipName);
515
        list($targetPrimaryKey, $targetTable) =
516 9
            $this->getModelSchemes()->getReversePrimaryKey($this->getModelClass(), $relationshipName);
517
518
        // no filters for intermediate table
519 9
        $intFilterLink = null;
520 9
        $intFilters    = null;
521 9
        $this->innerJoinTwoSequentialTables(
522 9
            $this->getMainAlias(),
523 9
            $primaryKey,
524 9
            $intermediateTable,
525 9
            $intermediatePk,
526 9
            $intermediateFk,
527 9
            $targetTable,
528 9
            $targetPrimaryKey,
529 9
            $intFilterLink,
530 9
            $intFilters,
531 9
            $targetFilterLink,
532 9
            $relationshipFilters,
533 9
            $relationshipSorts
534
        );
535 9
        $this->andWhere($targetFilterLink);
536
537 9
        return $this;
538
    }
539
540
    /**
541
     * @param string                   $fromAlias
542
     * @param string                   $fromColumn
543
     * @param string                   $targetTable
544
     * @param string                   $targetColumn
545
     * @param CompositeExpression|null $targetFilterLink
546
     * @param iterable|null            $targetFilterParams
547
     * @param iterable|null            $relationshipSorts
548
     *
549
     * @return string
550
     */
551 24
    private function innerJoinOneTable(
552
        string $fromAlias,
553
        string $fromColumn,
554
        string $targetTable,
555
        string $targetColumn,
556
        ?CompositeExpression $targetFilterLink,
557
        ?iterable $targetFilterParams,
558
        ?iterable $relationshipSorts
559
    ): string {
560 24
        $targetAlias   = $this->createAlias($targetTable);
561 24
        $joinCondition = $this->buildColumnName($fromAlias, $fromColumn) . '=' .
562 24
            $this->buildColumnName($targetAlias, $targetColumn);
563
564 24
        $this->innerJoin(
565 24
            $this->quoteTableName($fromAlias),
566 24
            $this->quoteTableName($targetTable),
567 24
            $this->quoteTableName($targetAlias),
568 24
            $joinCondition
569
        );
570
571 24
        if ($targetFilterLink !== null && $targetFilterParams !== null) {
572 24
            foreach ($targetFilterParams as $columnName => $operationsWithArgs) {
573 24
                assert(is_string($columnName) === true);
574 24
                $fullColumnName = $this->buildColumnName($targetAlias, $columnName);
575 24
                $this->applyFilter($targetFilterLink, $fullColumnName, $operationsWithArgs);
576
            }
577
        }
578 24
        if ($relationshipSorts !== null) {
579 19
            foreach ($relationshipSorts as $columnName => $isAsc) {
580 7
                assert(is_string($columnName) === true && is_bool($isAsc) === true);
581 7
                $fullColumnName = $this->buildColumnName($targetAlias, $columnName);
582 7
                $this->addOrderBy($fullColumnName, $isAsc === true ? 'ASC' : 'DESC');
583
            }
584
        }
585
586 24
        return $targetAlias;
587
    }
588
589
    /** @noinspection PhpTooManyParametersInspection
590
     * @param string                   $fromAlias
591
     * @param string                   $fromColumn
592
     * @param string                   $intTable
593
     * @param string                   $intToFromColumn
594
     * @param string                   $intToTargetColumn
595
     * @param string                   $targetTable
596
     * @param string                   $targetColumn
597
     * @param CompositeExpression|null $intFilterLink
598
     * @param iterable|null            $intFilterParams
599
     * @param CompositeExpression|null $targetFilterLink
600
     * @param iterable|null            $targetFilterParams
601
     * @param iterable|null            $targetSortParams
602
     *
603
     * @return string
604
     *
605
     * @SuppressWarnings(PHPMD.ExcessiveParameterList)
606
     */
607 9
    private function innerJoinTwoSequentialTables(
608
        string $fromAlias,
609
        string $fromColumn,
610
        string $intTable,
611
        string $intToFromColumn,
612
        string $intToTargetColumn,
613
        string $targetTable,
614
        string $targetColumn,
615
        ?CompositeExpression $intFilterLink,
616
        ?iterable $intFilterParams,
617
        ?CompositeExpression $targetFilterLink,
618
        ?iterable $targetFilterParams,
619
        ?iterable $targetSortParams
620
    ): string {
621 9
        $intNoSorting = null;
622 9
        $intAlias     = $this->innerJoinOneTable(
623 9
            $fromAlias,
624 9
            $fromColumn,
625 9
            $intTable,
626 9
            $intToFromColumn,
627 9
            $intFilterLink,
628 9
            $intFilterParams,
629 9
            $intNoSorting
630
        );
631 9
        $targetAlias  = $this->innerJoinOneTable(
632 9
            $intAlias,
633 9
            $intToTargetColumn,
634 9
            $targetTable,
635 9
            $targetColumn,
636 9
            $targetFilterLink,
637 9
            $targetFilterParams,
638 9
            $targetSortParams
639
        );
640
641 9
        return $targetAlias;
642
    }
643
644
    /**
645
     * @param string $tableName
646
     *
647
     * @return string
648
     */
649 45
    private function createAlias(string $tableName): string
650
    {
651 45
        $alias                          = $tableName . (++$this->aliasIdCounter);
652 45
        $this->knownAliases[$tableName] = $alias;
653
654 45
        return $alias;
655
    }
656
657
    /**
658
     * @inheritdoc
659
     */
660 45
    private function quoteTableName(string $tableName): string
661
    {
662 45
        return "`$tableName`";
663
    }
664
665
    /**
666
     * @inheritdoc
667
     */
668 7
    private function quoteColumnName(string $columnName): string
669
    {
670 7
        return "`$columnName`";
671
    }
672
673
    /**
674
     * @inheritdoc
675
     */
676 45
    private function buildColumnName(string $table, string $column): string
677
    {
678 45
        return "`$table`.`$column`";
679
    }
680
681
    /**
682
     * @param CompositeExpression $filterLink
683
     * @param string              $fullColumnName
684
     * @param iterable            $operationsWithArgs
685
     *
686
     * @return void
687
     *
688
     * @SuppressWarnings(PHPMD.CyclomaticComplexity)
689
     */
690 44
    private function applyFilter(
691
        CompositeExpression $filterLink,
692
        string $fullColumnName,
693
        iterable $operationsWithArgs
694
    ): void {
695 44
        foreach ($operationsWithArgs as $operation => $arguments) {
696 44
            assert(is_int($operation));
697 44
            assert(
698 44
                is_array($arguments) || $arguments instanceof Generator,
699 44
                "Filter argument(s) for $fullColumnName must be iterable (an array or Generator)."
700
            );
701
            switch ($operation) {
702 44
                case FilterParameterInterface::OPERATION_EQUALS:
0 ignored issues
show
Coding Style introduced by
case statements should be defined using a colon.

As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next break.

There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.

switch ($expr) {
    case "A": { //wrong
        doSomething();
        break;
    }
    case "B"; //wrong
        doSomething();
        break;
    case "C": //right
        doSomething();
        break;
}

To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.

Loading history...
703 38
                    $expression = $this->expr()->eq($fullColumnName, $this->createSingleNamedParameter($arguments));
0 ignored issues
show
Documentation introduced by
$arguments is of type array|object<Generator>, but the function expects a object<Limoncello\Flute\Adapters\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
704 37
                    break;
705 14
                case FilterParameterInterface::OPERATION_NOT_EQUALS:
706 1
                    $expression = $this->expr()->neq($fullColumnName, $this->createSingleNamedParameter($arguments));
0 ignored issues
show
Documentation introduced by
$arguments is of type array|object<Generator>, but the function expects a object<Limoncello\Flute\Adapters\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
707 1
                    break;
708 14
                case FilterParameterInterface::OPERATION_LESS_THAN:
709 5
                    $expression = $this->expr()->lt($fullColumnName, $this->createSingleNamedParameter($arguments));
0 ignored issues
show
Documentation introduced by
$arguments is of type array|object<Generator>, but the function expects a object<Limoncello\Flute\Adapters\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
710 5
                    break;
711 14
                case FilterParameterInterface::OPERATION_LESS_OR_EQUALS:
712 4
                    $expression = $this->expr()->lte($fullColumnName, $this->createSingleNamedParameter($arguments));
0 ignored issues
show
Documentation introduced by
$arguments is of type array|object<Generator>, but the function expects a object<Limoncello\Flute\Adapters\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
713 4
                    break;
714 13
                case FilterParameterInterface::OPERATION_GREATER_THAN:
715 2
                    $expression = $this->expr()->gt($fullColumnName, $this->createSingleNamedParameter($arguments));
0 ignored issues
show
Documentation introduced by
$arguments is of type array|object<Generator>, but the function expects a object<Limoncello\Flute\Adapters\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
716 2
                    break;
717 12
                case FilterParameterInterface::OPERATION_GREATER_OR_EQUALS:
718 3
                    $expression = $this->expr()->gte($fullColumnName, $this->createSingleNamedParameter($arguments));
0 ignored issues
show
Documentation introduced by
$arguments is of type array|object<Generator>, but the function expects a object<Limoncello\Flute\Adapters\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
719 3
                    break;
720 10
                case FilterParameterInterface::OPERATION_LIKE:
721 8
                    $expression = $this->expr()->like($fullColumnName, $this->createSingleNamedParameter($arguments));
0 ignored issues
show
Documentation introduced by
$arguments is of type array|object<Generator>, but the function expects a object<Limoncello\Flute\Adapters\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
722 8
                    break;
723 5
                case FilterParameterInterface::OPERATION_NOT_LIKE:
724 1
                    $parameter  = $this->createSingleNamedParameter($arguments);
0 ignored issues
show
Documentation introduced by
$arguments is of type array|object<Generator>, but the function expects a object<Limoncello\Flute\Adapters\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
725 1
                    $expression = $this->expr()->notLike($fullColumnName, $parameter);
726 1
                    break;
727 5
                case FilterParameterInterface::OPERATION_IN:
728 5
                    $expression = $this->expr()->in($fullColumnName, $this->createNamedParameterArray($arguments));
0 ignored issues
show
Documentation introduced by
$arguments is of type array|object<Generator>, but the function expects a object<Limoncello\Flute\Adapters\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
729 5
                    break;
730 1
                case FilterParameterInterface::OPERATION_NOT_IN:
731 1
                    $expression = $this->expr()->notIn($fullColumnName, $this->createNamedParameterArray($arguments));
0 ignored issues
show
Documentation introduced by
$arguments is of type array|object<Generator>, but the function expects a object<Limoncello\Flute\Adapters\iterable>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
732 1
                    break;
733 1
                case FilterParameterInterface::OPERATION_IS_NULL:
734 1
                    $expression = $this->expr()->isNull($fullColumnName);
735 1
                    break;
736 1
                case FilterParameterInterface::OPERATION_IS_NOT_NULL:
737
                default:
738 1
                    $expression = $this->expr()->isNotNull($fullColumnName);
739 1
                    break;
740
            }
741
742 43
            $filterLink->add($expression);
743
        }
744
    }
745
746
    /**
747
     * @param iterable $arguments
748
     *
749
     * @return string
750
     */
751 42
    private function createSingleNamedParameter(iterable $arguments): string
752
    {
753 42
        foreach ($arguments as $argument) {
754 41
            $paramName = $this->createNamedParameter($argument, $this->getPdoType($argument));
755
756 41
            return $paramName;
757
        }
758
759
        // arguments are empty
760 1
        throw new InvalidArgumentException();
761
    }
762
763
    /**
764
     * @param iterable $arguments
765
     *
766
     * @return string[]
767
     */
768 5
    private function createNamedParameterArray(iterable $arguments): array
769
    {
770 5
        $names = [];
771
772 5
        foreach ($arguments as $argument) {
773 5
            $names[] = $this->createNamedParameter($argument, $this->getPdoType($argument));
774
        }
775
776 5
        return $names;
777
    }
778
779
    /**
780
     * @param mixed $value
781
     *
782
     * @return int
783
     *
784
     * @SuppressWarnings(PHPMD.ElseExpression)
785
     */
786 43
    private function getPdoType($value): int
787
    {
788 43
        if (is_int($value) === true) {
789 31
            $type = PDO::PARAM_INT;
790 26
        } elseif (is_bool($value)) {
791 1
            $type = PDO::PARAM_BOOL;
792
        } else {
793 25
            assert(is_string($value), "Only strings, booleans, integers and nulls are supported.");
794 25
            $type = PDO::PARAM_STR;
795
        }
796
797 43
        return $type;
798
    }
799
}
800