AbstractQuery.php 13.2 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
<?php
/*
 *  $Id: Abstract.php 1393 2008-03-06 17:49:16Z guilhermeblanco $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information, see
19
 * <http://www.doctrine-project.org>.
20 21
 */

22 23
namespace Doctrine\ORM;

24
/**
25
 * Base class for Query and NativeQuery.
26 27
 *
 * @license     http://www.opensource.org/licenses/lgpl-license.php LGPL
28
 * @link        www.doctrine-project.com
29 30 31 32
 * @since       1.0
 * @version     $Revision: 1393 $
 * @author      Guilherme Blanco <guilhermeblanco@hotmail.com>
 * @author      Konsta Vesterinen <kvesteri@cc.hut.fi>
33
 * @author      Roman Borschel <roman@code-factory.org>
34
 */
35
abstract class AbstractQuery
36
{
37
    /* Hydration mode constants */
38
    /**
39
     * Hydrates an object graph. This is the default behavior.
40
     */
41
    const HYDRATE_OBJECT = 1;
42
    /**
43
     * Hydrates an array graph.
44
     */
45
    const HYDRATE_ARRAY = 2;
46
    /**
47
     * Hydrates a flat, rectangular result set with scalar values.
48
     */
49
    const HYDRATE_SCALAR = 3;
50
    /**
51
     * Hydrates a single scalar value.
52
     */
53
    const HYDRATE_SINGLE_SCALAR = 4;
54
    /**
55
     * Hydrates nothing.
56
     */
57
    const HYDRATE_NONE = 5;
58 59 60 61 62 63 64

    /**
     * @var array $params Parameters of this query.
     */
    protected $_params = array();

    /**
65 66 67
     * The user-specified ResultSetMapping to use.
     *
     * @var ResultSetMapping
68
     */
69
    protected $_resultSetMapping;
70 71

    /**
72
     * @var Doctrine\ORM\EntityManager The entity manager used by this query object.
73
     */
74
    protected $_em;
75 76

    /**
77 78 79
     * A set of query hints.
     *
     * @var array
80
     */
81
    protected $_hints = array();
82 83

    /**
84
     * @var integer The hydration mode.
85
     */
86
    protected $_hydrationMode = self::HYDRATE_OBJECT;
87 88

    /**
89
     * The locally set cache driver used for caching result sets of this query.
90
     *
91
     * @var CacheDriver
92
     */
93
    protected $_resultCache;
94 95

    /**
96
     * @var boolean Boolean value that indicates whether or not expire the result cache.
97
     */
98
    protected $_expireResultCache = false;
99

100 101 102 103
    /**
     * @var int Result Cache lifetime.
     */
    protected $_resultCacheTTL;
104 105

    /**
106 107 108
     * Initializes a new instance of a class derived from <tt>AbstractQuery</tt>.
     *
     * @param Doctrine\ORM\EntityManager $entityManager
109
     */
110
    public function __construct(EntityManager $entityManager)
111
    {
112
        $this->_em = $entityManager;
113 114 115
    }

    /**
116 117 118
     * Retrieves the associated EntityManager of this Query instance.
     *
     * @return Doctrine\ORM\EntityManager
119
     */
120
    public function getEntityManager()
121
    {
122
        return $this->_em;
123 124 125
    }

    /**
126
     * Frees the resources used by the query object.
127
     */
128
    public function free()
129
    {
130
        $this->_params = array();
131 132 133
    }

    /**
134
     * Get all defined parameters
135
     *
136
     * @return array Defined parameters
137
     */
romanb's avatar
romanb committed
138
    public function getParameters($params = array())
139
    {
romanb's avatar
romanb committed
140 141 142 143
        if ($params) {
            return array_merge($this->_params, $params);
        }
        return $this->_params;
144
    }
romanb's avatar
romanb committed
145
    
146
    /**
romanb's avatar
romanb committed
147 148 149 150
     * Gets a query parameter.
     * 
     * @param mixed $key The key (index or name) of the bound parameter.
     * @return mixed The value of the bound parameter.
151
     */
romanb's avatar
romanb committed
152
    public function getParameter($key)
153
    {
romanb's avatar
romanb committed
154
        return isset($this->_params[$key]) ? $this->_params[$key] : null;
155 156 157
    }

    /**
158 159 160
     * Gets the SQL query that corresponds to this query object.
     * The returned SQL syntax depends on the connection driver that is used
     * by this query object at the time of this method call.
161
     *
162
     * @return string SQL query
163
     */
164 165
    abstract public function getSql();
    
166
    /**
167
     * Sets a query parameter.
168
     *
169 170
     * @param string|integer $key The parameter position or name.
     * @param mixed $value The parameter value.
171
     */
172
    public function setParameter($key, $value)
173
    {
174
        $this->_params[$key] = $value;
175
    }
176
    
177
    /**
178
     * Sets a collection of query parameters.
179
     *
180
     * @param array $params
181
     */
182
    public function setParameters(array $params)
183
    {
184 185
        foreach ($params as $key => $value) {
            $this->setParameter($key, $value);
186 187 188 189
        }
    }

    /**
190
     * Sets the ResultSetMapping that should be used for hydration.
191
     *
192
     * @param ResultSetMapping $rsm
193
     */
194
    public function setResultSetMapping($rsm)
195
    {
196
        $this->_resultSetMapping = $rsm;
197 198 199
    }

    /**
200
     * Defines a cache driver to be used for caching result sets.
201
     *
202
     * @param Doctrine\Common\Cache\Cache $driver Cache driver
203
     * @return Doctrine\ORM\Query
204
     */
205
    public function setResultCache($resultCache = null)
206
    {
207
        if ($resultCache !== null && ! ($resultCache instanceof \Doctrine\Common\Cache\Cache)) {
208 209 210
            throw DoctrineException::updateMe(
                'Method setResultCache() accepts only an instance of Doctrine_Cache_Interface or null.'
            );
211
        }
212 213
        $this->_resultCache = $resultCache;
        return $this;
214 215 216
    }

    /**
217
     * Returns the cache driver used for caching result sets.
218
     *
219
     * @return Doctrine\Common\Cache\Cache Cache driver
220
     */
221
    public function getResultCacheDriver()
222
    {
223
        if ($this->_resultCache) {
224
            return $this->_resultCache;
225
        } else {
226
            return $this->_em->getConfiguration()->getResultCacheImpl();
227 228 229 230
        }
    }

    /**
231
     * Defines how long the result cache will be active before expire.
232
     *
233 234
     * @param integer $timeToLive How long the cache entry is valid
     * @return Doctrine\ORM\Query
235
     */
236
    public function setResultCacheLifetime($timeToLive)
237
    {
238 239
        if ($timeToLive !== null) {
            $timeToLive = (int) $timeToLive;
240 241
        }

242
        $this->_resultCacheTTL = $timeToLive;
243

244
        return $this;
245 246 247
    }

    /**
248
     * Retrieves the lifetime of resultset cache.
249
     *
250
     * @return int
251
     */
252
    public function getResultCacheLifetime()
253
    {
254
        return $this->_resultCacheTTL;
255 256 257
    }

    /**
258
     * Defines if the resultset cache is active or not.
259
     *
260
     * @param boolean $expire Whether or not to force resultset cache expiration.
261 262
     * @return Doctrine_ORM_Query
     */
263
    public function setExpireResultCache($expire = true)
264
    {
265
        $this->_expireResultCache = (bool) $expire;
266

267
        return $this;
268 269 270
    }

    /**
271
     * Retrieves if the resultset cache is active or not.
272
     *
273
     * @return bool
274
     */
275
    public function getExpireResultCache()
276
    {
277
        return $this->_expireResultCache;
278 279 280
    }

    /**
281
     * Defines the processing mode to be used during hydration.
282
     *
283 284 285
     * @param integer $hydrationMode Doctrine processing mode to be used during hydration process.
     *                               One of the Query::HYDRATE_* constants.
     * @return Doctrine\ORM\Query
286
     */
287
    public function setHydrationMode($hydrationMode)
288
    {
289 290
        $this->_hydrationMode = $hydrationMode;
        return $this;
291 292 293
    }

    /**
294
     * Gets the hydration mode currently used by the query.
295
     *
296
     * @return integer
297
     */
298
    public function getHydrationMode()
299
    {
300
        return $this->_hydrationMode;
301 302 303
    }

    /**
304
     * Gets the list of results for the query.
305
     *
306
     * Alias for execute(array(), HYDRATE_OBJECT).
307
     *
308
     * @return Collection
309
     */
310
    public function getResultList()
311
    {
312
        return $this->execute(array(), self::HYDRATE_OBJECT);
313 314 315
    }

    /**
316
     * Gets the array of results for the query.
317
     *
318
     * Alias for execute(array(), HYDRATE_ARRAY).
319
     *
320
     * @return array
321
     */
322
    public function getResultArray()
323
    {
324
        return $this->execute(array(), self::HYDRATE_ARRAY);
325 326 327
    }

    /**
328
     * Gets the scalar results for the query.
329
     *
330
     * Alias for execute(array(), HYDRATE_SCALAR).
331
     *
332
     * @return array
333
     */
334
    public function getScalarResult()
335
    {
336
        return $this->execute(array(), self::HYDRATE_SCALAR);
337 338 339
    }

    /**
340 341 342
     * Gets the single result of the query.
     * Enforces the uniqueness of the result. If the result is not unique,
     * a QueryException is thrown.
343
     *
344 345 346
     * @param integer $hydrationMode
     * @return mixed
     * @throws QueryException If the query result is not unique.
347
     */
348
    public function getSingleResult($hydrationMode = null)
349
    {
350 351 352 353 354 355 356 357 358 359
        $result = $this->execute(array(), $hydrationMode);
        if (is_array($result)) {
            if (count($result) > 1) {
                throw QueryException::nonUniqueResult();
            }
            return array_shift($result);
        } else if (is_object($result)) {
            if (count($result) > 1) {
                throw QueryException::nonUniqueResult();
            }
360
            return $result->first();
361
        }
362
        return $result;
363 364 365
    }

    /**
366
     * Gets the single scalar result of the query.
367
     *
368
     * Alias for getSingleResult(HYDRATE_SINGLE_SCALAR).
369
     *
370
     * @return mixed
371
     * @throws QueryException If the query result is not unique.
372
     */
373
    public function getSingleScalarResult()
374
    {
375
        return $this->getSingleResult(self::HYDRATE_SINGLE_SCALAR);
376 377 378
    }

    /**
379 380
     * Sets an implementation-specific hint. If the hint name is not recognized,
     * it is silently ignored.
381
     *
382 383
     * @param string $name The name of the hint.
     * @param mixed $value The value of the hint.
384
     */
385
    public function setHint($name, $value)
386
    {
387
        $this->_hints[$name] = $value;
388 389 390
    }

    /**
391 392
     * Gets an implementation-specific hint. If the hint name is not recognized,
     * FALSE is returned.
393
     *
394
     * @param string $name The name of the hint.
395
     * @return mixed The value of the hint or FALSE, if the hint name is not recognized.
396
     */
397
    public function getHint($name)
398
    {
399
        return isset($this->_hints[$name]) ? $this->_hints[$name] : false;
400 401 402
    }

    /**
403 404
     * Executes the query and returns an IterableResult that can be used to incrementally
     * iterated over the result.
405
     *
406
     * @param array $params The query parameters.
407
     * @param integer $hydrationMode The hydration mode to use.
408
     * @return IterableResult
409
     */
410
    public function iterate(array $params = array(), $hydrationMode = self::HYDRATE_OBJECT)
411
    {
412 413 414
        return $this->_em->getHydrator($this->_hydrationMode)->iterate(
            $this->_execute($params, $hydrationMode), $this->_resultSetMapping
        );
415 416 417
    }

    /**
418
     * Executes the query.
419
     *
420 421
     * @param string $params Any additional query parameters.
     * @param integer $hydrationMode Processing mode to be used during the hydration process.
422
     * @return mixed
423
     */
424
    public function execute($params = array(), $hydrationMode = null)
425
    {
426 427
        if ($this->_em->getUnitOfWork()->hasPendingInsertions()) {
            $this->_em->flush();
428 429
        }

430 431 432
        if ($hydrationMode !== null) {
            $this->_hydrationMode = $hydrationMode;
        }
romanb's avatar
romanb committed
433 434
    
        $params = $this->getParameters($params);
435

436 437
        // Check result cache
        if ($cacheDriver = $this->getResultCacheDriver()) {
438 439 440
            // Calculate hash for DQL query.
            $hash = md5($this->getDql() . var_export($params, true));
            $cached = ($this->_expireResultCache) ? false : $cacheDriver->fetch($hash);
441

442 443 444 445 446
            if ($cached === false) {
                // Cache miss.
                $result = $this->_doExecute($params);
                $queryResult = CacheHandler::fromResultSet($this, $result);
                $cacheDriver->save($hash, $queryResult->toCachedForm(), $this->_resultCacheTTL);
447

448 449 450 451
                return $result;
            } else {
                // Cache hit.
                $queryResult = CacheHandler::fromCachedResult($this, $cached);
452

453 454
                return $queryResult->getResultSet();
            }
455 456
        }

457
        $stmt = $this->_doExecute($params);
458

459 460
        if (is_integer($stmt)) {
            return $stmt;
461 462
        }

463
        return $this->_em->getHydrator($this->_hydrationMode)->hydrateAll($stmt, $this->_resultSetMapping);
464 465 466
    }

    /**
romanb's avatar
romanb committed
467 468 469 470 471 472 473
     * Prepares the given parameters for execution in an SQL statement.
     * 
     * Note to inheritors: This method must return a numerically, continously indexed array,
     * starting with index 0 where the values (the parameter values) are in the order
     * in which the parameters appear in the SQL query.
     * 
     * @return array The SQL parameter array.
474
     */
romanb's avatar
romanb committed
475
    abstract protected function _prepareParams(array $params);
476 477

    /**
478
     * Executes the query and returns a reference to the resulting Statement object.
479
     *
480
     * @param array $params
481
     */
482
    abstract protected function _doExecute(array $params);
483
}