622 lines
18 KiB
PHP
Executable File
622 lines
18 KiB
PHP
Executable File
<?php
|
|
|
|
/*
|
|
* $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>.
|
|
*/
|
|
|
|
/**
|
|
* A Doctrine_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 dbms independant.
|
|
*
|
|
* @package Doctrine
|
|
* @subpackage Query
|
|
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
|
|
* @link www.phpdoctrine.org
|
|
* @since 1.0
|
|
* @version $Revision: 3938 $
|
|
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
|
|
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
|
|
*/
|
|
class Doctrine_Query extends Doctrine_Query_Abstract
|
|
{
|
|
/**
|
|
* @var Doctrine_EntityManager The entity manager used by this query object.
|
|
*/
|
|
protected $_entityManager;
|
|
|
|
/**
|
|
* @var Doctrine_Hydrator The hydrator object used to hydrate query results.
|
|
*/
|
|
protected $_hydrator;
|
|
|
|
/**
|
|
* @var Doctrine_Query_ParserResult The parser result that holds DQL => SQL information.
|
|
*/
|
|
protected $_parserResult;
|
|
|
|
/**
|
|
* @var string $_sql Cached SQL query.
|
|
*/
|
|
protected $_sql = null;
|
|
|
|
|
|
// Caching Stuff
|
|
|
|
/**
|
|
* @var Doctrine_Cache_Interface The cache driver used for caching result sets.
|
|
*/
|
|
protected $_resultCache;
|
|
|
|
/**
|
|
* @var boolean Boolean value that indicates whether or not expire the result cache.
|
|
*/
|
|
protected $_expireResultCache = false;
|
|
|
|
/**
|
|
* @var int Result Cache lifetime.
|
|
*/
|
|
protected $_resultCacheTTL;
|
|
|
|
|
|
/**
|
|
* @var Doctrine_Cache_Interface The cache driver used for caching queries.
|
|
*/
|
|
protected $_queryCache;
|
|
|
|
/**
|
|
* @var boolean Boolean value that indicates whether or not expire the query cache.
|
|
*/
|
|
protected $_expireQueryCache = false;
|
|
|
|
/**
|
|
* @var int Query Cache lifetime.
|
|
*/
|
|
protected $_queryCacheTTL;
|
|
|
|
// End of Caching Stuff
|
|
|
|
|
|
public function __construct(Doctrine_EntityManager $entityManager)
|
|
{
|
|
$this->_entityManager = $entityManager;
|
|
$this->_hydrator = new Doctrine_Hydrator($entityManager);
|
|
|
|
$this->free();
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns a new Doctrine_Query object
|
|
*
|
|
* @param Doctrine_Connection $conn optional connection parameter
|
|
* @return Doctrine_Query
|
|
*/
|
|
public static function create($conn = null)
|
|
{
|
|
return new self($conn);
|
|
}
|
|
|
|
|
|
/**
|
|
* Retrieves the assocated Doctrine_EntityManager to this Doctrine_Query
|
|
*
|
|
* @return Doctrine_EntityManager
|
|
*/
|
|
public function getEntityManager()
|
|
{
|
|
return $this->_entityManager;
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns the hydrator associated with this query object
|
|
*
|
|
* @return Doctrine_Hydrator The hydrator associated with this query object
|
|
*/
|
|
public function getHydrator()
|
|
{
|
|
return $this->_hydrator;
|
|
}
|
|
|
|
|
|
/**
|
|
* Convenience method to execute using array fetching as hydration mode.
|
|
*
|
|
* @param string $params
|
|
* @return array
|
|
*/
|
|
public function fetchArray($params = array()) {
|
|
return $this->execute($params, Doctrine::HYDRATE_ARRAY);
|
|
}
|
|
|
|
|
|
/**
|
|
* Convenience method to execute the query and return the first item
|
|
* of the collection.
|
|
*
|
|
* @param string $params Parameters
|
|
* @param int $hydrationMode Hydration mode
|
|
* @return mixed Array or Doctrine_Collection or false if no result.
|
|
*/
|
|
public function fetchOne($params = array(), $hydrationMode = null)
|
|
{
|
|
$collection = $this->limit(1)->execute($params, $hydrationMode);
|
|
|
|
if (count($collection) === 0) {
|
|
return false;
|
|
}
|
|
|
|
if ($collection instanceof Doctrine_Collection) {
|
|
return $collection->getFirst();
|
|
} else if (is_array($collection)) {
|
|
return array_shift($collection);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
/**
|
|
* Query the database with DQL (Doctrine Query Language).
|
|
*
|
|
* @param string $query DQL query
|
|
* @param array $params prepared statement parameters
|
|
* @param int $hydrationMode Doctrine::FETCH_ARRAY or Doctrine::FETCH_RECORD
|
|
* @see Doctrine::FETCH_* constants
|
|
* @return mixed
|
|
*/
|
|
public function query($query, $params = array(), $hydrationMode = null)
|
|
{
|
|
$this->setDql($query);
|
|
return $this->execute($params, $hydrationMode);
|
|
}
|
|
|
|
|
|
/**
|
|
* Builds the sql query from the given parameters and applies things such as
|
|
* column aggregation inheritance and limit subqueries if needed
|
|
*
|
|
* @return mixed The built sql query or an array of all sql queries.
|
|
*/
|
|
public function getSql()
|
|
{
|
|
return $this->parse()->getSqlExecutor()->getSqlStatements();
|
|
}
|
|
|
|
|
|
/**
|
|
* Parses the DQL query, if necessary, and stores the parser result.
|
|
*
|
|
* @return Doctrine_Query_ParserResult
|
|
*/
|
|
public function parse()
|
|
{
|
|
if ($this->_state === self::STATE_DIRTY) {
|
|
$parser = new Doctrine_Query_Parser($this);
|
|
$this->_parserResult = $parser->parse();
|
|
$this->_state = self::STATE_CLEAN;
|
|
}
|
|
|
|
return $this->_parserResult;
|
|
}
|
|
|
|
|
|
/**
|
|
* Executes the query and populates the data set.
|
|
*
|
|
* @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 Doctrine_Collection The root collection
|
|
*/
|
|
public function execute($params = array(), $hydrationMode = null)
|
|
{
|
|
$params = $this->getParams($params);
|
|
|
|
// If there is a CacheDriver associated to cache resultsets...
|
|
if ($this->_resultCache && $this->_type === self::SELECT) { // Only executes if "SELECT"
|
|
$cacheDriver = $this->getResultCacheDriver();
|
|
|
|
// Calculate hash for dql query.
|
|
$hash = md5($this->getDql() . var_export($params, true));
|
|
$cached = ($this->_expireResultCache) ? false : $cacheDriver->fetch($hash);
|
|
|
|
if ($cached === false) {
|
|
// Cache does not exist, we have to create it.
|
|
$result = $this->_execute($params, Doctrine::HYDRATE_ARRAY);
|
|
$queryResult = Doctrine_Query_CacheHandler::fromResultSet($this, $result);
|
|
$cacheDriver->save($hash, $queryResult->toCachedForm(), $this->_resultCacheTTL);
|
|
|
|
return $result;
|
|
} else {
|
|
// Cache exists, recover it and return the results.
|
|
$queryResult = Doctrine_Query_CacheHandler::fromCachedResult($this, $cached);
|
|
|
|
return $queryResult->getResultSet();
|
|
}
|
|
}
|
|
|
|
return $this->_execute($params, $hydrationMode);
|
|
}
|
|
|
|
|
|
/**
|
|
* _execute
|
|
*
|
|
* @param string $params Parameters to be sent to query.
|
|
* @param int $hydrationMode Method of hydration to be used.
|
|
* @return Doctrine_Collection The root collection
|
|
*/
|
|
protected function _execute($params, $hydrationMode)
|
|
{
|
|
// preQuery invoking
|
|
$this->preQuery();
|
|
|
|
// Query execution
|
|
$stmt = $this->_execute2($params);
|
|
|
|
// postQuery invoking
|
|
$this->postQuery();
|
|
|
|
if (is_integer($stmt)) {
|
|
return $stmt;
|
|
}
|
|
|
|
return $this->_hydrator->hydrateResultSet($stmt, $hydrationMode);
|
|
}
|
|
|
|
|
|
/**
|
|
* _execute2
|
|
*
|
|
* @param array $params
|
|
* @return PDOStatement The executed PDOStatement.
|
|
*/
|
|
protected function _execute2($params)
|
|
{
|
|
// If there is a CacheDriver associated to cache queries...
|
|
if ($this->_queryCache || $this->_entityManager->getConnection()->getAttribute(Doctrine::ATTR_QUERY_CACHE)) {
|
|
$queryCacheDriver = $this->getQueryCacheDriver();
|
|
|
|
// Calculate hash for dql query.
|
|
$hash = md5($this->getDql() . 'DOCTRINE_QUERY_CACHE_SALT');
|
|
$cached = ($this->_expireQueryCache) ? false : $queryCacheDriver->fetch($hash);
|
|
|
|
if ($cached === false) {
|
|
// Cache does not exist, we have to create it.
|
|
$executor = $this->parse()->getSqlExecutor();
|
|
|
|
// To-be cached item is parserResult
|
|
$cacheDriver->save($hash, $this->_parserResult->toCachedForm(), $this->_queryCacheTTL);
|
|
} else {
|
|
// Cache exists, recover it and return the results.
|
|
$this->_parserResult = Doctrine_Query_CacheHandler::fromCachedQuery($this, $cached);
|
|
|
|
$executor = $this->_parserResult->getSqlExecutor();
|
|
}
|
|
} else {
|
|
$executor = $this->parse()->getSqlExecutor();
|
|
}
|
|
|
|
// Assignments for Hydrator and Enums
|
|
$this->_hydrator->setQueryComponents($this->_parserResult->getQueryComponents());
|
|
$this->_hydrator->setTableAliasMap($this->_parserResult->getTableAliasMap());
|
|
$this->_setEnumParams($this->_parserResult->getEnumParams());
|
|
|
|
// Converting parameters
|
|
$params = $this->_prepareParams($params);
|
|
|
|
// Double the params if we are using limit-subquery algorithm
|
|
// We always have an instance of Doctrine_Query_ParserResult on hands...
|
|
if ($this->_parserResult->isLimitSubqueryUsed() &&
|
|
$this->_entityManager->getConnection()->getAttribute(Doctrine::ATTR_DRIVER_NAME) !== 'mysql') {
|
|
$params = array_merge($params, $params);
|
|
}
|
|
|
|
// Executing the query and assigning PDOStatement
|
|
return $executor->execute($this->_conn, $params);
|
|
}
|
|
|
|
|
|
/**
|
|
* @nodoc
|
|
*/
|
|
protected function _prepareParams(array $params)
|
|
{
|
|
// Convert boolean params
|
|
$params = $this->_entityManager->getConnection()->convertBooleans($params);
|
|
|
|
// Convert enum params
|
|
return $this->convertEnums($params);
|
|
}
|
|
|
|
|
|
/**
|
|
* Defines a cache driver to be used for caching result sets.
|
|
*
|
|
* @param Doctrine_Cache_Interface|null $driver Cache driver
|
|
* @return Doctrine_Query
|
|
*/
|
|
public function setResultCache($resultCache)
|
|
{
|
|
if ($resultCache !== null && ! ($resultCache instanceof Doctrine_Cache_Interface)) {
|
|
throw new Doctrine_Query_Exception(
|
|
'Method setResultCache() accepts only an instance of Doctrine_Cache_Interface or null.'
|
|
);
|
|
}
|
|
|
|
$this->_resultCache = $resultCache;
|
|
|
|
return $this;
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns the cache driver used for caching result sets.
|
|
*
|
|
* @return Doctrine_Cache_Interface Cache driver
|
|
*/
|
|
public function getResultCache()
|
|
{
|
|
if ($this->_resultCache instanceof Doctrine_Cache_Interface) {
|
|
return $this->_resultCache;
|
|
} else {
|
|
return $this->_entityManager->getConnection()->getResultCacheDriver();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Defines how long the result cache will be active before expire.
|
|
*
|
|
* @param integer $timeToLive How long the cache entry is valid
|
|
* @return Doctrine_Query
|
|
*/
|
|
public function setResultCacheLifetime($timeToLive)
|
|
{
|
|
if ($timeToLive !== null) {
|
|
$timeToLive = (int) $timeToLive;
|
|
}
|
|
|
|
$this->_resultCacheTTL = $timeToLive;
|
|
|
|
return $this;
|
|
}
|
|
|
|
|
|
/**
|
|
* Retrieves the lifetime of resultset cache.
|
|
*
|
|
* @return int
|
|
*/
|
|
public function getResultCacheLifetime()
|
|
{
|
|
return $this->_resultCacheTTL;
|
|
}
|
|
|
|
|
|
/**
|
|
* Defines if the resultset cache is active or not.
|
|
*
|
|
* @param boolean $expire Whether or not to force resultset cache expiration.
|
|
* @return Doctrine_Query
|
|
*/
|
|
public function setExpireResultCache($expire = true)
|
|
{
|
|
$this->_expireResultCache = (bool) $expire;
|
|
|
|
return $this;
|
|
}
|
|
|
|
|
|
/**
|
|
* Retrieves if the resultset cache is active or not.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function getExpireResultCache()
|
|
{
|
|
return $this->_expireResultCache;
|
|
}
|
|
|
|
|
|
/**
|
|
* Defines a cache driver to be used for caching queries.
|
|
*
|
|
* @param Doctrine_Cache_Interface|null $driver Cache driver
|
|
* @return Doctrine_Query
|
|
*/
|
|
public function setQueryCache($queryCache)
|
|
{
|
|
if ($queryCache !== null && ! ($queryCache instanceof Doctrine_Cache_Interface)) {
|
|
throw new Doctrine_Query_Exception(
|
|
'Method setResultCache() accepts only an instance of Doctrine_Cache_Interface or null.'
|
|
);
|
|
}
|
|
|
|
$this->_queryCache = $queryCache;
|
|
|
|
return $this;
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns the cache driver used for caching queries.
|
|
*
|
|
* @return Doctrine_Cache_Interface Cache driver
|
|
*/
|
|
public function getQueryCache()
|
|
{
|
|
if ($this->_queryCache instanceof Doctrine_Cache_Interface) {
|
|
return $this->_queryCache;
|
|
} else {
|
|
return $this->_entityManager->getConnection()->getQueryCacheDriver();
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Defines how long the query cache will be active before expire.
|
|
*
|
|
* @param integer $timeToLive How long the cache entry is valid
|
|
* @return Doctrine_Query
|
|
*/
|
|
public function setQueryCacheLifetime($timeToLive)
|
|
{
|
|
if ($timeToLive !== null) {
|
|
$timeToLive = (int) $timeToLive;
|
|
}
|
|
|
|
$this->_queryCacheTTL = $timeToLive;
|
|
|
|
return $this;
|
|
}
|
|
|
|
|
|
/**
|
|
* Retrieves the lifetime of resultset cache.
|
|
*
|
|
* @return int
|
|
*/
|
|
public function getQueryCacheLifetime()
|
|
{
|
|
return $this->_queryCacheTTL;
|
|
}
|
|
|
|
|
|
/**
|
|
* Defines if the query cache is active or not.
|
|
*
|
|
* @param boolean $expire Whether or not to force query cache expiration.
|
|
* @return Doctrine_Query
|
|
*/
|
|
public function setExpireQueryCache($expire = true)
|
|
{
|
|
$this->_expireQueryCache = (bool) $expire;
|
|
|
|
return $this;
|
|
}
|
|
|
|
|
|
/**
|
|
* Retrieves if the query cache is active or not.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function getExpireQueryCache()
|
|
{
|
|
return $this->_expireQueryCache;
|
|
}
|
|
|
|
|
|
/**
|
|
* Defines the processing mode to be used during hydration process.
|
|
*
|
|
* @param integer $hydrationMode Doctrine processing mode to be used during hydration process.
|
|
* One of the Doctrine::HYDRATE_* constants.
|
|
* @return Doctrine_Query
|
|
*/
|
|
public function setHydrationMode($hydrationMode)
|
|
{
|
|
$this->_hydrator->setHydrationMode($hydrationMode);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Empty template method to provide Query subclasses with the possibility
|
|
* to hook into the query building procedure, doing any custom / specialized
|
|
* query building procedures that are neccessary.
|
|
*
|
|
* @return void
|
|
* @deprecated Should be removed. Extending Query is no good solution. Should
|
|
* Things like this should be done through listeners.
|
|
*/
|
|
public function preQuery()
|
|
{
|
|
|
|
}
|
|
|
|
/**
|
|
* Empty template method to provide Query subclasses with the possibility
|
|
* to hook into the query building procedure, doing any custom / specialized
|
|
* post query procedures (for example logging) that are neccessary.
|
|
*
|
|
* @return void
|
|
* @deprecated Should be removed. Extending Query is no good solution. Should
|
|
* Things like this should be done through listeners.
|
|
*/
|
|
public function postQuery()
|
|
{
|
|
|
|
}
|
|
|
|
/**
|
|
* Gets the list of results for the query.
|
|
*
|
|
* @param integer $hydrationMode
|
|
* @return mixed
|
|
*/
|
|
public function getResultList($hydrationMode = null)
|
|
{
|
|
return $this->execute(array(), $hydrationMode);
|
|
}
|
|
|
|
/**
|
|
* 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 Doctrine_Query_Exception::nonUniqueResult();
|
|
}
|
|
|
|
return is_array($result) ? array_shift($result) : $result->getFirst();
|
|
}
|
|
|
|
/**
|
|
* This method is automatically called when this Doctrine_Hydrate is serialized.
|
|
*
|
|
* @return array An array of serialized properties
|
|
*/
|
|
public function serialize()
|
|
{
|
|
$vars = get_object_vars($this);
|
|
}
|
|
|
|
/**
|
|
* This method is automatically called everytime a Doctrine_Hydrate object is unserialized.
|
|
*
|
|
* @param string $serialized Doctrine_Record as serialized string
|
|
* @return void
|
|
*/
|
|
public function unserialize($serialized)
|
|
{
|
|
|
|
}
|
|
}
|