1
0
mirror of synced 2024-12-15 15:46:02 +03:00

Merge pull request #360 from doctrine/DDC-1840

[DDC-1840] Implemented parameters as a collection.
This commit is contained in:
Guilherme Blanco 2012-05-29 11:41:00 -07:00
commit e4935e58f2
14 changed files with 404 additions and 182 deletions

View File

@ -7,3 +7,21 @@ and strip a trailing "s" character if there is one.
# Merge copies non persisted properties too # Merge copies non persisted properties too
When merging an entity in UoW not only mapped properties are copied, but also others. When merging an entity in UoW not only mapped properties are copied, but also others.
# Query, QueryBuilder and NativeQuery parameters *BC break*
From now on, parameters in queries is an ArrayCollection instead of a simple array.
This affects heavily the usage of setParameters(), because it will not append anymore
parameters to query, but will actually override the already defined ones.
Whenever you are retrieving a parameter (ie. $query->getParameter(1)), you will
receive an instance of Query\Parameter, which contains the methods "getName",
"getValue" and "getType". Parameters are also only converted to when necessary, and
not when they are set.
Also, related functions were affected:
* execute($parameters, $hydrationMode) the argument $parameters can be either an key=>value array or an ArrayCollection instance
* iterate($parameters, $hydrationMode) the argument $parameters can be either an key=>value array or an ArrayCollection instance
* setParameters($parameters) the argument $parameters can be either an key=>value array or an ArrayCollection instance
* getParameters() now returns ArrayCollection instead of array
* getParameter($key) now returns Parameter instance instead of parameter value

View File

@ -19,15 +19,17 @@
namespace Doctrine\ORM; namespace Doctrine\ORM;
use Doctrine\DBAL\Types\Type, use Doctrine\Common\Util\ClassUtils;
Doctrine\DBAL\Cache\QueryCacheProfile, use Doctrine\Common\Collections\ArrayCollection;
Doctrine\ORM\Query\QueryException,
Doctrine\Common\Util\ClassUtils; use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Cache\QueryCacheProfile;
use Doctrine\ORM\Query\QueryException;
/** /**
* Base contract for ORM queries. Base class for Query and NativeQuery. * Base contract for ORM queries. Base class for Query and NativeQuery.
* *
*
* @link www.doctrine-project.org * @link www.doctrine-project.org
* @since 2.0 * @since 2.0
* @author Benjamin Eberlei <kontakt@beberlei.de> * @author Benjamin Eberlei <kontakt@beberlei.de>
@ -62,14 +64,9 @@ abstract class AbstractQuery
const HYDRATE_SIMPLEOBJECT = 5; const HYDRATE_SIMPLEOBJECT = 5;
/** /**
* @var array The parameter map of this query. * @var \Doctrine\Common\Collections\ArrayCollection The parameter map of this query.
*/ */
protected $_params = array(); protected $parameters;
/**
* @var array The parameter type map of this query.
*/
protected $_paramTypes = array();
/** /**
* @var ResultSetMapping The user-specified ResultSetMapping to use. * @var ResultSetMapping The user-specified ResultSetMapping to use.
@ -114,8 +111,18 @@ abstract class AbstractQuery
public function __construct(EntityManager $em) public function __construct(EntityManager $em)
{ {
$this->_em = $em; $this->_em = $em;
$this->parameters = new ArrayCollection();
} }
/**
* 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();
/** /**
* Retrieves the associated EntityManager of this Query instance. * Retrieves the associated EntityManager of this Query instance.
* *
@ -135,8 +142,8 @@ abstract class AbstractQuery
*/ */
public function free() public function free()
{ {
$this->_params = array(); $this->parameters = new ArrayCollection();
$this->_paramTypes = array();
$this->_hints = array(); $this->_hints = array();
} }
@ -147,57 +154,55 @@ abstract class AbstractQuery
*/ */
public function getParameters() public function getParameters()
{ {
return $this->_params; return $this->parameters;
}
/**
* Get all defined parameter types.
*
* @return array The defined query parameter types.
*/
public function getParameterTypes()
{
return $this->_paramTypes;
} }
/** /**
* Gets a query parameter. * Gets a query parameter.
* *
* @param mixed $key The key (index or name) of the bound parameter. * @param mixed $key The key (index or name) of the bound parameter.
*
* @return mixed The value of the bound parameter. * @return mixed The value of the bound parameter.
*/ */
public function getParameter($key) public function getParameter($key)
{ {
if (isset($this->_params[$key])) { $filteredParameters = $this->parameters->filter(
return $this->_params[$key]; function ($parameter) use ($key)
}
return null;
}
/**
* Gets a query parameter type.
*
* @param mixed $key The key (index or name) of the bound parameter.
* @return mixed The parameter type of the bound parameter.
*/
public function getParameterType($key)
{ {
if (isset($this->_paramTypes[$key])) { // Must not be identical because of string to integer conversion
return $this->_paramTypes[$key]; return ($key == $parameter->getName());
} }
);
return null; return count($filteredParameters) ? $filteredParameters->first() : null;
} }
/** /**
* Gets the SQL query that corresponds to this query object. * Sets a collection of query parameters.
* 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 * @param \Doctrine\Common\Collections\ArrayCollection|array $parameters
*
* @return \Doctrine\ORM\AbstractQuery This query instance.
*/ */
abstract public function getSQL(); public function setParameters($parameters)
{
// BC compatibility with 2.3-
if (is_array($parameters)) {
$parameterCollection = new ArrayCollection();
foreach ($parameters as $key => $value) {
$parameter = new Query\Parameter($key, $value);
$parameterCollection->add($parameter);
}
$parameters = $parameterCollection;
}
$this->parameters = $parameters;
return $this;
}
/** /**
* Sets a query parameter. * Sets a query parameter.
@ -207,19 +212,29 @@ abstract class AbstractQuery
* @param string $type The parameter type. If specified, the given value will be run through * @param string $type The parameter type. If specified, the given value will be run through
* the type conversion of this type. This is usually not needed for * the type conversion of this type. This is usually not needed for
* strings and numeric types. * strings and numeric types.
*
* @return \Doctrine\ORM\AbstractQuery This query instance. * @return \Doctrine\ORM\AbstractQuery This query instance.
*/ */
public function setParameter($key, $value, $type = null) public function setParameter($key, $value, $type = null)
{ {
$key = trim($key, ':'); $filteredParameters = $this->parameters->filter(
$value = $this->processParameterValue($value); function ($parameter) use ($key)
{
// Must not be identical because of string to integer conversion
return ($key == $parameter->getName());
}
);
if ($type === null) { if (count($filteredParameters)) {
$type = Query\ParameterTypeInferer::inferType($value); $parameter = $filteredParameters->first();
$parameter->setValue($value, $type);
return $this;
} }
$this->_paramTypes[$key] = $type; $parameter = new Query\Parameter($key, $value, $type);
$this->_params[$key] = $value;
$this->parameters->add($parameter);
return $this; return $this;
} }
@ -230,7 +245,7 @@ abstract class AbstractQuery
* @param mixed $value * @param mixed $value
* @return array * @return array
*/ */
private function processParameterValue($value) public function processParameterValue($value)
{ {
switch (true) { switch (true) {
case is_array($value): case is_array($value):
@ -249,7 +264,7 @@ abstract class AbstractQuery
} }
} }
protected function convertObjectParameterToScalarValue($value) private function convertObjectParameterToScalarValue($value)
{ {
$class = $this->_em->getClassMetadata(get_class($value)); $class = $this->_em->getClassMetadata(get_class($value));
@ -275,22 +290,6 @@ abstract class AbstractQuery
return $value; return $value;
} }
/**
* Sets a collection of query parameters.
*
* @param array $params
* @param array $types
* @return \Doctrine\ORM\AbstractQuery This query instance.
*/
public function setParameters(array $params, array $types = array())
{
foreach ($params as $key => $value) {
$this->setParameter($key, $value, isset($types[$key]) ? $types[$key] : null);
}
return $this;
}
/** /**
* Sets the ResultSetMapping that should be used for hydration. * Sets the ResultSetMapping that should be used for hydration.
* *
@ -530,37 +529,37 @@ abstract class AbstractQuery
/** /**
* Gets the list of results for the query. * Gets the list of results for the query.
* *
* Alias for execute(array(), $hydrationMode = HYDRATE_OBJECT). * Alias for execute(null, $hydrationMode = HYDRATE_OBJECT).
* *
* @return array * @return array
*/ */
public function getResult($hydrationMode = self::HYDRATE_OBJECT) public function getResult($hydrationMode = self::HYDRATE_OBJECT)
{ {
return $this->execute(array(), $hydrationMode); return $this->execute(null, $hydrationMode);
} }
/** /**
* Gets the array of results for the query. * Gets the array of results for the query.
* *
* Alias for execute(array(), HYDRATE_ARRAY). * Alias for execute(null, HYDRATE_ARRAY).
* *
* @return array * @return array
*/ */
public function getArrayResult() public function getArrayResult()
{ {
return $this->execute(array(), self::HYDRATE_ARRAY); return $this->execute(null, self::HYDRATE_ARRAY);
} }
/** /**
* Gets the scalar results for the query. * Gets the scalar results for the query.
* *
* Alias for execute(array(), HYDRATE_SCALAR). * Alias for execute(null, HYDRATE_SCALAR).
* *
* @return array * @return array
*/ */
public function getScalarResult() public function getScalarResult()
{ {
return $this->execute(array(), self::HYDRATE_SCALAR); return $this->execute(null, self::HYDRATE_SCALAR);
} }
/** /**
@ -572,7 +571,7 @@ abstract class AbstractQuery
*/ */
public function getOneOrNullResult($hydrationMode = null) public function getOneOrNullResult($hydrationMode = null)
{ {
$result = $this->execute(array(), $hydrationMode); $result = $this->execute(null, $hydrationMode);
if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) { if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {
return null; return null;
@ -604,7 +603,7 @@ abstract class AbstractQuery
*/ */
public function getSingleResult($hydrationMode = null) public function getSingleResult($hydrationMode = null)
{ {
$result = $this->execute(array(), $hydrationMode); $result = $this->execute(null, $hydrationMode);
if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) { if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {
throw new NoResultException; throw new NoResultException;
@ -673,18 +672,18 @@ abstract class AbstractQuery
* Executes the query and returns an IterableResult that can be used to incrementally * Executes the query and returns an IterableResult that can be used to incrementally
* iterate over the result. * iterate over the result.
* *
* @param array $params The query parameters. * @param \Doctrine\Common\Collections\ArrayCollection|array $parameters The query parameters.
* @param integer $hydrationMode The hydration mode to use. * @param integer $hydrationMode The hydration mode to use.
* @return \Doctrine\ORM\Internal\Hydration\IterableResult * @return \Doctrine\ORM\Internal\Hydration\IterableResult
*/ */
public function iterate(array $params = array(), $hydrationMode = null) public function iterate($parameters = null, $hydrationMode = null)
{ {
if ($hydrationMode !== null) { if ($hydrationMode !== null) {
$this->setHydrationMode($hydrationMode); $this->setHydrationMode($hydrationMode);
} }
if ($params) { if ( ! empty($parameters)) {
$this->setParameters($params); $this->setParameters($parameters);
} }
$stmt = $this->_doExecute(); $stmt = $this->_doExecute();
@ -697,18 +696,18 @@ abstract class AbstractQuery
/** /**
* Executes the query. * Executes the query.
* *
* @param array $params Any additional query parameters. * @param \Doctrine\Common\Collections\ArrayCollection|array $parameters Query parameters.
* @param integer $hydrationMode Processing mode to be used during the hydration process. * @param integer $hydrationMode Processing mode to be used during the hydration process.
* @return mixed * @return mixed
*/ */
public function execute($params = array(), $hydrationMode = null) public function execute($parameters = null, $hydrationMode = null)
{ {
if ($hydrationMode !== null) { if ($hydrationMode !== null) {
$this->setHydrationMode($hydrationMode); $this->setHydrationMode($hydrationMode);
} }
if ($params) { if ( ! empty($parameters)) {
$this->setParameters($params); $this->setParameters($parameters);
} }
$setCacheEntry = function() {}; $setCacheEntry = function() {};
@ -730,6 +729,7 @@ abstract class AbstractQuery
$setCacheEntry = function($data) use ($cache, $result, $cacheKey, $realCacheKey, $queryCacheProfile) { $setCacheEntry = function($data) use ($cache, $result, $cacheKey, $realCacheKey, $queryCacheProfile) {
$result[$realCacheKey] = $data; $result[$realCacheKey] = $data;
$cache->save($cacheKey, $result, $queryCacheProfile->getLifetime()); $cache->save($cacheKey, $result, $queryCacheProfile->getLifetime());
}; };
} }
@ -760,19 +760,20 @@ abstract class AbstractQuery
*/ */
protected function getHydrationCacheId() protected function getHydrationCacheId()
{ {
$params = $this->getParameters(); $parameters = array();
foreach ($params AS $key => $value) { foreach ($this->getParameters()->getIterator() as $parameter) {
$params[$key] = $this->processParameterValue($value); $parameters[$parameter->getName()] = $this->processParameterValue($parameter->getValue());
} }
$sql = $this->getSQL(); $sql = $this->getSQL();
$queryCacheProfile = $this->getHydrationCacheProfile(); $queryCacheProfile = $this->getHydrationCacheProfile();
$hints = $this->getHints(); $hints = $this->getHints();
$hints['hydrationMode'] = $this->getHydrationMode(); $hints['hydrationMode'] = $this->getHydrationMode();
ksort($hints); ksort($hints);
return $queryCacheProfile->generateCacheKeys($sql, $params, $hints); return $queryCacheProfile->generateCacheKeys($sql, $parameters, $hints);
} }
/** /**
@ -817,8 +818,8 @@ abstract class AbstractQuery
*/ */
public function __clone() public function __clone()
{ {
$this->_params = array(); $this->parameters = new ArrayCollection();
$this->_paramTypes = array();
$this->_hints = array(); $this->_hints = array();
} }
} }

View File

@ -58,19 +58,30 @@ final class NativeQuery extends AbstractQuery
*/ */
protected function _doExecute() protected function _doExecute()
{ {
$params = $this->_params; $parameters = array();
$types = $this->_paramTypes; $types = array();
if ($params && is_int(key($params))) { foreach ($this->getParameters()->getIterator() as $parameter) {
ksort($params); $name = $parameter->getName();
$value = $this->processParameterValue($parameter->getValue());
$type = ($parameter->getValue() === $value)
? $parameter->getType()
: Query\ParameterTypeInferer::inferType($value);
$parameters[$name] = $value;
$types[$name] = $type;
}
if ($parameters && is_int(key($parameters))) {
ksort($parameters);
ksort($types); ksort($types);
$params = array_values($params); $parameters = array_values($parameters);
$types = array_values($types); $types = array_values($types);
} }
return $this->_em->getConnection()->executeQuery( return $this->_em->getConnection()->executeQuery(
$this->_sql, $params, $types, $this->_queryCacheProfile $this->_sql, $parameters, $types, $this->_queryCacheProfile
); );
} }
} }

View File

@ -19,10 +19,13 @@
namespace Doctrine\ORM; namespace Doctrine\ORM;
use Doctrine\DBAL\LockMode, use Doctrine\Common\Collections\ArrayCollection;
Doctrine\ORM\Query\Parser,
Doctrine\ORM\Query\ParserResult, use Doctrine\DBAL\LockMode;
Doctrine\ORM\Query\QueryException;
use Doctrine\ORM\Query\Parser;
use Doctrine\ORM\Query\ParserResult;
use Doctrine\ORM\Query\QueryException;
/** /**
* A Query object represents a DQL query. * A Query object represents a DQL query.
@ -248,7 +251,7 @@ final class Query extends AbstractQuery
// Prepare parameters // Prepare parameters
$paramMappings = $this->_parserResult->getParameterMappings(); $paramMappings = $this->_parserResult->getParameterMappings();
if (count($paramMappings) != count($this->_params)) { if (count($paramMappings) != count($this->parameters)) {
throw QueryException::invalidParameterNumber(); throw QueryException::invalidParameterNumber();
} }
@ -269,17 +272,23 @@ final class Query extends AbstractQuery
*/ */
private function processParameterMappings($paramMappings) private function processParameterMappings($paramMappings)
{ {
$sqlParams = $types = array(); $sqlParams = array();
$types = array();
foreach ($this->parameters->getIterator() as $parameter) {
$key = $parameter->getName();
foreach ($this->_params as $key => $value) {
if ( ! isset($paramMappings[$key])) { if ( ! isset($paramMappings[$key])) {
throw QueryException::unknownParameter($key); throw QueryException::unknownParameter($key);
} }
if (isset($this->_paramTypes[$key])) { $value = $this->processParameterValue($parameter->getValue());
$type = ($parameter->getValue() === $value)
? $parameter->getType()
: Query\ParameterTypeInferer::inferType($value);
foreach ($paramMappings[$key] as $position) { foreach ($paramMappings[$key] as $position) {
$types[$position] = $this->_paramTypes[$key]; $types[$position] = $type;
}
} }
$sqlPositions = $paramMappings[$key]; $sqlPositions = $paramMappings[$key];
@ -517,15 +526,15 @@ final class Query extends AbstractQuery
* Executes the query and returns an IterableResult that can be used to incrementally * Executes the query and returns an IterableResult that can be used to incrementally
* iterated over the result. * iterated over the result.
* *
* @param array $params The query parameters. * @param \Doctrine\Common\Collections\ArrayCollection|array $parameters The query parameters.
* @param integer $hydrationMode The hydration mode to use. * @param integer $hydrationMode The hydration mode to use.
* @return \Doctrine\ORM\Internal\Hydration\IterableResult * @return \Doctrine\ORM\Internal\Hydration\IterableResult
*/ */
public function iterate(array $params = array(), $hydrationMode = self::HYDRATE_OBJECT) public function iterate($parameters = null, $hydrationMode = self::HYDRATE_OBJECT)
{ {
$this->setHint(self::HINT_INTERNAL_ITERATION, true); $this->setHint(self::HINT_INTERNAL_ITERATION, true);
return parent::iterate($params, $hydrationMode); return parent::iterate($parameters, $hydrationMode);
} }
/** /**

View File

@ -19,9 +19,11 @@
namespace Doctrine\ORM\Query\Exec; namespace Doctrine\ORM\Query\Exec;
use Doctrine\DBAL\Connection, use Doctrine\DBAL\Connection;
Doctrine\DBAL\Types\Type, use Doctrine\DBAL\Types\Type;
Doctrine\ORM\Query\AST;
use Doctrine\ORM\Query\ParameterTypeInferer;
use Doctrine\ORM\Query\AST;
/** /**
* Executes the SQL statements for bulk DQL UPDATE statements on classes in * Executes the SQL statements for bulk DQL UPDATE statements on classes in
@ -105,9 +107,16 @@ class MultiTableUpdateExecutor extends AbstractSqlExecutor
//FIXME: parameters can be more deeply nested. traverse the tree. //FIXME: parameters can be more deeply nested. traverse the tree.
//FIXME (URGENT): With query cache the parameter is out of date. Move to execute() stage. //FIXME (URGENT): With query cache the parameter is out of date. Move to execute() stage.
if ($newValue instanceof AST\InputParameter) { if ($newValue instanceof AST\InputParameter) {
$paramKey = $newValue->name; $parameterName = $newValue->name;
$this->_sqlParameters[$i]['parameters'][] = $sqlWalker->getQuery()->getParameter($paramKey); $parameter = $sqlWalker->getQuery()->getParameter($parameterName);
$this->_sqlParameters[$i]['types'][] = $sqlWalker->getQuery()->getParameterType($paramKey);
$value = $sqlWalker->getQuery()->processParameterValue($parameter->getValue());
$type = ($parameter->getValue() === $value)
? $parameter->getType()
: ParameterTypeInferer::inferType($value);
$this->_sqlParameters[$i]['parameters'][] = $value;
$this->_sqlParameters[$i]['types'][] = $type;
++$this->_numParametersInUpdateClause; ++$this->_numParametersInUpdateClause;
} }

View File

@ -0,0 +1,101 @@
<?php
/*
* 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 MIT license. For more information, see
* <http://www.doctrine-project.org>.
*/
namespace Doctrine\ORM\Query;
/**
* Define a Query Parameter
*
* @link www.doctrine-project.org
* @since 2.3
* @author Guilherme Blanco <guilhermeblanco@hotmail.com>
*/
class Parameter
{
/**
* @var string Parameter name
*/
private $name;
/**
* @var mixed Parameter value
*/
private $value;
/**
* @var mixed Parameter type
*/
private $type;
/**
* Constructor.
*
* @param string $name Parameter name
* @param mixed $value Parameter value
* @param mixed $type Parameter type
*/
public function __construct($name, $value, $type = null)
{
$this->name = trim($name, ':');
$this->setValue($value);
}
/**
* Retrieve the Parameter name.
*
* @return string
*/
public function getName()
{
return $this->name;
}
/**
* Retrieve the Parameter value.
*
* @return mixed
*/
public function getValue()
{
return $this->value;
}
/**
* Retrieve the Parameter type.
*
* @return mixed
*/
public function getType()
{
return $this->type;
}
/**
* Define the Parameter value.
*
* @param mixed $value Parameter value
* @param mixed $type Parameter type
*/
public function setValue($value, $type = null)
{
$this->value = $value;
$this->type = $type ?: ParameterTypeInferer::inferType($value);
}
}

View File

@ -1687,7 +1687,6 @@ class SqlWalker implements TreeWalker
// InputParameter // InputParameter
case ($entityExpr instanceof AST\InputParameter): case ($entityExpr instanceof AST\InputParameter):
$dqlParamKey = $entityExpr->name; $dqlParamKey = $entityExpr->name;
$entity = $this->_query->getParameter($dqlParamKey);
$entitySql = '?'; $entitySql = '?';
break; break;
@ -1853,7 +1852,6 @@ class SqlWalker implements TreeWalker
$dqlAlias = $instanceOfExpr->identificationVariable; $dqlAlias = $instanceOfExpr->identificationVariable;
$discrClass = $class = $this->_queryComponents[$dqlAlias]['metadata']; $discrClass = $class = $this->_queryComponents[$dqlAlias]['metadata'];
$fieldName = null;
if ($class->discriminatorColumn) { if ($class->discriminatorColumn) {
$discrClass = $this->_em->getClassMetadata($class->rootEntityName); $discrClass = $this->_em->getClassMetadata($class->rootEntityName);
@ -1871,7 +1869,8 @@ class SqlWalker implements TreeWalker
if ($parameter instanceof AST\InputParameter) { if ($parameter instanceof AST\InputParameter) {
// We need to modify the parameter value to be its correspondent mapped value // We need to modify the parameter value to be its correspondent mapped value
$dqlParamKey = $parameter->name; $dqlParamKey = $parameter->name;
$paramValue = $this->_query->getParameter($dqlParamKey); $dqlParam = $this->_query->getParameter($dqlParamKey);
$paramValue = $this->_query->processParameterValue($dqlParam->getValue());
if ( ! ($paramValue instanceof \Doctrine\ORM\Mapping\ClassMetadata)) { if ( ! ($paramValue instanceof \Doctrine\ORM\Mapping\ClassMetadata)) {
throw QueryException::invalidParameterType('ClassMetadata', get_class($paramValue)); throw QueryException::invalidParameterType('ClassMetadata', get_class($paramValue));

View File

@ -19,6 +19,8 @@
namespace Doctrine\ORM; namespace Doctrine\ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Query\Expr; use Doctrine\ORM\Query\Expr;
/** /**
@ -77,14 +79,9 @@ class QueryBuilder
private $_dql; private $_dql;
/** /**
* @var array The query parameters. * @var \Doctrine\Common\Collections\ArrayCollection The query parameters.
*/ */
private $_params = array(); private $parameters = array();
/**
* @var array The parameter type map of this query.
*/
private $_paramTypes = array();
/** /**
* @var integer The index of the first result to retrieve. * @var integer The index of the first result to retrieve.
@ -109,6 +106,7 @@ class QueryBuilder
public function __construct(EntityManager $em) public function __construct(EntityManager $em)
{ {
$this->_em = $em; $this->_em = $em;
$this->parameters = new ArrayCollection();
} }
/** /**
@ -218,8 +216,10 @@ class QueryBuilder
*/ */
public function getQuery() public function getQuery()
{ {
$parameters = clone $this->parameters;
return $this->_em->createQuery($this->getDQL()) return $this->_em->createQuery($this->getDQL())
->setParameters($this->_params, $this->_paramTypes) ->setParameters($parameters)
->setFirstResult($this->_firstResult) ->setFirstResult($this->_firstResult)
->setMaxResults($this->_maxResults); ->setMaxResults($this->_maxResults);
} }
@ -355,10 +355,7 @@ class QueryBuilder
*/ */
public function setParameter($key, $value, $type = null) public function setParameter($key, $value, $type = null)
{ {
$key = trim($key, ':'); $this->parameters->add(new Query\Parameter($key, $value, $type));
$this->_paramTypes[$key] = $type ?: Query\ParameterTypeInferer::inferType($value);
$this->_params[$key] = $value;
return $this; return $this;
} }
@ -371,20 +368,18 @@ class QueryBuilder
* ->select('u') * ->select('u')
* ->from('User', 'u') * ->from('User', 'u')
* ->where('u.id = :user_id1 OR u.id = :user_id2') * ->where('u.id = :user_id1 OR u.id = :user_id2')
* ->setParameters(array( * ->setParameters(new ArrayCollection(array(
* 'user_id1' => 1, * new Parameter('user_id1', 1),
* 'user_id2' => 2 * new Parameter('user_id2', 2)
)); )));
* </code> * </code>
* *
* @param array $params The query parameters to set. * @param \Doctrine\Common\Collections\ArrayCollections $params The query parameters to set.
* @return QueryBuilder This QueryBuilder instance. * @return QueryBuilder This QueryBuilder instance.
*/ */
public function setParameters(array $params, array $types = array()) public function setParameters(ArrayCollection $parameters)
{ {
foreach ($params as $key => $value) { $this->parameters = $parameters;
$this->setParameter($key, $value, isset($types[$key]) ? $types[$key] : null);
}
return $this; return $this;
} }
@ -396,18 +391,25 @@ class QueryBuilder
*/ */
public function getParameters() public function getParameters()
{ {
return $this->_params; return $this->parameters;
} }
/** /**
* Gets a (previously set) query parameter of the query being constructed. * Gets a (previously set) query parameter of the query being constructed.
* *
* @param mixed $key The key (index or name) of the bound parameter. * @param mixed $key The key (index or name) of the bound parameter.
*
* @return mixed The value of the bound parameter. * @return mixed The value of the bound parameter.
*/ */
public function getParameter($key) public function getParameter($key)
{ {
return isset($this->_params[$key]) ? $this->_params[$key] : null; foreach ($this->parameters->getIterator() as $parameter) {
if ($parameter->getName() === $key) {
return $parameter;
}
}
return null;
} }
/** /**

View File

@ -14,6 +14,7 @@ class HydrationCacheTest extends OrmFunctionalTestCase
public function setUp() public function setUp()
{ {
$this->useModelSet('cms'); $this->useModelSet('cms');
parent::setUp(); parent::setUp();
$user = new CmsUser; $user = new CmsUser;
@ -78,8 +79,11 @@ class HydrationCacheTest extends OrmFunctionalTestCase
->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache)); ->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache));
$query->getResult(); $query->getResult();
$c = $this->getCurrentQueryCount(); $c = $this->getCurrentQueryCount();
$query->getResult(); $query->getResult();
$this->assertEquals($c, $this->getCurrentQueryCount(), "Should not execute query. Its cached!"); $this->assertEquals($c, $this->getCurrentQueryCount(), "Should not execute query. Its cached!");
} }
} }

View File

@ -2,8 +2,12 @@
namespace Doctrine\Tests\ORM\Functional; namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Query\ResultSetMapping; use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\ORM\Query\ResultSetMappingBuilder; use Doctrine\ORM\Query\ResultSetMappingBuilder;
use Doctrine\ORM\Query\Parameter;
use Doctrine\Tests\Models\CMS\CmsUser; use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsPhonenumber; use Doctrine\Tests\Models\CMS\CmsPhonenumber;
use Doctrine\Tests\Models\CMS\CmsAddress; use Doctrine\Tests\Models\CMS\CmsAddress;
@ -191,6 +195,10 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
public function testFluentInterface() public function testFluentInterface()
{ {
$parameters = new ArrayCollection;
$parameters->add(new Parameter(1, 'foo'));
$parameters->add(new Parameter(2, 'bar'));
$rsm = new ResultSetMapping; $rsm = new ResultSetMapping;
$q = $this->_em->createNativeQuery('SELECT id, name, status, phonenumber FROM cms_users INNER JOIN cms_phonenumbers ON id = user_id WHERE username = ?', $rsm); $q = $this->_em->createNativeQuery('SELECT id, name, status, phonenumber FROM cms_users INNER JOIN cms_phonenumbers ON id = user_id WHERE username = ?', $rsm);
@ -199,7 +207,7 @@ class NativeQueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
->expireResultCache(true) ->expireResultCache(true)
->setHint('foo', 'bar') ->setHint('foo', 'bar')
->setParameter(1, 'foo') ->setParameter(1, 'foo')
->setParameters(array(2 => 'bar')) ->setParameters($parameters)
->setResultCacheDriver(null) ->setResultCacheDriver(null)
->setResultCacheLifetime(3500); ->setResultCacheLifetime(3500);

View File

@ -19,10 +19,13 @@ class QueryCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
*/ */
private $cacheDataReflection; private $cacheDataReflection;
protected function setUp() { protected function setUp()
{
$this->cacheDataReflection = new \ReflectionProperty("Doctrine\Common\Cache\ArrayCache", "data"); $this->cacheDataReflection = new \ReflectionProperty("Doctrine\Common\Cache\ArrayCache", "data");
$this->cacheDataReflection->setAccessible(true); $this->cacheDataReflection->setAccessible(true);
$this->useModelSet('cms'); $this->useModelSet('cms');
parent::setUp(); parent::setUp();
} }

View File

@ -2,11 +2,16 @@
namespace Doctrine\Tests\ORM\Functional; namespace Doctrine\Tests\ORM\Functional;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\DBAL\Connection; use Doctrine\DBAL\Connection;
use Doctrine\Tests\Models\CMS\CmsUser,
Doctrine\Tests\Models\CMS\CmsArticle;
use Doctrine\ORM\Mapping\ClassMetadata; use Doctrine\ORM\Mapping\ClassMetadata;
use Doctrine\ORM\Query; use Doctrine\ORM\Query;
use Doctrine\ORM\Query\Parameter;
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsArticle;
require_once __DIR__ . '/../../TestInit.php'; require_once __DIR__ . '/../../TestInit.php';
@ -149,9 +154,22 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
} }
public function testSetParameters() public function testSetParameters()
{
$q = $this->_em->createQuery('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = ?1 AND u.status = ?2');
$parameters = new ArrayCollection();
$parameters->add(new Parameter(1, 'jwage'));
$parameters->add(new Parameter(2, 'active'));
$q->setParameters($parameters);
$users = $q->getResult();
}
public function testSetParametersBackwardsCompatible()
{ {
$q = $this->_em->createQuery('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = ?1 AND u.status = ?2'); $q = $this->_em->createQuery('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = ?1 AND u.status = ?2');
$q->setParameters(array(1 => 'jwage', 2 => 'active')); $q->setParameters(array(1 => 'jwage', 2 => 'active'));
$users = $q->getResult(); $users = $q->getResult();
} }
@ -176,7 +194,7 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$articleId = $article1->id; $articleId = $article1->id;
$query = $this->_em->createQuery("select a from Doctrine\Tests\Models\CMS\CmsArticle a WHERE a.topic = ?1"); $query = $this->_em->createQuery("select a from Doctrine\Tests\Models\CMS\CmsArticle a WHERE a.topic = ?1");
$articles = $query->iterate(array(1 => 'Doctrine 2'), Query::HYDRATE_ARRAY); $articles = $query->iterate(new ArrayCollection(array(new Parameter(1, 'Doctrine 2'))), Query::HYDRATE_ARRAY);
$found = array(); $found = array();
foreach ($articles AS $article) { foreach ($articles AS $article) {
@ -520,10 +538,10 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$this->_em->clear(); $this->_em->clear();
$query = $this->_em->createQuery("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.status = :a AND u.id IN (:b)"); $query = $this->_em->createQuery("SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.status = :a AND u.id IN (:b)");
$query->setParameters(array( $query->setParameters(new ArrayCollection(array(
'b' => array($user1->id, $user2->id, $user3->id), new Parameter('b', array($user1->id, $user2->id, $user3->id)),
'a' => 'developer', new Parameter('a', 'developer')
)); )));
$result = $query->getResult(); $result = $query->getResult();
$this->assertEquals(3, count($result)); $this->assertEquals(3, count($result));
@ -639,7 +657,7 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
/** /**
* @group DDC-1651 * @group DDC-1651
*/ */
public function testSetParameterBindingSingleIdentifierObjectConverted() public function testSetParameterBindingSingleIdentifierObject()
{ {
$userC = new CmsUser; $userC = new CmsUser;
$userC->name = 'Jonathan'; $userC->name = 'Jonathan';
@ -653,7 +671,10 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase
$q = $this->_em->createQuery("SELECT DISTINCT u from Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1"); $q = $this->_em->createQuery("SELECT DISTINCT u from Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1");
$q->setParameter(1, $userC); $q->setParameter(1, $userC);
$this->assertEquals($userC->id, $q->getParameter(1)); $this->assertEquals($userC, $q->getParameter(1)->getValue());
// Parameter is not converted before, but it should be converted during execution. Test should not fail here
$q->getResult();
} }

View File

@ -3,6 +3,9 @@
namespace Doctrine\Tests\ORM\Query; namespace Doctrine\Tests\ORM\Query;
use Doctrine\Common\Cache\ArrayCache; use Doctrine\Common\Cache\ArrayCache;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Query\Parameter;
class QueryTest extends \Doctrine\Tests\OrmTestCase class QueryTest extends \Doctrine\Tests\OrmTestCase
{ {
@ -16,21 +19,34 @@ class QueryTest extends \Doctrine\Tests\OrmTestCase
public function testGetParameters() public function testGetParameters()
{ {
$query = $this->_em->createQuery("select u from Doctrine\Tests\Models\CMS\CmsUser u where u.username = ?1"); $query = $this->_em->createQuery("select u from Doctrine\Tests\Models\CMS\CmsUser u where u.username = ?1");
$this->assertEquals(array(), $query->getParameters());
$parameters = new ArrayCollection();
$this->assertEquals($parameters, $query->getParameters());
} }
public function testGetParameters_HasSomeAlready() public function testGetParameters_HasSomeAlready()
{ {
$query = $this->_em->createQuery("select u from Doctrine\Tests\Models\CMS\CmsUser u where u.username = ?1"); $query = $this->_em->createQuery("select u from Doctrine\Tests\Models\CMS\CmsUser u where u.username = ?1");
$query->setParameter(2, 84); $query->setParameter(2, 84);
$this->assertEquals(array(2 => 84), $query->getParameters());
$parameters = new ArrayCollection();
$parameters->add(new Parameter(2, 84));
$this->assertEquals($parameters, $query->getParameters());
} }
public function testSetParameters() public function testSetParameters()
{ {
$query = $this->_em->createQuery("select u from Doctrine\Tests\Models\CMS\CmsUser u where u.username = ?1"); $query = $this->_em->createQuery("select u from Doctrine\Tests\Models\CMS\CmsUser u where u.username = ?1");
$query->setParameters(array(1 => 'foo', 2 => 'bar'));
$this->assertEquals(array(1 => 'foo', 2 => 'bar'), $query->getParameters()); $parameters = new ArrayCollection();
$parameters->add(new Parameter(1, 'foo'));
$parameters->add(new Parameter(2, 'bar'));
$query->setParameters($parameters);
$this->assertEquals($parameters, $query->getParameters());
} }
public function testFree() public function testFree()
@ -40,7 +56,7 @@ class QueryTest extends \Doctrine\Tests\OrmTestCase
$query->free(); $query->free();
$this->assertEquals(array(), $query->getParameters()); $this->assertEquals(0, count($query->getParameters()));
} }
public function testClone() public function testClone()
@ -54,7 +70,7 @@ class QueryTest extends \Doctrine\Tests\OrmTestCase
$cloned = clone $query; $cloned = clone $query;
$this->assertEquals($dql, $cloned->getDql()); $this->assertEquals($dql, $cloned->getDql());
$this->assertEquals(array(), $cloned->getParameters()); $this->assertEquals(0, count($cloned->getParameters()));
$this->assertFalse($cloned->getHint('foo')); $this->assertFalse($cloned->getHint('foo'));
} }
@ -68,7 +84,7 @@ class QueryTest extends \Doctrine\Tests\OrmTestCase
->setHint('foo', 'bar') ->setHint('foo', 'bar')
->setHint('bar', 'baz') ->setHint('bar', 'baz')
->setParameter(1, 'bar') ->setParameter(1, 'bar')
->setParameters(array(2 => 'baz')) ->setParameters(new ArrayCollection(array(new Parameter(2, 'baz'))))
->setResultCacheDriver(null) ->setResultCacheDriver(null)
->setResultCacheId('foo') ->setResultCacheId('foo')
->setDql('foo') ->setDql('foo')
@ -129,7 +145,7 @@ class QueryTest extends \Doctrine\Tests\OrmTestCase
/** /**
* @group DDC-1697 * @group DDC-1697
*/ */
public function testKeyValueParameters() public function testCollectionParameters()
{ {
$cities = array( $cities = array(
0 => "Paris", 0 => "Paris",
@ -142,8 +158,9 @@ class QueryTest extends \Doctrine\Tests\OrmTestCase
->setParameter('cities', $cities); ->setParameter('cities', $cities);
$parameters = $query->getParameters(); $parameters = $query->getParameters();
$parameter = $parameters->first();
$this->assertArrayHasKey('cities', $parameters); $this->assertEquals('cities', $parameter->getName());
$this->assertEquals($cities, $parameters['cities']); $this->assertEquals($cities, $parameter->getValue());
} }
} }

View File

@ -19,8 +19,12 @@
namespace Doctrine\Tests\ORM; namespace Doctrine\Tests\ORM;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\QueryBuilder, use Doctrine\ORM\QueryBuilder,
Doctrine\ORM\Query\Expr; Doctrine\ORM\Query\Expr,
Doctrine\ORM\Query\Parameter,
Doctrine\ORM\Query\ParameterTypeInferer;
require_once __DIR__ . '/../TestInit.php'; require_once __DIR__ . '/../TestInit.php';
@ -385,7 +389,9 @@ class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase
->where('u.id = :id') ->where('u.id = :id')
->setParameter('id', 1); ->setParameter('id', 1);
$this->assertEquals(array('id' => 1), $qb->getParameters()); $parameter = new Parameter('id', 1, ParameterTypeInferer::inferType(1));
$this->assertEquals($parameter, $qb->getParameter('id'));
} }
public function testSetParameters() public function testSetParameters()
@ -395,9 +401,13 @@ class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u') ->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->where($qb->expr()->orx('u.username = :username', 'u.username = :username2')); ->where($qb->expr()->orx('u.username = :username', 'u.username = :username2'));
$qb->setParameters(array('username' => 'jwage', 'username2' => 'jonwage')); $parameters = new ArrayCollection();
$parameters->add(new Parameter('username', 'jwage'));
$parameters->add(new Parameter('username2', 'jonwage'));
$this->assertEquals(array('username' => 'jwage', 'username2' => 'jonwage'), $qb->getQuery()->getParameters()); $qb->setParameters($parameters);
$this->assertEquals($parameters, $qb->getQuery()->getParameters());
} }
@ -408,8 +418,12 @@ class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u') ->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->where('u.id = :id'); ->where('u.id = :id');
$qb->setParameters(array('id' => 1)); $parameters = new ArrayCollection();
$this->assertEquals(array('id' => 1), $qb->getParameters()); $parameters->add(new Parameter('id', 1));
$qb->setParameters($parameters);
$this->assertEquals($parameters, $qb->getParameters());
} }
public function testGetParameter() public function testGetParameter()
@ -419,8 +433,12 @@ class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u') ->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->where('u.id = :id'); ->where('u.id = :id');
$qb->setParameters(array('id' => 1)); $parameters = new ArrayCollection();
$this->assertEquals(1, $qb->getParameter('id')); $parameters->add(new Parameter('id', 1));
$qb->setParameters($parameters);
$this->assertEquals($parameters->first(), $qb->getParameter('id'));
} }
public function testMultipleWhere() public function testMultipleWhere()
@ -536,6 +554,7 @@ class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase
$qb->setParameter('name', 'romanb'); $qb->setParameter('name', 'romanb');
$q1 = $qb->getQuery(); $q1 = $qb->getQuery();
$this->assertEquals('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = :name', $q1->getDql()); $this->assertEquals('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = :name', $q1->getDql());
$this->assertEquals(1, count($q1->getParameters())); $this->assertEquals(1, count($q1->getParameters()));