1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
declare(strict_types=1); |
4
|
|
|
|
5
|
|
|
namespace Doctrine\ORM\Query; |
6
|
|
|
|
7
|
|
|
use Doctrine\Common\Collections\ArrayCollection; |
8
|
|
|
use Doctrine\Common\Collections\Collection; |
9
|
|
|
use Doctrine\Common\Collections\Expr\Comparison; |
10
|
|
|
use Doctrine\Common\Collections\Expr\CompositeExpression; |
11
|
|
|
use Doctrine\Common\Collections\Expr\ExpressionVisitor; |
12
|
|
|
use Doctrine\Common\Collections\Expr\Value; |
13
|
|
|
use function count; |
14
|
|
|
use function str_replace; |
15
|
|
|
use function strpos; |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* Converts Collection expressions to Query expressions. |
19
|
|
|
*/ |
20
|
|
|
class QueryExpressionVisitor extends ExpressionVisitor |
21
|
|
|
{ |
22
|
|
|
/** |
23
|
|
|
* @var string[] |
24
|
|
|
*/ |
25
|
|
|
private static $operatorMap = [ |
26
|
|
|
Comparison::GT => Expr\Comparison::GT, |
27
|
|
|
Comparison::GTE => Expr\Comparison::GTE, |
28
|
|
|
Comparison::LT => Expr\Comparison::LT, |
29
|
|
|
Comparison::LTE => Expr\Comparison::LTE, |
30
|
|
|
]; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* @var string[] |
34
|
|
|
*/ |
35
|
|
|
private $queryAliases; |
36
|
|
|
|
37
|
|
|
/** |
38
|
|
|
* @var Expr |
39
|
|
|
*/ |
40
|
|
|
private $expr; |
41
|
|
|
|
42
|
|
|
/** |
43
|
|
|
* @var mixed[] |
44
|
|
|
*/ |
45
|
|
|
private $parameters = []; |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* @param string[] $queryAliases |
49
|
|
|
*/ |
50
|
34 |
|
public function __construct($queryAliases) |
51
|
|
|
{ |
52
|
34 |
|
$this->queryAliases = $queryAliases; |
53
|
34 |
|
$this->expr = new Expr(); |
54
|
34 |
|
} |
55
|
|
|
|
56
|
|
|
/** |
57
|
|
|
* Gets bound parameters. |
58
|
|
|
* Filled after {@link dispach()}. |
59
|
|
|
* |
60
|
|
|
* @return Collection|mixed[] |
61
|
|
|
*/ |
62
|
24 |
|
public function getParameters() |
63
|
|
|
{ |
64
|
24 |
|
return new ArrayCollection($this->parameters); |
65
|
|
|
} |
66
|
|
|
|
67
|
|
|
/** |
68
|
|
|
* Clears parameters. |
69
|
|
|
*/ |
70
|
1 |
|
public function clearParameters() |
71
|
|
|
{ |
72
|
1 |
|
$this->parameters = []; |
73
|
1 |
|
} |
74
|
|
|
|
75
|
|
|
/** |
76
|
|
|
* Converts Criteria expression to Query one based on static map. |
77
|
|
|
* |
78
|
|
|
* @param string $criteriaOperator |
79
|
|
|
* |
80
|
|
|
* @return string|null |
81
|
|
|
*/ |
82
|
11 |
|
private static function convertComparisonOperator($criteriaOperator) |
83
|
|
|
{ |
84
|
11 |
|
return self::$operatorMap[$criteriaOperator] ?? null; |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
/** |
88
|
|
|
* {@inheritDoc} |
89
|
|
|
*/ |
90
|
10 |
|
public function walkCompositeExpression(CompositeExpression $expr) |
91
|
|
|
{ |
92
|
10 |
|
$expressionList = []; |
93
|
|
|
|
94
|
10 |
|
foreach ($expr->getExpressionList() as $child) { |
95
|
10 |
|
$expressionList[] = $this->dispatch($child); |
96
|
|
|
} |
97
|
|
|
|
98
|
10 |
|
switch ($expr->getType()) { |
99
|
|
|
case CompositeExpression::TYPE_AND: |
|
|
|
|
100
|
9 |
|
return new Expr\Andx($expressionList); |
101
|
|
|
|
102
|
|
|
case CompositeExpression::TYPE_OR: |
|
|
|
|
103
|
1 |
|
return new Expr\Orx($expressionList); |
104
|
|
|
|
105
|
|
|
default: |
106
|
|
|
throw new \RuntimeException('Unknown composite ' . $expr->getType()); |
107
|
|
|
} |
108
|
|
|
} |
109
|
|
|
|
110
|
|
|
/** |
111
|
|
|
* {@inheritDoc} |
112
|
|
|
*/ |
113
|
28 |
|
public function walkComparison(Comparison $comparison) |
114
|
|
|
{ |
115
|
28 |
|
if (! isset($this->queryAliases[0])) { |
116
|
|
|
throw new QueryException('No aliases are set before invoking walkComparison().'); |
117
|
|
|
} |
118
|
|
|
|
119
|
28 |
|
$field = $this->queryAliases[0] . '.' . $comparison->getField(); |
120
|
|
|
|
121
|
28 |
|
foreach ($this->queryAliases as $alias) { |
122
|
28 |
|
if (strpos($comparison->getField() . '.', $alias . '.') === 0) { |
123
|
5 |
|
$field = $comparison->getField(); |
124
|
28 |
|
break; |
125
|
|
|
} |
126
|
|
|
} |
127
|
|
|
|
128
|
28 |
|
$parameterName = str_replace('.', '_', $comparison->getField()); |
129
|
28 |
|
$parameterCount = count($this->parameters); |
130
|
|
|
|
131
|
28 |
|
foreach ($this->parameters as $parameter) { |
132
|
10 |
|
if ($parameter->getName() === $parameterName) { |
133
|
4 |
|
$parameterName .= '_' . $parameterCount; |
134
|
10 |
|
break; |
135
|
|
|
} |
136
|
|
|
} |
137
|
|
|
|
138
|
28 |
|
$parameter = new Parameter($parameterName, $this->walkValue($comparison->getValue())); |
139
|
28 |
|
$placeholder = ':' . $parameterName; |
140
|
|
|
|
141
|
28 |
|
switch ($comparison->getOperator()) { |
142
|
|
|
case Comparison::IN: |
143
|
1 |
|
$this->parameters[] = $parameter; |
144
|
|
|
|
145
|
1 |
|
return $this->expr->in($field, $placeholder); |
146
|
|
|
case Comparison::NIN: |
147
|
1 |
|
$this->parameters[] = $parameter; |
148
|
|
|
|
149
|
1 |
|
return $this->expr->notIn($field, $placeholder); |
150
|
|
|
case Comparison::EQ: |
151
|
|
|
case Comparison::IS: |
152
|
17 |
|
if ($this->walkValue($comparison->getValue()) === null) { |
153
|
2 |
|
return $this->expr->isNull($field); |
154
|
|
|
} |
155
|
15 |
|
$this->parameters[] = $parameter; |
156
|
|
|
|
157
|
15 |
|
return $this->expr->eq($field, $placeholder); |
158
|
|
|
case Comparison::NEQ: |
159
|
2 |
|
if ($this->walkValue($comparison->getValue()) === null) { |
160
|
1 |
|
return $this->expr->isNotNull($field); |
161
|
|
|
} |
162
|
1 |
|
$this->parameters[] = $parameter; |
163
|
|
|
|
164
|
1 |
|
return $this->expr->neq($field, $placeholder); |
165
|
|
|
case Comparison::CONTAINS: |
166
|
1 |
|
$parameter->setValue('%' . $parameter->getValue() . '%', $parameter->getType()); |
167
|
1 |
|
$this->parameters[] = $parameter; |
168
|
|
|
|
169
|
1 |
|
return $this->expr->like($field, $placeholder); |
170
|
|
|
case Comparison::STARTS_WITH: |
171
|
1 |
|
$parameter->setValue($parameter->getValue() . '%', $parameter->getType()); |
172
|
1 |
|
$this->parameters[] = $parameter; |
173
|
|
|
|
174
|
1 |
|
return $this->expr->like($field, $placeholder); |
175
|
|
|
case Comparison::ENDS_WITH: |
176
|
1 |
|
$parameter->setValue('%' . $parameter->getValue(), $parameter->getType()); |
177
|
1 |
|
$this->parameters[] = $parameter; |
178
|
|
|
|
179
|
1 |
|
return $this->expr->like($field, $placeholder); |
180
|
|
|
default: |
181
|
11 |
|
$operator = self::convertComparisonOperator($comparison->getOperator()); |
182
|
11 |
|
if ($operator) { |
183
|
11 |
|
$this->parameters[] = $parameter; |
184
|
|
|
|
185
|
11 |
|
return new Expr\Comparison( |
186
|
11 |
|
$field, |
187
|
11 |
|
$operator, |
188
|
11 |
|
$placeholder |
189
|
|
|
); |
190
|
|
|
} |
191
|
|
|
|
192
|
|
|
throw new \RuntimeException('Unknown comparison operator: ' . $comparison->getOperator()); |
193
|
|
|
} |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
/** |
197
|
|
|
* {@inheritDoc} |
198
|
|
|
*/ |
199
|
29 |
|
public function walkValue(Value $value) |
200
|
|
|
{ |
201
|
29 |
|
return $value->getValue(); |
202
|
|
|
} |
203
|
|
|
} |
204
|
|
|
|
As per the PSR-2 coding standard, case statements should not be wrapped in curly braces. There is no need for braces, since each case is terminated by the next
break
.There is also the option to use a semicolon instead of a colon, this is discouraged because many programmers do not even know it works and the colon is universal between programming languages.
To learn more about the PSR-2 coding standard, please refer to the PHP-Fig.