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 |
|
|
|
|
152
|
3 |
|
: Connection::PARAM_INT_ARRAY |
|
|
|
|
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 |
|
|
|
|
203
|
3 |
|
: Connection::PARAM_INT_ARRAY |
|
|
|
|
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
|
|
|
|
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.