1 | <?php |
||||
2 | |||||
3 | declare(strict_types=1); |
||||
4 | |||||
5 | namespace Doctrine\ORM; |
||||
6 | |||||
7 | use Doctrine\Common\Collections\ArrayCollection; |
||||
8 | use Doctrine\Common\Collections\Collection; |
||||
9 | use Doctrine\DBAL\Cache\QueryCacheProfile; |
||||
10 | use Doctrine\ORM\Cache\QueryCacheKey; |
||||
11 | use Doctrine\ORM\Query\Parameter; |
||||
12 | use Doctrine\ORM\Utility\StaticClassNameConverter; |
||||
13 | |||||
14 | /** |
||||
15 | * Base contract for ORM queries. Base class for Query and NativeQuery. |
||||
16 | */ |
||||
17 | abstract class AbstractQuery |
||||
18 | { |
||||
19 | /* Hydration mode constants */ |
||||
20 | |||||
21 | /** |
||||
22 | * Hydrates an object graph. This is the default behavior. |
||||
23 | */ |
||||
24 | public const HYDRATE_OBJECT = 1; |
||||
25 | |||||
26 | /** |
||||
27 | * Hydrates an array graph. |
||||
28 | */ |
||||
29 | public const HYDRATE_ARRAY = 2; |
||||
30 | |||||
31 | /** |
||||
32 | * Hydrates a flat, rectangular result set with scalar values. |
||||
33 | */ |
||||
34 | public const HYDRATE_SCALAR = 3; |
||||
35 | |||||
36 | /** |
||||
37 | * Hydrates a single scalar value. |
||||
38 | */ |
||||
39 | public const HYDRATE_SINGLE_SCALAR = 4; |
||||
40 | |||||
41 | /** |
||||
42 | * Very simple object hydrator (optimized for performance). |
||||
43 | */ |
||||
44 | public const HYDRATE_SIMPLEOBJECT = 5; |
||||
45 | |||||
46 | /** |
||||
47 | * The parameter map of this query. |
||||
48 | * |
||||
49 | * @var \Doctrine\Common\Collections\ArrayCollection |
||||
50 | */ |
||||
51 | protected $parameters; |
||||
52 | |||||
53 | /** |
||||
54 | * The user-specified ResultSetMapping to use. |
||||
55 | * |
||||
56 | * @var \Doctrine\ORM\Query\ResultSetMapping |
||||
57 | */ |
||||
58 | protected $resultSetMapping; |
||||
59 | |||||
60 | /** |
||||
61 | * The entity manager used by this query object. |
||||
62 | * |
||||
63 | * @var EntityManagerInterface |
||||
64 | */ |
||||
65 | protected $em; |
||||
66 | |||||
67 | /** |
||||
68 | * The map of query hints. |
||||
69 | * |
||||
70 | * @var mixed[] |
||||
71 | */ |
||||
72 | protected $hints = []; |
||||
73 | |||||
74 | /** |
||||
75 | * The hydration mode. |
||||
76 | * |
||||
77 | * @var int |
||||
78 | */ |
||||
79 | protected $hydrationMode = self::HYDRATE_OBJECT; |
||||
80 | |||||
81 | /** |
||||
82 | * @var \Doctrine\DBAL\Cache\QueryCacheProfile |
||||
83 | */ |
||||
84 | protected $queryCacheProfile; |
||||
85 | |||||
86 | /** |
||||
87 | * Whether or not expire the result cache. |
||||
88 | * |
||||
89 | * @var bool |
||||
90 | */ |
||||
91 | protected $expireResultCache = false; |
||||
92 | |||||
93 | /** |
||||
94 | * @var \Doctrine\DBAL\Cache\QueryCacheProfile |
||||
95 | */ |
||||
96 | protected $hydrationCacheProfile; |
||||
97 | |||||
98 | /** |
||||
99 | * Whether to use second level cache, if available. |
||||
100 | * |
||||
101 | * @var bool |
||||
102 | */ |
||||
103 | protected $cacheable = false; |
||||
104 | |||||
105 | /** |
||||
106 | * @var bool |
||||
107 | */ |
||||
108 | protected $hasCache = false; |
||||
109 | |||||
110 | /** |
||||
111 | * Second level cache region name. |
||||
112 | * |
||||
113 | * @var string|null |
||||
114 | */ |
||||
115 | protected $cacheRegion; |
||||
116 | |||||
117 | /** |
||||
118 | * Second level query cache mode. |
||||
119 | * |
||||
120 | * @var int|null |
||||
121 | */ |
||||
122 | protected $cacheMode; |
||||
123 | |||||
124 | /** |
||||
125 | * @var \Doctrine\ORM\Cache\Logging\CacheLogger|null |
||||
126 | */ |
||||
127 | protected $cacheLogger; |
||||
128 | |||||
129 | /** |
||||
130 | * @var int |
||||
131 | */ |
||||
132 | protected $lifetime = 0; |
||||
133 | |||||
134 | /** |
||||
135 | * Initializes a new instance of a class derived from <tt>AbstractQuery</tt>. |
||||
136 | */ |
||||
137 | 973 | public function __construct(EntityManagerInterface $em) |
|||
138 | { |
||||
139 | 973 | $this->em = $em; |
|||
140 | 973 | $this->parameters = new ArrayCollection(); |
|||
141 | 973 | $this->hints = $em->getConfiguration()->getDefaultQueryHints(); |
|||
142 | 973 | $this->hasCache = $this->em->getConfiguration()->isSecondLevelCacheEnabled(); |
|||
143 | |||||
144 | 973 | if ($this->hasCache) { |
|||
145 | 32 | $this->cacheLogger = $em->getConfiguration() |
|||
146 | 32 | ->getSecondLevelCacheConfiguration() |
|||
147 | 32 | ->getCacheLogger(); |
|||
148 | } |
||||
149 | 973 | } |
|||
150 | |||||
151 | /** |
||||
152 | * Enable/disable second level query (result) caching for this query. |
||||
153 | * |
||||
154 | * @param bool $cacheable |
||||
155 | * |
||||
156 | * @return static This query instance. |
||||
157 | */ |
||||
158 | 133 | public function setCacheable($cacheable) |
|||
159 | { |
||||
160 | 133 | $this->cacheable = (bool) $cacheable; |
|||
161 | |||||
162 | 133 | return $this; |
|||
163 | } |
||||
164 | |||||
165 | /** |
||||
166 | * @return bool TRUE if the query results are enable for second level cache, FALSE otherwise. |
||||
167 | */ |
||||
168 | 90 | public function isCacheable() |
|||
169 | { |
||||
170 | 90 | return $this->cacheable; |
|||
171 | } |
||||
172 | |||||
173 | /** |
||||
174 | * @param string $cacheRegion |
||||
175 | * |
||||
176 | * @return static This query instance. |
||||
177 | */ |
||||
178 | 2 | public function setCacheRegion($cacheRegion) |
|||
179 | { |
||||
180 | 2 | $this->cacheRegion = (string) $cacheRegion; |
|||
181 | |||||
182 | 2 | return $this; |
|||
183 | } |
||||
184 | |||||
185 | /** |
||||
186 | * Obtain the name of the second level query cache region in which query results will be stored |
||||
187 | * |
||||
188 | * @return string|null The cache region name; NULL indicates the default region. |
||||
189 | */ |
||||
190 | 1 | public function getCacheRegion() |
|||
191 | { |
||||
192 | 1 | return $this->cacheRegion; |
|||
193 | } |
||||
194 | |||||
195 | /** |
||||
196 | * @return bool TRUE if the query cache and second level cache are enabled, FALSE otherwise. |
||||
197 | */ |
||||
198 | 29 | protected function isCacheEnabled() |
|||
199 | { |
||||
200 | 29 | return $this->cacheable && $this->hasCache; |
|||
201 | } |
||||
202 | |||||
203 | /** |
||||
204 | * @return int |
||||
205 | */ |
||||
206 | 1 | public function getLifetime() |
|||
207 | { |
||||
208 | 1 | return $this->lifetime; |
|||
209 | } |
||||
210 | |||||
211 | /** |
||||
212 | * Sets the life-time for this query into second level cache. |
||||
213 | * |
||||
214 | * @param int $lifetime |
||||
215 | * |
||||
216 | * @return \Doctrine\ORM\AbstractQuery This query instance. |
||||
217 | */ |
||||
218 | 2 | public function setLifetime($lifetime) |
|||
219 | { |
||||
220 | 2 | $this->lifetime = (integer) $lifetime; |
|||
221 | |||||
222 | 2 | return $this; |
|||
223 | } |
||||
224 | |||||
225 | /** |
||||
226 | * @return int |
||||
227 | */ |
||||
228 | 1 | public function getCacheMode() |
|||
229 | { |
||||
230 | 1 | return $this->cacheMode; |
|||
231 | } |
||||
232 | |||||
233 | /** |
||||
234 | * @param int $cacheMode |
||||
235 | * |
||||
236 | * @return \Doctrine\ORM\AbstractQuery This query instance. |
||||
237 | */ |
||||
238 | 4 | public function setCacheMode($cacheMode) |
|||
239 | { |
||||
240 | 4 | $this->cacheMode = (integer) $cacheMode; |
|||
241 | |||||
242 | 4 | return $this; |
|||
243 | } |
||||
244 | |||||
245 | /** |
||||
246 | * Gets the SQL query that corresponds to this query object. |
||||
247 | * The returned SQL syntax depends on the connection driver that is used |
||||
248 | * by this query object at the time of this method call. |
||||
249 | * |
||||
250 | * @return string SQL query |
||||
251 | */ |
||||
252 | abstract public function getSQL(); |
||||
253 | |||||
254 | /** |
||||
255 | * Retrieves the associated EntityManager of this Query instance. |
||||
256 | * |
||||
257 | * @return \Doctrine\ORM\EntityManagerInterface |
||||
258 | */ |
||||
259 | 888 | public function getEntityManager() |
|||
260 | { |
||||
261 | 888 | return $this->em; |
|||
262 | } |
||||
263 | |||||
264 | /** |
||||
265 | * Frees the resources used by the query object. |
||||
266 | * |
||||
267 | * Resets Parameters, Parameter Types and Query Hints. |
||||
268 | */ |
||||
269 | 219 | public function free() |
|||
270 | { |
||||
271 | 219 | $this->parameters = new ArrayCollection(); |
|||
272 | |||||
273 | 219 | $this->hints = $this->em->getConfiguration()->getDefaultQueryHints(); |
|||
274 | 219 | } |
|||
275 | |||||
276 | /** |
||||
277 | * Get all defined parameters. |
||||
278 | * |
||||
279 | * @return \Doctrine\Common\Collections\ArrayCollection The defined query parameters. |
||||
280 | */ |
||||
281 | 142 | public function getParameters() |
|||
282 | { |
||||
283 | 142 | return $this->parameters; |
|||
284 | } |
||||
285 | |||||
286 | /** |
||||
287 | * Gets a query parameter. |
||||
288 | * |
||||
289 | * @param mixed $key The key (index or name) of the bound parameter. |
||||
290 | * |
||||
291 | * @return Query\Parameter|null The value of the bound parameter, or NULL if not available. |
||||
292 | */ |
||||
293 | 267 | public function getParameter($key) |
|||
294 | { |
||||
295 | 267 | $filteredParameters = $this->parameters->filter( |
|||
296 | 267 | function (Query\Parameter $parameter) use ($key) : bool { |
|||
297 | 147 | $parameterName = $parameter->getName(); |
|||
298 | |||||
299 | 147 | return $key === $parameterName || (string) $key === (string) $parameterName; |
|||
300 | 267 | } |
|||
301 | ); |
||||
302 | |||||
303 | 267 | return $filteredParameters->isEmpty() ? null : $filteredParameters->first(); |
|||
304 | } |
||||
305 | |||||
306 | /** |
||||
307 | * Sets a collection of query parameters. |
||||
308 | * |
||||
309 | * @param \Doctrine\Common\Collections\ArrayCollection|array|Parameter[]|mixed[] $parameters |
||||
310 | * |
||||
311 | * @return static This query instance. |
||||
312 | */ |
||||
313 | 188 | public function setParameters($parameters) |
|||
314 | { |
||||
315 | // BC compatibility with 2.3- |
||||
316 | 188 | if (is_array($parameters)) { |
|||
317 | 1 | $parameterCollection = new ArrayCollection(); |
|||
318 | |||||
319 | 1 | foreach ($parameters as $key => $value) { |
|||
320 | 1 | $parameterCollection->add(new Parameter($key, $value)); |
|||
321 | } |
||||
322 | |||||
323 | 1 | $parameters = $parameterCollection; |
|||
324 | } |
||||
325 | |||||
326 | 188 | $this->parameters = $parameters; |
|||
327 | |||||
328 | 188 | return $this; |
|||
329 | } |
||||
330 | |||||
331 | /** |
||||
332 | * Sets a query parameter. |
||||
333 | * |
||||
334 | * @param string|int $key The parameter position or name. |
||||
335 | * @param mixed $value The parameter value. |
||||
336 | * @param string|null $type The parameter type. If specified, the given value will be run through |
||||
337 | * the type conversion of this type. This is usually not needed for |
||||
338 | * strings and numeric types. |
||||
339 | * |
||||
340 | * @return static This query instance. |
||||
341 | */ |
||||
342 | 179 | public function setParameter($key, $value, $type = null) |
|||
343 | { |
||||
344 | 179 | $existingParameter = $this->getParameter($key); |
|||
345 | |||||
346 | 179 | if ($existingParameter !== null) { |
|||
347 | 5 | $existingParameter->setValue($value, $type); |
|||
348 | |||||
349 | 5 | return $this; |
|||
350 | } |
||||
351 | |||||
352 | 177 | $this->parameters->add(new Parameter($key, $value, $type)); |
|||
353 | |||||
354 | 177 | return $this; |
|||
355 | } |
||||
356 | |||||
357 | /** |
||||
358 | * Processes an individual parameter value. |
||||
359 | * |
||||
360 | * @param mixed $value |
||||
361 | * |
||||
362 | * @return string|mixed[] |
||||
363 | * |
||||
364 | * @throws \Doctrine\ORM\ORMInvalidArgumentException |
||||
365 | */ |
||||
366 | 174 | public function processParameterValue($value) |
|||
367 | { |
||||
368 | 174 | if (is_scalar($value)) { |
|||
369 | 162 | return $value; |
|||
370 | } |
||||
371 | |||||
372 | 89 | if ($value instanceof Mapping\ClassMetadata) { |
|||
373 | 1 | return $value->discriminatorValue ?: $value->getClassName(); |
|||
374 | } |
||||
375 | |||||
376 | 88 | if ($value instanceof Collection) { |
|||
377 | 1 | $value = $value->toArray(); |
|||
378 | } |
||||
379 | |||||
380 | 88 | if (is_array($value)) { |
|||
381 | 73 | foreach ($value as $key => $paramValue) { |
|||
382 | 73 | $paramValue = $this->processParameterValue($paramValue); |
|||
383 | 73 | $value[$key] = is_array($paramValue) ? reset($paramValue) : $paramValue; |
|||
384 | } |
||||
385 | |||||
386 | 73 | return $value; |
|||
387 | } |
||||
388 | |||||
389 | 18 | if (is_object($value) && $this->em->getMetadataFactory()->hasMetadataFor(StaticClassNameConverter::getClass($value))) { |
|||
390 | 13 | $value = $this->em->getUnitOfWork()->getSingleIdentifierValue($value); |
|||
391 | |||||
392 | 13 | if ($value === null) { |
|||
393 | throw ORMInvalidArgumentException::invalidIdentifierBindingEntity(); |
||||
394 | } |
||||
395 | } |
||||
396 | |||||
397 | 18 | return $value; |
|||
398 | } |
||||
399 | |||||
400 | /** |
||||
401 | * Sets the ResultSetMapping that should be used for hydration. |
||||
402 | * |
||||
403 | * |
||||
404 | * @return static This query instance. |
||||
405 | */ |
||||
406 | 31 | public function setResultSetMapping(Query\ResultSetMapping $rsm) |
|||
407 | { |
||||
408 | 31 | $this->resultSetMapping = $rsm; |
|||
409 | |||||
410 | 31 | return $this; |
|||
411 | } |
||||
412 | |||||
413 | /** |
||||
414 | * Gets the ResultSetMapping used for hydration. |
||||
415 | * |
||||
416 | * @return \Doctrine\ORM\Query\ResultSetMapping |
||||
417 | */ |
||||
418 | 23 | protected function getResultSetMapping() |
|||
419 | { |
||||
420 | 23 | return $this->resultSetMapping; |
|||
421 | } |
||||
422 | |||||
423 | /** |
||||
424 | * Set a cache profile for hydration caching. |
||||
425 | * |
||||
426 | * If no result cache driver is set in the QueryCacheProfile, the default |
||||
427 | * result cache driver is used from the configuration. |
||||
428 | * |
||||
429 | * Important: Hydration caching does NOT register entities in the |
||||
430 | * UnitOfWork when retrieved from the cache. Never use result cached |
||||
431 | * entities for requests that also flush the EntityManager. If you want |
||||
432 | * some form of caching with UnitOfWork registration you should use |
||||
433 | * {@see AbstractQuery::setResultCacheProfile()}. |
||||
434 | * |
||||
435 | * @example |
||||
436 | * $lifetime = 100; |
||||
437 | * $resultKey = "abc"; |
||||
438 | * $query->setHydrationCacheProfile(new QueryCacheProfile()); |
||||
439 | * $query->setHydrationCacheProfile(new QueryCacheProfile($lifetime, $resultKey)); |
||||
440 | * |
||||
441 | * |
||||
442 | * @return static This query instance. |
||||
443 | */ |
||||
444 | 3 | public function setHydrationCacheProfile(?QueryCacheProfile $profile = null) |
|||
445 | { |
||||
446 | 3 | if ($profile !== null && ! $profile->getResultCacheDriver()) { |
|||
447 | $resultCacheDriver = $this->em->getConfiguration()->getHydrationCacheImpl(); |
||||
448 | $profile = $profile->setResultCacheDriver($resultCacheDriver); |
||||
449 | } |
||||
450 | |||||
451 | 3 | $this->hydrationCacheProfile = $profile; |
|||
452 | |||||
453 | 3 | return $this; |
|||
454 | } |
||||
455 | |||||
456 | /** |
||||
457 | * @return \Doctrine\DBAL\Cache\QueryCacheProfile |
||||
458 | */ |
||||
459 | 3 | public function getHydrationCacheProfile() |
|||
460 | { |
||||
461 | 3 | return $this->hydrationCacheProfile; |
|||
462 | } |
||||
463 | |||||
464 | /** |
||||
465 | * Set a cache profile for the result cache. |
||||
466 | * |
||||
467 | * If no result cache driver is set in the QueryCacheProfile, the default |
||||
468 | * result cache driver is used from the configuration. |
||||
469 | * |
||||
470 | * |
||||
471 | * @return static This query instance. |
||||
472 | */ |
||||
473 | 1 | public function setResultCacheProfile(?QueryCacheProfile $profile = null) |
|||
474 | { |
||||
475 | 1 | if ($profile !== null && ! $profile->getResultCacheDriver()) { |
|||
476 | $resultCacheDriver = $this->em->getConfiguration()->getResultCacheImpl(); |
||||
477 | $profile = $profile->setResultCacheDriver($resultCacheDriver); |
||||
478 | } |
||||
479 | |||||
480 | 1 | $this->queryCacheProfile = $profile; |
|||
481 | |||||
482 | 1 | return $this; |
|||
483 | } |
||||
484 | |||||
485 | /** |
||||
486 | * Defines a cache driver to be used for caching result sets and implicitly enables caching. |
||||
487 | * |
||||
488 | * @param \Doctrine\Common\Cache\Cache|null $resultCacheDriver Cache driver |
||||
489 | * |
||||
490 | * @return static This query instance. |
||||
491 | * |
||||
492 | * @throws ORMException |
||||
493 | */ |
||||
494 | 8 | public function setResultCacheDriver($resultCacheDriver = null) |
|||
495 | { |
||||
496 | 8 | if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) { |
|||
497 | throw ORMException::invalidResultCacheDriver(); |
||||
498 | } |
||||
499 | |||||
500 | 8 | $this->queryCacheProfile = $this->queryCacheProfile |
|||
501 | 1 | ? $this->queryCacheProfile->setResultCacheDriver($resultCacheDriver) |
|||
0 ignored issues
–
show
Bug
introduced
by
Loading history...
|
|||||
502 | 7 | : new QueryCacheProfile(0, null, $resultCacheDriver); |
|||
503 | |||||
504 | 8 | return $this; |
|||
505 | } |
||||
506 | |||||
507 | /** |
||||
508 | * Returns the cache driver used for caching result sets. |
||||
509 | * |
||||
510 | * @deprecated |
||||
511 | * |
||||
512 | * @return \Doctrine\Common\Cache\Cache Cache driver |
||||
513 | */ |
||||
514 | 3 | public function getResultCacheDriver() |
|||
515 | { |
||||
516 | 3 | if ($this->queryCacheProfile && $this->queryCacheProfile->getResultCacheDriver()) { |
|||
517 | 3 | return $this->queryCacheProfile->getResultCacheDriver(); |
|||
518 | } |
||||
519 | |||||
520 | return $this->em->getConfiguration()->getResultCacheImpl(); |
||||
521 | } |
||||
522 | |||||
523 | /** |
||||
524 | * Set whether or not to cache the results of this query and if so, for |
||||
525 | * how long and which ID to use for the cache entry. |
||||
526 | * |
||||
527 | * @param bool $bool |
||||
528 | * @param int $lifetime |
||||
529 | * @param string $resultCacheId |
||||
530 | * |
||||
531 | * @return static This query instance. |
||||
532 | */ |
||||
533 | 9 | public function useResultCache($bool, $lifetime = null, $resultCacheId = null) |
|||
534 | { |
||||
535 | 9 | if ($bool) { |
|||
536 | 9 | $this->setResultCacheLifetime($lifetime); |
|||
537 | 9 | $this->setResultCacheId($resultCacheId); |
|||
538 | |||||
539 | 9 | return $this; |
|||
540 | } |
||||
541 | |||||
542 | 1 | $this->queryCacheProfile = null; |
|||
543 | |||||
544 | 1 | return $this; |
|||
545 | } |
||||
546 | |||||
547 | /** |
||||
548 | * Defines how long the result cache will be active before expire. |
||||
549 | * |
||||
550 | * @param int $lifetime How long the cache entry is valid. |
||||
551 | * |
||||
552 | * @return static This query instance. |
||||
553 | */ |
||||
554 | 10 | public function setResultCacheLifetime($lifetime) |
|||
555 | { |
||||
556 | 10 | $lifetime = ($lifetime !== null) ? (int) $lifetime : 0; |
|||
0 ignored issues
–
show
|
|||||
557 | |||||
558 | 10 | $this->queryCacheProfile = $this->queryCacheProfile |
|||
559 | 4 | ? $this->queryCacheProfile->setLifetime($lifetime) |
|||
560 | 6 | : new QueryCacheProfile($lifetime, null, $this->em->getConfiguration()->getResultCacheImpl()); |
|||
561 | |||||
562 | 10 | return $this; |
|||
563 | } |
||||
564 | |||||
565 | /** |
||||
566 | * Retrieves the lifetime of resultset cache. |
||||
567 | * |
||||
568 | * @deprecated |
||||
569 | * |
||||
570 | * @return int |
||||
571 | */ |
||||
572 | public function getResultCacheLifetime() |
||||
573 | { |
||||
574 | return $this->queryCacheProfile ? $this->queryCacheProfile->getLifetime() : 0; |
||||
575 | } |
||||
576 | |||||
577 | /** |
||||
578 | * Defines if the result cache is active or not. |
||||
579 | * |
||||
580 | * @param bool $expire Whether or not to force resultset cache expiration. |
||||
581 | * |
||||
582 | * @return static This query instance. |
||||
583 | */ |
||||
584 | 4 | public function expireResultCache($expire = true) |
|||
585 | { |
||||
586 | 4 | $this->expireResultCache = $expire; |
|||
587 | |||||
588 | 4 | return $this; |
|||
589 | } |
||||
590 | |||||
591 | /** |
||||
592 | * Retrieves if the resultset cache is active or not. |
||||
593 | * |
||||
594 | * @return bool |
||||
595 | */ |
||||
596 | 8 | public function getExpireResultCache() |
|||
597 | { |
||||
598 | 8 | return $this->expireResultCache; |
|||
599 | } |
||||
600 | |||||
601 | /** |
||||
602 | * @return QueryCacheProfile |
||||
603 | */ |
||||
604 | 1 | public function getQueryCacheProfile() |
|||
605 | { |
||||
606 | 1 | return $this->queryCacheProfile; |
|||
607 | } |
||||
608 | |||||
609 | /** |
||||
610 | * Change the default fetch mode of an association for this query. |
||||
611 | * |
||||
612 | * $fetchMode can be one of FetchMode::EAGER, FetchMode::LAZY or FetchMode::EXTRA_LAZY |
||||
613 | * |
||||
614 | * @param string $class |
||||
615 | * @param string $assocName |
||||
616 | * @param int $fetchMode |
||||
617 | * |
||||
618 | * @return static This query instance. |
||||
619 | */ |
||||
620 | 3 | public function setFetchMode($class, $assocName, $fetchMode) |
|||
621 | { |
||||
622 | 3 | if ($fetchMode !== Mapping\FetchMode::EAGER) { |
|||
0 ignored issues
–
show
|
|||||
623 | $fetchMode = Mapping\FetchMode::LAZY; |
||||
624 | } |
||||
625 | |||||
626 | 3 | $this->hints['fetchMode'][$class][$assocName] = $fetchMode; |
|||
627 | |||||
628 | 3 | return $this; |
|||
629 | } |
||||
630 | |||||
631 | /** |
||||
632 | * Defines the processing mode to be used during hydration / result set transformation. |
||||
633 | * |
||||
634 | * @param int $hydrationMode Doctrine processing mode to be used during hydration process. |
||||
635 | * One of the Query::HYDRATE_* constants. |
||||
636 | * |
||||
637 | * @return static This query instance. |
||||
638 | */ |
||||
639 | 380 | public function setHydrationMode($hydrationMode) |
|||
640 | { |
||||
641 | 380 | $this->hydrationMode = $hydrationMode; |
|||
642 | |||||
643 | 380 | return $this; |
|||
644 | } |
||||
645 | |||||
646 | /** |
||||
647 | * Gets the hydration mode currently used by the query. |
||||
648 | * |
||||
649 | * @return int |
||||
650 | */ |
||||
651 | 652 | public function getHydrationMode() |
|||
652 | { |
||||
653 | 652 | return $this->hydrationMode; |
|||
654 | } |
||||
655 | |||||
656 | /** |
||||
657 | * Gets the list of results for the query. |
||||
658 | * |
||||
659 | * Alias for execute(null, $hydrationMode = HYDRATE_OBJECT). |
||||
660 | * |
||||
661 | * @param int $hydrationMode |
||||
662 | * |
||||
663 | * @return mixed |
||||
664 | */ |
||||
665 | 303 | public function getResult($hydrationMode = self::HYDRATE_OBJECT) |
|||
666 | { |
||||
667 | 303 | return $this->execute(null, $hydrationMode); |
|||
668 | } |
||||
669 | |||||
670 | /** |
||||
671 | * Gets the array of results for the query. |
||||
672 | * |
||||
673 | * Alias for execute(null, HYDRATE_ARRAY). |
||||
674 | * |
||||
675 | * @return mixed[] |
||||
676 | */ |
||||
677 | 25 | public function getArrayResult() |
|||
678 | { |
||||
679 | 25 | return $this->execute(null, self::HYDRATE_ARRAY); |
|||
680 | } |
||||
681 | |||||
682 | /** |
||||
683 | * Gets the scalar results for the query. |
||||
684 | * |
||||
685 | * Alias for execute(null, HYDRATE_SCALAR). |
||||
686 | * |
||||
687 | * @return mixed[] |
||||
688 | */ |
||||
689 | 87 | public function getScalarResult() |
|||
690 | { |
||||
691 | 87 | return $this->execute(null, self::HYDRATE_SCALAR); |
|||
692 | } |
||||
693 | |||||
694 | /** |
||||
695 | * Get exactly one result or null. |
||||
696 | * |
||||
697 | * @param int $hydrationMode |
||||
698 | * |
||||
699 | * @return mixed |
||||
700 | * |
||||
701 | * @throws NonUniqueResultException |
||||
702 | */ |
||||
703 | 14 | public function getOneOrNullResult($hydrationMode = null) |
|||
704 | { |
||||
705 | try { |
||||
706 | 14 | $result = $this->execute(null, $hydrationMode); |
|||
707 | 1 | } catch (NoResultException $e) { |
|||
708 | 1 | return null; |
|||
709 | } |
||||
710 | |||||
711 | 13 | if ($this->hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) { |
|||
712 | 1 | return null; |
|||
713 | } |
||||
714 | |||||
715 | 12 | if (! is_array($result)) { |
|||
716 | 1 | return $result; |
|||
717 | } |
||||
718 | |||||
719 | 12 | if (count($result) > 1) { |
|||
720 | 1 | throw new NonUniqueResultException; |
|||
721 | } |
||||
722 | |||||
723 | 11 | return array_shift($result); |
|||
724 | } |
||||
725 | |||||
726 | /** |
||||
727 | * Gets the single result of the query. |
||||
728 | * |
||||
729 | * Enforces the presence as well as the uniqueness of the result. |
||||
730 | * |
||||
731 | * If the result is not unique, a NonUniqueResultException is thrown. |
||||
732 | * If there is no result, a NoResultException is thrown. |
||||
733 | * |
||||
734 | * @param int $hydrationMode |
||||
735 | * |
||||
736 | * @return mixed |
||||
737 | * |
||||
738 | * @throws NonUniqueResultException If the query result is not unique. |
||||
739 | * @throws NoResultException If the query returned no result |
||||
740 | */ |
||||
741 | 99 | public function getSingleResult($hydrationMode = null) |
|||
742 | { |
||||
743 | 99 | $result = $this->execute(null, $hydrationMode); |
|||
744 | |||||
745 | 93 | if ($this->hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) { |
|||
746 | 2 | throw new NoResultException; |
|||
747 | } |
||||
748 | |||||
749 | 92 | if (! is_array($result)) { |
|||
750 | 9 | return $result; |
|||
751 | } |
||||
752 | |||||
753 | 84 | if (count($result) > 1) { |
|||
754 | 1 | throw new NonUniqueResultException; |
|||
755 | } |
||||
756 | |||||
757 | 83 | return array_shift($result); |
|||
758 | } |
||||
759 | |||||
760 | /** |
||||
761 | * Gets the single scalar result of the query. |
||||
762 | * |
||||
763 | * Alias for getSingleResult(HYDRATE_SINGLE_SCALAR). |
||||
764 | * |
||||
765 | * @return mixed The scalar result, or NULL if the query returned no result. |
||||
766 | * |
||||
767 | * @throws NonUniqueResultException If the query result is not unique. |
||||
768 | * @throws NoResultException If the query returned no result |
||||
769 | */ |
||||
770 | 11 | public function getSingleScalarResult() |
|||
771 | { |
||||
772 | 11 | return $this->getSingleResult(self::HYDRATE_SINGLE_SCALAR); |
|||
773 | } |
||||
774 | |||||
775 | /** |
||||
776 | * Sets a query hint. If the hint name is not recognized, it is silently ignored. |
||||
777 | * |
||||
778 | * @param string $name The name of the hint. |
||||
779 | * @param mixed $value The value of the hint. |
||||
780 | * |
||||
781 | * @return static This query instance. |
||||
782 | */ |
||||
783 | 464 | public function setHint($name, $value) |
|||
784 | { |
||||
785 | 464 | $this->hints[$name] = $value; |
|||
786 | |||||
787 | 464 | return $this; |
|||
788 | } |
||||
789 | |||||
790 | /** |
||||
791 | * Gets the value of a query hint. If the hint name is not recognized, FALSE is returned. |
||||
792 | * |
||||
793 | * @param string $name The name of the hint. |
||||
794 | * |
||||
795 | * @return mixed The value of the hint or FALSE, if the hint name is not recognized. |
||||
796 | */ |
||||
797 | 795 | public function getHint($name) |
|||
798 | { |
||||
799 | 795 | return $this->hints[$name] ?? false; |
|||
800 | } |
||||
801 | |||||
802 | /** |
||||
803 | * Check if the query has a hint |
||||
804 | * |
||||
805 | * @param string $name The name of the hint |
||||
806 | * |
||||
807 | * @return bool False if the query does not have any hint |
||||
808 | */ |
||||
809 | 17 | public function hasHint($name) |
|||
810 | { |
||||
811 | 17 | return isset($this->hints[$name]); |
|||
812 | } |
||||
813 | |||||
814 | /** |
||||
815 | * Return the key value map of query hints that are currently set. |
||||
816 | * |
||||
817 | * @return mixed[] |
||||
818 | */ |
||||
819 | 136 | public function getHints() |
|||
820 | { |
||||
821 | 136 | return $this->hints; |
|||
822 | } |
||||
823 | |||||
824 | /** |
||||
825 | * Executes the query and returns an IterableResult that can be used to incrementally |
||||
826 | * iterate over the result. |
||||
827 | * |
||||
828 | * @param ArrayCollection|array|Parameter[]|mixed[]|null $parameters The query parameters. |
||||
829 | * @param int|null $hydrationMode The hydration mode to use. |
||||
830 | * |
||||
831 | * @return \Doctrine\ORM\Internal\Hydration\IterableResult |
||||
832 | */ |
||||
833 | 10 | public function iterate($parameters = null, $hydrationMode = null) |
|||
834 | { |
||||
835 | 10 | if ($hydrationMode !== null) { |
|||
836 | 10 | $this->setHydrationMode($hydrationMode); |
|||
837 | } |
||||
838 | |||||
839 | 10 | if (! empty($parameters)) { |
|||
840 | 1 | $this->setParameters($parameters); |
|||
841 | } |
||||
842 | |||||
843 | 10 | $rsm = $this->getResultSetMapping(); |
|||
844 | 7 | $stmt = $this->doExecute(); |
|||
845 | |||||
846 | 7 | return $this->em->newHydrator($this->hydrationMode)->iterate($stmt, $rsm, $this->hints); |
|||
847 | } |
||||
848 | |||||
849 | /** |
||||
850 | * Executes the query. |
||||
851 | * |
||||
852 | * @param ArrayCollection|array|Parameter[]|mixed[]|null $parameters Query parameters. |
||||
853 | * @param int|null $hydrationMode Processing mode to be used during the hydration process. |
||||
854 | * |
||||
855 | * @return mixed |
||||
856 | */ |
||||
857 | 471 | public function execute($parameters = null, $hydrationMode = null) |
|||
858 | { |
||||
859 | 471 | if ($this->cacheable && $this->isCacheEnabled()) { |
|||
860 | 29 | return $this->executeUsingQueryCache($parameters, $hydrationMode); |
|||
861 | } |
||||
862 | |||||
863 | 444 | return $this->executeIgnoreQueryCache($parameters, $hydrationMode); |
|||
864 | } |
||||
865 | |||||
866 | /** |
||||
867 | * Execute query ignoring second level cache. |
||||
868 | * |
||||
869 | * @param ArrayCollection|array|Parameter[]|mixed[]|null $parameters |
||||
870 | * @param int|null $hydrationMode |
||||
871 | * |
||||
872 | * @return mixed |
||||
873 | */ |
||||
874 | 471 | private function executeIgnoreQueryCache($parameters = null, $hydrationMode = null) |
|||
875 | { |
||||
876 | 471 | if ($hydrationMode !== null) { |
|||
877 | 370 | $this->setHydrationMode($hydrationMode); |
|||
878 | } |
||||
879 | |||||
880 | 471 | if (! empty($parameters)) { |
|||
881 | $this->setParameters($parameters); |
||||
882 | } |
||||
883 | |||||
884 | 471 | $setCacheEntry = function () { |
|||
885 | 471 | }; |
|||
886 | |||||
887 | 471 | if ($this->hydrationCacheProfile !== null) { |
|||
888 | 2 | list($cacheKey, $realCacheKey) = $this->getHydrationCacheId(); |
|||
889 | |||||
890 | 2 | $queryCacheProfile = $this->getHydrationCacheProfile(); |
|||
891 | 2 | $cache = $queryCacheProfile->getResultCacheDriver(); |
|||
892 | 2 | $result = $cache->fetch($cacheKey); |
|||
893 | |||||
894 | 2 | if (isset($result[$realCacheKey])) { |
|||
895 | 2 | return $result[$realCacheKey]; |
|||
896 | } |
||||
897 | |||||
898 | 2 | if (! $result) { |
|||
899 | 2 | $result = []; |
|||
900 | } |
||||
901 | |||||
902 | 2 | $setCacheEntry = function ($data) use ($cache, $result, $cacheKey, $realCacheKey, $queryCacheProfile) { |
|||
903 | 2 | $result[$realCacheKey] = $data; |
|||
904 | |||||
905 | 2 | $cache->save($cacheKey, $result, $queryCacheProfile->getLifetime()); |
|||
906 | 2 | }; |
|||
907 | } |
||||
908 | |||||
909 | 471 | $stmt = $this->doExecute(); |
|||
910 | |||||
911 | 461 | if (is_numeric($stmt)) { |
|||
912 | 27 | $setCacheEntry($stmt); |
|||
913 | |||||
914 | 27 | return $stmt; |
|||
915 | } |
||||
916 | |||||
917 | 446 | $rsm = $this->getResultSetMapping(); |
|||
918 | 446 | $data = $this->em->newHydrator($this->hydrationMode)->hydrateAll($stmt, $rsm, $this->hints); |
|||
919 | |||||
920 | 442 | $setCacheEntry($data); |
|||
921 | |||||
922 | 442 | return $data; |
|||
923 | } |
||||
924 | |||||
925 | /** |
||||
926 | * Load from second level cache or executes the query and put into cache. |
||||
927 | * |
||||
928 | * @param ArrayCollection|array|Parameter[]|mixed[]|null $parameters |
||||
929 | * @param int|null $hydrationMode |
||||
930 | * |
||||
931 | * @return mixed |
||||
932 | */ |
||||
933 | 29 | private function executeUsingQueryCache($parameters = null, $hydrationMode = null) |
|||
934 | { |
||||
935 | 29 | $rsm = $this->getResultSetMapping(); |
|||
936 | 29 | $queryCache = $this->em->getCache()->getQueryCache($this->cacheRegion); |
|||
937 | 29 | $queryKey = new QueryCacheKey( |
|||
938 | 29 | $this->getHash(), |
|||
939 | 29 | $this->lifetime, |
|||
940 | 29 | $this->cacheMode ?: Cache::MODE_NORMAL, |
|||
941 | 29 | $this->getTimestampKey() |
|||
942 | ); |
||||
943 | |||||
944 | 29 | $result = $queryCache->get($queryKey, $rsm, $this->hints); |
|||
945 | |||||
946 | 29 | if ($result !== null) { |
|||
947 | 16 | if ($this->cacheLogger) { |
|||
948 | 16 | $this->cacheLogger->queryCacheHit($queryCache->getRegion()->getName(), $queryKey); |
|||
949 | } |
||||
950 | |||||
951 | 16 | return $result; |
|||
952 | } |
||||
953 | |||||
954 | 29 | $result = $this->executeIgnoreQueryCache($parameters, $hydrationMode); |
|||
955 | 29 | $cached = $queryCache->put($queryKey, $rsm, $result, $this->hints); |
|||
956 | |||||
957 | 26 | if ($this->cacheLogger) { |
|||
958 | 26 | $this->cacheLogger->queryCacheMiss($queryCache->getRegion()->getName(), $queryKey); |
|||
959 | |||||
960 | 26 | if ($cached) { |
|||
961 | 26 | $this->cacheLogger->queryCachePut($queryCache->getRegion()->getName(), $queryKey); |
|||
962 | } |
||||
963 | } |
||||
964 | |||||
965 | 26 | return $result; |
|||
966 | } |
||||
967 | |||||
968 | /** |
||||
969 | * @return \Doctrine\ORM\Cache\TimestampCacheKey|null |
||||
970 | */ |
||||
971 | 29 | private function getTimestampKey() |
|||
972 | { |
||||
973 | 29 | $entityName = reset($this->resultSetMapping->aliasMap); |
|||
974 | |||||
975 | 29 | if (empty($entityName)) { |
|||
976 | 2 | return null; |
|||
977 | } |
||||
978 | |||||
979 | 27 | $metadata = $this->em->getClassMetadata($entityName); |
|||
980 | |||||
981 | 27 | return new Cache\TimestampCacheKey($metadata->getRootClassName()); |
|||
0 ignored issues
–
show
The method
getRootClassName() does not exist on Doctrine\Common\Persistence\Mapping\ClassMetadata .
(
Ignorable by Annotation
)
If this is a false-positive, you can also ignore this issue in your code via the
This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces. This is most likely a typographical error or the method has been renamed.
Loading history...
|
|||||
982 | } |
||||
983 | |||||
984 | /** |
||||
985 | * Get the result cache id to use to store the result set cache entry. |
||||
986 | * Will return the configured id if it exists otherwise a hash will be |
||||
987 | * automatically generated for you. |
||||
988 | * |
||||
989 | * @return string[] ($key, $hash) |
||||
990 | */ |
||||
991 | 2 | protected function getHydrationCacheId() |
|||
992 | { |
||||
993 | 2 | $parameters = []; |
|||
994 | |||||
995 | 2 | foreach ($this->getParameters() as $parameter) { |
|||
996 | 1 | $parameters[$parameter->getName()] = $this->processParameterValue($parameter->getValue()); |
|||
997 | } |
||||
998 | |||||
999 | 2 | $sql = $this->getSQL(); |
|||
1000 | 2 | $queryCacheProfile = $this->getHydrationCacheProfile(); |
|||
1001 | 2 | $hints = $this->getHints(); |
|||
1002 | 2 | $hints['hydrationMode'] = $this->getHydrationMode(); |
|||
1003 | |||||
1004 | 2 | ksort($hints); |
|||
1005 | |||||
1006 | 2 | return $queryCacheProfile->generateCacheKeys($sql, $parameters, $hints); |
|||
1007 | } |
||||
1008 | |||||
1009 | /** |
||||
1010 | * Set the result cache id to use to store the result set cache entry. |
||||
1011 | * If this is not explicitly set by the developer then a hash is automatically |
||||
1012 | * generated for you. |
||||
1013 | * |
||||
1014 | * @param string $id |
||||
1015 | * |
||||
1016 | * @return static This query instance. |
||||
1017 | */ |
||||
1018 | 12 | public function setResultCacheId($id) |
|||
1019 | { |
||||
1020 | 12 | $this->queryCacheProfile = $this->queryCacheProfile |
|||
1021 | 12 | ? $this->queryCacheProfile->setCacheKey($id) |
|||
1022 | : new QueryCacheProfile(0, $id, $this->em->getConfiguration()->getResultCacheImpl()); |
||||
1023 | |||||
1024 | 12 | return $this; |
|||
1025 | } |
||||
1026 | |||||
1027 | /** |
||||
1028 | * Get the result cache id to use to store the result set cache entry if set. |
||||
1029 | * |
||||
1030 | * @deprecated |
||||
1031 | * |
||||
1032 | * @return string |
||||
1033 | */ |
||||
1034 | public function getResultCacheId() |
||||
1035 | { |
||||
1036 | return $this->queryCacheProfile ? $this->queryCacheProfile->getCacheKey() : null; |
||||
1037 | } |
||||
1038 | |||||
1039 | /** |
||||
1040 | * Executes the query and returns a the resulting Statement object. |
||||
1041 | * |
||||
1042 | * @return \Doctrine\DBAL\Driver\Statement The executed database statement that holds the results. |
||||
1043 | */ |
||||
1044 | abstract protected function doExecute(); |
||||
1045 | |||||
1046 | /** |
||||
1047 | * Cleanup Query resource when clone is called. |
||||
1048 | */ |
||||
1049 | 139 | public function __clone() |
|||
1050 | { |
||||
1051 | 139 | $this->parameters = new ArrayCollection(); |
|||
1052 | 139 | $this->hints = $this->em->getConfiguration()->getDefaultQueryHints(); |
|||
1053 | 139 | } |
|||
1054 | |||||
1055 | /** |
||||
1056 | * Generates a string of currently query to use for the cache second level cache. |
||||
1057 | * |
||||
1058 | * @return string |
||||
1059 | */ |
||||
1060 | 29 | protected function getHash() |
|||
1061 | { |
||||
1062 | 29 | $query = $this->getSQL(); |
|||
1063 | 29 | $hints = $this->getHints(); |
|||
1064 | 29 | $params = array_map(function (Parameter $parameter) { |
|||
1065 | 5 | $value = $parameter->getValue(); |
|||
1066 | |||||
1067 | // Small optimization |
||||
1068 | // Does not invoke processParameterValue for scalar values |
||||
1069 | 5 | if (is_scalar($value)) { |
|||
1070 | 4 | return $value; |
|||
1071 | } |
||||
1072 | |||||
1073 | 1 | return $this->processParameterValue($value); |
|||
1074 | 29 | }, $this->parameters->getValues()); |
|||
1075 | |||||
1076 | 29 | ksort($hints); |
|||
1077 | |||||
1078 | 29 | return sha1($query . '-' . serialize($params) . '-' . serialize($hints)); |
|||
1079 | } |
||||
1080 | } |
||||
1081 |