Completed
Push — master ( 0d7cdc...c6a355 )
by Dmytro
06:08
created

CriteriaQueryBuilder::orderByDefaultField()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
dl 0
loc 4
ccs 0
cts 0
cp 0
rs 10
c 0
b 0
f 0
cc 1
nc 1
nop 0
crap 2
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Paymaxi\Component\Query;
6
7
use Doctrine\Common\Collections\Criteria;
8
use Doctrine\ORM\EntityRepository;
9
use Paymaxi\Component\Query\Filter\FilterInterface;
10
use Paymaxi\Component\Query\Handler\AbstractHandler;
11
use Paymaxi\Component\Query\Handler\CriteriaHandler;
12
use Paymaxi\Component\Query\Handler\FilterHandlerInterface;
13
use Paymaxi\Component\Query\Handler\HandlerInterface;
14
use Paymaxi\Component\Query\Handler\QueryBuilderHandler;
15
use Paymaxi\Component\Query\Handler\SortHandlerInterface;
16
use Paymaxi\Component\Query\Sort\SortInterface;
17
use Sylius\Component\Registry\ServiceRegistry;
18
19
/**
20
 * Class CriteriaQueryBuilder
21
 *
22
 */
23
class CriteriaQueryBuilder implements CriteriaQueryBuilderInterface
24
{
25
    /** @var Criteria */
26
    protected $criteria;
27
28
    /** @var \Doctrine\ORM\QueryBuilder */
29
    protected $qb;
30
31
    /** @var array */
32
    private $filterParams;
33
34
    /** @var array */
35
    private $sortingFields;
36
37
    /** @var array */
38
    private $defaultOrder;
39
40
    /** @var ServiceRegistry|HandlerInterface[]|AbstractHandler[] */
41
    private $handlers;
42
43
    /** @var bool */
44
    private $applied = false;
45
46
    public const DEFAULT_ORDER_FIELD = 'created';
47
48
    private $supportedSortingFields = [];
49
50
    /**
51
     * @param EntityRepository $repository
52
     * @param array $filterParams
53 4
     * @param array $sortingFields
54
     *
55 4
     * @internal param ApiManagerInterface $manager
56 4
     */
57 4
    public function __construct(EntityRepository $repository, array $filterParams = [], array $sortingFields = [])
58
    {
59 4
        $this->qb = $repository->createQueryBuilder('e');
60 4
        $this->criteria = new Criteria();
61 4
        $this->handlers = new ServiceRegistry(HandlerInterface::class);
62 4
63
        $this->initDefaultHandlers();
64 4
        $this->setFilterParams($filterParams);
65
        $this->setSortingFields($sortingFields);
66 4
    }
67 4
68 4
    protected function initDefaultHandlers(): void
69
    {
70
        $this->handlers->register(CriteriaHandler::class, new CriteriaHandler($this->criteria));
71
        $this->handlers->register(QueryBuilderHandler::class, new QueryBuilderHandler($this->qb));
72
    }
73
74
    /**
75 4
     * @param FilterInterface $filter
76
     *
77 4
     * @return CriteriaQueryBuilderInterface
78
     */
79
    public function addFilter(FilterInterface $filter): CriteriaQueryBuilderInterface
80 4
    {
81 4
        $supports = false;
82 4
83 4
        /** @var AbstractHandler $handler */
84
        foreach ($this->handlers->all() as $handler) {
85
            if ($handler instanceof FilterHandlerInterface && $handler->supports($filter)) {
86
                $supports = true;
87 4
                $handler->addFilter($filter);
88
            }
89
        }
90
91 4
        if (!$supports) {
92
            throw new \RuntimeException('No available handler for this filter.');
93 4
        }
94
95
        $this->resetApply();
96 4
97
        return $this;
98 4
    }
99 4
100
    protected function resetApply():void
101
    {
102
        $this->applied = false;
103
    }
104
105
    /**
106
     * @param SortInterface $sort
107
     *
108
     * @return CriteriaQueryBuilderInterface
109
     */
110
    public function addSorting(SortInterface $sort): CriteriaQueryBuilderInterface
111
    {
112
        $supports = false;
113
114
        /** @var AbstractHandler $handler */
115
        foreach ($this->handlers->all() as $handler) {
116
            if ($handler instanceof SortHandlerInterface && $handler->supports($sort)) {
117
                $supports = true;
118
                $handler->addSorting($sort);
119
                $this->supportedSortingFields[] = $sort->getFieldName();
120
            }
121
        }
122
123
        if (!$supports) {
124
            throw new \RuntimeException('No available handler for this sorting.');
125
        }
126
127
        $this->resetApply();
128
129
        return $this;
130
    }
131 3
132
    /**
133 3
     * @return \Doctrine\ORM\QueryBuilder
134 3
     * @throws \Doctrine\ORM\Query\QueryException
135
     */
136 3
    public function getQb(): \Doctrine\ORM\QueryBuilder
137
    {
138
        $clone = clone $this;
139
        $clone->apply();
140
141
        return $clone->qb->addCriteria($clone->getCriteria());
142 4
    }
143
144 4
    /**
145 3
     * It caused changes in qb and criteria
146
     */
147
    protected function apply(): void
148 4
    {
149 4
        if ($this->applied) {
150
            return;
151
        }
152 4
153 4
        if (0 === \count($this->sortingFields)) {
154
            $this->criteria->orderBy($this->getDefaultOrder());
155 4
        } else {
156 4
            if (!isset($this->sortingFields[self::DEFAULT_ORDER_FIELD])) {
157
                $this->sortingFields = array_merge($this->sortingFields, $this->orderByDefaultField());
158
            }
159
        }
160
161 4
        $this->applySorting();
162
        $this->applyFilters();
163 4
164
        $this->applied = true;
165 4
    }
166
167
    /**
168 4
     * @return Criteria
169
     */
170 4
    public function getCriteria(): Criteria
171 3
    {
172 3
        $this->apply();
173 3
174
        return $this->criteria;
175
    }
176
177 4
    private function applyFilters()
178
    {
179 4
        foreach ($this->filterParams as $field => $value) {
180
            foreach ($this->handlers->all() as $handler) {
181 4
                if ($handler instanceof FilterHandlerInterface) {
182
                    $handler->filter($field, $value);
183
                }
184
            }
185
        }
186
    }
187
188 4
    private function applySorting()
189
    {
190
        foreach ($this->sortingFields as $field => $order) {
191
            foreach ($this->handlers->all() as $handler) {
192
                if ($handler instanceof SortHandlerInterface) {
193 4
                    $handler->sort($field, $order);
194
                }
195 4
            }
196
        }
197
    }
198
199
    /**
200
     * @return array
201 4
     */
202
    public function getDefaultOrder(): array
203 4
    {
204 4
        $order = $this->defaultOrder ?? [];
205
        if (in_array(self::DEFAULT_ORDER_FIELD, $this->supportedSortingFields) &&
206
            !isset($order[self::DEFAULT_ORDER_FIELD])) {
207
            $order = array_merge($order, $this->orderByDefaultField());
208
        }
209 4
        return $order;
210
    }
211 4
212
    /**
213 4
     * @param array $defaultOrder
214 4
     */
215
    public function setDefaultOrder(array $defaultOrder): void
216
    {
217
        $this->defaultOrder = $defaultOrder;
218
    }
219 4
220
    /**
221 4
     * @param array $filterParams
222
     */
223 4
    public function setFilterParams(array $filterParams)
224 4
    {
225
        $this->resetApply();
226
227
        $this->filterParams = $filterParams;
228
    }
229
230
    /**
231
     * @param array $sortingFields
232
     */
233
    public function setSortingFields(array $sortingFields)
234
    {
235
        $this->resetApply();
236
        
237
        $this->sortingFields = $sortingFields;
238
    }
239
240
    /**
241
     * @return ServiceRegistry
242
     */
243
    public function getHandlers():ServiceRegistry
244
    {
245
        return $this->handlers;
246
    }
247
248
    /**
249
     * @param string $identifier
250
     * @param HandlerInterface $handler
251
     */
252
    public function addHandler(string $identifier, HandlerInterface $handler):void
253
    {
254
        $this->handlers->register($identifier, $handler);
255
    }
256
257
    public function getSupportedSortingFields() : array
258
    {
259
        return $this->supportedSortingFields;
260
    }
261
262
    private function orderByDefaultField() : array
263
    {
264
        return [self::DEFAULT_ORDER_FIELD => 'DESC'];
265
    }
266
}
267