1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* This file is part of the Kdyby (http://www.kdyby.org) |
5
|
|
|
* |
6
|
|
|
* Copyright (c) 2008 Filip Procházka ([email protected]) |
7
|
|
|
* |
8
|
|
|
* For the full copyright and license information, please view the file license.txt that was distributed with this source code. |
9
|
|
|
*/ |
10
|
|
|
|
11
|
|
|
namespace Kdyby\Doctrine; |
12
|
|
|
|
13
|
|
|
use Doctrine; |
14
|
|
|
use Kdyby; |
15
|
|
|
use Nette; |
16
|
|
|
|
17
|
|
|
|
18
|
|
|
|
19
|
|
|
/** |
20
|
|
|
* @author Filip Procházka <[email protected]> |
21
|
|
|
* |
22
|
|
|
* @method NativeQueryBuilder setParameter($key, $value, $type = null) |
23
|
|
|
* @method NativeQueryBuilder setParameters(array $params, array $types = []) |
24
|
|
|
* @method NativeQueryBuilder setFirstResult($firstResult) |
25
|
|
|
* @method NativeQueryBuilder setMaxResults($maxResults) |
26
|
|
|
* @method NativeQueryBuilder select($select = NULL) |
27
|
|
|
* @method NativeQueryBuilder addSelect($select = NULL) |
28
|
|
|
* @method NativeQueryBuilder delete($delete = null, $alias = null) |
29
|
|
|
* @method NativeQueryBuilder update($update = null, $alias = null) |
30
|
|
|
* @method NativeQueryBuilder groupBy($groupBy) |
31
|
|
|
* @method NativeQueryBuilder addGroupBy($groupBy) |
32
|
|
|
* @method NativeQueryBuilder having($having) |
33
|
|
|
* @method NativeQueryBuilder andHaving($having) |
34
|
|
|
* @method NativeQueryBuilder orHaving($having) |
35
|
|
|
* @method NativeQueryBuilder orderBy($sort, $order = null) |
36
|
|
|
* @method NativeQueryBuilder addOrderBy($sort, $order = null) |
37
|
|
|
*/ |
38
|
|
|
class NativeQueryBuilder extends Doctrine\DBAL\Query\QueryBuilder |
39
|
|
|
{ |
40
|
|
|
|
41
|
|
|
use \Kdyby\StrictObjects\Scream; |
42
|
|
|
|
43
|
|
|
/** |
44
|
|
|
* @var Mapping\ResultSetMappingBuilder |
45
|
|
|
*/ |
46
|
|
|
private $rsm; |
47
|
|
|
|
48
|
|
|
/** |
49
|
|
|
* @var Doctrine\ORM\EntityManager |
50
|
|
|
*/ |
51
|
|
|
private $em; |
52
|
|
|
|
53
|
|
|
/** |
54
|
|
|
* @var int |
55
|
|
|
*/ |
56
|
|
|
private $defaultRenameMode = Doctrine\ORM\Query\ResultSetMappingBuilder::COLUMN_RENAMING_INCREMENT; |
57
|
|
|
|
58
|
|
|
|
59
|
|
|
|
60
|
|
|
public function __construct(Doctrine\ORM\EntityManager $em) |
|
|
|
|
61
|
|
|
{ |
62
|
|
|
parent::__construct($em->getConnection()); |
63
|
|
|
$this->em = $em; |
64
|
|
|
} |
65
|
|
|
|
66
|
|
|
|
67
|
|
|
|
68
|
|
|
/** |
69
|
|
|
* @return NativeQueryWrapper |
70
|
|
|
*/ |
71
|
|
|
public function getQuery() |
72
|
|
|
{ |
73
|
|
|
$query = new Doctrine\ORM\NativeQuery($this->em); |
74
|
|
|
$query->setResultSetMapping($this->getResultSetMapper()); |
75
|
|
|
$query->setParameters($this->getParameters()); |
76
|
|
|
|
77
|
|
|
$wrapped = new NativeQueryWrapper($query); |
78
|
|
|
$wrapped->setFirstResult($this->getFirstResult()); |
79
|
|
|
$wrapped->setMaxResults($this->getMaxResults()); |
80
|
|
|
|
81
|
|
|
$hasSelect = (bool)$this->getQueryPart('select'); |
82
|
|
|
if (!$hasSelect && $this->getType() === self::SELECT) { |
83
|
|
|
$select = $this->getResultSetMapper()->generateSelectClause(); |
84
|
|
|
$this->select($select ?: '*'); |
85
|
|
|
} |
86
|
|
|
|
87
|
|
|
$query->setSQL($this->getSQL()); |
88
|
|
|
|
89
|
|
|
$this->setFirstResult($wrapped->getFirstResult()); |
90
|
|
|
$this->setMaxResults($wrapped->getMaxResults()); |
91
|
|
|
|
92
|
|
|
if (!$hasSelect && $this->getType() === self::SELECT) { |
93
|
|
|
$this->resetQueryPart('select'); |
94
|
|
|
} |
95
|
|
|
|
96
|
|
|
$rsm = $this->getResultSetMapper(); |
97
|
|
|
if (empty($rsm->fieldMappings) && empty($rsm->scalarMappings)) { |
98
|
|
|
throw new InvalidStateException("No field or columns mapping found, please configure the ResultSetMapper and some fields."); |
99
|
|
|
} |
100
|
|
|
|
101
|
|
|
return $wrapped; |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
|
105
|
|
|
|
106
|
|
|
/** |
107
|
|
|
* @param int $defaultRenameMode |
108
|
|
|
* @return NativeQueryBuilder |
109
|
|
|
*/ |
110
|
|
|
public function setDefaultRenameMode($defaultRenameMode) |
111
|
|
|
{ |
112
|
|
|
if ($this->rsm !== NULL) { |
113
|
|
|
throw new InvalidStateException("It's too late for changing rename mode for ResultSetMappingBuilder, it has already been created. Call this method earlier."); |
114
|
|
|
} |
115
|
|
|
|
116
|
|
|
$this->defaultRenameMode = $defaultRenameMode; |
117
|
|
|
return $this; |
118
|
|
|
} |
119
|
|
|
|
120
|
|
|
|
121
|
|
|
|
122
|
|
|
public function getResultSetMapper() |
123
|
|
|
{ |
124
|
|
|
if ($this->rsm === NULL) { |
125
|
|
|
$this->rsm = new Mapping\ResultSetMappingBuilder($this->em, $this->defaultRenameMode); |
126
|
|
|
} |
127
|
|
|
|
128
|
|
|
return $this->rsm; |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
|
132
|
|
|
|
133
|
|
|
/** |
134
|
|
|
* @param string $tableAlias |
135
|
|
|
* @param string|array $columns |
136
|
|
|
* @return NativeQueryBuilder |
137
|
|
|
*/ |
138
|
|
|
public function addColumn($tableAlias, $columns) |
139
|
|
|
{ |
140
|
|
|
$rsm = $this->getResultSetMapper(); |
141
|
|
|
|
142
|
|
|
$args = func_get_args(); |
143
|
|
|
array_shift($args); // shit tableAlias |
144
|
|
|
|
145
|
|
|
$class = $this->em->getClassMetadata($rsm->aliasMap[$tableAlias]); |
146
|
|
|
|
147
|
|
|
foreach (is_array($columns) ? $columns : $args as $column) { |
148
|
|
|
try { |
149
|
|
|
$field = $class->getFieldForColumn($column); |
150
|
|
|
if ($class->hasField($field)) { |
151
|
|
|
$type = $class->getTypeOfField($field); |
152
|
|
|
|
153
|
|
|
} else { |
154
|
|
|
$type = $class->hasAssociation($field) ? 'integer' : 'string'; |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
} catch (Doctrine\ORM\Mapping\MappingException $e) { |
158
|
|
|
$type = 'string'; |
159
|
|
|
|
160
|
|
|
if ($class->discriminatorColumn['fieldName'] === $column) { |
161
|
|
|
$type = $class->discriminatorColumn['type']; |
162
|
|
|
} |
163
|
|
|
} |
164
|
|
|
|
165
|
|
|
$this->addSelect("{$tableAlias}.{$column} as {$tableAlias}_{$column}"); |
166
|
|
|
$rsm->addScalarResult("{$tableAlias}_{$column}", "{$tableAlias}_{$column}", $type); |
167
|
|
|
} |
168
|
|
|
|
169
|
|
|
return $this; |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
|
173
|
|
|
|
174
|
|
|
public function addSelect($select = NULL) |
175
|
|
|
{ |
176
|
|
|
$selects = is_array($select) ? $select : func_get_args(); |
177
|
|
|
foreach ($selects as &$arg) { |
178
|
|
|
if ($arg instanceof Doctrine\DBAL\Query\QueryBuilder) { |
179
|
|
|
$arg = '(' . $arg->getSQL() . ')'; |
180
|
|
|
} |
181
|
|
|
} |
182
|
|
|
|
183
|
|
|
return parent::addSelect($selects); |
184
|
|
|
} |
185
|
|
|
|
186
|
|
|
|
187
|
|
|
|
188
|
|
|
/** |
189
|
|
|
* {@inheritdoc} |
190
|
|
|
*/ |
191
|
|
|
public function from($from, $alias = NULL) |
192
|
|
|
{ |
193
|
|
|
return parent::from($this->addTableResultMapping($from, $alias), $alias); |
194
|
|
|
} |
195
|
|
|
|
196
|
|
|
|
197
|
|
|
|
198
|
|
|
/** |
199
|
|
|
* {@inheritdoc} |
200
|
|
|
* @return NativeQueryBuilder |
201
|
|
|
*/ |
202
|
|
|
public function join($fromAlias, $join, $alias, $condition = null) |
203
|
|
|
{ |
204
|
|
|
return call_user_func_array([$this, 'innerJoin'], func_get_args()); |
205
|
|
|
} |
206
|
|
|
|
207
|
|
|
|
208
|
|
|
|
209
|
|
|
/** |
210
|
|
|
* {@inheritdoc} |
211
|
|
|
* @return NativeQueryBuilder |
212
|
|
|
*/ |
213
|
|
|
public function innerJoin($fromAlias, $join, $alias, $condition = null) |
214
|
|
|
{ |
215
|
|
|
if ($condition !== NULL) { |
216
|
|
|
list($condition) = array_values(Helpers::separateParameters($this, array_slice(func_get_args(), 3))); |
217
|
|
|
} |
218
|
|
|
|
219
|
|
|
return parent::innerJoin($fromAlias, $this->addTableResultMapping($join, $alias, $fromAlias), $alias, $condition); |
220
|
|
|
} |
221
|
|
|
|
222
|
|
|
|
223
|
|
|
|
224
|
|
|
/** |
225
|
|
|
* {@inheritdoc} |
226
|
|
|
* @return NativeQueryBuilder |
227
|
|
|
*/ |
228
|
|
|
public function leftJoin($fromAlias, $join, $alias, $condition = null) |
229
|
|
|
{ |
230
|
|
|
if ($condition !== NULL) { |
231
|
|
|
list($condition) = array_values(Helpers::separateParameters($this, array_slice(func_get_args(), 3))); |
232
|
|
|
} |
233
|
|
|
|
234
|
|
|
return parent::leftJoin($fromAlias, $this->addTableResultMapping($join, $alias, $fromAlias), $alias, $condition); |
235
|
|
|
} |
236
|
|
|
|
237
|
|
|
|
238
|
|
|
|
239
|
|
|
/** |
240
|
|
|
* {@inheritdoc} |
241
|
|
|
* @return NativeQueryBuilder |
242
|
|
|
*/ |
243
|
|
|
public function rightJoin($fromAlias, $join, $alias, $condition = null) |
244
|
|
|
{ |
245
|
|
|
if ($condition !== NULL) { |
246
|
|
|
list($condition) = array_values(Helpers::separateParameters($this, array_slice(func_get_args(), 3))); |
247
|
|
|
} |
248
|
|
|
|
249
|
|
|
return parent::leftJoin($fromAlias, $this->addTableResultMapping($join, $alias, $fromAlias), $alias, $condition); |
|
|
|
|
250
|
|
|
} |
251
|
|
|
|
252
|
|
|
|
253
|
|
|
|
254
|
|
|
/** |
255
|
|
|
* @param string $table |
256
|
|
|
* @param string|null $alias |
257
|
|
|
* @param string $joinedFrom |
258
|
|
|
* @throws \Doctrine\ORM\Mapping\MappingException |
259
|
|
|
* @return string |
260
|
|
|
*/ |
261
|
|
|
protected function addTableResultMapping($table, $alias, $joinedFrom = NULL) |
262
|
|
|
{ |
263
|
|
|
$rsm = $this->getResultSetMapper(); |
264
|
|
|
$class = $relation = NULL; |
265
|
|
|
|
266
|
|
|
if (substr_count($table, '\\')) { |
267
|
|
|
$class = $this->em->getClassMetadata($table); |
268
|
|
|
$table = $class->getTableName(); |
269
|
|
|
|
270
|
|
|
} elseif (isset($rsm->aliasMap[$joinedFrom])) { |
271
|
|
|
$fromClass = $this->em->getClassMetadata($rsm->aliasMap[$joinedFrom]); |
272
|
|
|
|
273
|
|
|
foreach (array_merge([$fromClass->getName()], $fromClass->subClasses) as $fromClass) { |
274
|
|
|
$fromClass = $this->em->getClassMetadata($fromClass); |
275
|
|
|
|
276
|
|
|
if ($fromClass->hasAssociation($table)) { |
277
|
|
|
$class = $this->em->getClassMetadata($fromClass->getAssociationTargetClass($table)); |
278
|
|
|
$relation = $fromClass->getAssociationMapping($table); |
279
|
|
|
$table = $class->getTableName(); |
280
|
|
|
break; |
281
|
|
|
|
282
|
|
|
} else { |
283
|
|
|
foreach ($fromClass->getAssociationMappings() as $mapping) { |
284
|
|
|
$targetClass = $this->em->getClassMetadata($mapping['targetEntity']); |
285
|
|
|
if ($targetClass->getTableName() === $table) { |
286
|
|
|
$class = $targetClass; |
287
|
|
|
$relation = $mapping; |
288
|
|
|
$table = $class->getTableName(); |
289
|
|
|
break 2; |
290
|
|
|
} |
291
|
|
|
} |
292
|
|
|
} |
293
|
|
|
} |
294
|
|
|
|
295
|
|
|
} else { |
296
|
|
|
/** @var Kdyby\Doctrine\Mapping\ClassMetadata $class */ |
297
|
|
|
foreach ($this->em->getMetadataFactory()->getAllMetadata() as $class) { |
298
|
|
|
if ($class->getTableName() === $table) { |
299
|
|
|
break; |
300
|
|
|
} |
301
|
|
|
} |
302
|
|
|
} |
303
|
|
|
|
304
|
|
|
if (!$class instanceof Doctrine\ORM\Mapping\ClassMetadata || $class->getTableName() !== $table) { |
305
|
|
|
return $table; |
306
|
|
|
} |
307
|
|
|
|
308
|
|
|
if ($alias === NULL && ($joinedFrom === NULL || $relation !== NULL)) { |
309
|
|
|
throw new InvalidArgumentException('Parameter alias is required'); |
310
|
|
|
} |
311
|
|
|
|
312
|
|
|
if ($joinedFrom === NULL) { |
313
|
|
|
$rsm->addEntityResult($class->getName(), $alias); |
314
|
|
|
|
315
|
|
|
} elseif ($relation !== NULL) { |
316
|
|
|
$rsm->addJoinedEntityResult($class->getName(), $alias, $joinedFrom, $relation); |
317
|
|
|
} |
318
|
|
|
|
319
|
|
|
return $class->getTableName(); |
320
|
|
|
} |
321
|
|
|
|
322
|
|
|
|
323
|
|
|
|
324
|
|
|
/** |
325
|
|
|
* {@inheritdoc} |
326
|
|
|
* @return NativeQueryBuilder |
327
|
|
|
*/ |
328
|
|
|
public function where($predicates) |
329
|
|
|
{ |
330
|
|
|
return call_user_func_array('parent::where', Helpers::separateParameters($this, func_get_args())); |
331
|
|
|
} |
332
|
|
|
|
333
|
|
|
|
334
|
|
|
|
335
|
|
|
/** |
336
|
|
|
* {@inheritdoc} |
337
|
|
|
* @return NativeQueryBuilder |
338
|
|
|
*/ |
339
|
|
|
public function andWhere($where) |
340
|
|
|
{ |
341
|
|
|
return call_user_func_array('parent::andWhere', Helpers::separateParameters($this, func_get_args())); |
342
|
|
|
} |
343
|
|
|
|
344
|
|
|
|
345
|
|
|
|
346
|
|
|
/** |
347
|
|
|
* {@inheritdoc} |
348
|
|
|
* @return NativeQueryBuilder |
349
|
|
|
*/ |
350
|
|
|
public function orWhere($where) |
351
|
|
|
{ |
352
|
|
|
return call_user_func_array('parent::orWhere', Helpers::separateParameters($this, func_get_args())); |
353
|
|
|
} |
354
|
|
|
|
355
|
|
|
} |
356
|
|
|
|
The
EntityManager
might become unusable for example if a transaction is rolled back and it gets closed. Let’s assume that somewhere in your application, or in a third-party library, there is code such as the following:If that code throws an exception and the
EntityManager
is closed. Any other code which depends on the same instance of theEntityManager
during this request will fail.On the other hand, if you instead inject the
ManagerRegistry
, thegetManager()
method guarantees that you will always get a usable manager instance.