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