Completed
Push — 3.x ( df0d37...0e617d )
by Grégoire
02:54
created

Datagrid::getPaginationParameters()   A

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
 * @final since sonata-project/admin-bundle 3.52
28
 *
29
 * @author Thomas Rabaix <[email protected]>
30
 */
31
class Datagrid implements DatagridInterface
32
{
33
    /**
34
     * The filter instances.
35
     *
36
     * @var array
37
     */
38
    protected $filters = [];
39
40
    /**
41
     * @var array
42
     */
43
    protected $values;
44
45
    /**
46
     * @var FieldDescriptionCollection
47
     */
48
    protected $columns;
49
50
    /**
51
     * @var PagerInterface
52
     */
53
    protected $pager;
54
55
    /**
56
     * @var bool
57
     */
58
    protected $bound = false;
59
60
    /**
61
     * @var ProxyQueryInterface
62
     */
63
    protected $query;
64
65
    /**
66
     * @var FormBuilderInterface
67
     */
68
    protected $formBuilder;
69
70
    /**
71
     * @var FormInterface
72
     */
73
    protected $form;
74
75
    /**
76
     * @var array
77
     */
78
    protected $results;
79
80
    public function __construct(
81
        ProxyQueryInterface $query,
82
        FieldDescriptionCollection $columns,
83
        PagerInterface $pager,
84
        FormBuilderInterface $formBuilder,
85
        array $values = []
86
    ) {
87
        $this->pager = $pager;
88
        $this->query = $query;
89
        $this->values = $values;
90
        $this->columns = $columns;
91
        $this->formBuilder = $formBuilder;
92
    }
93
94
    public function getPager()
95
    {
96
        return $this->pager;
97
    }
98
99
    public function getResults()
100
    {
101
        $this->buildPager();
102
103
        if (null === $this->results) {
104
            $this->results = $this->pager->getResults();
105
        }
106
107
        return $this->results;
108
    }
109
110
    public function buildPager()
111
    {
112
        if ($this->bound) {
113
            return;
114
        }
115
116
        foreach ($this->getFilters() as $name => $filter) {
117
            list($type, $options) = $filter->getRenderSettings();
118
119
            $this->formBuilder->add($filter->getFormName(), $type, $options);
120
        }
121
122
        $hiddenType = HiddenType::class;
123
124
        $this->formBuilder->add('_sort_by', $hiddenType);
125
        $this->formBuilder->get('_sort_by')->addViewTransformer(new CallbackTransformer(
126
            static function ($value) {
127
                return $value;
128
            },
129
            static function ($value) {
130
                return $value instanceof FieldDescriptionInterface ? $value->getName() : $value;
131
            }
132
        ));
133
134
        $this->formBuilder->add('_sort_order', $hiddenType);
135
        $this->formBuilder->add('_page', $hiddenType);
136
137
        if (isset($this->values['_per_page']) && \is_array($this->values['_per_page'])) {
138
            $this->formBuilder->add('_per_page', CollectionType::class, [
139
                'entry_type' => $hiddenType,
140
                'allow_add' => true,
141
            ]);
142
        } else {
143
            $this->formBuilder->add('_per_page', $hiddenType);
144
        }
145
146
        $this->form = $this->formBuilder->getForm();
147
        $this->form->submit($this->values);
148
149
        $data = $this->form->getData();
150
151
        foreach ($this->getFilters() as $name => $filter) {
152
            $this->values[$name] = isset($this->values[$name]) ? $this->values[$name] : null;
153
            $filterFormName = $filter->getFormName();
154
            if (isset($this->values[$filterFormName]['value']) && '' !== $this->values[$filterFormName]['value']) {
155
                $filter->apply($this->query, $data[$filterFormName]);
156
            }
157
        }
158
159
        if (isset($this->values['_sort_by'])) {
160
            if (!$this->values['_sort_by'] instanceof FieldDescriptionInterface) {
161
                throw new UnexpectedTypeException($this->values['_sort_by'], FieldDescriptionInterface::class);
162
            }
163
164
            if ($this->values['_sort_by']->isSortable()) {
165
                $this->query->setSortBy($this->values['_sort_by']->getSortParentAssociationMapping(), $this->values['_sort_by']->getSortFieldMapping());
166
167
                $this->values['_sort_order'] = $this->values['_sort_order'] ?? 'ASC';
168
                $this->query->setSortOrder($this->values['_sort_order']);
169
            }
170
        }
171
172
        $maxPerPage = 25;
173
        if (isset($this->values['_per_page'])) {
174
            if (isset($this->values['_per_page']['value'])) {
175
                $maxPerPage = $this->values['_per_page']['value'];
176
            } else {
177
                $maxPerPage = $this->values['_per_page'];
178
            }
179
        }
180
        $this->pager->setMaxPerPage($maxPerPage);
181
182
        $page = 1;
183
        if (isset($this->values['_page'])) {
184
            if (isset($this->values['_page']['value'])) {
185
                $page = $this->values['_page']['value'];
186
            } else {
187
                $page = $this->values['_page'];
188
            }
189
        }
190
191
        $this->pager->setPage($page);
192
193
        $this->pager->setQuery($this->query);
194
        $this->pager->init();
195
196
        $this->bound = true;
197
    }
198
199
    public function addFilter(FilterInterface $filter)
200
    {
201
        $this->filters[$filter->getName()] = $filter;
202
    }
203
204
    public function hasFilter($name)
205
    {
206
        return isset($this->filters[$name]);
207
    }
208
209
    public function removeFilter($name)
210
    {
211
        unset($this->filters[$name]);
212
    }
213
214
    public function getFilter($name)
215
    {
216
        if (!$this->hasFilter($name)) {
217
            @trigger_error(sprintf(
0 ignored issues
show
Security Best Practice introduced by
It seems like you do not handle an error condition here. This can introduce security issues, and is generally not recommended.

If you suppress an error, we recommend checking for the error condition explicitly:

// For example instead of
@mkdir($dir);

// Better use
if (@mkdir($dir) === false) {
    throw new \RuntimeException('The directory '.$dir.' could not be created.');
}
Loading history...
218
                'Passing a nonexistent filter name as argument 1 to %s() is deprecated since sonata-project/admin-bundle 3.52 and will throw an exception in 4.0.',
219
                __METHOD__
220
            ), E_USER_DEPRECATED);
221
222
            // NEXT_MAJOR : remove the previous `trigger_error()` call, the `return null` statement, uncomment the following exception and declare FilterInterface as return type
223
            // throw new \InvalidArgumentException(sprintf(
224
            //    'Filter named "%s" doesn\'t exist.',
225
            //    $name
226
            // ));
227
228
            return null;
229
        }
230
231
        return $this->filters[$name];
232
    }
233
234
    public function getFilters()
235
    {
236
        return $this->filters;
237
    }
238
239
    public function reorderFilters(array $keys)
240
    {
241
        $this->filters = array_merge(array_flip($keys), $this->filters);
242
    }
243
244
    public function getValues()
245
    {
246
        return $this->values;
247
    }
248
249
    public function setValue($name, $operator, $value)
250
    {
251
        $this->values[$name] = [
252
            'type' => $operator,
253
            'value' => $value,
254
        ];
255
    }
256
257
    public function hasActiveFilters()
258
    {
259
        foreach ($this->filters as $name => $filter) {
260
            if ($filter->isActive()) {
261
                return true;
262
            }
263
        }
264
265
        return false;
266
    }
267
268
    public function hasDisplayableFilters()
269
    {
270
        foreach ($this->filters as $name => $filter) {
271
            $showFilter = $filter->getOption('show_filter', null);
272
            if (($filter->isActive() && null === $showFilter) || (true === $showFilter)) {
273
                return true;
274
            }
275
        }
276
277
        return false;
278
    }
279
280
    public function getColumns()
281
    {
282
        return $this->columns;
283
    }
284
285
    public function getQuery()
286
    {
287
        return $this->query;
288
    }
289
290
    public function getForm()
291
    {
292
        $this->buildPager();
293
294
        return $this->form;
295
    }
296
297
    public function getSortParameters(FieldDescriptionInterface $fieldDescription): array
298
    {
299
        $values = $this->getValues();
300
301
        if ($this->isFieldAlreadySorted($fieldDescription)) {
302
            if ('ASC' === $values['_sort_order']) {
303
                $values['_sort_order'] = 'DESC';
304
            } else {
305
                $values['_sort_order'] = 'ASC';
306
            }
307
        } else {
308
            $values['_sort_order'] = 'ASC';
309
        }
310
311
        $values['_sort_by'] = \is_string($fieldDescription->getOption('sortable'))
312
            ? $fieldDescription->getOption('sortable')
313
            : $fieldDescription->getName();
314
315
        return ['filter' => $values];
316
    }
317
318
    public function getPaginationParameters(int $page): array
319
    {
320
        $values = $this->getValues();
321
322
        if (isset($values['_sort_by']) && $values['_sort_by'] instanceof FieldDescriptionInterface) {
323
            $values['_sort_by'] = $values['_sort_by']->getName();
324
        }
325
        $values['_page'] = $page;
326
327
        return ['filter' => $values];
328
    }
329
330
    private function isFieldAlreadySorted(FieldDescriptionInterface $fieldDescription): bool
331
    {
332
        $values = $this->getValues();
333
334
        if (!isset($values['_sort_by']) || !$values['_sort_by'] instanceof FieldDescriptionInterface) {
335
            return false;
336
        }
337
338
        return $values['_sort_by']->getName() === $fieldDescription->getName()
339
            || $values['_sort_by']->getName() === $fieldDescription->getOption('sortable');
340
    }
341
}
342