Failed Conditions
Pull Request — 2.6 (#7180)
by Ben
11:16
created

Query::setFirstResult()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 6
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 4
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 3
nc 1
nop 1
dl 0
loc 6
ccs 4
cts 4
cp 1
crap 1
rs 9.4285
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\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
use Doctrine\ORM\Utility\HierarchyDiscriminatorResolver;
31
32
/**
33
 * A Query object represents a DQL query.
34
 *
35
 * @since   1.0
36
 * @author  Guilherme Blanco <[email protected]>
37
 * @author  Konsta Vesterinen <[email protected]>
38
 * @author  Roman Borschel <[email protected]>
39
 */
40
final class Query extends AbstractQuery
41
{
42
    /**
43
     * A query object is in CLEAN state when it has NO unparsed/unprocessed DQL parts.
44
     */
45
    const STATE_CLEAN  = 1;
46
47
    /**
48
     * A query object is in state DIRTY when it has DQL parts that have not yet been
49
     * parsed/processed. This is automatically defined as DIRTY when addDqlQueryPart
50
     * is called.
51
     */
52
    const STATE_DIRTY = 2;
53
54
    /* Query HINTS */
55
56
    /**
57
     * The refresh hint turns any query into a refresh query with the result that
58
     * any local changes in entities are overridden with the fetched values.
59
     *
60
     * @var string
61
     */
62
    const HINT_REFRESH = 'doctrine.refresh';
63
64
    /**
65
     * @var string
66
     */
67
    const HINT_CACHE_ENABLED = 'doctrine.cache.enabled';
68
69
    /**
70
     * @var string
71
     */
72
    const HINT_CACHE_EVICT = 'doctrine.cache.evict';
73
74
    /**
75
     * Internal hint: is set to the proxy entity that is currently triggered for loading
76
     *
77
     * @var string
78
     */
79
    const HINT_REFRESH_ENTITY = 'doctrine.refresh.entity';
80
81
    /**
82
     * The forcePartialLoad query hint forces a particular query to return
83
     * partial objects.
84
     *
85
     * @var string
86
     * @todo Rename: HINT_OPTIMIZE
87
     */
88
    const HINT_FORCE_PARTIAL_LOAD = 'doctrine.forcePartialLoad';
89
90
    /**
91
     * The includeMetaColumns query hint causes meta columns like foreign keys and
92
     * discriminator columns to be selected and returned as part of the query result.
93
     *
94
     * This hint does only apply to non-object queries.
95
     *
96
     * @var string
97
     */
98
    const HINT_INCLUDE_META_COLUMNS = 'doctrine.includeMetaColumns';
99
100
    /**
101
     * An array of class names that implement \Doctrine\ORM\Query\TreeWalker and
102
     * are iterated and executed after the DQL has been parsed into an AST.
103
     *
104
     * @var string
105
     */
106
    const HINT_CUSTOM_TREE_WALKERS = 'doctrine.customTreeWalkers';
107
108
    /**
109
     * A string with a class name that implements \Doctrine\ORM\Query\TreeWalker
110
     * and is used for generating the target SQL from any DQL AST tree.
111
     *
112
     * @var string
113
     */
114
    const HINT_CUSTOM_OUTPUT_WALKER = 'doctrine.customOutputWalker';
115
116
    //const HINT_READ_ONLY = 'doctrine.readOnly';
0 ignored issues
show
Unused Code Comprehensibility introduced by
38% of this comment could be valid code. Did you maybe forget this after debugging?

Sometimes obsolete code just ends up commented out instead of removed. In this case it is better to remove the code once you have checked you do not need it.

The code might also have been commented out for debugging purposes. In this case it is vital that someone uncomments it again or your project may behave in very unexpected ways in production.

This check looks for comments that seem to be mostly valid code and reports them.

Loading history...
117
118
    /**
119
     * @var string
120
     */
121
    const HINT_INTERNAL_ITERATION = 'doctrine.internal.iteration';
122
123
    /**
124
     * @var string
125
     */
126
    const HINT_LOCK_MODE = 'doctrine.lockMode';
127
128
    /**
129
     * The current state of this query.
130
     *
131
     * @var integer
132
     */
133
    private $_state = self::STATE_CLEAN;
134
135
    /**
136
     * A snapshot of the parameter types the query was parsed with.
137
     *
138
     * @var array
139
     */
140
    private $_parsedTypes = [];
141
142
    /**
143
     * Cached DQL query.
144
     *
145
     * @var string
146
     */
147
    private $_dql = null;
148
149
    /**
150
     * The parser result that holds DQL => SQL information.
151
     *
152
     * @var \Doctrine\ORM\Query\ParserResult
153
     */
154
    private $_parserResult;
155
156
    /**
157
     * The first result to return (the "offset").
158
     *
159
     * @var integer
160
     */
161
    private $_firstResult = null;
162
163
    /**
164
     * The maximum number of results to return (the "limit").
165
     *
166
     * @var integer|null
167
     */
168
    private $_maxResults = null;
169
170
    /**
171
     * The cache driver used for caching queries.
172
     *
173
     * @var \Doctrine\Common\Cache\Cache|null
174
     */
175
    private $_queryCache;
176
177
    /**
178
     * Whether or not expire the query cache.
179
     *
180
     * @var boolean
181
     */
182
    private $_expireQueryCache = false;
183
184
    /**
185
     * The query cache lifetime.
186
     *
187
     * @var int
188
     */
189
    private $_queryCacheTTL;
190
191
    /**
192
     * Whether to use a query cache, if available. Defaults to TRUE.
193
     *
194
     * @var boolean
195
     */
196
    private $_useQueryCache = true;
197
198
    /**
199
     * Gets the SQL query/queries that correspond to this DQL query.
200
     *
201
     * @return mixed The built sql query or an array of all sql queries.
202
     *
203
     * @override
204
     */
205 340
    public function getSQL()
206
    {
207 340
        return $this->_parse()->getSqlExecutor()->getSqlStatements();
0 ignored issues
show
Bug Best Practice introduced by
The expression return $this->_parse()->...r()->getSqlStatements() returns the type array which is incompatible with the return type mandated by Doctrine\ORM\AbstractQuery::getSQL() of string.

In the issue above, the returned value is violating the contract defined by the mentioned interface.

Let's take a look at an example:

interface HasName {
    /** @return string */
    public function getName();
}

class Name {
    public $name;
}

class User implements HasName {
    /** @return string|Name */
    public function getName() {
        return new Name('foo'); // This is a violation of the ``HasName`` interface
                                // which only allows a string value to be returned.
    }
}
Loading history...
208
    }
209
210
    /**
211
     * Returns the corresponding AST for this DQL query.
212
     *
213
     * @return \Doctrine\ORM\Query\AST\SelectStatement |
214
     *         \Doctrine\ORM\Query\AST\UpdateStatement |
215
     *         \Doctrine\ORM\Query\AST\DeleteStatement
216
     */
217 2
    public function getAST()
218
    {
219 2
        $parser = new Parser($this);
220
221 2
        return $parser->getAST();
222
    }
223
224
    /**
225
     * {@inheritdoc}
226
     */
227 457
    protected function getResultSetMapping()
228
    {
229
        // parse query or load from cache
230 457
        if ($this->_resultSetMapping === null) {
231 38
            $this->_resultSetMapping = $this->_parse()->getResultSetMapping();
232
        }
233
234 454
        return $this->_resultSetMapping;
235
    }
236
237
    /**
238
     * Parses the DQL query, if necessary, and stores the parser result.
239
     *
240
     * Note: Populates $this->_parserResult as a side-effect.
241
     *
242
     * @return \Doctrine\ORM\Query\ParserResult
243
     */
244 782
    private function _parse()
245
    {
246 782
        $types = [];
247
248 782
        foreach ($this->parameters as $parameter) {
249
            /** @var Query\Parameter $parameter */
250 179
            $types[$parameter->getName()] = $parameter->getType();
251
        }
252
253
        // Return previous parser result if the query and the filter collection are both clean
254 782
        if ($this->_state === self::STATE_CLEAN && $this->_parsedTypes === $types && $this->_em->isFiltersStateClean()) {
255 39
            return $this->_parserResult;
256
        }
257
258 782
        $this->_state = self::STATE_CLEAN;
259 782
        $this->_parsedTypes = $types;
260
261
        // Check query cache.
262 782
        if ( ! ($this->_useQueryCache && ($queryCache = $this->getQueryCacheDriver()))) {
263 172
            $parser = new Parser($this);
264
265 172
            $this->_parserResult = $parser->parse();
266
267 168
            return $this->_parserResult;
268
        }
269
270 610
        $hash   = $this->_getQueryCacheId();
271 610
        $cached = $this->_expireQueryCache ? false : $queryCache->fetch($hash);
272
273 610
        if ($cached instanceof ParserResult) {
274
            // Cache hit.
275 122
            $this->_parserResult = $cached;
276
277 122
            return $this->_parserResult;
278
        }
279
280
        // Cache miss.
281 558
        $parser = new Parser($this);
282
283 558
        $this->_parserResult = $parser->parse();
284
285 537
        $queryCache->save($hash, $this->_parserResult, $this->_queryCacheTTL);
286
287 537
        return $this->_parserResult;
288
    }
289
290
    /**
291
     * {@inheritdoc}
292
     */
293 474
    protected function _doExecute()
294
    {
295 474
        $executor = $this->_parse()->getSqlExecutor();
296
297 465
        if ($this->_queryCacheProfile) {
298 8
            $executor->setQueryCacheProfile($this->_queryCacheProfile);
299
        } else {
300 459
            $executor->removeQueryCacheProfile();
301
        }
302
303 465
        if ($this->_resultSetMapping === null) {
304 423
            $this->_resultSetMapping = $this->_parserResult->getResultSetMapping();
305
        }
306
307
        // Prepare parameters
308 465
        $paramMappings = $this->_parserResult->getParameterMappings();
309 465
        $paramCount = count($this->parameters);
310 465
        $mappingCount = count($paramMappings);
311
312 465
        if ($paramCount > $mappingCount) {
313 1
            throw QueryException::tooManyParameters($mappingCount, $paramCount);
314
        }
315
316 464
        if ($paramCount < $mappingCount) {
317 1
            throw QueryException::tooFewParameters($mappingCount, $paramCount);
318
        }
319
320
        // evict all cache for the entity region
321 463
        if ($this->hasCache && isset($this->_hints[self::HINT_CACHE_EVICT]) && $this->_hints[self::HINT_CACHE_EVICT]) {
322 2
            $this->evictEntityCacheRegion();
323
        }
324
325 463
        list($sqlParams, $types) = $this->processParameterMappings($paramMappings);
326
327 462
        $this->evictResultSetCache(
328 462
            $executor,
329 462
            $sqlParams,
330 462
            $types,
331 462
            $this->_em->getConnection()->getParams()
332
        );
333
334 462
        return $executor->execute($this->_em->getConnection(), $sqlParams, $types);
335
    }
336
337 462
    private function evictResultSetCache(
338
        AbstractSqlExecutor $executor,
339
        array $sqlParams,
340
        array $types,
341
        array $connectionParams
342
    ) {
343 462
        if (null === $this->_queryCacheProfile || ! $this->getExpireResultCache()) {
344 462
            return;
345
        }
346
347 2
        $cacheDriver = $this->_queryCacheProfile->getResultCacheDriver();
348 2
        $statements  = (array) $executor->getSqlStatements(); // Type casted since it can either be a string or an array
349
350 2
        foreach ($statements as $statement) {
351 2
            $cacheKeys = $this->_queryCacheProfile->generateCacheKeys($statement, $sqlParams, $types, $connectionParams);
352
353 2
            $cacheDriver->delete(reset($cacheKeys));
354
        }
355 2
    }
356
357
    /**
358
     * Evict entity cache region
359
     */
360 2
    private function evictEntityCacheRegion()
361
    {
362 2
        $AST = $this->getAST();
363
364 2
        if ($AST instanceof \Doctrine\ORM\Query\AST\SelectStatement) {
0 ignored issues
show
introduced by
$AST is always a sub-type of Doctrine\ORM\Query\AST\SelectStatement.
Loading history...
365
            throw new QueryException('The hint "HINT_CACHE_EVICT" is not valid for select statements.');
366
        }
367
368 2
        $className = ($AST instanceof \Doctrine\ORM\Query\AST\DeleteStatement)
369 1
            ? $AST->deleteClause->abstractSchemaName
370 2
            : $AST->updateClause->abstractSchemaName;
371
372 2
        $this->_em->getCache()->evictEntityRegion($className);
373 2
    }
374
375
    /**
376
     * Processes query parameter mappings.
377
     *
378
     * @param array $paramMappings
379
     *
380
     * @return array
381
     *
382
     * @throws Query\QueryException
383
     */
384 463
    private function processParameterMappings($paramMappings)
385
    {
386 463
        $sqlParams = [];
387 463
        $types     = [];
388
389 463
        foreach ($this->parameters as $parameter) {
390 167
            $key    = $parameter->getName();
391 167
            $value  = $parameter->getValue();
392 167
            $rsm    = $this->getResultSetMapping();
393
394 167
            if ( ! isset($paramMappings[$key])) {
395 1
                throw QueryException::unknownParameter($key);
396
            }
397
398 166
            if (isset($rsm->metadataParameterMapping[$key]) && $value instanceof ClassMetadata) {
399
                $value = $value->getMetadataValue($rsm->metadataParameterMapping[$key]);
400
            }
401
402 166
            if (isset($rsm->discriminatorParameters[$key]) && $value instanceof ClassMetadata) {
403 3
                $value = array_keys(HierarchyDiscriminatorResolver::resolveDiscriminatorsForClass($value, $this->_em));
404
            }
405
406 166
            $value = $this->processParameterValue($value);
407 166
            $type  = ($parameter->getValue() === $value)
408 154
                ? $parameter->getType()
409 166
                : ParameterTypeInferer::inferType($value);
410
411 166
            foreach ($paramMappings[$key] as $position) {
412 166
                $types[$position] = $type;
413
            }
414
415 166
            $sqlPositions = $paramMappings[$key];
416
417
            // optimized multi value sql positions away for now,
418
            // they are not allowed in DQL anyways.
419 166
            $value = [$value];
420 166
            $countValue = count($value);
421
422 166
            for ($i = 0, $l = count($sqlPositions); $i < $l; $i++) {
423 166
                $sqlParams[$sqlPositions[$i]] = $value[($i % $countValue)];
424
            }
425
        }
426
427 462
        if (count($sqlParams) != count($types)) {
428
            throw QueryException::parameterTypeMismatch();
429
        }
430
431 462
        if ($sqlParams) {
432 166
            ksort($sqlParams);
433 166
            $sqlParams = array_values($sqlParams);
434
435 166
            ksort($types);
436 166
            $types = array_values($types);
437
        }
438
439 462
        return [$sqlParams, $types];
440
    }
441
442
    /**
443
     * Defines a cache driver to be used for caching queries.
444
     *
445
     * @param \Doctrine\Common\Cache\Cache|null $queryCache Cache driver.
446
     *
447
     * @return Query This query instance.
448
     */
449 6
    public function setQueryCacheDriver($queryCache)
450
    {
451 6
        $this->_queryCache = $queryCache;
452
453 6
        return $this;
454
    }
455
456
    /**
457
     * Defines whether the query should make use of a query cache, if available.
458
     *
459
     * @param boolean $bool
460
     *
461
     * @return Query This query instance.
462
     */
463 175
    public function useQueryCache($bool)
464
    {
465 175
        $this->_useQueryCache = $bool;
466
467 175
        return $this;
468
    }
469
470
    /**
471
     * Returns the cache driver used for query caching.
472
     *
473
     * @return \Doctrine\Common\Cache\Cache|null The cache driver used for query caching or NULL, if
474
     *                                           this Query does not use query caching.
475
     */
476 610
    public function getQueryCacheDriver()
477
    {
478 610
        if ($this->_queryCache) {
479 9
            return $this->_queryCache;
480
        }
481
482 601
        return $this->_em->getConfiguration()->getQueryCacheImpl();
483
    }
484
485
    /**
486
     * Defines how long the query cache will be active before expire.
487
     *
488
     * @param integer $timeToLive How long the cache entry is valid.
489
     *
490
     * @return Query This query instance.
491
     */
492 1
    public function setQueryCacheLifetime($timeToLive)
493
    {
494 1
        if ($timeToLive !== null) {
0 ignored issues
show
introduced by
The condition $timeToLive !== null is always true.
Loading history...
495 1
            $timeToLive = (int) $timeToLive;
496
        }
497
498 1
        $this->_queryCacheTTL = $timeToLive;
499
500 1
        return $this;
501
    }
502
503
    /**
504
     * Retrieves the lifetime of resultset cache.
505
     *
506
     * @return int
507
     */
508
    public function getQueryCacheLifetime()
509
    {
510
        return $this->_queryCacheTTL;
511
    }
512
513
    /**
514
     * Defines if the query cache is active or not.
515
     *
516
     * @param boolean $expire Whether or not to force query cache expiration.
517
     *
518
     * @return Query This query instance.
519
     */
520 7
    public function expireQueryCache($expire = true)
521
    {
522 7
        $this->_expireQueryCache = $expire;
523
524 7
        return $this;
525
    }
526
527
    /**
528
     * Retrieves if the query cache is active or not.
529
     *
530
     * @return bool
531
     */
532
    public function getExpireQueryCache()
533
    {
534
        return $this->_expireQueryCache;
535
    }
536
537
    /**
538
     * @override
539
     */
540 212
    public function free()
541
    {
542 212
        parent::free();
543
544 212
        $this->_dql = null;
545 212
        $this->_state = self::STATE_CLEAN;
546 212
    }
547
548
    /**
549
     * Sets a DQL query string.
550
     *
551
     * @param string $dqlQuery DQL Query.
552
     *
553
     * @return \Doctrine\ORM\AbstractQuery
554
     */
555 970
    public function setDQL($dqlQuery)
556
    {
557 970
        if ($dqlQuery !== null) {
0 ignored issues
show
introduced by
The condition $dqlQuery !== null is always true.
Loading history...
558 970
            $this->_dql = $dqlQuery;
559 970
            $this->_state = self::STATE_DIRTY;
560
        }
561
562 970
        return $this;
563
    }
564
565
    /**
566
     * Returns the DQL query that is represented by this query object.
567
     *
568
     * @return string DQL query.
569
     */
570 917
    public function getDQL()
571
    {
572 917
        return $this->_dql;
573
    }
574
575
    /**
576
     * Returns the state of this query object
577
     * By default the type is Doctrine_ORM_Query_Abstract::STATE_CLEAN but if it appears any unprocessed DQL
578
     * part, it is switched to Doctrine_ORM_Query_Abstract::STATE_DIRTY.
579
     *
580
     * @see AbstractQuery::STATE_CLEAN
581
     * @see AbstractQuery::STATE_DIRTY
582
     *
583
     * @return integer The query state.
584
     */
585
    public function getState()
586
    {
587
        return $this->_state;
588
    }
589
590
    /**
591
     * Method to check if an arbitrary piece of DQL exists
592
     *
593
     * @param string $dql Arbitrary piece of DQL to check for.
594
     *
595
     * @return boolean
596
     */
597
    public function contains($dql)
598
    {
599
        return stripos($this->getDQL(), $dql) !== false;
600
    }
601
602
    /**
603
     * Sets the position of the first result to retrieve (the "offset").
604
     *
605
     * @param integer $firstResult The first result to return.
606
     *
607
     * @return Query This query object.
608
     */
609 225
    public function setFirstResult($firstResult)
610
    {
611 225
        $this->_firstResult = $firstResult;
612 225
        $this->_state       = self::STATE_DIRTY;
613
614 225
        return $this;
615
    }
616
617
    /**
618
     * Gets the position of the first result the query object was set to retrieve (the "offset").
619
     * Returns NULL if {@link setFirstResult} was not applied to this query.
620
     *
621
     * @return integer The position of the first result.
622
     */
623 665
    public function getFirstResult()
624
    {
625 665
        return $this->_firstResult;
626
    }
627
628
    /**
629
     * Sets the maximum number of results to retrieve (the "limit").
630
     *
631
     * @param integer|null $maxResults
632
     *
633
     * @return Query This query object.
634
     */
635 247
    public function setMaxResults($maxResults)
636
    {
637 247
        $this->_maxResults = $maxResults;
638 247
        $this->_state      = self::STATE_DIRTY;
639
640 247
        return $this;
641
    }
642
643
    /**
644
     * Gets the maximum number of results the query object was set to retrieve (the "limit").
645
     * Returns NULL if {@link setMaxResults} was not applied to this query.
646
     *
647
     * @return integer|null Maximum number of results.
648
     */
649 665
    public function getMaxResults()
650
    {
651 665
        return $this->_maxResults;
652
    }
653
654
    /**
655
     * Executes the query and returns an IterableResult that can be used to incrementally
656
     * iterated over the result.
657
     *
658
     * @param ArrayCollection|array|null $parameters    The query parameters.
659
     * @param integer                    $hydrationMode The hydration mode to use.
660
     *
661
     * @return \Doctrine\ORM\Internal\Hydration\IterableResult
662
     */
663 10
    public function iterate($parameters = null, $hydrationMode = self::HYDRATE_OBJECT)
664
    {
665 10
        $this->setHint(self::HINT_INTERNAL_ITERATION, true);
666
667 10
        return parent::iterate($parameters, $hydrationMode);
668
    }
669
670
    /**
671
     * {@inheritdoc}
672
     */
673 464
    public function setHint($name, $value)
674
    {
675 464
        $this->_state = self::STATE_DIRTY;
676
677 464
        return parent::setHint($name, $value);
678
    }
679
680
    /**
681
     * {@inheritdoc}
682
     */
683 367
    public function setHydrationMode($hydrationMode)
684
    {
685 367
        $this->_state = self::STATE_DIRTY;
686
687 367
        return parent::setHydrationMode($hydrationMode);
688
    }
689
690
    /**
691
     * Set the lock mode for this Query.
692
     *
693
     * @see \Doctrine\DBAL\LockMode
694
     *
695
     * @param int $lockMode
696
     *
697
     * @return Query
698
     *
699
     * @throws TransactionRequiredException
700
     */
701
    public function setLockMode($lockMode)
702
    {
703
        if (in_array($lockMode, [LockMode::NONE, LockMode::PESSIMISTIC_READ, LockMode::PESSIMISTIC_WRITE], true)) {
704
            if ( ! $this->_em->getConnection()->isTransactionActive()) {
705
                throw TransactionRequiredException::transactionRequired();
706
            }
707
        }
708
709
        $this->setHint(self::HINT_LOCK_MODE, $lockMode);
710
711
        return $this;
712
    }
713
714
    /**
715
     * Get the current lock mode for this query.
716
     *
717
     * @return int|null The current lock mode of this query or NULL if no specific lock mode is set.
718
     */
719
    public function getLockMode()
720
    {
721
        $lockMode = $this->getHint(self::HINT_LOCK_MODE);
722
723
        if (false === $lockMode) {
724
            return null;
725
        }
726
727
        return $lockMode;
728
    }
729
730
    /**
731
     * Generate a cache id for the query cache - reusing the Result-Cache-Id generator.
732
     *
733
     * @return string
734
     */
735 610
    protected function _getQueryCacheId()
736
    {
737 610
        ksort($this->_hints);
738
739 610
        $platform = $this->getEntityManager()
740 610
            ->getConnection()
741 610
            ->getDatabasePlatform()
742 610
            ->getName();
743
744 610
        return md5(
745 610
            $this->getDQL() . serialize($this->_hints) .
746 610
            '&platform=' . $platform .
747 610
            ($this->_em->hasFilters() ? $this->_em->getFilters()->getHash() : '') .
748 610
            '&firstResult=' . $this->_firstResult . '&maxResult=' . $this->_maxResults .
749 610
            '&hydrationMode=' . $this->_hydrationMode . '&types=' . serialize($this->_parsedTypes) . 'DOCTRINE_QUERY_CACHE_SALT'
750
        );
751
    }
752
753
     /**
754
     * {@inheritdoc}
755
     */
756 28
    protected function getHash()
757
    {
758 28
        return sha1(parent::getHash(). '-'. $this->_firstResult . '-' . $this->_maxResults);
759
    }
760
761
    /**
762
     * Cleanup Query resource when clone is called.
763
     *
764
     * @return void
765
     */
766 139
    public function __clone()
767
    {
768 139
        parent::__clone();
769
770 139
        $this->_state = self::STATE_DIRTY;
771 139
    }
772
}
773