Builder::notLike()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Atlance\HttpDoctrineOrmFilter\Query;
6
7
use Doctrine\DBAL\Connection;
8
use Doctrine\DBAL\Query\Expression\CompositeExpression;
9
use Doctrine\ORM\Query\Expr;
10
use Doctrine\ORM\QueryBuilder;
11
use Webmozart\Assert\Assert;
12
13
final class Builder
14
{
15
    /** @var string[] */
16
    public const SUPPORTED_EXPRESSIONS = [
17
        'eq',
18
        'neq',
19
        'gt',
20
        'gte',
21
        'in',
22
        'not_in',
23
        'is_null',
24
        'is_not_null',
25
        'ilike',
26
        'like',
27
        'not_like',
28
        'lt',
29
        'lte',
30
        'between',
31
        'order_by',
32
    ];
33
34 71
    public function __construct(private readonly QueryBuilder $qb)
35
    {
36 71
    }
37
38 71
    public function andWhere(Field $field): void
39
    {
40 71
        $this->{$field->getExprMethod()}($field);
41
    }
42
43 14
    private function andWhereAndX(Field $field): void
44
    {
45 14
        $this->andWhereComposite($field, CompositeExpression::TYPE_AND);
46
    }
47
48
    /**
49
     * @psalm-suppress MixedAssignment
50
     * @psalm-suppress MixedArgumentTypeCoercion
51
     */
52 32
    private function andWhereComposite(Field $field, string $type): void
53
    {
54 32
        Assert::inArray($type, [CompositeExpression::TYPE_AND, CompositeExpression::TYPE_OR]);
55 32
        $composite = CompositeExpression::TYPE_AND === $type ? $this->qb->expr()->andX() : $this->qb->expr()->orX();
56 32
        $parts = [];
57
        /**
58
         * @var int   $i
59
         * @var mixed $value
60
         */
61 32
        foreach ($field->getValues() as $i => $value) {
62
            /** @var string|Expr $expr */
63 32
            $expr = $this->qb->expr()->{$field->getExprMethod()}(
64 32
                $field->getPropertyPath(),
65 32
                $field->generateParameter($i)
66 32
            );
67
68 32
            $parts[] = $expr;
69 32
            if ($field->isLike()) {
70 14
                $this->qb->setParameter($field->generateParameter($i), "%{$value}%");
71
72 14
                continue;
73
            }
74
75 18
            $this->qb->setParameter($field->generateParameter($i), $value);
76
        }
77
78 32
        $this->qb->andWhere($composite->addMultiple($parts));
79
    }
80
81 18
    private function andWhereOrX(Field $field): void
82
    {
83 18
        $this->andWhereComposite($field, CompositeExpression::TYPE_OR);
84
    }
85
86 4
    private function between(Field $field): void
87
    {
88 4
        Assert::eq($field->countValues(), 2, 'Invalid format for between, expected "min|max"');
89 3
        [$min, $max] = $field->getValues();
90 3
        Assert::lessThan($min, $max, 'Invalid values for between, expected min < max');
91
92 3
        $from = $field->generateParameter('from');
93 3
        $to = $field->generateParameter('to');
94 3
        $this->qb->andWhere(sprintf('%s BETWEEN %s AND %s', $field->getPropertyPath(), $from, $to))
95 3
            ->setParameter($from, $min)
96 3
            ->setParameter($to, $max);
97
    }
98
99 11
    private function eq(Field $field): void
100
    {
101 11
        $this->andWhereOrX($field);
102
    }
103
104 3
    private function gt(Field $field): void
105
    {
106 3
        Assert::eq($field->countValues(), 1, 'expected single value');
107 2
        $this->qb->andWhere($this->qb->expr()->gt($field->getPropertyPath(), $field->generateParameter('gt')))
108 2
            ->setParameter($field->generateParameter('gt'), $field->getValues()[0]);
109
    }
110
111 3
    private function gte(Field $field): void
112
    {
113 3
        Assert::eq($field->countValues(), 1, 'expected single value');
114 2
        $this->qb->andWhere($this->qb->expr()->gte($field->getPropertyPath(), $field->generateParameter('gte')))
115 2
            ->setParameter($field->generateParameter('gte'), $field->getValues()[0]);
116
    }
117
118 7
    private function ilike(Field $field): void
119
    {
120 7
        $parts = [];
121 7
        $composite = $this->qb->expr()->orX();
122
        /**
123
         * @var int   $i
124
         * @var mixed $value
125
         */
126 7
        foreach ($field->getValues() as $i => $value) {
127 7
            $parts[] = $this->qb->expr()->like(
128 7
                sprintf('LOWER(%s)', $field->getPropertyPath()),
129 7
                sprintf('LOWER(%s)', $field->generateParameter($i))
130 7
            );
131
132 7
            $this->qb->setParameter($field->generateParameter($i), mb_strtolower("%{$value}%"));
133
        }
134
135 7
        $this->qb->andWhere($composite->addMultiple($parts));
136
    }
137
138 4
    private function in(Field $field): void
139
    {
140 4
        Assert::greaterThanEq(
141 4
            $field->countValues(),
142 4
            2,
143 4
            'expression "in" expected multiple value. Use "eq" for single value.'
144 4
        );
145
146 3
        $this->qb->andWhere($this->qb->expr()->in($field->getPropertyPath(), $field->generateParameter('in')))
147 3
            ->setParameter(
148 3
                $field->generateParameter('in'),
149 3
                $field->getValues(),
150 3
                \is_string($field->getValues()[0])
151 2
                    ? Connection::PARAM_STR_ARRAY
0 ignored issues
show
Deprecated Code introduced by
The constant Doctrine\DBAL\Connection::PARAM_STR_ARRAY has been deprecated: Use {@see ArrayParameterType::STRING} instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

151
                    ? /** @scrutinizer ignore-deprecated */ Connection::PARAM_STR_ARRAY

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
152 3
                    : Connection::PARAM_INT_ARRAY
0 ignored issues
show
Deprecated Code introduced by
The constant Doctrine\DBAL\Connection::PARAM_INT_ARRAY has been deprecated: Use {@see ArrayParameterType::INTEGER} instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

152
                    : /** @scrutinizer ignore-deprecated */ Connection::PARAM_INT_ARRAY

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
153 3
            );
154
    }
155
156 3
    private function isNotNull(Field $field): void
157
    {
158 3
        $this->qb->andWhere($this->qb->expr()->isNotNull($field->getPropertyPath()));
159
    }
160
161 3
    private function isNull(Field $field): void
162
    {
163 3
        $this->qb->andWhere($this->qb->expr()->isNull($field->getPropertyPath()));
164
    }
165
166 7
    private function like(Field $field): void
167
    {
168 7
        $this->andWhereOrX($field);
169
    }
170
171 4
    private function lt(Field $field): void
172
    {
173 4
        Assert::eq($field->countValues(), 1, 'expected single value');
174 3
        $this->qb->andWhere($this->qb->expr()->lt($field->getPropertyPath(), $field->generateParameter('lt')))
175 3
            ->setParameter($field->generateParameter('lt'), $field->getValues()[0]);
176
    }
177
178 3
    private function lte(Field $field): void
179
    {
180 3
        Assert::eq($field->countValues(), 1, 'expected single value');
181 2
        $this->qb->andWhere($this->qb->expr()->lte($field->getPropertyPath(), $field->generateParameter('lte')))
182 2
            ->setParameter($field->generateParameter('lte'), $field->getValues()[0]);
183
    }
184
185 7
    private function neq(Field $field): void
186
    {
187 7
        $this->andWhereAndX($field);
188
    }
189
190 4
    private function notIn(Field $field): void
191
    {
192 4
        Assert::greaterThanEq(
193 4
            $field->countValues(),
194 4
            2,
195 4
            'expression "not_in" expected multiple value. Use "eq" for single value.'
196 4
        );
197 3
        $this->qb->andWhere($this->qb->expr()->notIn($field->getPropertyPath(), $field->generateParameter('not_in')))
198 3
            ->setParameter(
199 3
                $field->generateParameter('not_in'),
200 3
                $field->getValues(),
201 3
                \is_string($field->getValues()[0])
202 2
                    ? Connection::PARAM_STR_ARRAY
0 ignored issues
show
Deprecated Code introduced by
The constant Doctrine\DBAL\Connection::PARAM_STR_ARRAY has been deprecated: Use {@see ArrayParameterType::STRING} instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

202
                    ? /** @scrutinizer ignore-deprecated */ Connection::PARAM_STR_ARRAY

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
203 3
                    : Connection::PARAM_INT_ARRAY
0 ignored issues
show
Deprecated Code introduced by
The constant Doctrine\DBAL\Connection::PARAM_INT_ARRAY has been deprecated: Use {@see ArrayParameterType::INTEGER} instead. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-deprecated  annotation

203
                    : /** @scrutinizer ignore-deprecated */ Connection::PARAM_INT_ARRAY

This class constant has been deprecated. The supplier of the class has supplied an explanatory message.

The explanatory message should give you some clue as to whether and when the constant will be removed from the class and what other constant to use instead.

Loading history...
204 3
            );
205
    }
206
207 7
    private function notLike(Field $field): void
208
    {
209 7
        $this->andWhereAndX($field);
210
    }
211
212 1
    private function orderBy(Field $field): void
213
    {
214 1
        Assert::eq($field->countValues(), 1, 'expected single value');
215 1
        $order = $field->getValues()[0];
216 1
        Assert::true(\is_string($order));
217 1
        $order = mb_strtolower($order);
218 1
        Assert::true('asc' === $order || 'desc' === $order);
219 1
        $this->qb->addOrderBy($field->getPropertyPath(), (string) $field->getValues()[0]);
220
    }
221
}
222