1
0
mirror of synced 2024-12-13 22:56:04 +03:00
doctrine2/lib/Doctrine/ORM/Query.php

599 lines
17 KiB
PHP
Raw Normal View History

<?php
2008-05-24 22:18:37 +04:00
/*
2008-05-24 22:18:37 +04:00
* $Id: Query.php 3938 2008-03-06 19:36:50Z romanb $
*
* 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
* <http://www.phpdoctrine.org>.
*/
namespace Doctrine\ORM;
use Doctrine\ORM\Query\Parser;
/**
* A Doctrine_ORM_Query object represents a DQL query. It is used to query databases for
* data in an object-oriented fashion. A DQL query understands relations and inheritance
* and is to a large degree dbms independant.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
2009-01-04 19:15:32 +03:00
* @link www.doctrine-project.org
* @since 1.0
2008-05-24 22:18:37 +04:00
* @version $Revision: 3938 $
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Roman Borschel <roman@code-factory.org>
*/
class Query extends AbstractQuery
2007-05-16 23:20:55 +04:00
{
/* Hydration mode constants */
/**
* Hydrates an object graph. This is the default behavior.
*/
const HYDRATE_OBJECT = 1;
/**
* Hydrates an array graph.
*/
const HYDRATE_ARRAY = 2;
/**
* Hydrates a flat, rectangular result set with scalar values.
*/
const HYDRATE_SCALAR = 3;
/**
* Hydrates a single scalar value.
*/
const HYDRATE_SINGLE_SCALAR = 4;
/**
* Hydrates nothing.
*/
const HYDRATE_NONE = 5;
/**
2009-01-04 19:15:32 +03:00
* @var Doctrine\ORM\EntityManager The entity manager used by this query object.
*/
protected $_entityManager;
/**
* @var integer The hydration mode.
*/
protected $_hydrationMode = self::HYDRATE_OBJECT;
/**
2009-01-04 19:15:32 +03:00
* @var Doctrine\ORM\Query\ParserResult The parser result that holds DQL => SQL information.
*/
2008-05-24 22:18:37 +04:00
protected $_parserResult;
2007-05-26 23:50:40 +04:00
/**
* A set of query hints.
*
* @var array
2007-05-26 23:50:40 +04:00
*/
protected $_hints = array();
2008-05-24 22:18:37 +04:00
// Caching Stuff
2007-05-19 22:29:29 +04:00
/**
2008-05-24 22:18:37 +04:00
* @var Doctrine_Cache_Interface The cache driver used for caching result sets.
2007-05-16 23:20:55 +04:00
*/
2008-05-24 22:18:37 +04:00
protected $_resultCache;
2007-05-27 15:38:16 +04:00
/**
2008-05-24 22:18:37 +04:00
* @var boolean Boolean value that indicates whether or not expire the result cache.
*/
2008-05-24 22:18:37 +04:00
protected $_expireResultCache = false;
/**
2008-05-24 22:18:37 +04:00
* @var int Result Cache lifetime.
*/
2008-05-24 22:18:37 +04:00
protected $_resultCacheTTL;
/**
2008-05-24 22:18:37 +04:00
* @var Doctrine_Cache_Interface The cache driver used for caching queries.
*/
2008-05-24 22:18:37 +04:00
protected $_queryCache;
/**
2008-05-24 22:18:37 +04:00
* @var boolean Boolean value that indicates whether or not expire the query cache.
*/
2008-05-24 22:18:37 +04:00
protected $_expireQueryCache = false;
2007-05-24 20:13:50 +04:00
/**
2008-05-24 22:18:37 +04:00
* @var int Query Cache lifetime.
2007-05-24 20:13:50 +04:00
*/
2008-05-24 22:18:37 +04:00
protected $_queryCacheTTL;
// End of Caching Stuff
/**
* Initializes a new instance of the Query class.
*
2009-01-04 19:15:32 +03:00
* @param Doctrine\ORM\EntityManager $entityManager
*/
public function __construct(EntityManager $entityManager)
2007-05-24 20:13:50 +04:00
{
$this->_entityManager = $entityManager;
2008-05-24 22:18:37 +04:00
$this->free();
}
/**
* Retrieves the assocated EntityManager to this Query instance.
*
2009-01-04 19:15:32 +03:00
* @return Doctrine\ORM\EntityManager
*/
public function getEntityManager()
{
return $this->_entityManager;
}
2008-05-24 22:18:37 +04:00
/**
* Convenience method to execute using array fetching as hydration mode.
*
* @param string $params
* @return array
*/
public function fetchArray($params = array())
{
return $this->execute($params, self::HYDRATE_ARRAY);
}
2008-05-24 22:18:37 +04:00
/**
* Convenience method to execute the query and return the first item
* of the collection.
*
* @param string $params Parameters
* @param int $hydrationMode Hydration mode
2009-01-04 19:15:32 +03:00
* @return mixed Array or Doctrine\Common\Collection or false if no result.
*/
public function fetchOne($params = array(), $hydrationMode = null)
{
2008-07-01 09:57:19 +04:00
$collection = $this->limit(1)->execute($params, $hydrationMode);
if (count($collection) === 0) {
return false;
}
if ($collection instanceof Collection) {
return $collection->getFirst();
} else if (is_array($collection)) {
return array_shift($collection);
}
return false;
2007-05-18 03:13:58 +04:00
}
/**
2008-05-24 22:18:37 +04:00
* Query the database with DQL (Doctrine Query Language).
*
* @param string $query The DQL query.
* @param array $params The query parameters.
* @param int $hydrationMode
2008-05-24 22:18:37 +04:00
* @return mixed
*/
2008-05-24 22:18:37 +04:00
public function query($query, $params = array(), $hydrationMode = null)
{
2008-05-24 22:18:37 +04:00
$this->setDql($query);
return $this->execute($params, $hydrationMode);
}
2007-07-09 00:18:50 +04:00
/**
* Gets the SQL query/queries that correspond to this DQL query.
2007-07-09 00:18:50 +04:00
*
2008-05-24 22:18:37 +04:00
* @return mixed The built sql query or an array of all sql queries.
2007-07-09 00:18:50 +04:00
*/
2008-05-24 22:18:37 +04:00
public function getSql()
2007-07-09 00:18:50 +04:00
{
2008-05-24 22:18:37 +04:00
return $this->parse()->getSqlExecutor()->getSqlStatements();
2007-07-09 00:18:50 +04:00
}
2007-05-16 23:20:55 +04:00
/**
2008-05-24 22:18:37 +04:00
* Parses the DQL query, if necessary, and stores the parser result.
*
* Note: Populates $this->_parserResult as a side-effect.
2007-05-16 23:20:55 +04:00
*
* @return Doctrine\ORM\Query\ParserResult
2007-05-16 23:20:55 +04:00
*/
2008-05-24 22:18:37 +04:00
public function parse()
2007-05-16 23:20:55 +04:00
{
2008-05-24 22:18:37 +04:00
if ($this->_state === self::STATE_DIRTY) {
$parser = new Parser($this);
2008-05-24 22:18:37 +04:00
$this->_parserResult = $parser->parse();
$this->_state = self::STATE_CLEAN;
2007-11-19 20:55:23 +03:00
}
2008-05-24 22:18:37 +04:00
return $this->_parserResult;
2007-09-13 01:42:42 +04:00
}
2007-09-13 01:42:42 +04:00
/**
* Executes the query.
2007-09-13 01:42:42 +04:00
*
2008-05-24 22:18:37 +04:00
* @param string $params Parameters to be sent to query.
* @param integer $hydrationMode Doctrine processing mode to be used during hydration process.
* One of the Doctrine::HYDRATE_* constants.
* @return mixed
2007-09-13 01:42:42 +04:00
*/
2008-05-24 22:18:37 +04:00
public function execute($params = array(), $hydrationMode = null)
2007-09-13 01:42:42 +04:00
{
if ($hydrationMode !== null) {
$this->_hydrationMode = $hydrationMode;
}
2008-05-24 22:18:37 +04:00
$params = $this->getParams($params);
// Check result cache
2008-05-24 22:18:37 +04:00
if ($this->_resultCache && $this->_type === self::SELECT) { // Only executes if "SELECT"
$cacheDriver = $this->getResultCacheDriver();
2007-09-13 01:42:42 +04:00
2008-05-24 22:18:37 +04:00
// Calculate hash for dql query.
$hash = md5($this->getDql() . var_export($params, true));
$cached = ($this->_expireResultCache) ? false : $cacheDriver->fetch($hash);
2007-09-13 01:42:42 +04:00
2008-05-24 22:18:37 +04:00
if ($cached === false) {
// Cache does not exist, we have to create it.
$result = $this->_execute($params, self::HYDRATE_ARRAY);
$queryResult = \Doctrine\ORM\Query\CacheHandler::fromResultSet($this, $result);
2008-05-24 22:18:37 +04:00
$cacheDriver->save($hash, $queryResult->toCachedForm(), $this->_resultCacheTTL);
2007-09-13 01:42:42 +04:00
2008-05-24 22:18:37 +04:00
return $result;
} else {
// Cache exists, recover it and return the results.
$queryResult = \Doctrine\ORM\Query\CacheHandler::fromCachedResult($this, $cached);
2007-09-13 01:42:42 +04:00
2008-05-24 22:18:37 +04:00
return $queryResult->getResultSet();
2007-09-13 01:42:42 +04:00
}
}
$stmt = $this->_execute($params);
2008-05-24 22:18:37 +04:00
if (is_integer($stmt)) {
return $stmt;
2007-09-13 01:42:42 +04:00
}
return $this->_em->getHydrator($this->_hydrationMode)->hydrateAll($stmt, $this->_parserResult);
}
2007-08-08 00:41:33 +04:00
/**
* _execute
2007-08-08 00:41:33 +04:00
*
2008-05-24 22:18:37 +04:00
* @param array $params
* @return PDOStatement The executed PDOStatement.
2007-08-08 00:41:33 +04:00
*/
protected function _execute(array $params)
2007-08-08 00:41:33 +04:00
{
2008-05-24 22:18:37 +04:00
// If there is a CacheDriver associated to cache queries...
if ($this->_queryCache || $this->_entityManager->getConnection()->getAttribute(Doctrine::ATTR_QUERY_CACHE)) {
2008-05-24 22:18:37 +04:00
$queryCacheDriver = $this->getQueryCacheDriver();
2008-05-24 22:18:37 +04:00
// Calculate hash for dql query.
$hash = md5($this->getDql() . 'DOCTRINE_QUERY_CACHE_SALT');
$cached = ($this->_expireQueryCache) ? false : $queryCacheDriver->fetch($hash);
2007-08-08 00:41:33 +04:00
2008-05-24 22:18:37 +04:00
if ($cached === false) {
// Cache does not exist, we have to create it.
$executor = $this->parse()->getSqlExecutor();
2007-12-10 21:59:12 +03:00
2008-05-24 22:18:37 +04:00
// To-be cached item is parserResult
$cacheDriver->save($hash, $this->_parserResult->toCachedForm(), $this->_queryCacheTTL);
2007-08-08 00:41:33 +04:00
} else {
2008-05-24 22:18:37 +04:00
// Cache exists, recover it and return the results.
$this->_parserResult = Doctrine\ORM\Query\CacheHandler::fromCachedQuery($this, $cached);
2007-12-10 21:59:12 +03:00
2008-05-24 22:18:37 +04:00
$executor = $this->_parserResult->getSqlExecutor();
}
} else {
$executor = $this->parse()->getSqlExecutor();
2007-12-10 21:59:12 +03:00
}
// Assignments for Enums
2008-05-24 22:18:37 +04:00
$this->_setEnumParams($this->_parserResult->getEnumParams());
2007-12-10 21:59:12 +03:00
2008-05-24 22:18:37 +04:00
// Converting parameters
$params = $this->_prepareParams($params);
2007-12-10 21:59:12 +03:00
2008-05-24 22:18:37 +04:00
// Double the params if we are using limit-subquery algorithm
// We always have an instance of Doctrine_ORM_Query_ParserResult on hands...
/*if ($this->_parserResult->isLimitSubqueryUsed() &&
$this->_entityManager->getConnection()->getAttribute(Doctrine::ATTR_DRIVER_NAME) !== 'mysql') {
2008-05-24 22:18:37 +04:00
$params = array_merge($params, $params);
}*/
2007-12-10 21:59:12 +03:00
// Executing the query and returning statement
2008-05-24 22:18:37 +04:00
return $executor->execute($this->_conn, $params);
2007-12-10 21:59:12 +03:00
}
2007-05-26 23:50:40 +04:00
/**
2008-05-24 22:18:37 +04:00
* @nodoc
2007-05-26 23:50:40 +04:00
*/
2008-05-24 22:18:37 +04:00
protected function _prepareParams(array $params)
2007-04-14 20:28:09 +04:00
{
2008-05-24 22:18:37 +04:00
// Convert boolean params
$params = $this->_entityManager->getConnection()->convertBooleans($params);
2007-04-14 20:28:09 +04:00
2008-05-24 22:18:37 +04:00
// Convert enum params
return $this->convertEnums($params);
2007-04-14 20:28:09 +04:00
}
/**
2008-05-24 22:18:37 +04:00
* Defines a cache driver to be used for caching result sets.
2007-05-26 23:50:40 +04:00
*
* @param Doctrine\ORM\Cache\Cache $driver Cache driver
* @return Doctrine\ORM\Query
2007-05-26 23:50:40 +04:00
*/
2008-05-24 22:18:37 +04:00
public function setResultCache($resultCache)
{
if ($resultCache !== null && ! ($resultCache instanceof \Doctrine\ORM\Cache\Cache)) {
throw new DoctrineException(
2008-05-24 22:18:37 +04:00
'Method setResultCache() accepts only an instance of Doctrine_Cache_Interface or null.'
);
}
$this->_resultCache = $resultCache;
2007-05-26 23:50:40 +04:00
2008-05-24 22:18:37 +04:00
return $this;
}
/**
2008-05-24 22:18:37 +04:00
* Returns the cache driver used for caching result sets.
*
2008-05-24 22:18:37 +04:00
* @return Doctrine_Cache_Interface Cache driver
*/
2008-05-24 22:18:37 +04:00
public function getResultCache()
{
if ($this->_resultCache instanceof \Doctrine\ORM\Cache\Cache) {
2008-05-24 22:18:37 +04:00
return $this->_resultCache;
} else {
return $this->_entityManager->getConnection()->getResultCacheDriver();
}
}
/**
2008-05-24 22:18:37 +04:00
* Defines how long the result cache will be active before expire.
*
2008-05-24 22:18:37 +04:00
* @param integer $timeToLive How long the cache entry is valid
* @return Doctrine\ORM\Query
*/
2008-05-24 22:18:37 +04:00
public function setResultCacheLifetime($timeToLive)
{
2008-05-24 22:18:37 +04:00
if ($timeToLive !== null) {
$timeToLive = (int) $timeToLive;
}
2008-05-24 22:18:37 +04:00
$this->_resultCacheTTL = $timeToLive;
2008-05-24 22:18:37 +04:00
return $this;
2007-06-19 14:43:19 +04:00
}
2007-06-19 14:43:19 +04:00
/**
2008-05-24 22:18:37 +04:00
* Retrieves the lifetime of resultset cache.
2007-06-19 14:43:19 +04:00
*
2008-05-24 22:18:37 +04:00
* @return int
2007-06-19 14:43:19 +04:00
*/
2008-05-24 22:18:37 +04:00
public function getResultCacheLifetime()
2007-06-19 14:43:19 +04:00
{
2008-05-24 22:18:37 +04:00
return $this->_resultCacheTTL;
2007-06-19 22:53:46 +04:00
}
2007-06-19 22:53:46 +04:00
/**
2008-05-24 22:18:37 +04:00
* Defines if the resultset cache is active or not.
2007-06-19 22:53:46 +04:00
*
2008-05-24 22:18:37 +04:00
* @param boolean $expire Whether or not to force resultset cache expiration.
* @return Doctrine_ORM_Query
2007-06-19 22:53:46 +04:00
*/
2008-05-24 22:18:37 +04:00
public function setExpireResultCache($expire = true)
2007-06-19 22:53:46 +04:00
{
2008-05-24 22:18:37 +04:00
$this->_expireResultCache = (bool) $expire;
2007-06-19 22:53:46 +04:00
2008-05-24 22:18:37 +04:00
return $this;
2007-09-13 01:42:42 +04:00
}
/**
2008-05-24 22:18:37 +04:00
* Retrieves if the resultset cache is active or not.
*
2008-05-24 22:18:37 +04:00
* @return bool
*/
2008-05-24 22:18:37 +04:00
public function getExpireResultCache()
{
2008-05-24 22:18:37 +04:00
return $this->_expireResultCache;
}
/**
2008-05-24 22:18:37 +04:00
* Defines a cache driver to be used for caching queries.
*
2008-05-24 22:18:37 +04:00
* @param Doctrine_Cache_Interface|null $driver Cache driver
* @return Doctrine_ORM_Query
*/
2008-05-24 22:18:37 +04:00
public function setQueryCache($queryCache)
{
if ($queryCache !== null && ! ($queryCache instanceof \Doctrine\ORM\Cache\Cache)) {
throw new DoctrineException(
'Method setResultCache() accepts only an instance of Doctrine_ORM_Cache_Interface or null.'
2008-05-24 22:18:37 +04:00
);
}
2008-05-24 22:18:37 +04:00
$this->_queryCache = $queryCache;
2007-06-28 02:29:57 +04:00
2008-05-24 22:18:37 +04:00
return $this;
}
2008-05-24 22:18:37 +04:00
/**
* Returns the cache driver used for caching queries.
*
* @return Doctrine_Cache_Interface Cache driver
*/
public function getQueryCache()
{
if ($this->_queryCache instanceof \Doctrine\ORM\Cache\Cache) {
2008-05-24 22:18:37 +04:00
return $this->_queryCache;
} else {
return $this->_entityManager->getConnection()->getQueryCacheDriver();
2007-06-28 02:29:57 +04:00
}
}
/**
2008-05-24 22:18:37 +04:00
* Defines how long the query cache will be active before expire.
*
2008-05-24 22:18:37 +04:00
* @param integer $timeToLive How long the cache entry is valid
* @return Doctrine_ORM_Query
*/
2008-05-24 22:18:37 +04:00
public function setQueryCacheLifetime($timeToLive)
{
2008-05-24 22:18:37 +04:00
if ($timeToLive !== null) {
$timeToLive = (int) $timeToLive;
2007-05-16 23:20:55 +04:00
}
2008-05-24 22:18:37 +04:00
$this->_queryCacheTTL = $timeToLive;
return $this;
}
2008-05-24 22:18:37 +04:00
/**
2008-05-24 22:18:37 +04:00
* Retrieves the lifetime of resultset cache.
*
* @return int
*/
2008-05-24 22:18:37 +04:00
public function getQueryCacheLifetime()
{
2008-05-24 22:18:37 +04:00
return $this->_queryCacheTTL;
2007-07-09 15:23:44 +04:00
}
2007-05-16 23:20:55 +04:00
/**
2008-05-24 22:18:37 +04:00
* Defines if the query cache is active or not.
2007-05-16 23:20:55 +04:00
*
2008-05-24 22:18:37 +04:00
* @param boolean $expire Whether or not to force query cache expiration.
* @return Doctrine_ORM_Query
2007-05-16 23:20:55 +04:00
*/
2008-05-24 22:18:37 +04:00
public function setExpireQueryCache($expire = true)
2007-05-16 23:20:55 +04:00
{
2008-05-24 22:18:37 +04:00
$this->_expireQueryCache = (bool) $expire;
2008-05-24 22:18:37 +04:00
return $this;
}
2007-06-19 02:30:15 +04:00
/**
2008-05-24 22:18:37 +04:00
* Retrieves if the query cache is active or not.
*
2008-05-24 22:18:37 +04:00
* @return bool
*/
2008-05-24 22:18:37 +04:00
public function getExpireQueryCache()
{
2008-05-24 22:18:37 +04:00
return $this->_expireQueryCache;
}
2007-05-16 23:20:55 +04:00
/**
2008-05-24 22:18:37 +04:00
* Defines the processing mode to be used during hydration process.
2007-05-16 23:20:55 +04:00
*
2008-05-24 22:18:37 +04:00
* @param integer $hydrationMode Doctrine processing mode to be used during hydration process.
* One of the Doctrine::HYDRATE_* constants.
* @return Doctrine\ORM\Query
2007-05-16 23:20:55 +04:00
*/
2008-05-24 22:18:37 +04:00
public function setHydrationMode($hydrationMode)
2007-05-16 23:20:55 +04:00
{
$this->_hydrationMode = $hydrationMode;
2008-05-24 22:18:37 +04:00
return $this;
2007-05-16 23:20:55 +04:00
}
/**
* Gets the list of results for the query.
*
* Alias for execute(array(), HYDRATE_OBJECT).
*
* @return Collection
*/
public function getResultList()
{
return $this->execute(array(), self::HYDRATE_OBJECT);
}
/**
* Gets the array of results for the query.
* Object graphs are represented as nested array structures.
*
* Alias for execute(array(), HYDRATE_ARRAY).
*
* @return array
*/
public function getResultArray()
{
return $this->execute(array(), self::HYDRATE_ARRAY);
}
/**
* Gets the scalar results for the query.
*
* Alias for execute(array(), HYDRATE_SCALAR).
*
* @return array
*/
public function getScalarResult()
{
return $this->execute(array(), self::HYDRATE_SCALAR);
}
/**
* Gets the single result of the query.
* Enforces the uniqueness of the result. If the result is not unique,
* a QueryException is thrown.
*
* @param integer $hydrationMode
* @return mixed
* @throws QueryException If the query result is not unique.
*/
public function getSingleResult($hydrationMode = null)
{
$result = $this->execute(array(), $hydrationMode);
if (count($result) > 1) {
throw QueryException::nonUniqueResult();
}
return is_array($result) ? array_shift($result) : $result->getFirst();
}
2008-05-24 22:18:37 +04:00
/**
* Sets an implementation-specific hint. If the hint name is not recognized,
* it is silently ignored.
*
* @param string $name The name of the hint.
* @param mixed $value The value of the hint.
*/
public function setHint($name, $value)
{
$this->_hints[$name] = $value;
}
/**
* Gets an implementation-specific hint. If the hint name is not recognized,
* FALSE is returned.
*
* @param string $name The name of the hint.
*/
public function getHint($name)
{
return isset($this->_hints[$name]) ? $this->_hints[$name] : false;
}
/**
* Executes the query and returns an IterableResult that can be iterated over.
* Objects in the result are initialized on-demand.
*
* @return IterableResult
*/
public function iterate(array $params = array(), $hydrationMode = self::HYDRATE_OBJECT)
{
return $this->_em->getHydrator($this->_hydrationMode)->iterate(
$this->_execute($params, $hydrationMode), $this->_parserResult
);
}
}