source: pro-violet-viettel/sourcecode/application/libraries/Doctrine/ORM/Query.php @ 356

Last change on this file since 356 was 345, checked in by quyenla, 11 years ago

collaborator page

File size: 16.4 KB
Line 
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 LGPL. For more information, see
17 * <http://www.doctrine-project.org>.
18 */
19
20namespace Doctrine\ORM;
21
22use Doctrine\DBAL\LockMode,
23    Doctrine\ORM\Query\Parser,
24    Doctrine\ORM\Query\ParserResult,
25    Doctrine\ORM\Query\QueryException;
26
27/**
28 * A Query object represents a DQL query.
29 *
30 * @since   1.0
31 * @author  Guilherme Blanco <guilhermeblanco@hotmail.com>
32 * @author  Konsta Vesterinen <kvesteri@cc.hut.fi>
33 * @author  Roman Borschel <roman@code-factory.org>
34 */
35final class Query extends AbstractQuery
36{
37    /* Query STATES */
38    /**
39     * A query object is in CLEAN state when it has NO unparsed/unprocessed DQL parts.
40     */
41    const STATE_CLEAN  = 1;
42    /**
43     * A query object is in state DIRTY when it has DQL parts that have not yet been
44     * parsed/processed. This is automatically defined as DIRTY when addDqlQueryPart
45     * is called.
46     */
47    const STATE_DIRTY = 2;
48
49    /* Query HINTS */
50    /**
51     * The refresh hint turns any query into a refresh query with the result that
52     * any local changes in entities are overridden with the fetched values.
53     *
54     * @var string
55     */
56    const HINT_REFRESH = 'doctrine.refresh';
57
58
59    /**
60     * Internal hint: is set to the proxy entity that is currently triggered for loading
61     *
62     * @var string
63     */
64    const HINT_REFRESH_ENTITY = 'doctrine.refresh.entity';
65
66    /**
67     * The forcePartialLoad query hint forces a particular query to return
68     * partial objects.
69     *
70     * @var string
71     * @todo Rename: HINT_OPTIMIZE
72     */
73    const HINT_FORCE_PARTIAL_LOAD = 'doctrine.forcePartialLoad';
74    /**
75     * The includeMetaColumns query hint causes meta columns like foreign keys and
76     * discriminator columns to be selected and returned as part of the query result.
77     *
78     * This hint does only apply to non-object queries.
79     *
80     * @var string
81     */
82    const HINT_INCLUDE_META_COLUMNS = 'doctrine.includeMetaColumns';
83
84    /**
85     * An array of class names that implement \Doctrine\ORM\Query\TreeWalker and
86     * are iterated and executed after the DQL has been parsed into an AST.
87     *
88     * @var string
89     */
90    const HINT_CUSTOM_TREE_WALKERS = 'doctrine.customTreeWalkers';
91
92    /**
93     * A string with a class name that implements \Doctrine\ORM\Query\TreeWalker
94     * and is used for generating the target SQL from any DQL AST tree.
95     *
96     * @var string
97     */
98    const HINT_CUSTOM_OUTPUT_WALKER = 'doctrine.customOutputWalker';
99
100    //const HINT_READ_ONLY = 'doctrine.readOnly';
101
102    /**
103     * @var string
104     */
105    const HINT_INTERNAL_ITERATION = 'doctrine.internal.iteration';
106
107    /**
108     * @var string
109     */
110    const HINT_LOCK_MODE = 'doctrine.lockMode';
111
112    /**
113     * @var integer $_state   The current state of this query.
114     */
115    private $_state = self::STATE_CLEAN;
116
117    /**
118     * @var string $_dql Cached DQL query.
119     */
120    private $_dql = null;
121
122    /**
123     * @var \Doctrine\ORM\Query\ParserResult  The parser result that holds DQL => SQL information.
124     */
125    private $_parserResult;
126
127    /**
128     * @var integer The first result to return (the "offset").
129     */
130    private $_firstResult = null;
131
132    /**
133     * @var integer The maximum number of results to return (the "limit").
134     */
135    private $_maxResults = null;
136
137    /**
138     * @var CacheDriver The cache driver used for caching queries.
139     */
140    private $_queryCache;
141
142    /**
143     * @var boolean Boolean value that indicates whether or not expire the query cache.
144     */
145    private $_expireQueryCache = false;
146
147    /**
148     * @var int Query Cache lifetime.
149     */
150    private $_queryCacheTTL;
151
152    /**
153     * @var boolean Whether to use a query cache, if available. Defaults to TRUE.
154     */
155    private $_useQueryCache = true;
156
157    // End of Caching Stuff
158
159    /**
160     * Initializes a new Query instance.
161     *
162     * @param \Doctrine\ORM\EntityManager $entityManager
163     */
164    /*public function __construct(EntityManager $entityManager)
165    {
166        parent::__construct($entityManager);
167    }*/
168
169    /**
170     * Gets the SQL query/queries that correspond to this DQL query.
171     *
172     * @return mixed The built sql query or an array of all sql queries.
173     * @override
174     */
175    public function getSQL()
176    {
177        return $this->_parse()->getSQLExecutor()->getSQLStatements();
178    }
179
180    /**
181     * Returns the corresponding AST for this DQL query.
182     *
183     * @return \Doctrine\ORM\Query\AST\SelectStatement |
184     *         \Doctrine\ORM\Query\AST\UpdateStatement |
185     *         \Doctrine\ORM\Query\AST\DeleteStatement
186     */
187    public function getAST()
188    {
189        $parser = new Parser($this);
190        return $parser->getAST();
191    }
192
193    /**
194     * Parses the DQL query, if necessary, and stores the parser result.
195     *
196     * Note: Populates $this->_parserResult as a side-effect.
197     *
198     * @return \Doctrine\ORM\Query\ParserResult
199     */
200    private function _parse()
201    {
202        // Return previous parser result if the query and the filter collection are both clean
203        if ($this->_state === self::STATE_CLEAN
204            && $this->_em->isFiltersStateClean()
205        ) {
206            return $this->_parserResult;
207        }
208
209        $this->_state = self::STATE_CLEAN;
210
211        // Check query cache.
212        if ( ! ($this->_useQueryCache && ($queryCache = $this->getQueryCacheDriver()))) {
213            $parser = new Parser($this);
214            $this->_parserResult = $parser->parse();
215
216            return $this->_parserResult;
217        }
218
219        $hash   = $this->_getQueryCacheId();
220        $cached = $this->_expireQueryCache ? false : $queryCache->fetch($hash);
221
222        if ($cached instanceof ParserResult) {
223            // Cache hit.
224            $this->_parserResult = $cached;
225
226            return $this->_parserResult;
227        }
228
229        // Cache miss.
230        $parser = new Parser($this);
231        $this->_parserResult = $parser->parse();
232        $queryCache->save($hash, $this->_parserResult, $this->_queryCacheTTL);
233
234        return $this->_parserResult;
235    }
236
237    /**
238     * {@inheritdoc}
239     */
240    protected function _doExecute()
241    {
242        $executor = $this->_parse()->getSqlExecutor();
243
244        if ($this->_queryCacheProfile) {
245            $executor->setQueryCacheProfile($this->_queryCacheProfile);
246        }
247
248        // Prepare parameters
249        $paramMappings = $this->_parserResult->getParameterMappings();
250
251        if (count($paramMappings) != count($this->_params)) {
252            throw QueryException::invalidParameterNumber();
253        }
254
255        list($sqlParams, $types) = $this->processParameterMappings($paramMappings);
256
257        if ($this->_resultSetMapping === null) {
258            $this->_resultSetMapping = $this->_parserResult->getResultSetMapping();
259        }
260
261        return $executor->execute($this->_em->getConnection(), $sqlParams, $types);
262    }
263
264    /**
265     * Processes query parameter mappings
266     *
267     * @param array $paramMappings
268     * @return array
269     */
270    private function processParameterMappings($paramMappings)
271    {
272        $sqlParams = $types = array();
273
274        foreach ($this->_params as $key => $value) {
275            if ( ! isset($paramMappings[$key])) {
276                throw QueryException::unknownParameter($key);
277            }
278
279            if (isset($this->_paramTypes[$key])) {
280                foreach ($paramMappings[$key] as $position) {
281                    $types[$position] = $this->_paramTypes[$key];
282                }
283            }
284
285            $sqlPositions = $paramMappings[$key];
286            // optimized multi value sql positions away for now, they are not allowed in DQL anyways.
287            $value = array($value);
288            $countValue = count($value);
289
290            for ($i = 0, $l = count($sqlPositions); $i < $l; $i++) {
291                $sqlParams[$sqlPositions[$i]] = $value[($i % $countValue)];
292            }
293        }
294
295        if (count($sqlParams) != count($types)) {
296            throw QueryException::parameterTypeMissmatch();
297        }
298
299        if ($sqlParams) {
300            ksort($sqlParams);
301            $sqlParams = array_values($sqlParams);
302
303            ksort($types);
304            $types = array_values($types);
305        }
306
307        return array($sqlParams, $types);
308    }
309
310    /**
311     * Defines a cache driver to be used for caching queries.
312     *
313     * @param Doctrine_Cache_Interface|null $driver Cache driver
314     * @return Query This query instance.
315     */
316    public function setQueryCacheDriver($queryCache)
317    {
318        $this->_queryCache = $queryCache;
319
320        return $this;
321    }
322
323    /**
324     * Defines whether the query should make use of a query cache, if available.
325     *
326     * @param boolean $bool
327     * @return @return Query This query instance.
328     */
329    public function useQueryCache($bool)
330    {
331        $this->_useQueryCache = $bool;
332
333        return $this;
334    }
335
336    /**
337     * Returns the cache driver used for query caching.
338     *
339     * @return CacheDriver The cache driver used for query caching or NULL, if this
340     *                                     Query does not use query caching.
341     */
342    public function getQueryCacheDriver()
343    {
344        if ($this->_queryCache) {
345            return $this->_queryCache;
346        }
347
348        return $this->_em->getConfiguration()->getQueryCacheImpl();
349    }
350
351    /**
352     * Defines how long the query cache will be active before expire.
353     *
354     * @param integer $timeToLive How long the cache entry is valid
355     * @return Query This query instance.
356     */
357    public function setQueryCacheLifetime($timeToLive)
358    {
359        if ($timeToLive !== null) {
360            $timeToLive = (int) $timeToLive;
361        }
362
363        $this->_queryCacheTTL = $timeToLive;
364
365        return $this;
366    }
367
368    /**
369     * Retrieves the lifetime of resultset cache.
370     *
371     * @return int
372     */
373    public function getQueryCacheLifetime()
374    {
375        return $this->_queryCacheTTL;
376    }
377
378    /**
379     * Defines if the query cache is active or not.
380     *
381     * @param boolean $expire Whether or not to force query cache expiration.
382     * @return Query This query instance.
383     */
384    public function expireQueryCache($expire = true)
385    {
386        $this->_expireQueryCache = $expire;
387
388        return $this;
389    }
390
391    /**
392     * Retrieves if the query cache is active or not.
393     *
394     * @return bool
395     */
396    public function getExpireQueryCache()
397    {
398        return $this->_expireQueryCache;
399    }
400
401    /**
402     * @override
403     */
404    public function free()
405    {
406        parent::free();
407
408        $this->_dql = null;
409        $this->_state = self::STATE_CLEAN;
410    }
411
412    /**
413     * Sets a DQL query string.
414     *
415     * @param string $dqlQuery DQL Query
416     * @return \Doctrine\ORM\AbstractQuery
417     */
418    public function setDQL($dqlQuery)
419    {
420        if ($dqlQuery !== null) {
421            $this->_dql = $dqlQuery;
422            $this->_state = self::STATE_DIRTY;
423        }
424
425        return $this;
426    }
427
428    /**
429     * Returns the DQL query that is represented by this query object.
430     *
431     * @return string DQL query
432     */
433    public function getDQL()
434    {
435        return $this->_dql;
436    }
437
438    /**
439     * Returns the state of this query object
440     * By default the type is Doctrine_ORM_Query_Abstract::STATE_CLEAN but if it appears any unprocessed DQL
441     * part, it is switched to Doctrine_ORM_Query_Abstract::STATE_DIRTY.
442     *
443     * @see AbstractQuery::STATE_CLEAN
444     * @see AbstractQuery::STATE_DIRTY
445     *
446     * @return integer Return the query state
447     */
448    public function getState()
449    {
450        return $this->_state;
451    }
452
453    /**
454     * Method to check if an arbitrary piece of DQL exists
455     *
456     * @param string $dql Arbitrary piece of DQL to check for
457     * @return boolean
458     */
459    public function contains($dql)
460    {
461        return stripos($this->getDQL(), $dql) === false ? false : true;
462    }
463
464    /**
465     * Sets the position of the first result to retrieve (the "offset").
466     *
467     * @param integer $firstResult The first result to return.
468     * @return Query This query object.
469     */
470    public function setFirstResult($firstResult)
471    {
472        $this->_firstResult = $firstResult;
473        $this->_state = self::STATE_DIRTY;
474
475        return $this;
476    }
477
478    /**
479     * Gets the position of the first result the query object was set to retrieve (the "offset").
480     * Returns NULL if {@link setFirstResult} was not applied to this query.
481     *
482     * @return integer The position of the first result.
483     */
484    public function getFirstResult()
485    {
486        return $this->_firstResult;
487    }
488
489    /**
490     * Sets the maximum number of results to retrieve (the "limit").
491     *
492     * @param integer $maxResults
493     * @return Query This query object.
494     */
495    public function setMaxResults($maxResults)
496    {
497        $this->_maxResults = $maxResults;
498        $this->_state = self::STATE_DIRTY;
499
500        return $this;
501    }
502
503    /**
504     * Gets the maximum number of results the query object was set to retrieve (the "limit").
505     * Returns NULL if {@link setMaxResults} was not applied to this query.
506     *
507     * @return integer Maximum number of results.
508     */
509    public function getMaxResults()
510    {
511        return $this->_maxResults;
512    }
513
514    /**
515     * Executes the query and returns an IterableResult that can be used to incrementally
516     * iterated over the result.
517     *
518     * @param array $params The query parameters.
519     * @param integer $hydrationMode The hydration mode to use.
520     * @return \Doctrine\ORM\Internal\Hydration\IterableResult
521     */
522    public function iterate(array $params = array(), $hydrationMode = self::HYDRATE_OBJECT)
523    {
524        $this->setHint(self::HINT_INTERNAL_ITERATION, true);
525
526        return parent::iterate($params, $hydrationMode);
527    }
528
529    /**
530     * {@inheritdoc}
531     */
532    public function setHint($name, $value)
533    {
534        $this->_state = self::STATE_DIRTY;
535
536        return parent::setHint($name, $value);
537    }
538
539    /**
540     * {@inheritdoc}
541     */
542    public function setHydrationMode($hydrationMode)
543    {
544        $this->_state = self::STATE_DIRTY;
545
546        return parent::setHydrationMode($hydrationMode);
547    }
548
549    /**
550     * Set the lock mode for this Query.
551     *
552     * @see \Doctrine\DBAL\LockMode
553     * @param  int $lockMode
554     * @return Query
555     */
556    public function setLockMode($lockMode)
557    {
558        if ($lockMode === LockMode::PESSIMISTIC_READ || $lockMode === LockMode::PESSIMISTIC_WRITE) {
559            if ( ! $this->_em->getConnection()->isTransactionActive()) {
560                throw TransactionRequiredException::transactionRequired();
561            }
562        }
563
564        $this->setHint(self::HINT_LOCK_MODE, $lockMode);
565
566        return $this;
567    }
568
569    /**
570     * Get the current lock mode for this query.
571     *
572     * @return int
573     */
574    public function getLockMode()
575    {
576        $lockMode = $this->getHint(self::HINT_LOCK_MODE);
577
578        if ( ! $lockMode) {
579            return LockMode::NONE;
580        }
581
582        return $lockMode;
583    }
584
585    /**
586     * Generate a cache id for the query cache - reusing the Result-Cache-Id generator.
587     *
588     * The query cache
589     *
590     * @return string
591     */
592    protected function _getQueryCacheId()
593    {
594        ksort($this->_hints);
595
596        return md5(
597            $this->getDql() . var_export($this->_hints, true) .
598            ($this->_em->hasFilters() ? $this->_em->getFilters()->getHash() : '') .
599            '&firstResult=' . $this->_firstResult . '&maxResult=' . $this->_maxResults .
600            '&hydrationMode='.$this->_hydrationMode.'DOCTRINE_QUERY_CACHE_SALT'
601        );
602    }
603
604    /**
605     * Cleanup Query resource when clone is called.
606     *
607     * @return void
608     */
609    public function __clone()
610    {
611        parent::__clone();
612
613        $this->_state = self::STATE_DIRTY;
614    }
615}
Note: See TracBrowser for help on using the repository browser.