1 | <?php |
||
2 | |||
3 | declare(strict_types=1); |
||
4 | |||
5 | namespace Doctrine\ORM; |
||
6 | |||
7 | use Doctrine\Common\Collections\ArrayCollection; |
||
8 | use Doctrine\DBAL\LockMode; |
||
9 | use Doctrine\DBAL\Types\Type; |
||
10 | use Doctrine\ORM\Mapping\ClassMetadata; |
||
11 | use Doctrine\ORM\Query\AST\DeleteStatement; |
||
12 | use Doctrine\ORM\Query\AST\SelectStatement; |
||
13 | use Doctrine\ORM\Query\Exec\AbstractSqlExecutor; |
||
14 | use Doctrine\ORM\Query\Parameter; |
||
15 | use Doctrine\ORM\Query\ParameterTypeInferer; |
||
16 | use Doctrine\ORM\Query\Parser; |
||
17 | use Doctrine\ORM\Query\ParserResult; |
||
18 | use Doctrine\ORM\Query\QueryException; |
||
19 | use Doctrine\ORM\Utility\HierarchyDiscriminatorResolver; |
||
20 | |||
21 | /** |
||
22 | * A Query object represents a DQL query. |
||
23 | * |
||
24 | */ |
||
25 | final class Query extends AbstractQuery |
||
26 | { |
||
27 | /** |
||
28 | * A query object is in CLEAN state when it has NO unparsed/unprocessed DQL parts. |
||
29 | */ |
||
30 | public const STATE_CLEAN = 1; |
||
31 | |||
32 | /** |
||
33 | * A query object is in state DIRTY when it has DQL parts that have not yet been |
||
34 | * parsed/processed. This is automatically defined as DIRTY when addDqlQueryPart |
||
35 | * is called. |
||
36 | */ |
||
37 | public const STATE_DIRTY = 2; |
||
38 | |||
39 | /* Query HINTS */ |
||
40 | |||
41 | /** |
||
42 | * The refresh hint turns any query into a refresh query with the result that |
||
43 | * any local changes in entities are overridden with the fetched values. |
||
44 | * |
||
45 | * @var string |
||
46 | */ |
||
47 | public const HINT_REFRESH = 'doctrine.refresh'; |
||
48 | |||
49 | /** |
||
50 | * @var string |
||
51 | */ |
||
52 | public const HINT_CACHE_ENABLED = 'doctrine.cache.enabled'; |
||
53 | |||
54 | /** |
||
55 | * @var string |
||
56 | */ |
||
57 | public const HINT_CACHE_EVICT = 'doctrine.cache.evict'; |
||
58 | |||
59 | /** |
||
60 | * Internal hint: is set to the proxy entity that is currently triggered for loading |
||
61 | * |
||
62 | * @var string |
||
63 | */ |
||
64 | public const HINT_REFRESH_ENTITY = 'doctrine.refresh.entity'; |
||
65 | |||
66 | /** |
||
67 | * The forcePartialLoad query hint forces a particular query to return |
||
68 | * partial objects. |
||
69 | * |
||
70 | * @var string |
||
71 | * @todo Rename: HINT_OPTIMIZE |
||
72 | */ |
||
73 | public const HINT_FORCE_PARTIAL_LOAD = 'doctrine.forcePartialLoad'; |
||
74 | |||
75 | /** |
||
76 | * The includeMetaColumns query hint causes meta columns like foreign keys and |
||
77 | * discriminator columns to be selected and returned as part of the query result. |
||
78 | * |
||
79 | * This hint does only apply to non-object queries. |
||
80 | * |
||
81 | * @var string |
||
82 | */ |
||
83 | public const HINT_INCLUDE_META_COLUMNS = 'doctrine.includeMetaColumns'; |
||
84 | |||
85 | /** |
||
86 | * An array of class names that implement \Doctrine\ORM\Query\TreeWalker and |
||
87 | * are iterated and executed after the DQL has been parsed into an AST. |
||
88 | * |
||
89 | * @var string |
||
90 | */ |
||
91 | public const HINT_CUSTOM_TREE_WALKERS = 'doctrine.customTreeWalkers'; |
||
92 | |||
93 | /** |
||
94 | * A string with a class name that implements \Doctrine\ORM\Query\TreeWalker |
||
95 | * and is used for generating the target SQL from any DQL AST tree. |
||
96 | * |
||
97 | * @var string |
||
98 | */ |
||
99 | public const HINT_CUSTOM_OUTPUT_WALKER = 'doctrine.customOutputWalker'; |
||
100 | |||
101 | //const HINT_READ_ONLY = 'doctrine.readOnly'; |
||
0 ignored issues
–
show
|
|||
102 | |||
103 | /** |
||
104 | * @var string |
||
105 | */ |
||
106 | public const HINT_INTERNAL_ITERATION = 'doctrine.internal.iteration'; |
||
107 | |||
108 | /** |
||
109 | * @var string |
||
110 | */ |
||
111 | public const HINT_LOCK_MODE = 'doctrine.lockMode'; |
||
112 | |||
113 | /** |
||
114 | * The current state of this query. |
||
115 | * |
||
116 | * @var int |
||
117 | */ |
||
118 | private $state = self::STATE_CLEAN; |
||
119 | |||
120 | /** |
||
121 | * A snapshot of the parameter types the query was parsed with. |
||
122 | * |
||
123 | * @var mixed[] |
||
124 | */ |
||
125 | private $parsedTypes = []; |
||
126 | |||
127 | /** |
||
128 | * Cached DQL query. |
||
129 | * |
||
130 | * @var string |
||
131 | */ |
||
132 | private $dql; |
||
133 | |||
134 | /** |
||
135 | * The parser result that holds DQL => SQL information. |
||
136 | * |
||
137 | * @var \Doctrine\ORM\Query\ParserResult |
||
138 | */ |
||
139 | private $parserResult; |
||
140 | |||
141 | /** |
||
142 | * The first result to return (the "offset"). |
||
143 | * |
||
144 | * @var int |
||
145 | */ |
||
146 | private $firstResult; |
||
147 | |||
148 | /** |
||
149 | * The maximum number of results to return (the "limit"). |
||
150 | * |
||
151 | * @var int|null |
||
152 | */ |
||
153 | private $maxResults; |
||
154 | |||
155 | /** |
||
156 | * The cache driver used for caching queries. |
||
157 | * |
||
158 | * @var \Doctrine\Common\Cache\Cache|null |
||
159 | */ |
||
160 | private $queryCache; |
||
161 | |||
162 | /** |
||
163 | * Whether or not expire the query cache. |
||
164 | * |
||
165 | * @var bool |
||
166 | */ |
||
167 | private $expireQueryCache = false; |
||
168 | |||
169 | /** |
||
170 | * The query cache lifetime. |
||
171 | * |
||
172 | * @var int |
||
173 | */ |
||
174 | private $queryCacheTTL; |
||
175 | |||
176 | /** |
||
177 | * Whether to use a query cache, if available. Defaults to TRUE. |
||
178 | * |
||
179 | * @var bool |
||
180 | */ |
||
181 | private $useQueryCache = true; |
||
182 | |||
183 | /** |
||
184 | * Gets the SQL query/queries that correspond to this DQL query. |
||
185 | * |
||
186 | * @return mixed The built sql query or an array of all sql queries. |
||
187 | * |
||
188 | * @override |
||
189 | */ |
||
190 | 342 | public function getSQL() |
|
191 | { |
||
192 | 342 | return $this->parse()->getSQLExecutor()->getSQLStatements(); |
|
193 | } |
||
194 | |||
195 | /** |
||
196 | * Returns the corresponding AST for this DQL query. |
||
197 | * |
||
198 | * @return \Doctrine\ORM\Query\AST\SelectStatement | |
||
199 | * \Doctrine\ORM\Query\AST\UpdateStatement | |
||
200 | * \Doctrine\ORM\Query\AST\DeleteStatement |
||
201 | */ |
||
202 | 2 | public function getAST() |
|
203 | { |
||
204 | 2 | $parser = new Parser($this); |
|
205 | |||
206 | 2 | return $parser->getAST(); |
|
207 | } |
||
208 | |||
209 | /** |
||
210 | * {@inheritdoc} |
||
211 | */ |
||
212 | 445 | protected function getResultSetMapping() |
|
213 | { |
||
214 | // parse query or load from cache |
||
215 | 445 | if ($this->resultSetMapping === null) { |
|
216 | 38 | $this->resultSetMapping = $this->parse()->getResultSetMapping(); |
|
217 | } |
||
218 | |||
219 | 442 | return $this->resultSetMapping; |
|
220 | } |
||
221 | |||
222 | /** |
||
223 | * Parses the DQL query, if necessary, and stores the parser result. |
||
224 | * |
||
225 | * Note: Populates $this->parserResult as a side-effect. |
||
226 | * |
||
227 | * @return \Doctrine\ORM\Query\ParserResult |
||
228 | */ |
||
229 | 770 | private function parse() |
|
230 | { |
||
231 | 770 | $types = []; |
|
232 | |||
233 | 770 | foreach ($this->parameters as $parameter) { |
|
234 | /** @var Query\Parameter $parameter */ |
||
235 | 172 | $types[$parameter->getName()] = $parameter->getType(); |
|
236 | } |
||
237 | |||
238 | // Return previous parser result if the query and the filter collection are both clean |
||
239 | 770 | if ($this->state === self::STATE_CLEAN && $this->parsedTypes === $types && $this->em->isFiltersStateClean()) { |
|
240 | 39 | return $this->parserResult; |
|
241 | } |
||
242 | |||
243 | 770 | $this->state = self::STATE_CLEAN; |
|
244 | 770 | $this->parsedTypes = $types; |
|
245 | |||
246 | // Check query cache. |
||
247 | 770 | $queryCache = $this->getQueryCacheDriver(); |
|
248 | 770 | if (! ($this->useQueryCache && $queryCache)) { |
|
249 | 181 | $parser = new Parser($this); |
|
250 | |||
251 | 181 | $this->parserResult = $parser->parse(); |
|
252 | |||
253 | 177 | return $this->parserResult; |
|
254 | } |
||
255 | |||
256 | 589 | $hash = $this->getQueryCacheId(); |
|
257 | 589 | $cached = $this->expireQueryCache ? false : $queryCache->fetch($hash); |
|
258 | |||
259 | 589 | if ($cached instanceof ParserResult) { |
|
260 | // Cache hit. |
||
261 | 118 | $this->parserResult = $cached; |
|
262 | |||
263 | 118 | return $this->parserResult; |
|
264 | } |
||
265 | |||
266 | // Cache miss. |
||
267 | 538 | $parser = new Parser($this); |
|
268 | |||
269 | 538 | $this->parserResult = $parser->parse(); |
|
270 | |||
271 | 519 | $queryCache->save($hash, $this->parserResult, $this->queryCacheTTL); |
|
272 | |||
273 | 519 | return $this->parserResult; |
|
274 | } |
||
275 | |||
276 | /** |
||
277 | * {@inheritdoc} |
||
278 | */ |
||
279 | 460 | protected function doExecute() |
|
280 | { |
||
281 | 460 | $executor = $this->parse()->getSqlExecutor(); |
|
282 | |||
283 | 453 | if ($this->queryCacheProfile) { |
|
284 | 8 | $executor->setQueryCacheProfile($this->queryCacheProfile); |
|
285 | } else { |
||
286 | 447 | $executor->removeQueryCacheProfile(); |
|
287 | } |
||
288 | |||
289 | 453 | if ($this->resultSetMapping === null) { |
|
290 | 411 | $this->resultSetMapping = $this->parserResult->getResultSetMapping(); |
|
291 | } |
||
292 | |||
293 | // Prepare parameters |
||
294 | 453 | $paramMappings = $this->parserResult->getParameterMappings(); |
|
295 | 453 | $paramCount = count($this->parameters); |
|
296 | 453 | $mappingCount = count($paramMappings); |
|
297 | |||
298 | 453 | if ($paramCount > $mappingCount) { |
|
299 | 1 | throw QueryException::tooManyParameters($mappingCount, $paramCount); |
|
300 | } |
||
301 | |||
302 | 452 | if ($paramCount < $mappingCount) { |
|
303 | 1 | throw QueryException::tooFewParameters($mappingCount, $paramCount); |
|
304 | } |
||
305 | |||
306 | // evict all cache for the entity region |
||
307 | 451 | if ($this->hasCache && isset($this->hints[self::HINT_CACHE_EVICT]) && $this->hints[self::HINT_CACHE_EVICT]) { |
|
308 | 2 | $this->evictEntityCacheRegion(); |
|
309 | } |
||
310 | |||
311 | 451 | list($sqlParams, $types) = $this->processParameterMappings($paramMappings); |
|
312 | |||
313 | 450 | $this->evictResultSetCache( |
|
314 | 450 | $executor, |
|
315 | 450 | $sqlParams, |
|
316 | 450 | $types, |
|
317 | 450 | $this->em->getConnection()->getParams() |
|
318 | ); |
||
319 | |||
320 | 450 | return $executor->execute($this->em->getConnection(), $sqlParams, $types); |
|
321 | } |
||
322 | |||
323 | /** |
||
324 | * @param mixed[] $sqlParams |
||
325 | * @param mixed[] $types |
||
326 | * @param mixed[] $connectionParams |
||
327 | */ |
||
328 | 450 | private function evictResultSetCache( |
|
329 | AbstractSqlExecutor $executor, |
||
330 | array $sqlParams, |
||
331 | array $types, |
||
332 | array $connectionParams |
||
333 | ) { |
||
334 | 450 | if ($this->queryCacheProfile === null || ! $this->getExpireResultCache()) { |
|
335 | 450 | return; |
|
336 | } |
||
337 | |||
338 | 2 | $cacheDriver = $this->queryCacheProfile->getResultCacheDriver(); |
|
339 | 2 | $statements = (array) $executor->getSqlStatements(); // Type casted since it can either be a string or an array |
|
340 | |||
341 | 2 | foreach ($statements as $statement) { |
|
342 | 2 | $cacheKeys = $this->queryCacheProfile->generateCacheKeys($statement, $sqlParams, $types, $connectionParams); |
|
343 | |||
344 | 2 | $cacheDriver->delete(reset($cacheKeys)); |
|
345 | } |
||
346 | 2 | } |
|
347 | |||
348 | /** |
||
349 | * Evict entity cache region |
||
350 | */ |
||
351 | 2 | private function evictEntityCacheRegion() |
|
352 | { |
||
353 | 2 | $AST = $this->getAST(); |
|
354 | |||
355 | 2 | if ($AST instanceof SelectStatement) { |
|
356 | throw new QueryException('The hint "HINT_CACHE_EVICT" is not valid for select statements.'); |
||
357 | } |
||
358 | |||
359 | 2 | $className = ($AST instanceof DeleteStatement) |
|
360 | 1 | ? $AST->deleteClause->abstractSchemaName |
|
361 | 2 | : $AST->updateClause->abstractSchemaName; |
|
362 | |||
363 | 2 | $this->em->getCache()->evictEntityRegion($className); |
|
364 | 2 | } |
|
365 | |||
366 | /** |
||
367 | * Processes query parameter mappings. |
||
368 | * |
||
369 | * @param mixed[] $paramMappings |
||
370 | * |
||
371 | * @return mixed[][] |
||
372 | * |
||
373 | * @throws Query\QueryException |
||
374 | */ |
||
375 | 451 | private function processParameterMappings($paramMappings) |
|
376 | { |
||
377 | 451 | $sqlParams = []; |
|
378 | 451 | $types = []; |
|
379 | |||
380 | 451 | foreach ($this->parameters as $parameter) { |
|
381 | 160 | $key = $parameter->getName(); |
|
382 | 160 | $value = $parameter->getValue(); |
|
383 | 160 | $rsm = $this->getResultSetMapping(); |
|
384 | |||
385 | 160 | if (! isset($paramMappings[$key])) { |
|
386 | 1 | throw QueryException::unknownParameter($key); |
|
387 | } |
||
388 | |||
389 | 159 | if (isset($rsm->metadataParameterMapping[$key]) && $value instanceof ClassMetadata) { |
|
390 | $value = $value->getMetadataValue($rsm->metadataParameterMapping[$key]); |
||
391 | } |
||
392 | |||
393 | 159 | if (isset($rsm->discriminatorParameters[$key]) && $value instanceof ClassMetadata) { |
|
394 | 3 | $value = array_keys(HierarchyDiscriminatorResolver::resolveDiscriminatorsForClass($value, $this->em)); |
|
395 | } |
||
396 | |||
397 | 159 | $value = $this->processParameterValue($value); |
|
398 | 159 | $type = ($parameter->getValue() === $value) |
|
399 | 148 | ? $parameter->getType() |
|
400 | 159 | : ParameterTypeInferer::inferType($value); |
|
401 | |||
402 | 159 | foreach ($paramMappings[$key] as $position) { |
|
403 | 159 | $types[$position] = $type; |
|
404 | } |
||
405 | |||
406 | 159 | $sqlPositions = $paramMappings[$key]; |
|
407 | 159 | $sqlPositionsCount = count($sqlPositions); |
|
408 | |||
409 | // optimized multi value sql positions away for now, |
||
410 | // they are not allowed in DQL anyways. |
||
411 | 159 | $value = [$value]; |
|
412 | 159 | $countValue = count($value); |
|
413 | |||
414 | 159 | for ($i = 0, $l = $sqlPositionsCount; $i < $l; $i++) { |
|
415 | 159 | $sqlParams[$sqlPositions[$i]] = $value[($i % $countValue)]; |
|
416 | } |
||
417 | } |
||
418 | |||
419 | 450 | if (count($sqlParams) !== count($types)) { |
|
420 | throw QueryException::parameterTypeMismatch(); |
||
421 | } |
||
422 | |||
423 | 450 | if ($sqlParams) { |
|
424 | 159 | ksort($sqlParams); |
|
425 | 159 | $sqlParams = array_values($sqlParams); |
|
426 | |||
427 | 159 | ksort($types); |
|
428 | 159 | $types = array_values($types); |
|
429 | } |
||
430 | |||
431 | 450 | return [$sqlParams, $types]; |
|
432 | } |
||
433 | |||
434 | /** |
||
435 | * Defines a cache driver to be used for caching queries. |
||
436 | * |
||
437 | * @param \Doctrine\Common\Cache\Cache|null $queryCache Cache driver. |
||
438 | * |
||
439 | * @return Query This query instance. |
||
440 | */ |
||
441 | 6 | public function setQueryCacheDriver($queryCache) |
|
442 | { |
||
443 | 6 | $this->queryCache = $queryCache; |
|
444 | |||
445 | 6 | return $this; |
|
446 | } |
||
447 | |||
448 | /** |
||
449 | * Defines whether the query should make use of a query cache, if available. |
||
450 | * |
||
451 | * @param bool $bool |
||
452 | * |
||
453 | * @return Query This query instance. |
||
454 | */ |
||
455 | 184 | public function useQueryCache($bool) |
|
456 | { |
||
457 | 184 | $this->useQueryCache = $bool; |
|
458 | |||
459 | 184 | return $this; |
|
460 | } |
||
461 | |||
462 | /** |
||
463 | * Returns the cache driver used for query caching. |
||
464 | * |
||
465 | * @return \Doctrine\Common\Cache\Cache|null The cache driver used for query caching or NULL, if |
||
466 | * this Query does not use query caching. |
||
467 | */ |
||
468 | 770 | public function getQueryCacheDriver() |
|
469 | { |
||
470 | 770 | if ($this->queryCache) { |
|
471 | 9 | return $this->queryCache; |
|
472 | } |
||
473 | |||
474 | 761 | return $this->em->getConfiguration()->getQueryCacheImpl(); |
|
475 | } |
||
476 | |||
477 | /** |
||
478 | * Defines how long the query cache will be active before expire. |
||
479 | * |
||
480 | * @param int $timeToLive How long the cache entry is valid. |
||
481 | * |
||
482 | * @return Query This query instance. |
||
483 | */ |
||
484 | 1 | public function setQueryCacheLifetime($timeToLive) |
|
485 | { |
||
486 | 1 | if ($timeToLive !== null) { |
|
487 | 1 | $timeToLive = (int) $timeToLive; |
|
488 | } |
||
489 | |||
490 | 1 | $this->queryCacheTTL = $timeToLive; |
|
491 | |||
492 | 1 | return $this; |
|
493 | } |
||
494 | |||
495 | /** |
||
496 | * Retrieves the lifetime of resultset cache. |
||
497 | * |
||
498 | * @return int |
||
499 | */ |
||
500 | public function getQueryCacheLifetime() |
||
501 | { |
||
502 | return $this->queryCacheTTL; |
||
503 | } |
||
504 | |||
505 | /** |
||
506 | * Defines if the query cache is active or not. |
||
507 | * |
||
508 | * @param bool $expire Whether or not to force query cache expiration. |
||
509 | * |
||
510 | * @return Query This query instance. |
||
511 | */ |
||
512 | 7 | public function expireQueryCache($expire = true) |
|
513 | { |
||
514 | 7 | $this->expireQueryCache = $expire; |
|
515 | |||
516 | 7 | return $this; |
|
517 | } |
||
518 | |||
519 | /** |
||
520 | * Retrieves if the query cache is active or not. |
||
521 | * |
||
522 | * @return bool |
||
523 | */ |
||
524 | public function getExpireQueryCache() |
||
525 | { |
||
526 | return $this->expireQueryCache; |
||
527 | } |
||
528 | |||
529 | 220 | public function free() |
|
530 | { |
||
531 | 220 | parent::free(); |
|
532 | |||
533 | 220 | $this->dql = null; |
|
534 | 220 | $this->state = self::STATE_CLEAN; |
|
535 | 220 | } |
|
536 | |||
537 | /** |
||
538 | * Sets a DQL query string. |
||
539 | * |
||
540 | * @param string $dqlQuery DQL Query. |
||
541 | * |
||
542 | * @return \Doctrine\ORM\AbstractQuery |
||
543 | */ |
||
544 | 959 | public function setDQL($dqlQuery) |
|
545 | { |
||
546 | 959 | if ($dqlQuery !== null) { |
|
547 | 959 | $this->dql = $dqlQuery; |
|
548 | 959 | $this->state = self::STATE_DIRTY; |
|
549 | } |
||
550 | |||
551 | 959 | return $this; |
|
552 | } |
||
553 | |||
554 | /** |
||
555 | * Returns the DQL query that is represented by this query object. |
||
556 | * |
||
557 | * @return string DQL query. |
||
558 | */ |
||
559 | 906 | public function getDQL() |
|
560 | { |
||
561 | 906 | return $this->dql; |
|
562 | } |
||
563 | |||
564 | /** |
||
565 | * Returns the state of this query object |
||
566 | * By default the type is Doctrine_ORM_Query_Abstract::STATE_CLEAN but if it appears any unprocessed DQL |
||
567 | * part, it is switched to Doctrine_ORM_Query_Abstract::STATE_DIRTY. |
||
568 | * |
||
569 | * @see AbstractQuery::STATE_CLEAN |
||
570 | * @see AbstractQuery::STATE_DIRTY |
||
571 | * |
||
572 | * @return int The query state. |
||
573 | */ |
||
574 | public function getState() |
||
575 | { |
||
576 | return $this->state; |
||
577 | } |
||
578 | |||
579 | /** |
||
580 | * Method to check if an arbitrary piece of DQL exists |
||
581 | * |
||
582 | * @param string $dql Arbitrary piece of DQL to check for. |
||
583 | * |
||
584 | * @return bool |
||
585 | */ |
||
586 | public function contains($dql) |
||
587 | { |
||
588 | return stripos($this->getDQL(), $dql) !== false; |
||
589 | } |
||
590 | |||
591 | /** |
||
592 | * Sets the position of the first result to retrieve (the "offset"). |
||
593 | * |
||
594 | * @param int $firstResult The first result to return. |
||
595 | * |
||
596 | * @return Query This query object. |
||
597 | */ |
||
598 | 221 | public function setFirstResult($firstResult) |
|
599 | { |
||
600 | 221 | $this->firstResult = $firstResult; |
|
601 | 221 | $this->state = self::STATE_DIRTY; |
|
602 | |||
603 | 221 | return $this; |
|
604 | } |
||
605 | |||
606 | /** |
||
607 | * Gets the position of the first result the query object was set to retrieve (the "offset"). |
||
608 | * Returns NULL if {@link setFirstResult} was not applied to this query. |
||
609 | * |
||
610 | * @return int The position of the first result. |
||
611 | */ |
||
612 | 657 | public function getFirstResult() |
|
613 | { |
||
614 | 657 | return $this->firstResult; |
|
615 | } |
||
616 | |||
617 | /** |
||
618 | * Sets the maximum number of results to retrieve (the "limit"). |
||
619 | * |
||
620 | * @param int|null $maxResults |
||
621 | * |
||
622 | * @return Query This query object. |
||
623 | */ |
||
624 | 241 | public function setMaxResults($maxResults) |
|
625 | { |
||
626 | 241 | $this->maxResults = $maxResults; |
|
627 | 241 | $this->state = self::STATE_DIRTY; |
|
628 | |||
629 | 241 | return $this; |
|
630 | } |
||
631 | |||
632 | /** |
||
633 | * Gets the maximum number of results the query object was set to retrieve (the "limit"). |
||
634 | * Returns NULL if {@link setMaxResults} was not applied to this query. |
||
635 | * |
||
636 | * @return int|null Maximum number of results. |
||
637 | */ |
||
638 | 657 | public function getMaxResults() |
|
639 | { |
||
640 | 657 | return $this->maxResults; |
|
641 | } |
||
642 | |||
643 | /** |
||
644 | * Executes the query and returns an IterableResult that can be used to incrementally |
||
645 | * iterated over the result. |
||
646 | * |
||
647 | * @param ArrayCollection|array|Parameter[]|mixed[]|null $parameters The query parameters. |
||
648 | * @param int $hydrationMode The hydration mode to use. |
||
649 | * |
||
650 | * @return \Doctrine\ORM\Internal\Hydration\IterableResult |
||
651 | */ |
||
652 | 10 | public function iterate($parameters = null, $hydrationMode = self::HYDRATE_OBJECT) |
|
653 | { |
||
654 | 10 | $this->setHint(self::HINT_INTERNAL_ITERATION, true); |
|
655 | |||
656 | 10 | return parent::iterate($parameters, $hydrationMode); |
|
657 | } |
||
658 | |||
659 | /** |
||
660 | * {@inheritdoc} |
||
661 | */ |
||
662 | 469 | public function setHint($name, $value) |
|
663 | { |
||
664 | 469 | $this->state = self::STATE_DIRTY; |
|
665 | |||
666 | 469 | return parent::setHint($name, $value); |
|
667 | } |
||
668 | |||
669 | /** |
||
670 | * {@inheritdoc} |
||
671 | */ |
||
672 | 361 | public function setHydrationMode($hydrationMode) |
|
673 | { |
||
674 | 361 | $this->state = self::STATE_DIRTY; |
|
675 | |||
676 | 361 | return parent::setHydrationMode($hydrationMode); |
|
677 | } |
||
678 | |||
679 | /** |
||
680 | * Set the lock mode for this Query. |
||
681 | * |
||
682 | * @see \Doctrine\DBAL\LockMode |
||
683 | * |
||
684 | * @param int $lockMode |
||
685 | * |
||
686 | * @return Query |
||
687 | * |
||
688 | * @throws TransactionRequiredException |
||
689 | */ |
||
690 | public function setLockMode($lockMode) |
||
691 | { |
||
692 | if (in_array($lockMode, [LockMode::NONE, LockMode::PESSIMISTIC_READ, LockMode::PESSIMISTIC_WRITE], true)) { |
||
693 | if (! $this->em->getConnection()->isTransactionActive()) { |
||
694 | throw TransactionRequiredException::transactionRequired(); |
||
695 | } |
||
696 | } |
||
697 | |||
698 | $this->setHint(self::HINT_LOCK_MODE, $lockMode); |
||
699 | |||
700 | return $this; |
||
701 | } |
||
702 | |||
703 | /** |
||
704 | * Get the current lock mode for this query. |
||
705 | * |
||
706 | * @return int|null The current lock mode of this query or NULL if no specific lock mode is set. |
||
707 | */ |
||
708 | public function getLockMode() |
||
709 | { |
||
710 | $lockMode = $this->getHint(self::HINT_LOCK_MODE); |
||
711 | |||
712 | if ($lockMode === false) { |
||
713 | return null; |
||
714 | } |
||
715 | |||
716 | return $lockMode; |
||
717 | } |
||
718 | |||
719 | /** |
||
720 | * Generate a cache id for the query cache - reusing the Result-Cache-Id generator. |
||
721 | * |
||
722 | * @return string |
||
723 | */ |
||
724 | 589 | protected function getQueryCacheId() |
|
725 | { |
||
726 | 589 | ksort($this->hints); |
|
727 | |||
728 | 589 | $platform = $this->getEntityManager() |
|
729 | 589 | ->getConnection() |
|
730 | 589 | ->getDatabasePlatform() |
|
731 | 589 | ->getName(); |
|
732 | |||
733 | 589 | return md5( |
|
734 | 589 | $this->getDQL() . serialize($this->hints) . |
|
735 | 589 | '&platform=' . $platform . |
|
736 | 589 | ($this->em->hasFilters() ? $this->em->getFilters()->getHash() : '') . |
|
737 | 589 | '&firstResult=' . $this->firstResult . '&maxResult=' . $this->maxResults . |
|
738 | 589 | '&hydrationMode=' . $this->hydrationMode . '&types=' . serialize($this->parsedTypes) . 'DOCTRINE_QUERY_CACHE_SALT' |
|
739 | ); |
||
740 | } |
||
741 | |||
742 | /** |
||
743 | * {@inheritdoc} |
||
744 | */ |
||
745 | 28 | protected function getHash() |
|
746 | { |
||
747 | 28 | return sha1(parent::getHash() . '-' . $this->firstResult . '-' . $this->maxResults); |
|
748 | } |
||
749 | |||
750 | /** |
||
751 | * Cleanup Query resource when clone is called. |
||
752 | */ |
||
753 | 139 | public function __clone() |
|
754 | { |
||
755 | 139 | parent::__clone(); |
|
756 | |||
757 | 139 | $this->state = self::STATE_DIRTY; |
|
758 | 139 | } |
|
759 | } |
||
760 |
Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.
The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.
This check looks for comments that seem to be mostly valid code and reports them.