Datagrid::getPaginationParameters()   A
last analyzed

Complexity

Conditions 3
Paths 2

Size

Total Lines 11

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 11
rs 9.9
c 0
b 0
f 0
cc 3
nc 2
nop 1
1
<?php
2
3
declare(strict_types=1);
4
5
/*
6
 * This file is part of the Sonata Project package.
7
 *
8
 * (c) Thomas Rabaix <[email protected]>
9
 *
10
 * For the full copyright and license information, please view the LICENSE
11
 * file that was distributed with this source code.
12
 */
13
14
namespace Sonata\AdminBundle\Datagrid;
15
16
use Sonata\AdminBundle\Admin\FieldDescriptionCollection;
17
use Sonata\AdminBundle\Admin\FieldDescriptionInterface;
18
use Sonata\AdminBundle\Filter\FilterInterface;
19
use Symfony\Component\Form\CallbackTransformer;
20
use Symfony\Component\Form\Exception\UnexpectedTypeException;
21
use Symfony\Component\Form\Extension\Core\Type\CollectionType;
22
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
23
use Symfony\Component\Form\FormBuilderInterface;
24
use Symfony\Component\Form\FormInterface;
25
26
/**
27
 * @author Thomas Rabaix <[email protected]>
28
 */
29
final class Datagrid implements DatagridInterface
30
{
31
    /**
32
     * The filter instances.
33
     *
34
     * @var array<string, FilterInterface>
35
     */
36
    private $filters = [];
37
38
    /**
39
     * @var array<string, mixed>
40
     */
41
    private $values = [];
42
43
    /**
44
     * @var FieldDescriptionCollection
45
     */
46
    private $columns;
47
48
    /**
49
     * @var PagerInterface
50
     */
51
    private $pager;
52
53
    /**
54
     * @var bool
55
     */
56
    private $bound = false;
57
58
    /**
59
     * @var ProxyQueryInterface
60
     */
61
    private $query;
62
63
    /**
64
     * @var FormBuilderInterface
65
     */
66
    private $formBuilder;
67
68
    /**
69
     * @var FormInterface
70
     */
71
    private $form;
72
73
    /**
74
     * Results are null prior to its initialization in `getResults()`.
75
     *
76
     * @var object[]|null
77
     */
78
    private $results;
79
80
    /**
81
     * @param array<string, mixed> $values
82
     */
83
    public function __construct(
84
        ProxyQueryInterface $query,
85
        FieldDescriptionCollection $columns,
86
        PagerInterface $pager,
87
        FormBuilderInterface $formBuilder,
88
        array $values = []
89
    ) {
90
        $this->pager = $pager;
91
        $this->query = $query;
92
        $this->values = $values;
93
        $this->columns = $columns;
94
        $this->formBuilder = $formBuilder;
95
    }
96
97
    public function getPager(): PagerInterface
98
    {
99
        return $this->pager;
100
    }
101
102
    public function getResults(): array
103
    {
104
        $this->buildPager();
105
106
        if (null === $this->results) {
107
            $this->results = $this->pager->getResults();
108
        }
109
110
        return $this->results;
111
    }
112
113
    public function buildPager(): void
114
    {
115
        if ($this->bound) {
116
            return;
117
        }
118
119
        foreach ($this->getFilters() as $name => $filter) {
120
            list($type, $options) = $filter->getRenderSettings();
121
122
            $this->formBuilder->add($filter->getFormName(), $type, $options);
123
        }
124
125
        $hiddenType = HiddenType::class;
126
127
        $this->formBuilder->add('_sort_by', $hiddenType);
128
        $this->formBuilder->get('_sort_by')->addViewTransformer(new CallbackTransformer(
129
            static function ($value) {
130
                return $value;
131
            },
132
            static function ($value) {
133
                return $value instanceof FieldDescriptionInterface ? $value->getName() : $value;
134
            }
135
        ));
136
137
        $this->formBuilder->add('_sort_order', $hiddenType);
138
        $this->formBuilder->add('_page', $hiddenType);
139
140
        if (isset($this->values['_per_page']) && \is_array($this->values['_per_page'])) {
141
            $this->formBuilder->add('_per_page', CollectionType::class, [
142
                'entry_type' => $hiddenType,
143
                'allow_add' => true,
144
            ]);
145
        } else {
146
            $this->formBuilder->add('_per_page', $hiddenType);
147
        }
148
149
        $this->form = $this->formBuilder->getForm();
150
        $this->form->submit($this->values);
151
152
        $data = $this->form->getData();
153
154
        foreach ($this->getFilters() as $name => $filter) {
155
            $this->values[$name] = isset($this->values[$name]) ? $this->values[$name] : null;
156
            $filterFormName = $filter->getFormName();
157
            if (isset($this->values[$filterFormName]['value']) && '' !== $this->values[$filterFormName]['value']) {
158
                $filter->apply($this->query, $data[$filterFormName]);
159
            }
160
        }
161
162
        if (isset($this->values['_sort_by'])) {
163
            if (!$this->values['_sort_by'] instanceof FieldDescriptionInterface) {
164
                throw new UnexpectedTypeException($this->values['_sort_by'], FieldDescriptionInterface::class);
165
            }
166
167
            if ($this->values['_sort_by']->isSortable()) {
168
                $this->query->setSortBy($this->values['_sort_by']->getSortParentAssociationMapping(), $this->values['_sort_by']->getSortFieldMapping());
169
170
                $this->values['_sort_order'] = $this->values['_sort_order'] ?? 'ASC';
171
                $this->query->setSortOrder($this->values['_sort_order']);
172
            }
173
        }
174
175
        $maxPerPage = 25;
176
        if (isset($this->values['_per_page'])) {
177
            if (isset($this->values['_per_page']['value'])) {
178
                $maxPerPage = $this->values['_per_page']['value'];
179
            } else {
180
                $maxPerPage = $this->values['_per_page'];
181
            }
182
        }
183
        $this->pager->setMaxPerPage($maxPerPage);
184
185
        $page = 1;
186
        if (isset($this->values['_page'])) {
187
            if (isset($this->values['_page']['value'])) {
188
                $page = $this->values['_page']['value'];
189
            } else {
190
                $page = $this->values['_page'];
191
            }
192
        }
193
194
        $this->pager->setPage($page);
195
196
        $this->pager->setQuery($this->query);
197
        $this->pager->init();
198
199
        $this->bound = true;
200
    }
201
202
    public function addFilter(FilterInterface $filter): void
203
    {
204
        $this->filters[$filter->getName()] = $filter;
205
    }
206
207
    public function hasFilter(string $name): bool
208
    {
209
        return isset($this->filters[$name]);
210
    }
211
212
    public function removeFilter(string $name): void
213
    {
214
        unset($this->filters[$name]);
215
    }
216
217
    public function getFilter(string $name): FilterInterface
218
    {
219
        if (!$this->hasFilter($name)) {
220
            throw new \InvalidArgumentException(sprintf(
221
                'Filter named "%s" doesn\'t exist.',
222
                $name
223
            ));
224
        }
225
226
        return $this->filters[$name];
227
    }
228
229
    public function getFilters(): array
230
    {
231
        return $this->filters;
232
    }
233
234
    public function reorderFilters(array $keys): void
235
    {
236
        $this->filters = array_merge(array_flip($keys), $this->filters);
237
    }
238
239
    public function getValues(): array
240
    {
241
        return $this->values;
242
    }
243
244
    public function setValue(string $name, ?string $operator, $value): void
245
    {
246
        $this->values[$name] = [
247
            'type' => $operator,
248
            'value' => $value,
249
        ];
250
    }
251
252
    public function hasActiveFilters(): bool
253
    {
254
        foreach ($this->filters as $name => $filter) {
255
            if ($filter->isActive()) {
256
                return true;
257
            }
258
        }
259
260
        return false;
261
    }
262
263
    public function hasDisplayableFilters(): bool
264
    {
265
        foreach ($this->filters as $name => $filter) {
266
            $showFilter = $filter->getOption('show_filter', null);
267
            if (($filter->isActive() && null === $showFilter) || (true === $showFilter)) {
268
                return true;
269
            }
270
        }
271
272
        return false;
273
    }
274
275
    public function getColumns(): FieldDescriptionCollection
276
    {
277
        return $this->columns;
278
    }
279
280
    public function getQuery(): ProxyQueryInterface
281
    {
282
        return $this->query;
283
    }
284
285
    public function getForm(): FormInterface
286
    {
287
        $this->buildPager();
288
289
        return $this->form;
290
    }
291
292
    public function getSortParameters(FieldDescriptionInterface $fieldDescription): array
293
    {
294
        $values = $this->getValues();
295
296
        if ($this->isFieldAlreadySorted($fieldDescription)) {
297
            if ('ASC' === $values['_sort_order']) {
298
                $values['_sort_order'] = 'DESC';
299
            } else {
300
                $values['_sort_order'] = 'ASC';
301
            }
302
        } else {
303
            $values['_sort_order'] = 'ASC';
304
        }
305
306
        $values['_sort_by'] = \is_string($fieldDescription->getOption('sortable'))
307
            ? $fieldDescription->getOption('sortable')
308
            : $fieldDescription->getName();
309
310
        return ['filter' => $values];
311
    }
312
313
    public function getPaginationParameters(int $page): array
314
    {
315
        $values = $this->getValues();
316
317
        if (isset($values['_sort_by']) && $values['_sort_by'] instanceof FieldDescriptionInterface) {
318
            $values['_sort_by'] = $values['_sort_by']->getName();
319
        }
320
        $values['_page'] = $page;
321
322
        return ['filter' => $values];
323
    }
324
325
    private function isFieldAlreadySorted(FieldDescriptionInterface $fieldDescription): bool
326
    {
327
        $values = $this->getValues();
328
329
        if (!isset($values['_sort_by']) || !$values['_sort_by'] instanceof FieldDescriptionInterface) {
330
            return false;
331
        }
332
333
        return $values['_sort_by']->getName() === $fieldDescription->getName()
334
            || $values['_sort_by']->getName() === $fieldDescription->getOption('sortable');
335
    }
336
}
337