QueryBuilderHelper::addOrderBy()   A
last analyzed

Complexity

Conditions 5
Paths 4

Size

Total Lines 19
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 5
eloc 10
c 0
b 0
f 0
nc 4
nop 1
dl 0
loc 19
rs 9.6111
1
<?php
2
3
declare(strict_types=1);
4
5
namespace LAG\AdminBundle\Bridge\Doctrine\ORM\QueryBuilder;
6
7
use Doctrine\ORM\Query\Expr\Join;
8
use Doctrine\ORM\QueryBuilder;
9
use Doctrine\Persistence\Mapping\ClassMetadata;
10
use LAG\AdminBundle\Exception\Exception;
11
use LAG\AdminBundle\Exception\UnexpectedTypeException;
12
use LAG\AdminBundle\Metadata\Filter\FilterInterface;
13
14
use function Symfony\Component\String\u;
15
16
class QueryBuilderHelper
17
{
18
    private string $rootAlias;
19
20
    public function __construct(
21
        private QueryBuilder $queryBuilder,
22
        private ClassMetadata $metadata,
23
    ) {
24
        $this->rootAlias = $this->queryBuilder->getRootAliases()[0];
25
    }
26
27
    public function addOrderBy(array $orderBy): self
28
    {
29
        foreach ($orderBy as $propertyPath => $order) {
30
            $propertyPath = u($propertyPath);
31
32
            if ($propertyPath->containsAny('.')) {
33
                $joinRootAlias = $this->rootAlias;
34
35
                foreach ($propertyPath->split('.') as $path) {
36
                    $this->leftJoin($path->toString(), $joinRootAlias);
37
                }
38
            } else {
39
                if ($this->metadata->hasField($propertyPath->toString())) {
40
                    $this->queryBuilder->addOrderBy($propertyPath->prepend($this->rootAlias.'.')->toString(), $order);
41
                }
42
            }
43
        }
44
45
        return $this;
46
    }
47
48
    public function addFilters(array $filters): self
49
    {
50
        foreach ($filters as $filter) {
51
            if (!$filter instanceof FilterInterface) {
52
                throw new UnexpectedTypeException($filter, FilterInterface::class);
53
            }
54
            $data = $filter->getData();
55
            $propertyPath = u($filter->getPropertyPath());
56
57
            // Do not filter on null values
58
            if ($data === null) {
59
                continue;
60
            }
61
62
            if ($propertyPath->containsAny('.')) {
63
                // TODO
64
            } elseif ($this->metadata->hasField($propertyPath->toString())) {
65
                $method = $filter->getOperator() === 'and' ? 'andWhere' : 'orWhere';
66
67
                if ('between' === $filter->getComparator()) {
68
                    if (!\is_array($data) || \count($data) === 2) {
69
                        throw new Exception('Parameters for a between comparison filter are invalid');
70
                    }
71
                    $parameterName1 = u($filter->getName())
72
                        ->prepend('filter_')
73
                        ->append('_1')
74
                        ->snake()
75
                        ->toString()
76
                    ;
77
                    $parameterName2 = u($filter->getName())
78
                        ->prepend('filter_')
79
                        ->append('_2')
80
                        ->snake()
81
                        ->toString()
82
                    ;
83
84
                    $dql = sprintf(
85
                        'entity.%s > :%s and entity.%s < :%s',
86
                        $filter->getPropertyPath(),
87
                        $parameterName1,
88
                        $filter->getPropertyPath(),
89
                        $parameterName2
90
                    );
91
                    $this->queryBuilder->$method($dql);
92
                    $this->queryBuilder->setParameter($parameterName1, $filter->getData()[0]);
93
                    $this->queryBuilder->setParameter($parameterName2, $filter->getData()[1]);
94
95
                    continue;
96
                }
97
                if ($filter->getComparator() === 'like') {
98
                    $data = '%'.$filter->getData().'%';
99
                }
100
                $parameterName = u($filter->getName())->prepend('filter_')->snake()->toString();
101
102
                $dql = sprintf(
103
                    '%s.%s %s :%s',
104
                    'entity',
105
                    $filter->getName(),
106
                    $filter->getComparator(),
107
                    $parameterName
108
                );
109
                $this->queryBuilder->$method($dql);
110
                $this->queryBuilder->setParameter($parameterName, $data);
111
            }
112
        }
113
114
        return $this;
115
    }
116
117
    public function leftJoin(string $joinAlias, string $rootAlias): self
118
    {
119
        if (!$this->hasJoin($joinAlias)) {
120
            $dql = u($rootAlias)
121
                ->append('.')
122
                ->append($joinAlias)
123
                ->toString()
124
            ;
125
            $this->queryBuilder->leftJoin($dql, u($joinAlias)->append('_resource')->toString());
126
        }
127
128
        return $this;
129
    }
130
131
    public function getQueryBuilder(): QueryBuilder
132
    {
133
        return $this->queryBuilder;
134
    }
135
136
    private function hasJoin(string $joinAlias): bool
137
    {
138
        $dqlPart = $this->queryBuilder->getDQLPart('join');
139
140
        foreach ($dqlPart as $rootAlias => $joins) {
141
            /** @var Join $join */
142
            foreach ($joins as $join) {
143
                if ($join->getAlias() === $joinAlias.'_resource') {
144
                    return true;
145
                }
146
            }
147
        }
148
149
        return false;
150
    }
151
}
152