AbstractDateFilter::filter()   F
last analyzed

Complexity

Conditions 31
Paths 2052

Size

Total Lines 105

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 105
rs 0
c 0
b 0
f 0
cc 31
nc 2052
nop 4

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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\DoctrineORMAdminBundle\Filter;
15
16
use Sonata\AdminBundle\Datagrid\ProxyQueryInterface;
17
use Sonata\AdminBundle\Form\Type\Filter\DateRangeType;
18
use Sonata\AdminBundle\Form\Type\Filter\DateTimeRangeType;
19
use Sonata\AdminBundle\Form\Type\Filter\DateTimeType;
20
use Sonata\AdminBundle\Form\Type\Filter\DateType;
21
use Sonata\AdminBundle\Form\Type\Operator\DateOperatorType;
22
use Sonata\AdminBundle\Form\Type\Operator\DateRangeOperatorType;
23
24
abstract class AbstractDateFilter extends Filter
25
{
26
    public const CHOICES = [
27
        DateOperatorType::TYPE_EQUAL => '=',
28
        DateOperatorType::TYPE_GREATER_EQUAL => '>=',
29
        DateOperatorType::TYPE_GREATER_THAN => '>',
30
        DateOperatorType::TYPE_LESS_EQUAL => '<=',
31
        DateOperatorType::TYPE_LESS_THAN => '<',
32
        DateOperatorType::TYPE_NULL => 'NULL',
33
        DateOperatorType::TYPE_NOT_NULL => 'NOT NULL',
34
    ];
35
36
    /**
37
     * Flag indicating that filter will have range.
38
     *
39
     * @var bool
40
     */
41
    protected $range = false;
42
43
    /**
44
     * Flag indicating that filter will filter by datetime instead by date.
45
     *
46
     * @var bool
47
     */
48
    protected $time = false;
49
50
    public function filter(ProxyQueryInterface $queryBuilder, $alias, $field, $data): void
51
    {
52
        // check data sanity
53
        if (!$data || !\is_array($data) || !\array_key_exists('value', $data)) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $data of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
54
            return;
55
        }
56
57
        if ($this->range) {
58
            // additional data check for ranged items
59
            if (!\array_key_exists('start', $data['value']) || !\array_key_exists('end', $data['value'])) {
60
                return;
61
            }
62
63
            if (!$data['value']['start'] && !$data['value']['end']) {
64
                return;
65
            }
66
67
            // date filter should filter records for the whole days
68
            if (false === $this->time) {
69
                if ($data['value']['start'] instanceof \DateTime) {
70
                    $data['value']['start']->setTime(0, 0, 0);
71
                }
72
                if ($data['value']['end'] instanceof \DateTime) {
73
                    $data['value']['end']->setTime(23, 59, 59);
74
                }
75
            }
76
77
            // transform types
78
            if ('timestamp' === $this->getOption('input_type')) {
79
                $data['value']['start'] = $data['value']['start'] instanceof \DateTime ? $data['value']['start']->getTimestamp() : 0;
80
                $data['value']['end'] = $data['value']['end'] instanceof \DateTime ? $data['value']['end']->getTimestamp() : 0;
81
            }
82
83
            // default type for range filter
84
            $data['type'] = !isset($data['type']) || !is_numeric($data['type']) ? DateRangeOperatorType::TYPE_BETWEEN : $data['type'];
85
86
            $startDateParameterName = $this->getNewParameterName($queryBuilder);
87
            $endDateParameterName = $this->getNewParameterName($queryBuilder);
88
89
            if (DateRangeOperatorType::TYPE_NOT_BETWEEN === $data['type']) {
90
                $this->applyWhere($queryBuilder, sprintf('%s.%s < :%s OR %s.%s > :%s', $alias, $field, $startDateParameterName, $alias, $field, $endDateParameterName));
91
            } else {
92
                if ($data['value']['start']) {
93
                    $this->applyWhere($queryBuilder, sprintf('%s.%s %s :%s', $alias, $field, '>=', $startDateParameterName));
94
                }
95
96
                if ($data['value']['end']) {
97
                    $this->applyWhere($queryBuilder, sprintf('%s.%s %s :%s', $alias, $field, '<=', $endDateParameterName));
98
                }
99
            }
100
101
            if ($data['value']['start']) {
102
                $queryBuilder->setParameter($startDateParameterName, $data['value']['start']);
103
            }
104
105
            if ($data['value']['end']) {
106
                $queryBuilder->setParameter($endDateParameterName, $data['value']['end']);
107
            }
108
        } else {
109
            if (!$data['value']) {
110
                return;
111
            }
112
113
            // default type for simple filter
114
            $data['type'] = !isset($data['type']) || !is_numeric($data['type']) ? DateOperatorType::TYPE_EQUAL : $data['type'];
115
116
            // just find an operator and apply query
117
            $operator = $this->getOperator($data['type']);
118
119
            // transform types
120
            if ('timestamp' === $this->getOption('input_type')) {
121
                $data['value'] = $data['value'] instanceof \DateTime ? $data['value']->getTimestamp() : 0;
122
            }
123
124
            // null / not null only check for col
125
            if (\in_array($operator, ['NULL', 'NOT NULL'], true)) {
126
                $this->applyWhere($queryBuilder, sprintf('%s.%s IS %s ', $alias, $field, $operator));
127
128
                return;
129
            }
130
131
            $parameterName = $this->getNewParameterName($queryBuilder);
132
133
            // date filter should filter records for the whole day
134
            if (false === $this->time && DateOperatorType::TYPE_EQUAL === $data['type']) {
135
                $this->applyWhere($queryBuilder, sprintf('%s.%s %s :%s', $alias, $field, '>=', $parameterName));
136
                $queryBuilder->setParameter($parameterName, $data['value']);
137
138
                $endDateParameterName = $this->getNewParameterName($queryBuilder);
139
                $this->applyWhere($queryBuilder, sprintf('%s.%s %s :%s', $alias, $field, '<', $endDateParameterName));
140
                if ('timestamp' === $this->getOption('input_type')) {
141
                    $endValue = strtotime('+1 day', $data['value']);
142
                } else {
143
                    $endValue = clone $data['value'];
144
                    $endValue->add(new \DateInterval('P1D'));
145
                }
146
                $queryBuilder->setParameter($endDateParameterName, $endValue);
147
148
                return;
149
            }
150
151
            $this->applyWhere($queryBuilder, sprintf('%s.%s %s :%s', $alias, $field, $operator, $parameterName));
152
            $queryBuilder->setParameter($parameterName, $data['value']);
153
        }
154
    }
155
156
    public function getDefaultOptions()
157
    {
158
        return [
159
            'input_type' => 'datetime',
160
        ];
161
    }
162
163
    public function getRenderSettings()
164
    {
165
        $name = DateType::class;
166
167
        if ($this->time && $this->range) {
168
            $name = DateTimeRangeType::class;
169
        } elseif ($this->time) {
170
            $name = DateTimeType::class;
171
        } elseif ($this->range) {
172
            $name = DateRangeType::class;
173
        }
174
175
        return [$name, [
176
            'field_type' => $this->getFieldType(),
177
            'field_options' => $this->getFieldOptions(),
178
            'label' => $this->getLabel(),
179
        ]];
180
    }
181
182
    /**
183
     * NEXT_MAJOR: Change the visibility for private.
184
     *
185
     * Resolves DateOperatorType:: constants to SQL operators.
186
     *
187
     * @param int $type
188
     *
189
     * @return string
190
     */
191
    protected function getOperator($type)
192
    {
193
        $type = (int) $type;
194
195
        return self::CHOICES[$type] ?? self::CHOICES[DateOperatorType::TYPE_EQUAL];
196
    }
197
}
198