Passed
Branch develop (bac6db)
by Ludwig
04:56
created

AbstractGrid::addSearch()   C

Complexity

Conditions 7
Paths 12

Size

Total Lines 39
Code Lines 25

Duplication

Lines 12
Ratio 30.77 %

Importance

Changes 0
Metric Value
dl 12
loc 39
rs 6.7272
c 0
b 0
f 0
cc 7
eloc 25
nc 12
nop 2
1
<?php
2
/*
3
 * This file is part of cwdFancyGridBundle
4
 *
5
 * (c)2017 cwd.at GmbH <[email protected]>
6
 *
7
 * For the full copyright and license information, please view the LICENSE
8
 * file that was distributed with this source code.
9
 */
10
declare(strict_types=1);
11
namespace Cwd\FancyGridBundle\Grid;
12
13
use Cwd\FancyGridBundle\Column\AbstractColumn;
14
use Cwd\FancyGridBundle\Column\ColumnInterface;
15
use Doctrine\Common\Persistence\ObjectManager;
16
use Doctrine\ORM\QueryBuilder;
17
use JMS\Serializer\Serializer;
18
use JMS\Serializer\SerializerBuilder;
19
use Pagerfanta\Adapter\DoctrineORMAdapter;
20
use Pagerfanta\Pagerfanta;
21
use Symfony\Component\OptionsResolver\OptionsResolver;
22
use Symfony\Component\PropertyAccess\PropertyAccess;
23
use Symfony\Component\Translation\TranslatorInterface;
24
25
/**
26
 * Class AbstractGrid
27
 * @package Cwd\FancyGridBundle\Grid
28
 * @author Ludwig Ruderstaler <[email protected]>
29
 */
30
abstract class AbstractGrid implements GridInterface, \IteratorAggregate
31
{
32
    /**
33
     * @var array
34
     */
35
    protected $options;
36
37
    /**
38
     * @var array
39
     */
40
    protected $children = [];
41
42
    /**
43
     * @var ObjectManager
44
     */
45
    protected $objectManager;
46
47
    /**
48
     * @var TranslatorInterface
49
     */
50
    protected $translator;
51
52
    /**
53
     * @var \Symfony\Component\PropertyAccess\PropertyAccessor
54
     */
55
    protected $accessor;
56
57
    /**
58
     * @var null|string
59
     */
60
    protected $primary = null;
61
62
    /**
63
     * @var \Twig_Environment
64
     */
65
    protected $twig;
66
67
    /**
68
     * AbstractGrid constructor.
69
     * @param array $options
70
     */
71
    public function __construct(TranslatorInterface $translator, array $options = array())
72
    {
73
        $resolver = new OptionsResolver();
74
        $this->configureOptions($resolver);
75
76
        $this->options = $resolver->resolve($options);
77
        $this->translator = $translator;
78
        $this->accessor = PropertyAccess::createPropertyAccessor();
79
    }
80
81
    /**
82
     * @param \Twig_Environment $twig
83
     */
84
    public function setTwig(\Twig_Environment $twig)
85
    {
86
        $this->twig = $twig;
87
    }
88
89
    /**
90
     * @param ObjectManager $objectManager
91
     * @return $this
92
     */
93
    public function setObjectManager($objectManager)
94
    {
95
        $this->objectManager = $objectManager;
96
97
        return $this;
98
    }
99
100
    /**
101
     * generate gridid
102
     * @return string
103
     */
104
    public function getId()
105
    {
106
        $data = [
107
            $this->getOption('data_route'),
108
            $this->getOption('data_route_options'),
109
            $this->getOption('template'),
110
        ];
111
112
        return md5(serialize($data));
113
    }
114
115
    /**
116
     * @return array
117
     */
118
    public function getOptions() : array
119
    {
120
        return $this->options;
121
    }
122
123
    /**
124
     * {@inheritdoc}
125
     */
126
    public function buildGrid(GridBuilderInterface $builder, array $options)
127
    {
128
    }
129
130
    /**
131
     * @return array
132
     */
133
    public function getData() : array
134
    {
135
        $queryBuilder = $this->getQueryBuilder($this->objectManager, $this->getOptions());
136
137
        if ($this->getOption('sortField') !== null) {
138
            $field = $this->getOption('sortField');
139
            if ($this->has($field)) {
140
                $column = $this->get($field);
141
                $queryBuilder->orderBy($column->getField(), $this->getOption('sortDir'));
142
            }
143
        }
144
145
        if ($this->getOption('filter', false)) {
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string|null.

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...
146
            $this->addSearch($queryBuilder, $this->all());
0 ignored issues
show
Documentation introduced by
$queryBuilder is of type object<Doctrine\ORM\QueryBuilder>, but the function expects a object<Doctrine\DBAL\Query\QueryBuilder>.

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...
147
        }
148
149
        $pager = $this->getPager($queryBuilder);
150
151
        return [
152
            'totalCount' => $pager->getNbResults(),
153
            'data'  => $this->parseData($pager->getCurrentPageResults()),
154
            'success' => true,
155
        ];
156
    }
157
158
    /**
159
     * @param \Doctrine\DBAL\Query\QueryBuilder      $queryBuilder
160
     * @param ColumnInterface[] $columns
161
     */
162
    protected function addSearch($queryBuilder, $columns)
0 ignored issues
show
Unused Code introduced by
The parameter $columns is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
163
    {
164
        $filter = json_decode($this->getOption('filter'));
165
        $where = $queryBuilder->expr()->andX();
166
        $i = 0;
167
168
        foreach ($filter as $filterSearch) {
169
            if (!$this->has($filterSearch->property)) {
170
                continue;
171
            }
172
173
            $property = sprintf(':%s%s', $filterSearch->property, $i);
174
175
            $column = $this->get($filterSearch->property);
176
177
            switch ($filterSearch->operator) {
178 View Code Duplication
                case 'like':
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
179
                    $where->add($queryBuilder->expr()->like($column->getField(), $property));
180
                    $queryBuilder->setParameter($property, sprintf('%%%s%%',$filterSearch->value));
181
                    break;
182 View Code Duplication
                case 'gteq':
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
183
                    $where->add($queryBuilder->expr()->gte($column->getField(), $property));
184
                    $queryBuilder->setParameter($property, $filterSearch->value);
185
                    break;
186 View Code Duplication
                case 'lteq':
1 ignored issue
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
187
                    $where->add($queryBuilder->expr()->lte($column->getField(), $property));
188
                    $queryBuilder->setParameter($property, $filterSearch->value);
189
                    break;
190
191
            }
192
193
194
            $i++;
195
        }
196
197
        if (count($where->getParts()) > 0) {
198
            $queryBuilder->having($where);
199
        }
200
    }
201
202
    /**
203
     * @param array|\Traversable $rows
204
     * @return array
205
     */
206
    protected function parseData($rows)
207
    {
208
        $data = [];
209
        foreach ($rows as $row) {
210
            $rowData = [];
211
212
            foreach ($this->all() as $column) {
213
                /** @var ColumnInterface $column */
214
                $value = $column->getValue($row, $column->getName(), $this->findPrimary(), $this->accessor);
215
                $value = $column->render($value, $row, $this->getPrimaryValue($row), $this->twig);
216
217
                if ($column->getOption('translatable', false)) {
0 ignored issues
show
Documentation introduced by
false is of type boolean, but the function expects a string|null.

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...
218
                    $value = $this->translator->trans($value, [], $column->getOption('translation_domain'));
219
                }
220
221
                $rowData[$column->getName()] = $value;
222
            }
223
224
            $data[] = $rowData;
225
        }
226
227
        return $data;
228
    }
229
230
    /**
231
     * @param QueryBuilder $queryBuilder
232
     * @return Pagerfanta
233
     */
234
    public function getPager(QueryBuilder $queryBuilder)
235
    {
236
        $adapter = new DoctrineORMAdapter($queryBuilder, false);
237
        $pager = new Pagerfanta($adapter);
238
        $page = $this->getOption('page', 1);
239
        if ($page < 1) {
240
            $page = 1;
241
        }
242
243
        $pager->setCurrentPage($page)
244
              ->setMaxPerPage($this->getOption('limit', 10));
245
246
        return $pager;
247
    }
248
249
    /**
250
     * Get value of primary column
251
     *
252
     * @param mixed $object
253
     *
254
     * @return mixed
255
     */
256
    public function getPrimaryValue($object)
257
    {
258
        if ($this->primary === null) {
259
            $this->primary = $this->findPrimary();
260
        }
261
262
        /** special case when counting */
263
        if (is_array($object)) {
264
            $object = $object[0];
265
        }
266
267
        return $this->accessor->getValue($object, $this->primary);
268
    }
269
270
    /**
271
     * @return null|string
272
     */
273
    public function findPrimary()
274
    {
275
        foreach ($this->all() as $column) {
276
            if (true === $column->getOption('identifier')) {
277
                return $column->getName();
278
            }
279
        }
280
281
        return null;
282
    }
283
284
    /**
285
     * @param OptionsResolver $resolver
286
     */
287
    public function configureOptions(OptionsResolver $resolver)
288
    {
289
        $resolver->setDefaults([
290
            'template' => 'CwdFancyGridBundle:Grid:template.html.twig',
291
            'current' => 1,
292
            'filter' => null,
293
            'sortField' => null,
294
            'sortDir' => null,
295
            'data_route_options' => [],
296
            'page' => 1,
297
            'limit' => 20,
298
        ]);
299
300
        $resolver->setRequired([
301
            'template',
302
            'data_route',
303
        ]);
304
    }
305
306
    public function getColumnDefinition()
307
    {
308
        $columns = [];
309
        /** @var AbstractColumn $column */
310
        foreach ($this->children as $column) {
311
            $column->setTranslator($this->translator);
312
            $columns[] = $column->buildColumnOptions();
313
        }
314
315
        return $columns;
316
    }
317
318
    /**
319
     * @param string $name
320
     * @return bool
321
     */
322
    public function hasOption(string $name)
323
    {
324
        return array_key_exists($name, $this->options);
325
    }
326
327
    /**
328
     * @param string      $name
329
     * @param string|null $default
330
     * @return misc
331
     */
332
    public function getOption(string $name, $default = null)
333
    {
334
        return array_key_exists($name, $this->options) ? $this->options[$name] : $default;
335
    }
336
337
    /**
338
     * @param string $name
339
     * @return ColumnInterface
340
     */
341 View Code Duplication
    public function get(string $name) : ColumnInterface
1 ignored issue
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
342
    {
343
        if (isset($this->children[$name])) {
344
            return $this->children[$name];
345
        }
346
347
        throw new InvalidArgumentException(sprintf('The child with the name "%s" does not exist.', $name));
348
    }
349
350
    /**
351
     * @param string $name
352
     * @return $this
353
     */
354
    public function remove(string $name)
355
    {
356
        unset($this->children[$name]);
357
358
        return $this;
359
    }
360
361
    /**
362
     * @param string $name
363
     * @return bool
364
     */
365
    public function has(string $name)
366
    {
367
        return isset($this->children[$name]);
368
    }
369
370
    /**
371
     * @return \Cwd\FancyGridBundle\Column\ColumnInterface[]
372
     */
373
    public function all()
374
    {
375
        return $this->children;
376
    }
377
378
    /**
379
     * @param array<ColumnInterface> $children
380
     * @return $this
381
     */
382
    public function setChildren($children)
383
    {
384
        $this->children = $children;
385
386
        return $this;
387
    }
388
389
    /**
390
     *
391
     * @return \ArrayIterator
392
     */
393
    public function getIterator()
394
    {
395
        return new \ArrayIterator($this->all());
396
    }
397
}
398