1
0
mirror of synced 2024-12-16 08:06:02 +03:00
doctrine2/lib/Doctrine/ORM/AbstractQuery.php

577 lines
16 KiB
PHP

<?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
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM;
use Doctrine\ORM\Query\QueryException;
/**
* Base class for Query and NativeQuery.
*
* @license http://www.opensource.org/licenses/lgpl-license.php LGPL
* @link www.doctrine-project.com
* @since 1.0
* @version $Revision: 1393 $
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
* @author Konsta Vesterinen <kvesteri@cc.hut.fi>
* @author Roman Borschel <roman@code-factory.org>
*/
abstract class AbstractQuery
{
/* 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;
/**
* @var array $params Parameters of this query.
*/
protected $_params = array();
/**
* The user-specified ResultSetMapping to use.
*
* @var ResultSetMapping
*/
protected $_resultSetMapping;
/**
* @var Doctrine\ORM\EntityManager The entity manager used by this query object.
*/
protected $_em;
/**
* A set of query hints.
*
* @var array
*/
protected $_hints = array();
/**
* @var integer The hydration mode.
*/
protected $_hydrationMode = self::HYDRATE_OBJECT;
/**
* The locally set cache driver used for caching result sets of this query.
*
* @var CacheDriver
*/
protected $_resultCacheDriver;
/**
* Boolean flag for whether or not to cache the result sets of this query.
*
* @var boolean
*/
protected $_useResultCache;
/**
* The id to store the result cache entry under.
*
* @var string
*/
protected $_resultCacheId;
/**
* @var boolean Boolean value that indicates whether or not expire the result cache.
*/
protected $_expireResultCache = false;
/**
* @var int Result Cache lifetime.
*/
protected $_resultCacheTTL;
/**
* Initializes a new instance of a class derived from <tt>AbstractQuery</tt>.
*
* @param Doctrine\ORM\EntityManager $entityManager
*/
public function __construct(EntityManager $entityManager)
{
$this->_em = $entityManager;
}
/**
* Retrieves the associated EntityManager of this Query instance.
*
* @return Doctrine\ORM\EntityManager
*/
public function getEntityManager()
{
return $this->_em;
}
/**
* Frees the resources used by the query object.
*/
public function free()
{
$this->_params = array();
}
/**
* Get all defined parameters.
*
* @return array Defined parameters
*/
public function getParameters($params = array())
{
if ($params) {
return ($this->_params + $params);
}
return $this->_params;
}
/**
* Gets a query parameter.
*
* @param mixed $key The key (index or name) of the bound parameter.
* @return mixed The value of the bound parameter.
*/
public function getParameter($key)
{
return isset($this->_params[$key]) ? $this->_params[$key] : null;
}
/**
* 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.
*
* @return string SQL query
*/
abstract public function getSql();
/**
* Sets a query parameter.
*
* @param string|integer $key The parameter position or name.
* @param mixed $value The parameter value.
* @return Doctrine\ORM\AbstractQuery
*/
public function setParameter($key, $value)
{
$this->_params[$key] = $value;
return $this;
}
/**
* Sets a collection of query parameters.
*
* @param array $params
* @return Doctrine\ORM\AbstractQuery
*/
public function setParameters(array $params)
{
foreach ($params as $key => $value) {
$this->setParameter($key, $value);
}
return $this;
}
/**
* Sets the ResultSetMapping that should be used for hydration.
*
* @param ResultSetMapping $rsm
* @return Doctrine\ORM\AbstractQuery
*/
public function setResultSetMapping($rsm)
{
$this->_resultSetMapping = $rsm;
return $this;
}
/**
* Defines a cache driver to be used for caching result sets.
*
* @param Doctrine\Common\Cache\Cache $driver Cache driver
* @return Doctrine\ORM\AbstractQuery
*/
public function setResultCacheDriver($resultCacheDriver = null)
{
if ($resultCacheDriver !== null && ! ($resultCacheDriver instanceof \Doctrine\Common\Cache\Cache)) {
throw ORMException::invalidResultCacheDriver();
}
$this->_resultCacheDriver = $resultCacheDriver;
if ($resultCacheDriver) {
$this->_useResultCache = true;
}
return $this;
}
/**
* Returns the cache driver used for caching result sets.
*
* @return Doctrine\Common\Cache\Cache Cache driver
*/
public function getResultCacheDriver()
{
if ($this->_resultCacheDriver) {
return $this->_resultCacheDriver;
} else {
return $this->_em->getConfiguration()->getResultCacheImpl();
}
}
/**
* Set whether or not to cache the result sets for this query
*
* @param boolean $bool
*/
public function useResultCache($bool, $timeToLive = null, $resultCacheId = null)
{
$this->_useResultCache = $bool;
if ($timeToLive) {
$this->setResultCacheLifetime($timeToLive);
}
if ($resultCacheId) {
$this->_resultCacheId = $resultCacheId;
}
}
/**
* Defines how long the result cache will be active before expire.
*
* @param integer $timeToLive How long the cache entry is valid
* @return Doctrine\ORM\AbstractQuery
*/
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 result cache is active or not.
*
* @param boolean $expire Whether or not to force resultset cache expiration.
* @return Doctrine\ORM\AbstractQuery
*/
public function expireResultCache($expire = true)
{
$this->_expireResultCache = $expire;
return $this;
}
/**
* Retrieves if the resultset cache is active or not.
*
* @return bool
*/
public function getExpireResultCache()
{
return $this->_expireResultCache;
}
/**
* Defines the processing mode to be used during hydration.
*
* @param integer $hydrationMode Doctrine processing mode to be used during hydration process.
* One of the Query::HYDRATE_* constants.
* @return Doctrine\ORM\AbstractQuery
*/
public function setHydrationMode($hydrationMode)
{
$this->_hydrationMode = $hydrationMode;
return $this;
}
/**
* Gets the hydration mode currently used by the query.
*
* @return integer
*/
public function getHydrationMode()
{
return $this->_hydrationMode;
}
/**
* Gets the list of results for the query.
*
* Alias for execute(array(), $hydrationMode = HYDRATE_OBJECT).
*
* @return array
*/
public function getResult($hydrationMode = self::HYDRATE_OBJECT)
{
return $this->execute(array(), $hydrationMode);
}
/**
* Gets the array of results for the query.
*
* Alias for execute(array(), HYDRATE_ARRAY).
*
* @return array
*/
public function getArrayResult()
{
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 presence as well as the uniqueness of the result.
*
* If the result is not unique, a NonUniqueResultException is thrown.
* If there is no result, a NoResultException is thrown.
*
* @param integer $hydrationMode
* @return mixed
* @throws QueryException If the query result is not unique.
* @throws NoResultException If the query returned no result.
*/
public function getSingleResult($hydrationMode = null)
{
$result = $this->execute(array(), $hydrationMode);
if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {
throw new NoResultException;
}
if (is_array($result)) {
if (count($result) > 1) {
throw new NonUniqueResultException;
}
return array_shift($result);
} else if (is_object($result)) {
if (count($result) > 1) {
throw new NonUniqueResultException;
}
return $result->first();
}
return $result;
}
/**
* Gets the single scalar result of the query.
*
* Alias for getSingleResult(HYDRATE_SINGLE_SCALAR).
*
* @return mixed
* @throws QueryException If the query result is not unique.
*/
public function getSingleScalarResult()
{
return $this->getSingleResult(self::HYDRATE_SINGLE_SCALAR);
}
/**
* Sets a query 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.
* @return Doctrine\ORM\AbstractQuery
*/
public function setHint($name, $value)
{
$this->_hints[$name] = $value;
return $this;
}
/**
* Gets the value of a query hint. If the hint name is not recognized, FALSE is returned.
*
* @param string $name The name of the hint.
* @return mixed The value of the hint or FALSE, if the hint name is not recognized.
*/
public function getHint($name)
{
return isset($this->_hints[$name]) ? $this->_hints[$name] : false;
}
/**
* Executes the query and returns an IterableResult that can be used to incrementally
* iterate over the result.
*
* @param array $params The query parameters.
* @param integer $hydrationMode The hydration mode to use.
* @return IterableResult
*/
public function iterate(array $params = array(), $hydrationMode = self::HYDRATE_OBJECT)
{
return $this->_em->newHydrator($this->_hydrationMode)->iterate(
$this->_doExecute($params, $hydrationMode), $this->_resultSetMapping
);
}
/**
* Executes the query.
*
* @param string $params Any additional query parameters.
* @param integer $hydrationMode Processing mode to be used during the hydration process.
* @return mixed
*/
public function execute($params = array(), $hydrationMode = null)
{
// If there are still pending insertions in the UnitOfWork we need to flush
// in order to guarantee a correct result.
//TODO: Think this over. Its tricky. Not doing this can lead to strange results
// potentially, but doing it could result in endless loops when querying during
// a flush, i.e. inside an event listener.
if ($this->_em->getUnitOfWork()->hasPendingInsertions()) {
$this->_em->flush();
}
if ($hydrationMode !== null) {
$this->setHydrationMode($hydrationMode);
}
$params = $this->getParameters($params);
if (isset($params[0])) {
throw QueryException::invalidParameterPosition(0);
}
// Check result cache
if ($this->_useResultCache && $cacheDriver = $this->getResultCacheDriver()) {
$id = $this->_getResultCacheId($params);
$cached = $this->_expireResultCache ? false : $cacheDriver->fetch($id);
if ($cached === false) {
// Cache miss.
$stmt = $this->_doExecute($params);
$result = $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
$stmt, $this->_resultSetMapping, $this->_hints
);
$cacheDriver->save($id, $result, $this->_resultCacheTTL);
return $result;
} else {
// Cache hit.
return $cached;
}
}
$stmt = $this->_doExecute($params);
if (is_numeric($stmt)) {
return $stmt;
}
return $this->_em->getHydrator($this->_hydrationMode)->hydrateAll(
$stmt, $this->_resultSetMapping, $this->_hints
);
}
/**
* Set the result cache id to use to store the result set cache entry.
* If this is not explicitely set by the developer then a hash is automatically
* generated for you.
*
* @param string $id
* @return Doctrine\ORM\AbstractQuery
*/
public function setResultCacheId($id)
{
$this->_resultCacheId = $id;
return $this;
}
/**
* Get the result cache id to use to store the result set cache entry.
* Will return the configured id if it exists otherwise a hash will be
* automatically generated for you.
*
* @param array $params
* @return string $id
*/
protected function _getResultCacheId(array $params)
{
if ($this->_resultCacheId) {
return $this->_resultCacheId;
} else {
$sql = $this->getSql();
ksort($this->_hints);
return md5(implode(";", (array)$sql) . var_export($params, true) .
var_export($this->_hints, true)."&hydrationMode=".$this->_hydrationMode);
}
}
/**
* Prepares the given parameters for execution in an SQL statement.
*
* Note to inheritors: This method must return a numerically, continuously 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.
*/
abstract protected function _prepareParams(array $params);
/**
* Executes the query and returns a reference to the resulting Statement object.
*
* @param array $params
*/
abstract protected function _doExecute(array $params);
}