Completed
Pull Request — master (#6417)
by Luís
15:32 queued 09:41
created

Query::setHydrationMode()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 3
CRAP Score 1

Importance

Changes 0
Metric Value
dl 0
loc 6
c 0
b 0
f 0
ccs 3
cts 3
cp 1
rs 9.4285
cc 1
eloc 3
nc 1
nop 1
crap 1
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\DBAL\LockMode;
23
use Doctrine\ORM\Query\Exec\AbstractSqlExecutor;
24
use Doctrine\ORM\Query\Parser;
25
use Doctrine\ORM\Query\ParserResult;
26
use Doctrine\ORM\Query\QueryException;
27
use Doctrine\ORM\Mapping\ClassMetadata;
28
use Doctrine\ORM\Query\ParameterTypeInferer;
29
use Doctrine\Common\Collections\ArrayCollection;
30
31
/**
32
 * A Query object represents a DQL query.
33
 *
34
 * @since   1.0
35
 * @author  Guilherme Blanco <[email protected]>
36
 * @author  Konsta Vesterinen <[email protected]>
37
 * @author  Roman Borschel <[email protected]>
38
 */
39
final class Query extends AbstractQuery
40
{
41
    /**
42
     * A query object is in CLEAN state when it has NO unparsed/unprocessed DQL parts.
43
     */
44
    const STATE_CLEAN  = 1;
45
46
    /**
47
     * A query object is in state DIRTY when it has DQL parts that have not yet been
48
     * parsed/processed. This is automatically defined as DIRTY when addDqlQueryPart
49
     * is called.
50
     */
51
    const STATE_DIRTY = 2;
52
53
    /* Query HINTS */
54
55
    /**
56
     * The refresh hint turns any query into a refresh query with the result that
57
     * any local changes in entities are overridden with the fetched values.
58
     *
59
     * @var string
60
     */
61
    const HINT_REFRESH = 'doctrine.refresh';
62
63
    /**
64
     * @var string
65
     */
66
    const HINT_CACHE_ENABLED = 'doctrine.cache.enabled';
67
68
    /**
69
     * @var string
70
     */
71
    const HINT_CACHE_EVICT = 'doctrine.cache.evict';
72
73
    /**
74
     * Internal hint: is set to the proxy entity that is currently triggered for loading
75
     *
76
     * @var string
77
     */
78
    const HINT_REFRESH_ENTITY = 'doctrine.refresh.entity';
79
80
    /**
81
     * The forcePartialLoad query hint forces a particular query to return
82
     * partial objects.
83
     *
84
     * @var string
85
     * @todo Rename: HINT_OPTIMIZE
86
     */
87
    const HINT_FORCE_PARTIAL_LOAD = 'doctrine.forcePartialLoad';
88
89
    /**
90
     * The includeMetaColumns query hint causes meta columns like foreign keys and
91
     * discriminator columns to be selected and returned as part of the query result.
92
     *
93
     * This hint does only apply to non-object queries.
94
     *
95
     * @var string
96
     */
97
    const HINT_INCLUDE_META_COLUMNS = 'doctrine.includeMetaColumns';
98
99
    /**
100
     * An array of class names that implement \Doctrine\ORM\Query\TreeWalker and
101
     * are iterated and executed after the DQL has been parsed into an AST.
102
     *
103
     * @var string
104
     */
105
    const HINT_CUSTOM_TREE_WALKERS = 'doctrine.customTreeWalkers';
106
107
    /**
108
     * A string with a class name that implements \Doctrine\ORM\Query\TreeWalker
109
     * and is used for generating the target SQL from any DQL AST tree.
110
     *
111
     * @var string
112
     */
113
    const HINT_CUSTOM_OUTPUT_WALKER = 'doctrine.customOutputWalker';
114
115
    //const HINT_READ_ONLY = 'doctrine.readOnly';
116
117
    /**
118
     * @var string
119
     */
120
    const HINT_INTERNAL_ITERATION = 'doctrine.internal.iteration';
121
122
    /**
123
     * @var string
124
     */
125
    const HINT_LOCK_MODE = 'doctrine.lockMode';
126
127
    /**
128
     * The current state of this query.
129
     *
130
     * @var integer
131
     */
132
    private $_state = self::STATE_CLEAN;
133
134
    /**
135
     * A snapshot of the parameter types the query was parsed with.
136
     *
137
     * @var array
138
     */
139
    private $_parsedTypes = [];
140
141
    /**
142
     * Cached DQL query.
143
     *
144
     * @var string
145
     */
146
    private $_dql = null;
147
148
    /**
149
     * The parser result that holds DQL => SQL information.
150
     *
151
     * @var \Doctrine\ORM\Query\ParserResult
152
     */
153
    private $_parserResult;
154
155
    /**
156
     * The first result to return (the "offset").
157
     *
158
     * @var integer
159
     */
160
    private $_firstResult = null;
161
162
    /**
163
     * The maximum number of results to return (the "limit").
164
     *
165
     * @var integer
166
     */
167
    private $_maxResults = null;
168
169
    /**
170
     * The cache driver used for caching queries.
171
     *
172
     * @var \Doctrine\Common\Cache\Cache|null
173
     */
174
    private $_queryCache;
175
176
    /**
177
     * Whether or not expire the query cache.
178
     *
179
     * @var boolean
180
     */
181
    private $_expireQueryCache = false;
182
183
    /**
184
     * The query cache lifetime.
185
     *
186
     * @var int
187
     */
188
    private $_queryCacheTTL;
189
190
    /**
191
     * Whether to use a query cache, if available. Defaults to TRUE.
192
     *
193
     * @var boolean
194
     */
195
    private $_useQueryCache = true;
196
197
    /**
198
     * Gets the SQL query/queries that correspond to this DQL query.
199
     *
200
     * @return mixed The built sql query or an array of all sql queries.
201
     *
202
     * @override
203
     */
204 332
    public function getSQL()
205
    {
206 332
        return $this->_parse()->getSQLExecutor()->getSQLStatements();
0 ignored issues
show
Bug Best Practice introduced by
The return type of return $this->_parse()->...()->getSQLStatements(); (array) is incompatible with the return type declared by the abstract method Doctrine\ORM\AbstractQuery::getSQL of type string.

If you return a value from a function or method, it should be a sub-type of the type that is given by the parent type f.e. an interface, or abstract method. This is more formally defined by the Lizkov substitution principle, and guarantees that classes that depend on the parent type can use any instance of a child type interchangably. This principle also belongs to the SOLID principles for object oriented design.

Let’s take a look at an example:

class Author {
    private $name;

    public function __construct($name) {
        $this->name = $name;
    }

    public function getName() {
        return $this->name;
    }
}

abstract class Post {
    public function getAuthor() {
        return 'Johannes';
    }
}

class BlogPost extends Post {
    public function getAuthor() {
        return new Author('Johannes');
    }
}

class ForumPost extends Post { /* ... */ }

function my_function(Post $post) {
    echo strtoupper($post->getAuthor());
}

Our function my_function expects a Post object, and outputs the author of the post. The base class Post returns a simple string and outputting a simple string will work just fine. However, the child class BlogPost which is a sub-type of Post instead decided to return an object, and is therefore violating the SOLID principles. If a BlogPost were passed to my_function, PHP would not complain, but ultimately fail when executing the strtoupper call in its body.

Loading history...
207
    }
208
209
    /**
210
     * Returns the corresponding AST for this DQL query.
211
     *
212
     * @return \Doctrine\ORM\Query\AST\SelectStatement |
213
     *         \Doctrine\ORM\Query\AST\UpdateStatement |
214
     *         \Doctrine\ORM\Query\AST\DeleteStatement
215
     */
216 2
    public function getAST()
217
    {
218 2
        $parser = new Parser($this);
219
220 2
        return $parser->getAST();
221
    }
222
223
    /**
224
     * {@inheritdoc}
225
     */
226 419
    protected function getResultSetMapping()
227
    {
228
        // parse query or load from cache
229 419
        if ($this->_resultSetMapping === null) {
230 37
            $this->_resultSetMapping = $this->_parse()->getResultSetMapping();
231
        }
232
233 416
        return $this->_resultSetMapping;
234
    }
235
236
    /**
237
     * Parses the DQL query, if necessary, and stores the parser result.
238
     *
239
     * Note: Populates $this->_parserResult as a side-effect.
240
     *
241
     * @return \Doctrine\ORM\Query\ParserResult
242
     */
243 737
    private function _parse()
244
    {
245 737
        $types = [];
246
247 737
        foreach ($this->parameters as $parameter) {
248
            /** @var Query\Parameter $parameter */
249 163
            $types[$parameter->getName()] = $parameter->getType();
250
        }
251
252
        // Return previous parser result if the query and the filter collection are both clean
253 737
        if ($this->_state === self::STATE_CLEAN && $this->_parsedTypes === $types && $this->_em->isFiltersStateClean()) {
254 39
            return $this->_parserResult;
255
        }
256
257 737
        $this->_state = self::STATE_CLEAN;
258 737
        $this->_parsedTypes = $types;
259
260
        // Check query cache.
261 737
        if ( ! ($this->_useQueryCache && ($queryCache = $this->getQueryCacheDriver()))) {
262 172
            $parser = new Parser($this);
263
264 172
            $this->_parserResult = $parser->parse();
265
266 168
            return $this->_parserResult;
267
        }
268
269 565
        $hash   = $this->_getQueryCacheId();
270 565
        $cached = $this->_expireQueryCache ? false : $queryCache->fetch($hash);
0 ignored issues
show
Bug introduced by
The variable $queryCache does not seem to be defined for all execution paths leading up to this point.

If you define a variable conditionally, it can happen that it is not defined for all execution paths.

Let’s take a look at an example:

function myFunction($a) {
    switch ($a) {
        case 'foo':
            $x = 1;
            break;

        case 'bar':
            $x = 2;
            break;
    }

    // $x is potentially undefined here.
    echo $x;
}

In the above example, the variable $x is defined if you pass “foo” or “bar” as argument for $a. However, since the switch statement has no default case statement, if you pass any other value, the variable $x would be undefined.

Available Fixes

  1. Check for existence of the variable explicitly:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        if (isset($x)) { // Make sure it's always set.
            echo $x;
        }
    }
    
  2. Define a default value for the variable:

    function myFunction($a) {
        $x = ''; // Set a default which gets overridden for certain paths.
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
        }
    
        echo $x;
    }
    
  3. Add a value for the missing path:

    function myFunction($a) {
        switch ($a) {
            case 'foo':
                $x = 1;
                break;
    
            case 'bar':
                $x = 2;
                break;
    
            // We add support for the missing case.
            default:
                $x = '';
                break;
        }
    
        echo $x;
    }
    
Loading history...
271
272 565
        if ($cached instanceof ParserResult) {
273
            // Cache hit.
274 115
            $this->_parserResult = $cached;
275
276 115
            return $this->_parserResult;
277
        }
278
279
        // Cache miss.
280 517
        $parser = new Parser($this);
281
282 517
        $this->_parserResult = $parser->parse();
283
284 498
        $queryCache->save($hash, $this->_parserResult, $this->_queryCacheTTL);
285
286 498
        return $this->_parserResult;
287
    }
288
289
    /**
290
     * {@inheritdoc}
291
     */
292 436
    protected function _doExecute()
293
    {
294 436
        $executor = $this->_parse()->getSqlExecutor();
295
296 427
        if ($this->_queryCacheProfile) {
297 7
            $executor->setQueryCacheProfile($this->_queryCacheProfile);
298
        } else {
299 422
            $executor->removeQueryCacheProfile();
300
        }
301
302 427
        if ($this->_resultSetMapping === null) {
303 386
            $this->_resultSetMapping = $this->_parserResult->getResultSetMapping();
304
        }
305
306
        // Prepare parameters
307 427
        $paramMappings = $this->_parserResult->getParameterMappings();
308 427
        $paramCount = count($this->parameters);
309 427
        $mappingCount = count($paramMappings);
310
311 427
        if ($paramCount > $mappingCount) {
312 1
            throw QueryException::tooManyParameters($mappingCount, $paramCount);
313
        }
314
315 426
        if ($paramCount < $mappingCount) {
316 1
            throw QueryException::tooFewParameters($mappingCount, $paramCount);
317
        }
318
319
        // evict all cache for the entity region
320 425
        if ($this->hasCache && isset($this->_hints[self::HINT_CACHE_EVICT]) && $this->_hints[self::HINT_CACHE_EVICT]) {
321 2
            $this->evictEntityCacheRegion();
322
        }
323
324 425
        list($sqlParams, $types) = $this->processParameterMappings($paramMappings);
325
326 424
        $this->evictResultSetCache($executor, $sqlParams, $types);
327
328 424
        return $executor->execute($this->_em->getConnection(), $sqlParams, $types);
329
    }
330
331 424
    private function evictResultSetCache(AbstractSqlExecutor $executor, array $sqlParams, array $types)
332
    {
333 424
        if (null === $this->_queryCacheProfile || ! $this->getExpireResultCache()) {
334 424
            return;
335
        }
336
337 1
        $cacheDriver = $this->_queryCacheProfile->getResultCacheDriver();
338
339 1
        foreach ((array) $executor->getSqlStatements() as $statement) {
340 1
            $cacheDriver->delete($this->_queryCacheProfile->generateCacheKeys($statement, $sqlParams, $types)[0]);
341
        }
342 1
    }
343
344
    /**
345
     * Evict entity cache region
346
     */
347 2
    private function evictEntityCacheRegion()
348
    {
349 2
        $AST = $this->getAST();
350
351 2
        if ($AST instanceof \Doctrine\ORM\Query\AST\SelectStatement) {
352
            throw new QueryException('The hint "HINT_CACHE_EVICT" is not valid for select statements.');
353
        }
354
355 2
        $className = ($AST instanceof \Doctrine\ORM\Query\AST\DeleteStatement)
356 1
            ? $AST->deleteClause->abstractSchemaName
357 2
            : $AST->updateClause->abstractSchemaName;
358
359 2
        $this->_em->getCache()->evictEntityRegion($className);
360 2
    }
361
362
    /**
363
     * Processes query parameter mappings.
364
     *
365
     * @param array $paramMappings
366
     *
367
     * @return array
368
     *
369
     * @throws Query\QueryException
370
     */
371 425
    private function processParameterMappings($paramMappings)
372
    {
373 425
        $sqlParams = [];
374 425
        $types     = [];
375
376 425
        foreach ($this->parameters as $parameter) {
377 151
            $key    = $parameter->getName();
378 151
            $value  = $parameter->getValue();
379 151
            $rsm    = $this->getResultSetMapping();
380
381 151
            if ( ! isset($paramMappings[$key])) {
382 1
                throw QueryException::unknownParameter($key);
383
            }
384
385 150
            if (isset($rsm->metadataParameterMapping[$key]) && $value instanceof ClassMetadata) {
386 2
                $value = $value->getMetadataValue($rsm->metadataParameterMapping[$key]);
387
            }
388
389 150
            $value = $this->processParameterValue($value);
390 150
            $type  = ($parameter->getValue() === $value)
391 140
                ? $parameter->getType()
392 150
                : ParameterTypeInferer::inferType($value);
393
394 150
            foreach ($paramMappings[$key] as $position) {
395 150
                $types[$position] = $type;
396
            }
397
398 150
            $sqlPositions = $paramMappings[$key];
399
400
            // optimized multi value sql positions away for now,
401
            // they are not allowed in DQL anyways.
402 150
            $value = [$value];
403 150
            $countValue = count($value);
404
405 150
            for ($i = 0, $l = count($sqlPositions); $i < $l; $i++) {
406 150
                $sqlParams[$sqlPositions[$i]] = $value[($i % $countValue)];
407
            }
408
        }
409
410 424
        if (count($sqlParams) != count($types)) {
411
            throw QueryException::parameterTypeMismatch();
412
        }
413
414 424
        if ($sqlParams) {
0 ignored issues
show
Bug Best Practice introduced by
The expression $sqlParams of type array is implicitly converted to a boolean; are you sure this is intended? If so, consider using ! empty($expr) instead to make it clear that you intend to check for an array without elements.

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
415 150
            ksort($sqlParams);
416 150
            $sqlParams = array_values($sqlParams);
417
418 150
            ksort($types);
419 150
            $types = array_values($types);
420
        }
421
422 424
        return [$sqlParams, $types];
423
    }
424
425
    /**
426
     * Defines a cache driver to be used for caching queries.
427
     *
428
     * @param \Doctrine\Common\Cache\Cache|null $queryCache Cache driver.
429
     *
430
     * @return Query This query instance.
431
     */
432 6
    public function setQueryCacheDriver($queryCache)
433
    {
434 6
        $this->_queryCache = $queryCache;
435
436 6
        return $this;
437
    }
438
439
    /**
440
     * Defines whether the query should make use of a query cache, if available.
441
     *
442
     * @param boolean $bool
443
     *
444
     * @return Query This query instance.
445
     */
446 175
    public function useQueryCache($bool)
447
    {
448 175
        $this->_useQueryCache = $bool;
449
450 175
        return $this;
451
    }
452
453
    /**
454
     * Returns the cache driver used for query caching.
455
     *
456
     * @return \Doctrine\Common\Cache\Cache|null The cache driver used for query caching or NULL, if
457
     *                                           this Query does not use query caching.
458
     */
459 565
    public function getQueryCacheDriver()
460
    {
461 565
        if ($this->_queryCache) {
462 9
            return $this->_queryCache;
463
        }
464
465 556
        return $this->_em->getConfiguration()->getQueryCacheImpl();
466
    }
467
468
    /**
469
     * Defines how long the query cache will be active before expire.
470
     *
471
     * @param integer $timeToLive How long the cache entry is valid.
472
     *
473
     * @return Query This query instance.
474
     */
475 1
    public function setQueryCacheLifetime($timeToLive)
476
    {
477 1
        if ($timeToLive !== null) {
478 1
            $timeToLive = (int) $timeToLive;
479
        }
480
481 1
        $this->_queryCacheTTL = $timeToLive;
482
483 1
        return $this;
484
    }
485
486
    /**
487
     * Retrieves the lifetime of resultset cache.
488
     *
489
     * @return int
490
     */
491
    public function getQueryCacheLifetime()
492
    {
493
        return $this->_queryCacheTTL;
494
    }
495
496
    /**
497
     * Defines if the query cache is active or not.
498
     *
499
     * @param boolean $expire Whether or not to force query cache expiration.
500
     *
501
     * @return Query This query instance.
502
     */
503 7
    public function expireQueryCache($expire = true)
504
    {
505 7
        $this->_expireQueryCache = $expire;
506
507 7
        return $this;
508
    }
509
510
    /**
511
     * Retrieves if the query cache is active or not.
512
     *
513
     * @return bool
514
     */
515
    public function getExpireQueryCache()
516
    {
517
        return $this->_expireQueryCache;
518
    }
519
520
    /**
521
     * @override
522
     */
523 211
    public function free()
524
    {
525 211
        parent::free();
526
527 211
        $this->_dql = null;
528 211
        $this->_state = self::STATE_CLEAN;
529 211
    }
530
531
    /**
532
     * Sets a DQL query string.
533
     *
534
     * @param string $dqlQuery DQL Query.
535
     *
536
     * @return \Doctrine\ORM\AbstractQuery
537
     */
538 920
    public function setDQL($dqlQuery)
539
    {
540 920
        if ($dqlQuery !== null) {
541 920
            $this->_dql = $dqlQuery;
542 920
            $this->_state = self::STATE_DIRTY;
543
        }
544
545 920
        return $this;
546
    }
547
548
    /**
549
     * Returns the DQL query that is represented by this query object.
550
     *
551
     * @return string DQL query.
552
     */
553 872
    public function getDQL()
554
    {
555 872
        return $this->_dql;
556
    }
557
558
    /**
559
     * Returns the state of this query object
560
     * By default the type is Doctrine_ORM_Query_Abstract::STATE_CLEAN but if it appears any unprocessed DQL
561
     * part, it is switched to Doctrine_ORM_Query_Abstract::STATE_DIRTY.
562
     *
563
     * @see AbstractQuery::STATE_CLEAN
564
     * @see AbstractQuery::STATE_DIRTY
565
     *
566
     * @return integer The query state.
567
     */
568
    public function getState()
569
    {
570
        return $this->_state;
571
    }
572
573
    /**
574
     * Method to check if an arbitrary piece of DQL exists
575
     *
576
     * @param string $dql Arbitrary piece of DQL to check for.
577
     *
578
     * @return boolean
579
     */
580
    public function contains($dql)
581
    {
582
        return stripos($this->getDQL(), $dql) !== false;
583
    }
584
585
    /**
586
     * Sets the position of the first result to retrieve (the "offset").
587
     *
588
     * @param integer $firstResult The first result to return.
589
     *
590
     * @return Query This query object.
591
     */
592 214
    public function setFirstResult($firstResult)
593
    {
594 214
        $this->_firstResult = $firstResult;
595 214
        $this->_state       = self::STATE_DIRTY;
596
597 214
        return $this;
598
    }
599
600
    /**
601
     * Gets the position of the first result the query object was set to retrieve (the "offset").
602
     * Returns NULL if {@link setFirstResult} was not applied to this query.
603
     *
604
     * @return integer The position of the first result.
605
     */
606 629
    public function getFirstResult()
607
    {
608 629
        return $this->_firstResult;
609
    }
610
611
    /**
612
     * Sets the maximum number of results to retrieve (the "limit").
613
     *
614
     * @param integer $maxResults
615
     *
616
     * @return Query This query object.
617
     */
618 223
    public function setMaxResults($maxResults)
619
    {
620 223
        $this->_maxResults = $maxResults;
621 223
        $this->_state      = self::STATE_DIRTY;
622
623 223
        return $this;
624
    }
625
626
    /**
627
     * Gets the maximum number of results the query object was set to retrieve (the "limit").
628
     * Returns NULL if {@link setMaxResults} was not applied to this query.
629
     *
630
     * @return integer Maximum number of results.
631
     */
632 629
    public function getMaxResults()
633
    {
634 629
        return $this->_maxResults;
635
    }
636
637
    /**
638
     * Executes the query and returns an IterableResult that can be used to incrementally
639
     * iterated over the result.
640
     *
641
     * @param ArrayCollection|array|null $parameters    The query parameters.
642
     * @param integer                    $hydrationMode The hydration mode to use.
643
     *
644
     * @return \Doctrine\ORM\Internal\Hydration\IterableResult
645
     */
646 9
    public function iterate($parameters = null, $hydrationMode = self::HYDRATE_OBJECT)
647
    {
648 9
        $this->setHint(self::HINT_INTERNAL_ITERATION, true);
649
650 9
        return parent::iterate($parameters, $hydrationMode);
651
    }
652
653
    /**
654
     * {@inheritdoc}
655
     */
656 459
    public function setHint($name, $value)
657
    {
658 459
        $this->_state = self::STATE_DIRTY;
659
660 459
        return parent::setHint($name, $value);
661
    }
662
663
    /**
664
     * {@inheritdoc}
665
     */
666 337
    public function setHydrationMode($hydrationMode)
667
    {
668 337
        $this->_state = self::STATE_DIRTY;
669
670 337
        return parent::setHydrationMode($hydrationMode);
671
    }
672
673
    /**
674
     * Set the lock mode for this Query.
675
     *
676
     * @see \Doctrine\DBAL\LockMode
677
     *
678
     * @param int $lockMode
679
     *
680
     * @return Query
681
     *
682
     * @throws TransactionRequiredException
683
     */
684
    public function setLockMode($lockMode)
685
    {
686
        if (in_array($lockMode, [LockMode::NONE, LockMode::PESSIMISTIC_READ, LockMode::PESSIMISTIC_WRITE], true)) {
687
            if ( ! $this->_em->getConnection()->isTransactionActive()) {
688
                throw TransactionRequiredException::transactionRequired();
689
            }
690
        }
691
692
        $this->setHint(self::HINT_LOCK_MODE, $lockMode);
693
694
        return $this;
695
    }
696
697
    /**
698
     * Get the current lock mode for this query.
699
     *
700
     * @return int|null The current lock mode of this query or NULL if no specific lock mode is set.
701
     */
702
    public function getLockMode()
703
    {
704
        $lockMode = $this->getHint(self::HINT_LOCK_MODE);
705
706
        if (false === $lockMode) {
707
            return null;
708
        }
709
710
        return $lockMode;
711
    }
712
713
    /**
714
     * Generate a cache id for the query cache - reusing the Result-Cache-Id generator.
715
     *
716
     * @return string
717
     */
718 565
    protected function _getQueryCacheId()
719
    {
720 565
        ksort($this->_hints);
721
722 565
        $platform = $this->getEntityManager()
723 565
            ->getConnection()
724 565
            ->getDatabasePlatform()
725 565
            ->getName();
726
727 565
        return md5(
728 565
            $this->getDql() . serialize($this->_hints) .
729 565
            '&platform=' . $platform .
730 565
            ($this->_em->hasFilters() ? $this->_em->getFilters()->getHash() : '') .
731 565
            '&firstResult=' . $this->_firstResult . '&maxResult=' . $this->_maxResults .
732 565
            '&hydrationMode=' . $this->_hydrationMode . '&types=' . serialize($this->_parsedTypes) . 'DOCTRINE_QUERY_CACHE_SALT'
733
        );
734
    }
735
736
     /**
737
     * {@inheritdoc}
738
     */
739 28
    protected function getHash()
740
    {
741 28
        return sha1(parent::getHash(). '-'. $this->_firstResult . '-' . $this->_maxResults);
742
    }
743
744
    /**
745
     * Cleanup Query resource when clone is called.
746
     *
747
     * @return void
748
     */
749 136
    public function __clone()
750
    {
751 136
        parent::__clone();
752
753 136
        $this->_state = self::STATE_DIRTY;
754 136
    }
755
}
756