Builder::notIn()   A
last analyzed

Complexity

Conditions 2
Paths 1

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 13
CRAP Score 2

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 2
eloc 10
c 1
b 0
f 0
nc 1
nop 1
dl 0
loc 15
ccs 13
cts 13
cp 1
crap 2
rs 9.9332
1
<?php
2
3
declare(strict_types=1);
4
5
namespace Atlance\HttpDbalFilter\Query;
6
7
use Atlance\HttpDbalFilter\Query\Expression\Condition;
8
use Doctrine\DBAL\Connection;
9
use Doctrine\DBAL\Query\Expression\CompositeExpression;
10
use Doctrine\DBAL\Query\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
        'ilike',
22
        'between',
23
        'in',
24
        'not_in',
25
        'is_null',
26
        'is_not_null',
27
        'like',
28
        'not_like',
29
        'lt',
30
        'lte',
31
        'order_by',
32
    ];
33
34 71
    public function __construct(private readonly QueryBuilder $qb)
35
    {
36 71
    }
37
38 71
    public function andWhere(Condition $condition): void
39
    {
40 71
        $this->{$condition->getExprMethod()}($condition);
41
    }
42
43 14
    private function andWhereAndX(Condition $condition): void
44
    {
45 14
        $this->andWhereComposite($condition, CompositeExpression::TYPE_AND);
46
    }
47
48
    /**
49
     * @psalm-suppress MixedAssignment
50
     * @psalm-suppress MixedArgumentTypeCoercion
51
     */
52 32
    private function andWhereComposite(Condition $condition, string $type): void
53
    {
54 32
        Assert::inArray($type, [CompositeExpression::TYPE_AND, CompositeExpression::TYPE_OR]);
55 32
        $parts = [];
56
57 32
        foreach ($condition->getValues() as $i => $value) {
58
            /** @var string $sql */
59 32
            $sql = $this->qb->expr()->{$condition->getExprMethod()}(
60 32
                $condition->getPropertyPath(),
61 32
                $condition->generateParameter($i)
62 32
            );
63
64 32
            $parts[] = $sql;
65 32
            if ($condition->isLike()) {
66 14
                $this->qb->setParameter($condition->generateParameter($i), sprintf('%%%s%%', (string) $value));
67
68 14
                continue;
69
            }
70
71 18
            $this->qb->setParameter($condition->generateParameter($i), $value);
72
        }
73
74 32
        $this->qb->andWhere(new CompositeExpression($type, $parts));
75
    }
76
77 18
    private function andWhereOrX(Condition $condition): void
78
    {
79 18
        $this->andWhereComposite($condition, CompositeExpression::TYPE_OR);
80
    }
81
82 4
    private function between(Condition $condition): void
83
    {
84 4
        Assert::eq($condition->countValues(), 2, 'Invalid format for between, expected "min|max"');
85 3
        [$min, $max] = $condition->getValues();
86 3
        Assert::lessThan($min, $max, 'Invalid values for between, expected min < max');
87
88 3
        $from = $condition->generateParameter('from');
89 3
        $to = $condition->generateParameter('to');
90 3
        $this->qb->andWhere(sprintf('%s BETWEEN %s AND %s', $condition->getPropertyPath(), $from, $to))
91 3
            ->setParameter($from, $min)
92 3
            ->setParameter($to, $max);
93
    }
94
95 11
    private function eq(Condition $condition): void
96
    {
97 11
        $this->andWhereOrX($condition);
98
    }
99
100 3
    private function gt(Condition $condition): void
101
    {
102 3
        Assert::eq($condition->countValues(), 1, 'expected single value');
103 2
        $this->qb->andWhere($this->qb->expr()->gt($condition->getPropertyPath(), $condition->generateParameter('gt')))
104 2
            ->setParameter($condition->generateParameter('gt'), $condition->getValues()[0]);
105
    }
106
107 3
    private function gte(Condition $condition): void
108
    {
109 3
        Assert::eq($condition->countValues(), 1, 'expected single value');
110 2
        $this->qb->andWhere($this->qb->expr()->gte($condition->getPropertyPath(), $condition->generateParameter('gte')))
111 2
            ->setParameter($condition->generateParameter('gte'), $condition->getValues()[0]);
112
    }
113
114 7
    private function ilike(Condition $condition): void
115
    {
116 7
        $parts = [];
117
118 7
        foreach ($condition->getValues() as $i => $value) {
119 7
            $parts[] = $this->qb->expr()->like(
120 7
                sprintf('LOWER(%s)', $condition->getPropertyPath()),
121 7
                sprintf('LOWER(%s)', $condition->generateParameter($i))
122 7
            );
123
124 7
            $this->qb->setParameter(
125 7
                $condition->generateParameter($i),
126 7
                mb_strtolower(sprintf('%%%s%%', (string) $value))
127 7
            );
128
        }
129
130 7
        $this->qb->andWhere(new CompositeExpression(CompositeExpression::TYPE_OR, $parts));
131
    }
132
133 4
    private function in(Condition $condition): void
134
    {
135 4
        Assert::greaterThanEq(
136 4
            $condition->countValues(),
137 4
            2,
138 4
            'expression "in" expected multiple value. Use "eq" for single value.'
139 4
        );
140
141 3
        $this->qb->andWhere($this->qb->expr()->in($condition->getPropertyPath(), $condition->generateParameter('in')))
142 3
            ->setParameter(
143 3
                $condition->generateParameter('in'),
144 3
                $condition->getValues(),
145 3
                \is_string($condition->getValues()[0])
146 2
                    ? Connection::PARAM_STR_ARRAY
147 3
                    : Connection::PARAM_INT_ARRAY
148 3
            );
149
    }
150
151 3
    private function isNotNull(Condition $condition): void
152
    {
153 3
        $this->qb->andWhere($this->qb->expr()->isNotNull($condition->getPropertyPath()));
154
    }
155
156 3
    private function isNull(Condition $condition): void
157
    {
158 3
        $this->qb->andWhere($this->qb->expr()->isNull($condition->getPropertyPath()));
159
    }
160
161 7
    private function like(Condition $condition): void
162
    {
163 7
        $this->andWhereOrX($condition);
164
    }
165
166 4
    private function lt(Condition $condition): void
167
    {
168 4
        Assert::eq($condition->countValues(), 1, 'expected single value');
169 3
        $this->qb->andWhere($this->qb->expr()->lt($condition->getPropertyPath(), $condition->generateParameter('lt')))
170 3
            ->setParameter($condition->generateParameter('lt'), $condition->getValues()[0]);
171
    }
172
173 3
    private function lte(Condition $condition): void
174
    {
175 3
        Assert::eq($condition->countValues(), 1, 'expected single value');
176 2
        $this->qb->andWhere($this->qb->expr()->lte($condition->getPropertyPath(), $condition->generateParameter('lte')))
177 2
            ->setParameter($condition->generateParameter('lte'), $condition->getValues()[0]);
178
    }
179
180 7
    private function neq(Condition $condition): void
181
    {
182 7
        $this->andWhereAndX($condition);
183
    }
184
185 4
    private function notIn(Condition $condition): void
186
    {
187 4
        Assert::greaterThanEq(
188 4
            $condition->countValues(),
189 4
            2,
190 4
            'expression "not_in" expected multiple value. Use "eq" for single value.'
191 4
        );
192
193 3
        $this->qb->andWhere($this->qb->expr()->notIn($condition->getPropertyPath(), $condition->generateParameter('not_in')))
194 3
            ->setParameter(
195 3
                $condition->generateParameter('not_in'),
196 3
                $condition->getValues(),
197 3
                \is_string($condition->getValues()[0])
198 2
                    ? Connection::PARAM_STR_ARRAY
199 3
                    : Connection::PARAM_INT_ARRAY
200 3
            );
201
    }
202
203 7
    private function notLike(Condition $condition): void
204
    {
205 7
        $this->andWhereAndX($condition);
206
    }
207
208 1
    private function orderBy(Condition $condition): void
209
    {
210 1
        Assert::eq($condition->countValues(), 1, 'expected single value');
211 1
        $order = $condition->getValues()[0];
212 1
        Assert::true(\is_string($order));
213 1
        $order = mb_strtolower($order);
214 1
        Assert::true('asc' === $order || 'desc' === $order);
215 1
        $this->qb->addOrderBy($condition->getPropertyPath(), (string) $condition->getValues()[0]);
216
    }
217
}
218