1
0
mirror of synced 2025-02-06 15:29:26 +03:00

[DDC-1840] Implemented parameters as a collection.

This commit is contained in:
Guilherme Blanco 2012-05-28 12:16:42 -04:00
parent 1f2ce21b56
commit 1635e0af4b
12 changed files with 357 additions and 179 deletions

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,58 +154,42 @@ 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])) { foreach ($this->parameters->getIterator() as $parameter) {
return $this->_params[$key]; // Must not be identical because of string to integer conversion
if ($parameter->getName() == $key) {
return $parameter;
}
} }
return null; return null;
} }
/** /**
* Gets a query parameter type. * Sets a collection of query parameters.
* *
* @param mixed $key The key (index or name) of the bound parameter. * @param \Doctrine\Common\Collections\ArrayCollection $parameters
* @return mixed The parameter type of the bound parameter. *
* @return \Doctrine\ORM\AbstractQuery This query instance.
*/ */
public function getParameterType($key) public function setParameters(ArrayCollection $parameters)
{ {
if (isset($this->_paramTypes[$key])) { $this->parameters = $parameters;
return $this->_paramTypes[$key];
}
return null; return $this;
} }
/**
* 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. * Sets a query parameter.
* *
@ -207,19 +198,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 +231,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 +250,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 +276,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 +515,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 +557,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 +589,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 +658,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 $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(ArrayCollection $parameters = null, $hydrationMode = null)
{ {
if ($hydrationMode !== null) { if ($hydrationMode !== null) {
$this->setHydrationMode($hydrationMode); $this->setHydrationMode($hydrationMode);
} }
if ($params) { if ($parameters) {
$this->setParameters($params); $this->setParameters($parameters);
} }
$stmt = $this->_doExecute(); $stmt = $this->_doExecute();
@ -697,18 +682,18 @@ abstract class AbstractQuery
/** /**
* Executes the query. * Executes the query.
* *
* @param array $params Any additional query parameters. * @param \Doctrine\Common\Collections\ArrayCollection $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(ArrayCollection $parameters = null, $hydrationMode = null)
{ {
if ($hydrationMode !== null) { if ($hydrationMode !== null) {
$this->setHydrationMode($hydrationMode); $this->setHydrationMode($hydrationMode);
} }
if ($params) { if ($parameters) {
$this->setParameters($params); $this->setParameters($parameters);
} }
$setCacheEntry = function() {}; $setCacheEntry = function() {};
@ -730,6 +715,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 +746,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 +804,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());
foreach ($paramMappings[$key] as $position) { $type = ($parameter->getValue() === $value)
$types[$position] = $this->_paramTypes[$key]; ? $parameter->getType()
} : Query\ParameterTypeInferer::inferType($value);
foreach ($paramMappings[$key] as $position) {
$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 $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(ArrayCollection $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

@ -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';
@ -151,7 +156,12 @@ 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'); $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'));
$parameters = new ArrayCollection();
$parameters->add(new Parameter(1, 'jwage'));
$parameters->add(new Parameter(2, 'active'));
$q->setParameters($parameters);
$users = $q->getResult(); $users = $q->getResult();
} }
@ -176,7 +186,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 +530,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 +649,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 +663,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()));