Passed
Pull Request — 2.7 (#7213)
by
unknown
08:50
created

AbstractQuery::getHydrationMode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license. For more information, see
17
 * <http://www.doctrine-project.org>.
18
 */
19
20
namespace Doctrine\ORM;
21
22
use Doctrine\Common\Util\ClassUtils;
23
use Doctrine\Common\Collections\Collection;
24
use Doctrine\Common\Collections\ArrayCollection;
25
26
use Doctrine\ORM\Query\Parameter;
27
use Doctrine\ORM\Cache\QueryCacheKey;
28
use Doctrine\DBAL\Cache\QueryCacheProfile;
29
30
/**
31
 * Base contract for ORM queries. Base class for Query and NativeQuery.
32
 *
33
 * @link    www.doctrine-project.org
34
 * @since   2.0
35
 * @author  Benjamin Eberlei <[email protected]>
36
 * @author  Guilherme Blanco <[email protected]>
37
 * @author  Jonathan Wage <[email protected]>
38
 * @author  Roman Borschel <[email protected]>
39
 * @author  Konsta Vesterinen <[email protected]>
40
 */
41
abstract class AbstractQuery
42
{
43
    /* Hydration mode constants */
44
45
    /**
46
     * Hydrates an object graph. This is the default behavior.
47
     */
48
    const HYDRATE_OBJECT = 1;
49
50
    /**
51
     * Hydrates an array graph.
52
     */
53
    const HYDRATE_ARRAY = 2;
54
55
    /**
56
     * Hydrates a flat, rectangular result set with scalar values.
57
     */
58
    const HYDRATE_SCALAR = 3;
59
60
    /**
61
     * Hydrates a single scalar value.
62
     */
63
    const HYDRATE_SINGLE_SCALAR = 4;
64
65
    /**
66
     * Very simple object hydrator (optimized for performance).
67
     */
68
    const HYDRATE_SIMPLEOBJECT = 5;
69
70
    /**
71
     * The parameter map of this query.
72
     *
73
     * @var \Doctrine\Common\Collections\ArrayCollection
74
     */
75
    protected $parameters;
76
77
    /**
78
     * The user-specified ResultSetMapping to use.
79
     *
80
     * @var \Doctrine\ORM\Query\ResultSetMapping
81
     */
82
    protected $_resultSetMapping;
83
84
    /**
85
     * The entity manager used by this query object.
86
     *
87
     * @var EntityManagerInterface
88
     */
89
    protected $_em;
90
91
    /**
92
     * The map of query hints.
93
     *
94
     * @var array
95
     */
96
    protected $_hints = [];
97
98
    /**
99
     * The hydration mode.
100
     *
101
     * @var integer
102
     */
103
    protected $_hydrationMode = self::HYDRATE_OBJECT;
104
105
    /**
106
     * @var \Doctrine\DBAL\Cache\QueryCacheProfile
107
     */
108
    protected $_queryCacheProfile;
109
110
    /**
111
     * Whether or not expire the result cache.
112
     *
113
     * @var boolean
114
     */
115
    protected $_expireResultCache = false;
116
117
    /**
118
     * @var \Doctrine\DBAL\Cache\QueryCacheProfile
119
     */
120
    protected $_hydrationCacheProfile;
121
122
    /**
123
     * Whether to use second level cache, if available.
124
     *
125
     * @var boolean
126
     */
127
    protected $cacheable = false;
128
129
    /**
130
     * @var boolean
131
     */
132
    protected $hasCache = false;
133
134
    /**
135
     * Second level cache region name.
136
     *
137
     * @var string|null
138
     */
139
    protected $cacheRegion;
140
141
    /**
142
     * Second level query cache mode.
143
     *
144
     * @var integer|null
145
     */
146
    protected $cacheMode;
147
148
    /**
149
     * @var \Doctrine\ORM\Cache\Logging\CacheLogger|null
150
     */
151
    protected $cacheLogger;
152
153
    /**
154
     * @var integer
155
     */
156
    protected $lifetime = 0;
157
158
    /**
159
     * Initializes a new instance of a class derived from <tt>AbstractQuery</tt>.
160
     *
161
     * @param \Doctrine\ORM\EntityManagerInterface $em
162
     */
163 998
    public function __construct(EntityManagerInterface $em)
164
    {
165 998
        $this->_em          = $em;
166 998
        $this->parameters   = new ArrayCollection();
167 998
        $this->_hints       = $em->getConfiguration()->getDefaultQueryHints();
168 998
        $this->hasCache     = $this->_em->getConfiguration()->isSecondLevelCacheEnabled();
169
170 998
        if ($this->hasCache) {
171 32
            $this->cacheLogger = $em->getConfiguration()
172 32
                ->getSecondLevelCacheConfiguration()
173 32
                ->getCacheLogger();
174
        }
175 998
    }
176
177
    /**
178
     * Enable/disable second level query (result) caching for this query.
179
     *
180
     * @param boolean $cacheable
181
     *
182
     * @return static This query instance.
183
     */
184 133
    public function setCacheable($cacheable)
185
    {
186 133
        $this->cacheable = (boolean) $cacheable;
187
188 133
        return $this;
189
    }
190
191
    /**
192
     * @return boolean TRUE if the query results are enable for second level cache, FALSE otherwise.
193
     */
194 90
    public function isCacheable()
195
    {
196 90
        return $this->cacheable;
197
    }
198
199
    /**
200
     * @param string $cacheRegion
201
     *
202
     * @return static This query instance.
203
     */
204 2
    public function setCacheRegion($cacheRegion)
205
    {
206 2
        $this->cacheRegion = (string) $cacheRegion;
207
208 2
        return $this;
209
    }
210
211
    /**
212
    * Obtain the name of the second level query cache region in which query results will be stored
213
    *
214
    * @return string|null The cache region name; NULL indicates the default region.
215
    */
216 1
    public function getCacheRegion()
217
    {
218 1
        return $this->cacheRegion;
219
    }
220
221
    /**
222
     * @return boolean TRUE if the query cache and second level cache are enabled, FALSE otherwise.
223
     */
224 29
    protected function isCacheEnabled()
225
    {
226 29
        return $this->cacheable && $this->hasCache;
227
    }
228
229
    /**
230
     * @return integer
231
     */
232 1
    public function getLifetime()
233
    {
234 1
        return $this->lifetime;
235
    }
236
237
    /**
238
     * Sets the life-time for this query into second level cache.
239
     *
240
     * @param integer $lifetime
241
     *
242
     * @return \Doctrine\ORM\AbstractQuery This query instance.
243
     */
244 2
    public function setLifetime($lifetime)
245
    {
246 2
        $this->lifetime = (integer) $lifetime;
247
248 2
        return $this;
249
    }
250
251
    /**
252
     * @return integer
253
     */
254 1
    public function getCacheMode()
255
    {
256 1
        return $this->cacheMode;
257
    }
258
259
    /**
260
     * @param integer $cacheMode
261
     *
262
     * @return \Doctrine\ORM\AbstractQuery This query instance.
263
     */
264 4
    public function setCacheMode($cacheMode)
265
    {
266 4
        $this->cacheMode = (integer) $cacheMode;
267
268 4
        return $this;
269
    }
270
271
    /**
272
     * Gets the SQL query that corresponds to this query object.
273
     * The returned SQL syntax depends on the connection driver that is used
274
     * by this query object at the time of this method call.
275
     *
276
     * @return string SQL query
277
     */
278
    abstract public function getSQL();
279
280
    /**
281
     * Retrieves the associated EntityManager of this Query instance.
282
     *
283
     * @return \Doctrine\ORM\EntityManager
284
     */
285 915
    public function getEntityManager()
286
    {
287 915
        return $this->_em;
288
    }
289
290
    /**
291
     * Frees the resources used by the query object.
292
     *
293
     * Resets Parameters, Parameter Types and Query Hints.
294
     *
295
     * @return void
296
     */
297 212
    public function free()
298
    {
299 212
        $this->parameters = new ArrayCollection();
300
301 212
        $this->_hints = $this->_em->getConfiguration()->getDefaultQueryHints();
302 212
    }
303
304
    /**
305
     * Get all defined parameters.
306
     *
307
     * @return \Doctrine\Common\Collections\ArrayCollection The defined query parameters.
308
     */
309 143
    public function getParameters()
310
    {
311 143
        return $this->parameters;
312
    }
313
314
    /**
315
     * Gets a query parameter.
316
     *
317
     * @param mixed $key The key (index or name) of the bound parameter.
318
     *
319
     * @return Query\Parameter|null The value of the bound parameter, or NULL if not available.
320
     */
321 274
    public function getParameter($key)
322
    {
323 274
        $filteredParameters = $this->parameters->filter(
324 274
            function (Query\Parameter $parameter) use ($key) : bool {
325 156
                $parameterName = $parameter->getName();
326
327 156
                return $key === $parameterName || (string) $key === (string) $parameterName;
328 274
            }
329
        );
330
331 274
        return ! $filteredParameters->isEmpty() ? $filteredParameters->first() : null;
332
    }
333
334
    /**
335
     * Sets a collection of query parameters.
336
     *
337
     * @param \Doctrine\Common\Collections\ArrayCollection|array $parameters
338
     *
339
     * @return static This query instance.
340
     */
341 191
    public function setParameters($parameters)
342
    {
343
        // BC compatibility with 2.3-
344 191
        if (is_array($parameters)) {
345 1
            $parameterCollection = new ArrayCollection();
346
347 1
            foreach ($parameters as $key => $value) {
348 1
                $parameterCollection->add(new Parameter($key, $value));
349
            }
350
351 1
            $parameters = $parameterCollection;
352
        }
353
354 191
        $this->parameters = $parameters;
355
356 191
        return $this;
357
    }
358
359
    /**
360
     * Sets a query parameter.
361
     *
362
     * @param string|int  $key   The parameter position or name.
363
     * @param mixed       $value The parameter value.
364
     * @param string|null $type  The parameter type. If specified, the given value will be run through
365
     *                           the type conversion of this type. This is usually not needed for
366
     *                           strings and numeric types.
367
     *
368
     * @return static This query instance.
369
     */
370 185
    public function setParameter($key, $value, $type = null)
371
    {
372 185
        $existingParameter = $this->getParameter($key);
373
374 185
        if ($existingParameter !== null) {
375 6
            $existingParameter->setValue($value, $type);
376
377 6
            return $this;
378
        }
379
380 183
        $this->parameters->add(new Parameter($key, $value, $type));
381
382 183
        return $this;
383
    }
384
385
    /**
386
     * Processes an individual parameter value.
387
     *
388
     * @param mixed $value
389
     *
390
     * @return array|string
391
     *
392
     * @throws \Doctrine\ORM\ORMInvalidArgumentException
393
     */
394 183
    public function processParameterValue($value)
395
    {
396 183
        if (is_scalar($value)) {
397 170
            return $value;
398
        }
399
400 90
        if ($value instanceof Collection) {
401 1
            $value = $value->toArray();
402
        }
403
404 90
        if (is_array($value)) {
405 73
            foreach ($value as $key => $paramValue) {
406 73
                $paramValue  = $this->processParameterValue($paramValue);
407 73
                $value[$key] = is_array($paramValue) ? reset($paramValue) : $paramValue;
408
            }
409
410 73
            return $value;
411
        }
412
413 20
        if (is_object($value) && $this->_em->getMetadataFactory()->hasMetadataFor(ClassUtils::getClass($value))) {
414 14
            $value = $this->_em->getUnitOfWork()->getSingleIdentifierValue($value);
415
416 14
            if ($value === null) {
417
                throw ORMInvalidArgumentException::invalidIdentifierBindingEntity();
418
            }
419
        }
420
421 20
        if ($value instanceof Mapping\ClassMetadata) {
422 1
            return $value->name;
423
        }
424
425 19
        return $value;
426
    }
427
428
    /**
429
     * Sets the ResultSetMapping that should be used for hydration.
430
     *
431
     * @param \Doctrine\ORM\Query\ResultSetMapping $rsm
432
     *
433
     * @return static This query instance.
434
     */
435 30
    public function setResultSetMapping(Query\ResultSetMapping $rsm)
436
    {
437 30
        $this->translateNamespaces($rsm);
438 30
        $this->_resultSetMapping = $rsm;
439
440 30
        return $this;
441
    }
442
443
    /**
444
     * Gets the ResultSetMapping used for hydration.
445
     *
446
     * @return \Doctrine\ORM\Query\ResultSetMapping
447
     */
448 22
    protected function getResultSetMapping()
449
    {
450 22
        return $this->_resultSetMapping;
451
    }
452
453
    /**
454
     * Allows to translate entity namespaces to full qualified names.
455
     *
456
     * @param Query\ResultSetMapping $rsm
457
     *
458
     * @return void
459
     */
460
    private function translateNamespaces(Query\ResultSetMapping $rsm)
461
    {
462 30
        $translate = function ($alias) {
463 18
            return $this->_em->getClassMetadata($alias)->getName();
464 30
        };
465
466 30
        $rsm->aliasMap = array_map($translate, $rsm->aliasMap);
467 30
        $rsm->declaringClasses = array_map($translate, $rsm->declaringClasses);
468 30
    }
469
470
    /**
471
     * Set a cache profile for hydration caching.
472
     *
473
     * If no result cache driver is set in the QueryCacheProfile, the default
474
     * result cache driver is used from the configuration.
475
     *
476
     * Important: Hydration caching does NOT register entities in the
477
     * UnitOfWork when retrieved from the cache. Never use result cached
478
     * entities for requests that also flush the EntityManager. If you want
479
     * some form of caching with UnitOfWork registration you should use
480
     * {@see AbstractQuery::setResultCacheProfile()}.
481
     *
482
     * @example
483
     * $lifetime = 100;
484
     * $resultKey = "abc";
485
     * $query->setHydrationCacheProfile(new QueryCacheProfile());
486
     * $query->setHydrationCacheProfile(new QueryCacheProfile($lifetime, $resultKey));
487
     *
488
     * @param \Doctrine\DBAL\Cache\QueryCacheProfile $profile
489
     *
490
     * @return static This query instance.
491
     */
492 3
    public function setHydrationCacheProfile(QueryCacheProfile $profile = null)
493
    {
494 3
        if ($profile !== null && ! $profile->getResultCacheDriver()) {
495
            $resultCacheDriver = $this->_em->getConfiguration()->getHydrationCacheImpl();
496
            $profile = $profile->setResultCacheDriver($resultCacheDriver);
497
        }
498
499 3
        $this->_hydrationCacheProfile = $profile;
500
501 3
        return $this;
502
    }
503
504
    /**
505
     * @return \Doctrine\DBAL\Cache\QueryCacheProfile
506
     */
507 3
    public function getHydrationCacheProfile()
508
    {
509 3
        return $this->_hydrationCacheProfile;
510
    }
511
512
    /**
513
     * Set a cache profile for the result cache.
514
     *
515
     * If no result cache driver is set in the QueryCacheProfile, the default
516
     * result cache driver is used from the configuration.
517
     *
518
     * @param \Doctrine\DBAL\Cache\QueryCacheProfile $profile
519
     *
520
     * @return static This query instance.
521
     */
522 1
    public function setResultCacheProfile(QueryCacheProfile $profile = null)
523
    {
524 1
        if ($profile !== null && ! $profile->getResultCacheDriver()) {
525
            $resultCacheDriver = $this->_em->getConfiguration()->getResultCacheImpl();
526
            $profile = $profile->setResultCacheDriver($resultCacheDriver);
527
        }
528
529 1
        $this->_queryCacheProfile = $profile;
530
531 1
        return $this;
532
    }
533
534
    /**
535
     * Defines a cache driver to be used for caching result sets and implicitly enables caching.
536
     *
537
     * @param \Doctrine\Common\Cache\Cache|null $resultCacheDriver Cache driver
538
     *
539
     * @return static This query instance.
540
     *
541
     * @throws ORMException
542
     */
543 12
    public function setResultCacheDriver($resultCacheDriver = null)
544
    {
545 12
        if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) {
0 ignored issues
show
introduced by
$resultCacheDriver is always a sub-type of Doctrine\Common\Cache\Cache.
Loading history...
546
            throw ORMException::invalidResultCacheDriver();
547
        }
548
549 12
        $this->_queryCacheProfile = $this->_queryCacheProfile
550 2
            ? $this->_queryCacheProfile->setResultCacheDriver($resultCacheDriver)
0 ignored issues
show
Bug introduced by
It seems like $resultCacheDriver can also be of type null; however, parameter $cache of Doctrine\DBAL\Cache\Quer...:setResultCacheDriver() does only seem to accept Doctrine\Common\Cache\Cache, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

550
            ? $this->_queryCacheProfile->setResultCacheDriver(/** @scrutinizer ignore-type */ $resultCacheDriver)
Loading history...
551 10
            : new QueryCacheProfile(0, null, $resultCacheDriver);
552
553 12
        return $this;
554
    }
555
556
    /**
557
     * Returns the cache driver used for caching result sets.
558
     *
559
     * @deprecated
560
     *
561
     * @return \Doctrine\Common\Cache\Cache Cache driver
562
     */
563 3
    public function getResultCacheDriver()
564
    {
565 3
        if ($this->_queryCacheProfile && $this->_queryCacheProfile->getResultCacheDriver()) {
566 3
            return $this->_queryCacheProfile->getResultCacheDriver();
567
        }
568
569
        return $this->_em->getConfiguration()->getResultCacheImpl();
570
    }
571
572
    /**
573
     * Set whether or not to cache the results of this query and if so, for
574
     * how long and which ID to use for the cache entry.
575
     *
576
     * @deprecated 3.0 Use {@see enableResultCache} and {@see disableResultCache} instead.
577
     *
578
     * @param boolean $useCache
579
     * @param integer $lifetime
580
     * @param string  $resultCacheId
581
     *
582
     * @return static This query instance.
583
     */
584 4
    public function useResultCache($useCache, $lifetime = null, $resultCacheId = null)
585
    {
586 4
        return $useCache
587 3
            ? $this->enableResultCache($lifetime, $resultCacheId)
588 4
            : $this->disableResultCache();
589
    }
590
591
    /**
592
     * Enables caching of the results of this query, for given or default amount of seconds
593
     * and optionally specifies which ID to use for the cache entry.
594
     *
595
     * @param integer|null $lifetime      How long the cache entry is valid, in seconds.
596
     * @param string|null  $resultCacheId ID to use for the cache entry.
597
     *
598
     * @return static This query instance.
599
     */
600 11
    public function enableResultCache($lifetime = null, $resultCacheId = null)
601
    {
602 11
        $this->setResultCacheLifetime($lifetime);
603 11
        $this->setResultCacheId($resultCacheId);
604
605 11
        return $this;
606
    }
607
608
    /**
609
     * Disables caching of the results of this query.
610
     *
611
     * @return static This query instance.
612
     */
613 3
    public function disableResultCache()
614
    {
615 3
        $this->_queryCacheProfile = null;
616
617 3
        return $this;
618
    }
619
620
    /**
621
     * Defines how long the result cache will be active before expire.
622
     *
623
     * @param integer $lifetime How long the cache entry is valid.
624
     *
625
     * @return static This query instance.
626
     */
627 12
    public function setResultCacheLifetime($lifetime)
628
    {
629 12
        $lifetime = ($lifetime !== null) ? (int) $lifetime : 0;
0 ignored issues
show
introduced by
The condition $lifetime !== null is always true.
Loading history...
630
631 12
        $this->_queryCacheProfile = $this->_queryCacheProfile
632 5
            ? $this->_queryCacheProfile->setLifetime($lifetime)
633 7
            : new QueryCacheProfile($lifetime, null, $this->_em->getConfiguration()->getResultCacheImpl());
634
635 12
        return $this;
636
    }
637
638
    /**
639
     * Retrieves the lifetime of resultset cache.
640
     *
641
     * @deprecated
642
     *
643
     * @return integer
644
     */
645
    public function getResultCacheLifetime()
646
    {
647
        return $this->_queryCacheProfile ? $this->_queryCacheProfile->getLifetime() : 0;
648
    }
649
650
    /**
651
     * Defines if the result cache is active or not.
652
     *
653
     * @param boolean $expire Whether or not to force resultset cache expiration.
654
     *
655
     * @return static This query instance.
656
     */
657 4
    public function expireResultCache($expire = true)
658
    {
659 4
        $this->_expireResultCache = $expire;
660
661 4
        return $this;
662
    }
663
664
    /**
665
     * Retrieves if the resultset cache is active or not.
666
     *
667
     * @return boolean
668
     */
669 10
    public function getExpireResultCache()
670
    {
671 10
        return $this->_expireResultCache;
672
    }
673
674
    /**
675
     * @return QueryCacheProfile
676
     */
677 1
    public function getQueryCacheProfile()
678
    {
679 1
        return $this->_queryCacheProfile;
680
    }
681
682
    /**
683
     * Change the default fetch mode of an association for this query.
684
     *
685
     * $fetchMode can be one of ClassMetadata::FETCH_EAGER or ClassMetadata::FETCH_LAZY
686
     *
687
     * @param string $class
688
     * @param string $assocName
689
     * @param int    $fetchMode
690
     *
691
     * @return static This query instance.
692
     */
693 3
    public function setFetchMode($class, $assocName, $fetchMode)
694
    {
695 3
        if ($fetchMode !== Mapping\ClassMetadata::FETCH_EAGER) {
696
            $fetchMode = Mapping\ClassMetadata::FETCH_LAZY;
697
        }
698
699 3
        $this->_hints['fetchMode'][$class][$assocName] = $fetchMode;
700
701 3
        return $this;
702
    }
703
704
    /**
705
     * Defines the processing mode to be used during hydration / result set transformation.
706
     *
707
     * @param integer $hydrationMode Doctrine processing mode to be used during hydration process.
708
     *                               One of the Query::HYDRATE_* constants.
709
     *
710
     * @return static This query instance.
711
     */
712 393
    public function setHydrationMode($hydrationMode)
713
    {
714 393
        $this->_hydrationMode = $hydrationMode;
715
716 393
        return $this;
717
    }
718
719
    /**
720
     * Gets the hydration mode currently used by the query.
721
     *
722
     * @return integer
723
     */
724 663
    public function getHydrationMode()
725
    {
726 663
        return $this->_hydrationMode;
727
    }
728
729
    /**
730
     * Gets the list of results for the query.
731
     *
732
     * Alias for execute(null, $hydrationMode = HYDRATE_OBJECT).
733
     *
734
     * @param int $hydrationMode
735
     *
736
     * @return mixed
737
     */
738 313
    public function getResult($hydrationMode = self::HYDRATE_OBJECT)
739
    {
740 313
        return $this->execute(null, $hydrationMode);
741
    }
742
743
    /**
744
     * Gets the array of results for the query.
745
     *
746
     * Alias for execute(null, HYDRATE_ARRAY).
747
     *
748
     * @return array
749
     */
750 26
    public function getArrayResult()
751
    {
752 26
        return $this->execute(null, self::HYDRATE_ARRAY);
753
    }
754
755
    /**
756
     * Gets the scalar results for the query.
757
     *
758
     * Alias for execute(null, HYDRATE_SCALAR).
759
     *
760
     * @return array
761
     */
762 87
    public function getScalarResult()
763
    {
764 87
        return $this->execute(null, self::HYDRATE_SCALAR);
765
    }
766
767
    /**
768
     * Get exactly one result or null.
769
     *
770
     * @param int $hydrationMode
771
     *
772
     * @return mixed
773
     *
774
     * @throws NonUniqueResultException
775
     */
776 18
    public function getOneOrNullResult($hydrationMode = null)
777
    {
778
        try {
779 18
            $result = $this->execute(null, $hydrationMode);
780 1
        } catch (NoResultException $e) {
781 1
            return null;
782
        }
783
784
785 17
        if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {
786 2
            return null;
787
        }
788
789 15
        if ( ! is_array($result)) {
790 1
            return $result;
791
        }
792
793 15
        if (count($result) > 1) {
794 1
            throw new NonUniqueResultException;
795
        }
796
797 14
        return array_shift($result);
798
    }
799
800
    /**
801
     * Gets the single result of the query.
802
     *
803
     * Enforces the presence as well as the uniqueness of the result.
804
     *
805
     * If the result is not unique, a NonUniqueResultException is thrown.
806
     * If there is no result, a NoResultException is thrown.
807
     *
808
     * @param integer $hydrationMode
809
     *
810
     * @return mixed
811
     *
812
     * @throws NonUniqueResultException If the query result is not unique.
813
     * @throws NoResultException        If the query returned no result and hydration mode is not HYDRATE_SINGLE_SCALAR.
814
     */
815 106
    public function getSingleResult($hydrationMode = null)
816
    {
817 106
        $result = $this->execute(null, $hydrationMode);
818
819 100
        if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {
820 2
            throw new NoResultException;
821
        }
822
823 99
        if ( ! is_array($result)) {
824 9
            return $result;
825
        }
826
827 91
        if (count($result) > 1) {
828 1
            throw new NonUniqueResultException;
829
        }
830
831 90
        return array_shift($result);
832
    }
833
834
    /**
835
     * Gets the single scalar result of the query.
836
     *
837
     * Alias for getSingleResult(HYDRATE_SINGLE_SCALAR).
838
     *
839
     * @return mixed The scalar result, or NULL if the query returned no result.
840
     *
841
     * @throws NonUniqueResultException If the query result is not unique.
842
     */
843 11
    public function getSingleScalarResult()
844
    {
845 11
        return $this->getSingleResult(self::HYDRATE_SINGLE_SCALAR);
846
    }
847
848
    /**
849
     * Sets a query hint. If the hint name is not recognized, it is silently ignored.
850
     *
851
     * @param string $name  The name of the hint.
852
     * @param mixed  $value The value of the hint.
853
     *
854
     * @return static This query instance.
855
     */
856 466
    public function setHint($name, $value)
857
    {
858 466
        $this->_hints[$name] = $value;
859
860 466
        return $this;
861
    }
862
863
    /**
864
     * Gets the value of a query hint. If the hint name is not recognized, FALSE is returned.
865
     *
866
     * @param string $name The name of the hint.
867
     *
868
     * @return mixed The value of the hint or FALSE, if the hint name is not recognized.
869
     */
870 808
    public function getHint($name)
871
    {
872 808
        return isset($this->_hints[$name]) ? $this->_hints[$name] : false;
873
    }
874
875
    /**
876
     * Check if the query has a hint
877
     *
878
     * @param string $name The name of the hint
879
     *
880
     * @return bool False if the query does not have any hint
881
     */
882 17
    public function hasHint($name)
883
    {
884 17
        return isset($this->_hints[$name]);
885
    }
886
887
    /**
888
     * Return the key value map of query hints that are currently set.
889
     *
890
     * @return array
891
     */
892 136
    public function getHints()
893
    {
894 136
        return $this->_hints;
895
    }
896
897
    /**
898
     * Executes the query and returns an IterableResult that can be used to incrementally
899
     * iterate over the result.
900
     *
901
     * @param ArrayCollection|array|null $parameters    The query parameters.
902
     * @param integer|null               $hydrationMode The hydration mode to use.
903
     *
904
     * @return \Doctrine\ORM\Internal\Hydration\IterableResult
905
     */
906 10
    public function iterate($parameters = null, $hydrationMode = null)
907
    {
908 10
        if ($hydrationMode !== null) {
909 10
            $this->setHydrationMode($hydrationMode);
910
        }
911
912 10
        if ( ! empty($parameters)) {
913 1
            $this->setParameters($parameters);
914
        }
915
916 10
        $rsm  = $this->getResultSetMapping();
917 7
        $stmt = $this->_doExecute();
918
919 7
        return $this->_em->newHydrator($this->_hydrationMode)->iterate($stmt, $rsm, $this->_hints);
920
    }
921
922
    /**
923
     * Executes the query.
924
     *
925
     * @param ArrayCollection|array|null $parameters Query parameters.
926
     * @param integer|null               $hydrationMode Processing mode to be used during the hydration process.
927
     *
928
     * @return mixed
929
     */
930 493
    public function execute($parameters = null, $hydrationMode = null)
931
    {
932 493
        if ($this->cacheable && $this->isCacheEnabled()) {
933 29
            return $this->executeUsingQueryCache($parameters, $hydrationMode);
934
        }
935
936 466
        return $this->executeIgnoreQueryCache($parameters, $hydrationMode);
937
    }
938
939
    /**
940
     * Execute query ignoring second level cache.
941
     *
942
     * @param ArrayCollection|array|null $parameters
943
     * @param integer|null               $hydrationMode
944
     *
945
     * @return mixed
946
     */
947 493
    private function executeIgnoreQueryCache($parameters = null, $hydrationMode = null)
948
    {
949 493
        if ($hydrationMode !== null) {
950 383
            $this->setHydrationMode($hydrationMode);
951
        }
952
953 493
        if ( ! empty($parameters)) {
954
            $this->setParameters($parameters);
955
        }
956
957
        $setCacheEntry = function() {};
958
959 493
        if ($this->_hydrationCacheProfile !== null) {
960 2
            list($cacheKey, $realCacheKey) = $this->getHydrationCacheId();
961
962 2
            $queryCacheProfile = $this->getHydrationCacheProfile();
963 2
            $cache             = $queryCacheProfile->getResultCacheDriver();
964 2
            $result            = $cache->fetch($cacheKey);
965
966 2
            if (isset($result[$realCacheKey])) {
967 2
                return $result[$realCacheKey];
968
            }
969
970 2
            if ( ! $result) {
971 2
                $result = [];
972
            }
973
974 2
            $setCacheEntry = function($data) use ($cache, $result, $cacheKey, $realCacheKey, $queryCacheProfile) {
975 2
                $result[$realCacheKey] = $data;
976
977 2
                $cache->save($cacheKey, $result, $queryCacheProfile->getLifetime());
978 2
            };
979
        }
980
981 493
        $stmt = $this->_doExecute();
982
983 481
        if (is_numeric($stmt)) {
0 ignored issues
show
introduced by
The condition is_numeric($stmt) is always false.
Loading history...
984 27
            $setCacheEntry($stmt);
985
986 27
            return $stmt;
987
        }
988
989 466
        $rsm  = $this->getResultSetMapping();
990 466
        $data = $this->_em->newHydrator($this->_hydrationMode)->hydrateAll($stmt, $rsm, $this->_hints);
991
992 462
        $setCacheEntry($data);
993
994 462
        return $data;
995
    }
996
997
    /**
998
     * Load from second level cache or executes the query and put into cache.
999
     *
1000
     * @param ArrayCollection|array|null $parameters
1001
     * @param integer|null               $hydrationMode
1002
     *
1003
     * @return mixed
1004
     */
1005 29
    private function executeUsingQueryCache($parameters = null, $hydrationMode = null)
1006
    {
1007 29
        $rsm        = $this->getResultSetMapping();
1008 29
        $queryCache = $this->_em->getCache()->getQueryCache($this->cacheRegion);
1009 29
        $queryKey   = new QueryCacheKey(
1010 29
            $this->getHash(),
1011 29
            $this->lifetime,
1012 29
            $this->cacheMode ?: Cache::MODE_NORMAL,
1013 29
            $this->getTimestampKey()
1014
        );
1015
1016 29
        $result     = $queryCache->get($queryKey, $rsm, $this->_hints);
1017
1018 29
        if ($result !== null) {
1019 16
            if ($this->cacheLogger) {
1020 16
                $this->cacheLogger->queryCacheHit($queryCache->getRegion()->getName(), $queryKey);
1021
            }
1022
1023 16
            return $result;
1024
        }
1025
1026 29
        $result = $this->executeIgnoreQueryCache($parameters, $hydrationMode);
1027 29
        $cached = $queryCache->put($queryKey, $rsm, $result, $this->_hints);
1028
1029 26
        if ($this->cacheLogger) {
1030 26
            $this->cacheLogger->queryCacheMiss($queryCache->getRegion()->getName(), $queryKey);
1031
1032 26
            if ($cached) {
1033 26
                $this->cacheLogger->queryCachePut($queryCache->getRegion()->getName(), $queryKey);
1034
            }
1035
        }
1036
1037 26
        return $result;
1038
    }
1039
1040
    /**
1041
     * @return \Doctrine\ORM\Cache\TimestampCacheKey|null
1042
     */
1043 29
    private function getTimestampKey()
1044
    {
1045 29
        $entityName = reset($this->_resultSetMapping->aliasMap);
1046
1047 29
        if (empty($entityName)) {
1048 2
            return null;
1049
        }
1050
1051 27
        $metadata = $this->_em->getClassMetadata($entityName);
1052
1053 27
        return new Cache\TimestampCacheKey($metadata->rootEntityName);
0 ignored issues
show
Bug introduced by
Accessing rootEntityName on the interface Doctrine\Common\Persistence\Mapping\ClassMetadata suggest that you code against a concrete implementation. How about adding an instanceof check?
Loading history...
1054
    }
1055
1056
    /**
1057
     * Get the result cache id to use to store the result set cache entry.
1058
     * Will return the configured id if it exists otherwise a hash will be
1059
     * automatically generated for you.
1060
     *
1061
     * @return array ($key, $hash)
1062
     */
1063 2
    protected function getHydrationCacheId()
1064
    {
1065 2
        $parameters = [];
1066
1067 2
        foreach ($this->getParameters() as $parameter) {
1068 1
            $parameters[$parameter->getName()] = $this->processParameterValue($parameter->getValue());
1069
        }
1070
1071 2
        $sql                    = $this->getSQL();
1072 2
        $queryCacheProfile      = $this->getHydrationCacheProfile();
1073 2
        $hints                  = $this->getHints();
1074 2
        $hints['hydrationMode'] = $this->getHydrationMode();
1075
1076 2
        ksort($hints);
1077
1078 2
        return $queryCacheProfile->generateCacheKeys($sql, $parameters, $hints);
1079
    }
1080
1081
    /**
1082
     * Set the result cache id to use to store the result set cache entry.
1083
     * If this is not explicitly set by the developer then a hash is automatically
1084
     * generated for you.
1085
     *
1086
     * @param string $id
1087
     *
1088
     * @return static This query instance.
1089
     */
1090 16
    public function setResultCacheId($id)
1091
    {
1092 16
        $this->_queryCacheProfile = $this->_queryCacheProfile
1093 16
            ? $this->_queryCacheProfile->setCacheKey($id)
1094
            : new QueryCacheProfile(0, $id, $this->_em->getConfiguration()->getResultCacheImpl());
1095
1096 16
        return $this;
1097
    }
1098
1099
    /**
1100
     * Get the result cache id to use to store the result set cache entry if set.
1101
     *
1102
     * @deprecated
1103
     *
1104
     * @return string
1105
     */
1106
    public function getResultCacheId()
1107
    {
1108
        return $this->_queryCacheProfile ? $this->_queryCacheProfile->getCacheKey() : null;
1109
    }
1110
1111
    /**
1112
     * Executes the query and returns a the resulting Statement object.
1113
     *
1114
     * @return \Doctrine\DBAL\Driver\Statement The executed database statement that holds the results.
1115
     */
1116
    abstract protected function _doExecute();
1117
1118
    /**
1119
     * Cleanup Query resource when clone is called.
1120
     *
1121
     * @return void
1122
     */
1123 139
    public function __clone()
1124
    {
1125 139
        $this->parameters = new ArrayCollection();
1126
1127 139
        $this->_hints = [];
1128 139
        $this->_hints = $this->_em->getConfiguration()->getDefaultQueryHints();
1129 139
    }
1130
1131
    /**
1132
     * Generates a string of currently query to use for the cache second level cache.
1133
     *
1134
     * @return string
1135
     */
1136 29
    protected function getHash()
1137
    {
1138 29
        $query  = $this->getSQL();
1139 29
        $hints  = $this->getHints();
1140 29
        $params = array_map(function(Parameter $parameter) {
1141
            // Small optimization
1142
            // Does not invoke processParameterValue for scalar values
1143 5
            if (is_scalar($value = $parameter->getValue())) {
1144 4
                return $value;
1145
            }
1146
1147 1
            return $this->processParameterValue($value);
1148 29
        }, $this->parameters->getValues());
1149
1150 29
        ksort($hints);
1151
1152 29
        return sha1($query . '-' . serialize($params) . '-' . serialize($hints));
1153
    }
1154
}
1155