Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP

Loading…

[DDC-1840] Implemented parameters as a collection. #360

Merged
merged 6 commits into from

7 participants

@guilhermeblanco

Breaking down the changes per method:

execute

BEFORE:

$query = $entityManager->createQuery(
    'SELECT u  FROM User u WHERE u.id = :id'
);
$result = $query->execute(array('id' => 1));

AFTER:

$query = $entityManager->createQuery(
    'SELECT u  FROM User u WHERE u.id = :id'
);
$result = $query->execute(new ArrayCollection(array(new Parameter('id', 1))));

setParameters

BEFORE:

$query = $entityManager->createQuery(
    'SELECT u  FROM User u WHERE u.id = :id AND u.status = :status'
);
$query->setParameters(array(
    'id'     => 1,
    'status' => 'active'
));
$result = $query->getResult();

AFTER:

$query = $entityManager->createQuery(
    'SELECT u  FROM User u WHERE u.id = :id AND u.status = :status'
);
$query->setParameters(new ArrayCollection(array(
    new Parameter('id', 1),
    new Parameter('status', 'active')
));
$result = $query->getResult();

getParameter

BEFORE:

$query = $entityManager->createQuery(
    'SELECT u  FROM User u WHERE u.id = :id AND u.status = :status'
);
$query->setParameters(array(
    'id'     => 1,
    'status' => 'active'
));

$parameter = $query->getParameter('id');

// $parameter === 1
// $query->getParameterType('id') === PDO::PARAM_INT

AFTER:

$query = $entityManager->createQuery(
    'SELECT u  FROM User u WHERE u.id = :id AND u.status = :status'
);
$query->setParameters(new ArrayCollection(array(
    new Parameter('id', 1),
    new Parameter('status', 'active')
));


$parameter = $query->getParameter('id');

// $parameter->getName() === 'id'
// $parameter->getValue() === 1
// $parameter->getType() === \PDO::PARAM_INT

Related changes:

BEFORE:

$user = $entityManager->getReference('User', 1);

$query->setParameter(1, $user);

// $query->getParameter(1) === 1

AFTER

$user = $entityManager->getReference('User', 1);

$query->setParameter(1, $user);

// $query->getParameter(1) instanceof Doctrine\ORM\Query\Parameter

$parameter = $query->getParameter(1);

// $parameter->getValue() === $user
@travisbot

This pull request fails (merged 1635e0a into 1f2ce21).

@Koc

For what this needed?

@beberlei
Owner

Its cleanup of the parameter handling, because you cannot reset parametrs and also its hard to work with existing parameers and modify them. Advanced Query Building use-cases.

@guilhermeblanco How about making it BC by allowing arrays and having a method to convert arrays into ArrayCollection?

@travisbot

This pull request fails (merged 79ff1f1 into 1f2ce21).

@travisbot

This pull request fails (merged d8e165d into 1f2ce21).

@guilhermeblanco

@beberlei I'd avoid adding magical transformations internally.
Specially because the BC break is already made (between key => value to a Parameter instance). So we can live with the complete change since we're already enforcing people to update 90% of it.

@schmittjoh

Could you add some before/after examples?

@guilhermeblanco

@schmittjoh updated and added examples of change impact =)

@Koc

So after this changes methods like cloneQuery will save binded types?

@guilhermeblanco

@Koc preliminary inference, yes.
All parameters are double inferred if the processed parameter value changed from the original (for example, an array transformation or a proxy to id transformation).

@travisbot

This pull request fails (merged b3e7493 into 1f2ce21).

@guilhermeblanco

@travisbot don't tell me this PR fails because you're lying! =P
The build is not passing because of a DBAL issue for generating cache keys which was not modified by my code.

@travisbot

This pull request fails (merged 161ae31 into 1f2ce21).

@guilhermeblanco guilhermeblanco merged commit e4935e5 into master
@travisbot

This pull request fails (merged 15f76c6 into 1f2ce21).

@stof stof commented on the diff
lib/Doctrine/ORM/AbstractQuery.php
@@ -697,18 +696,18 @@ public function iterate(array $params = array(), $hydrationMode = null)
/**
* Executes the query.
*
- * @param array $params Any additional query parameters.
+ * @param \Doctrine\Common\Collections\ArrayCollection|array $parameters Query parameters.
@stof
stof added a note

shouldn't the doc mention the interface instead ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@stof stof commented on the diff
lib/Doctrine/ORM/AbstractQuery.php
((6 lines not shown))
- foreach ($params AS $key => $value) {
- $params[$key] = $this->processParameterValue($value);
+ foreach ($this->getParameters()->getIterator() as $parameter) {
@stof
stof added a note

->getIterator() is not needed here thanks to \IteratorAggregate on the collection

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@stof stof commented on the diff
lib/Doctrine/ORM/NativeQuery.php
((8 lines not shown))
- if ($params && is_int(key($params))) {
- ksort($params);
+ foreach ($this->getParameters()->getIterator() as $parameter) {
@stof
stof added a note

same here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@stof stof commented on the diff
lib/Doctrine/ORM/Query.php
@@ -269,17 +272,23 @@ protected function _doExecute()
*/
private function processParameterMappings($paramMappings)
{
- $sqlParams = $types = array();
+ $sqlParams = array();
+ $types = array();
+
+ foreach ($this->parameters->getIterator() as $parameter) {
@stof
stof added a note

same here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@stof stof commented on the diff
lib/Doctrine/ORM/QueryBuilder.php
((16 lines not shown))
* @return QueryBuilder This QueryBuilder instance.
*/
- public function setParameters(array $params, array $types = array())
+ public function setParameters(ArrayCollection $parameters)
@stof
stof added a note

you should typehint the interface

@stof
stof added a note

btw, why not allowing an array here whereas the query allows it ? It is inconsistent

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@stof stof commented on the diff
lib/Doctrine/ORM/QueryBuilder.php
((13 lines not shown))
* @return mixed The value of the bound parameter.
*/
public function getParameter($key)
{
- return isset($this->_params[$key]) ? $this->_params[$key] : null;
+ foreach ($this->parameters->getIterator() as $parameter) {
@stof
stof added a note

no need of the explicit getIterator()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@stof

btw, I think the pagination might have side effects now: it clones the query and set the parameters again, but the collection will be set by reference in the cloned query now as it is an object whereas it was copied previously when using an array

@umpirsky

This commit produced a bug.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
This page is out of date. Refresh to see the latest.
View
18 UPGRADE_TO_2_3
@@ -7,3 +7,21 @@ and strip a trailing "s" character if there is one.
# Merge copies non persisted properties too
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
183 lib/Doctrine/ORM/AbstractQuery.php
@@ -19,15 +19,17 @@
namespace Doctrine\ORM;
-use Doctrine\DBAL\Types\Type,
- Doctrine\DBAL\Cache\QueryCacheProfile,
- Doctrine\ORM\Query\QueryException,
- Doctrine\Common\Util\ClassUtils;
+use Doctrine\Common\Util\ClassUtils;
+use Doctrine\Common\Collections\ArrayCollection;
+
+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.
*
- *
* @link www.doctrine-project.org
* @since 2.0
* @author Benjamin Eberlei <kontakt@beberlei.de>
@@ -62,14 +64,9 @@
const HYDRATE_SIMPLEOBJECT = 5;
/**
- * @var array The parameter map of this query.
- */
- protected $_params = array();
-
- /**
- * @var array The parameter type map of this query.
+ * @var \Doctrine\Common\Collections\ArrayCollection The parameter map of this query.
*/
- protected $_paramTypes = array();
+ protected $parameters;
/**
* @var ResultSetMapping The user-specified ResultSetMapping to use.
@@ -114,9 +111,19 @@
public function __construct(EntityManager $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.
*
* @return \Doctrine\ORM\EntityManager
@@ -135,8 +142,8 @@ public function getEntityManager()
*/
public function free()
{
- $this->_params = array();
- $this->_paramTypes = array();
+ $this->parameters = new ArrayCollection();
+
$this->_hints = array();
}
@@ -147,57 +154,55 @@ public function free()
*/
public function getParameters()
{
- return $this->_params;
- }
-
- /**
- * Get all defined parameter types.
- *
- * @return array The defined query parameter types.
- */
- public function getParameterTypes()
- {
- return $this->_paramTypes;
+ return $this->parameters;
}
/**
* 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)
{
- if (isset($this->_params[$key])) {
- return $this->_params[$key];
- }
+ $filteredParameters = $this->parameters->filter(
+ function ($parameter) use ($key)
+ {
+ // Must not be identical because of string to integer conversion
+ return ($key == $parameter->getName());
+ }
+ );
- return null;
+ return count($filteredParameters) ? $filteredParameters->first() : null;
}
/**
- * Gets a query parameter type.
+ * Sets a collection of query parameters.
*
- * @param mixed $key The key (index or name) of the bound parameter.
- * @return mixed The parameter type of the bound parameter.
+ * @param \Doctrine\Common\Collections\ArrayCollection|array $parameters
+ *
+ * @return \Doctrine\ORM\AbstractQuery This query instance.
*/
- public function getParameterType($key)
+ public function setParameters($parameters)
{
- if (isset($this->_paramTypes[$key])) {
- return $this->_paramTypes[$key];
+ // 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;
}
- return null;
- }
+ $this->parameters = $parameters;
- /**
- * 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();
+ return $this;
+ }
/**
* Sets a query parameter.
@@ -207,19 +212,29 @@ public function getParameterType($key)
* @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
* strings and numeric types.
+ *
* @return \Doctrine\ORM\AbstractQuery This query instance.
*/
public function setParameter($key, $value, $type = null)
{
- $key = trim($key, ':');
- $value = $this->processParameterValue($value);
+ $filteredParameters = $this->parameters->filter(
+ function ($parameter) use ($key)
+ {
+ // Must not be identical because of string to integer conversion
+ return ($key == $parameter->getName());
+ }
+ );
+
+ if (count($filteredParameters)) {
+ $parameter = $filteredParameters->first();
+ $parameter->setValue($value, $type);
- if ($type === null) {
- $type = Query\ParameterTypeInferer::inferType($value);
+ return $this;
}
- $this->_paramTypes[$key] = $type;
- $this->_params[$key] = $value;
+ $parameter = new Query\Parameter($key, $value, $type);
+
+ $this->parameters->add($parameter);
return $this;
}
@@ -230,7 +245,7 @@ public function setParameter($key, $value, $type = null)
* @param mixed $value
* @return array
*/
- private function processParameterValue($value)
+ public function processParameterValue($value)
{
switch (true) {
case is_array($value):
@@ -249,7 +264,7 @@ private function processParameterValue($value)
}
}
- protected function convertObjectParameterToScalarValue($value)
+ private function convertObjectParameterToScalarValue($value)
{
$class = $this->_em->getClassMetadata(get_class($value));
@@ -276,22 +291,6 @@ protected function convertObjectParameterToScalarValue($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.
*
* @param ResultSetMapping $rsm
@@ -530,37 +529,37 @@ public function getHydrationMode()
/**
* Gets the list of results for the query.
*
- * Alias for execute(array(), $hydrationMode = HYDRATE_OBJECT).
+ * Alias for execute(null, $hydrationMode = HYDRATE_OBJECT).
*
* @return array
*/
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.
*
- * Alias for execute(array(), HYDRATE_ARRAY).
+ * Alias for execute(null, HYDRATE_ARRAY).
*
* @return array
*/
public function getArrayResult()
{
- return $this->execute(array(), self::HYDRATE_ARRAY);
+ return $this->execute(null, self::HYDRATE_ARRAY);
}
/**
* Gets the scalar results for the query.
*
- * Alias for execute(array(), HYDRATE_SCALAR).
+ * Alias for execute(null, HYDRATE_SCALAR).
*
* @return array
*/
public function getScalarResult()
{
- return $this->execute(array(), self::HYDRATE_SCALAR);
+ return $this->execute(null, self::HYDRATE_SCALAR);
}
/**
@@ -572,7 +571,7 @@ public function getScalarResult()
*/
public function getOneOrNullResult($hydrationMode = null)
{
- $result = $this->execute(array(), $hydrationMode);
+ $result = $this->execute(null, $hydrationMode);
if ($this->_hydrationMode !== self::HYDRATE_SINGLE_SCALAR && ! $result) {
return null;
@@ -604,7 +603,7 @@ public function getOneOrNullResult($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) {
throw new NoResultException;
@@ -673,18 +672,18 @@ public function getHints()
* Executes the query and returns an IterableResult that can be used to incrementally
* 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.
* @return \Doctrine\ORM\Internal\Hydration\IterableResult
*/
- public function iterate(array $params = array(), $hydrationMode = null)
+ public function iterate($parameters = null, $hydrationMode = null)
{
if ($hydrationMode !== null) {
$this->setHydrationMode($hydrationMode);
}
- if ($params) {
- $this->setParameters($params);
+ if ( ! empty($parameters)) {
+ $this->setParameters($parameters);
}
$stmt = $this->_doExecute();
@@ -697,18 +696,18 @@ public function iterate(array $params = array(), $hydrationMode = null)
/**
* Executes the query.
*
- * @param array $params Any additional query parameters.
+ * @param \Doctrine\Common\Collections\ArrayCollection|array $parameters Query parameters.
@stof
stof added a note

shouldn't the doc mention the interface instead ?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
* @param integer $hydrationMode Processing mode to be used during the hydration process.
* @return mixed
*/
- public function execute($params = array(), $hydrationMode = null)
+ public function execute($parameters = null, $hydrationMode = null)
{
if ($hydrationMode !== null) {
$this->setHydrationMode($hydrationMode);
}
- if ($params) {
- $this->setParameters($params);
+ if ( ! empty($parameters)) {
+ $this->setParameters($parameters);
}
$setCacheEntry = function() {};
@@ -730,6 +729,7 @@ public function execute($params = array(), $hydrationMode = null)
$setCacheEntry = function($data) use ($cache, $result, $cacheKey, $realCacheKey, $queryCacheProfile) {
$result[$realCacheKey] = $data;
+
$cache->save($cacheKey, $result, $queryCacheProfile->getLifetime());
};
}
@@ -760,19 +760,20 @@ public function execute($params = array(), $hydrationMode = null)
*/
protected function getHydrationCacheId()
{
- $params = $this->getParameters();
+ $parameters = array();
- foreach ($params AS $key => $value) {
- $params[$key] = $this->processParameterValue($value);
+ foreach ($this->getParameters()->getIterator() as $parameter) {
@stof
stof added a note

->getIterator() is not needed here thanks to \IteratorAggregate on the collection

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ $parameters[$parameter->getName()] = $this->processParameterValue($parameter->getValue());
}
$sql = $this->getSQL();
$queryCacheProfile = $this->getHydrationCacheProfile();
$hints = $this->getHints();
$hints['hydrationMode'] = $this->getHydrationMode();
+
ksort($hints);
- return $queryCacheProfile->generateCacheKeys($sql, $params, $hints);
+ return $queryCacheProfile->generateCacheKeys($sql, $parameters, $hints);
}
/**
@@ -817,8 +818,8 @@ public function getResultCacheId()
*/
public function __clone()
{
- $this->_params = array();
- $this->_paramTypes = array();
+ $this->parameters = new ArrayCollection();
+
$this->_hints = array();
}
}
View
25 lib/Doctrine/ORM/NativeQuery.php
@@ -58,19 +58,30 @@ public function getSQL()
*/
protected function _doExecute()
{
- $params = $this->_params;
- $types = $this->_paramTypes;
+ $parameters = array();
+ $types = array();
- if ($params && is_int(key($params))) {
- ksort($params);
+ foreach ($this->getParameters()->getIterator() as $parameter) {
@stof
stof added a note

same here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ $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);
- $params = array_values($params);
- $types = array_values($types);
+ $parameters = array_values($parameters);
+ $types = array_values($types);
}
return $this->_em->getConnection()->executeQuery(
- $this->_sql, $params, $types, $this->_queryCacheProfile
+ $this->_sql, $parameters, $types, $this->_queryCacheProfile
);
}
}
View
37 lib/Doctrine/ORM/Query.php
@@ -19,10 +19,13 @@
namespace Doctrine\ORM;
-use Doctrine\DBAL\LockMode,
- Doctrine\ORM\Query\Parser,
- Doctrine\ORM\Query\ParserResult,
- Doctrine\ORM\Query\QueryException;
+use Doctrine\Common\Collections\ArrayCollection;
+
+use Doctrine\DBAL\LockMode;
+
+use Doctrine\ORM\Query\Parser;
+use Doctrine\ORM\Query\ParserResult;
+use Doctrine\ORM\Query\QueryException;
/**
* A Query object represents a DQL query.
@@ -248,7 +251,7 @@ protected function _doExecute()
// Prepare parameters
$paramMappings = $this->_parserResult->getParameterMappings();
- if (count($paramMappings) != count($this->_params)) {
+ if (count($paramMappings) != count($this->parameters)) {
throw QueryException::invalidParameterNumber();
}
@@ -269,17 +272,23 @@ protected function _doExecute()
*/
private function processParameterMappings($paramMappings)
{
- $sqlParams = $types = array();
+ $sqlParams = array();
+ $types = array();
+
+ foreach ($this->parameters->getIterator() as $parameter) {
@stof
stof added a note

same here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ $key = $parameter->getName();
- foreach ($this->_params as $key => $value) {
if ( ! isset($paramMappings[$key])) {
throw QueryException::unknownParameter($key);
}
- if (isset($this->_paramTypes[$key])) {
- foreach ($paramMappings[$key] as $position) {
- $types[$position] = $this->_paramTypes[$key];
- }
+ $value = $this->processParameterValue($parameter->getValue());
+ $type = ($parameter->getValue() === $value)
+ ? $parameter->getType()
+ : Query\ParameterTypeInferer::inferType($value);
+
+ foreach ($paramMappings[$key] as $position) {
+ $types[$position] = $type;
}
$sqlPositions = $paramMappings[$key];
@@ -517,15 +526,15 @@ public function getMaxResults()
* Executes the query and returns an IterableResult that can be used to incrementally
* 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.
* @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);
- return parent::iterate($params, $hydrationMode);
+ return parent::iterate($parameters, $hydrationMode);
}
/**
View
21 lib/Doctrine/ORM/Query/Exec/MultiTableUpdateExecutor.php
@@ -19,9 +19,11 @@
namespace Doctrine\ORM\Query\Exec;
-use Doctrine\DBAL\Connection,
- Doctrine\DBAL\Types\Type,
- Doctrine\ORM\Query\AST;
+use Doctrine\DBAL\Connection;
+use Doctrine\DBAL\Types\Type;
+
+use Doctrine\ORM\Query\ParameterTypeInferer;
+use Doctrine\ORM\Query\AST;
/**
* Executes the SQL statements for bulk DQL UPDATE statements on classes in
@@ -105,9 +107,16 @@ public function __construct(AST\Node $AST, $sqlWalker)
//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.
if ($newValue instanceof AST\InputParameter) {
- $paramKey = $newValue->name;
- $this->_sqlParameters[$i]['parameters'][] = $sqlWalker->getQuery()->getParameter($paramKey);
- $this->_sqlParameters[$i]['types'][] = $sqlWalker->getQuery()->getParameterType($paramKey);
+ $parameterName = $newValue->name;
+ $parameter = $sqlWalker->getQuery()->getParameter($parameterName);
+
+ $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;
}
View
101 lib/Doctrine/ORM/Query/Parameter.php
@@ -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
7 lib/Doctrine/ORM/Query/SqlWalker.php
@@ -1687,7 +1687,6 @@ public function walkCollectionMemberExpression($collMemberExpr)
// InputParameter
case ($entityExpr instanceof AST\InputParameter):
$dqlParamKey = $entityExpr->name;
- $entity = $this->_query->getParameter($dqlParamKey);
$entitySql = '?';
break;
@@ -1853,8 +1852,7 @@ public function walkInstanceOfExpression($instanceOfExpr)
$dqlAlias = $instanceOfExpr->identificationVariable;
$discrClass = $class = $this->_queryComponents[$dqlAlias]['metadata'];
- $fieldName = null;
-
+
if ($class->discriminatorColumn) {
$discrClass = $this->_em->getClassMetadata($class->rootEntityName);
}
@@ -1871,7 +1869,8 @@ public function walkInstanceOfExpression($instanceOfExpr)
if ($parameter instanceof AST\InputParameter) {
// We need to modify the parameter value to be its correspondent mapped value
$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)) {
throw QueryException::invalidParameterType('ClassMetadata', get_class($paramValue));
View
48 lib/Doctrine/ORM/QueryBuilder.php
@@ -19,6 +19,8 @@
namespace Doctrine\ORM;
+use Doctrine\Common\Collections\ArrayCollection;
+
use Doctrine\ORM\Query\Expr;
/**
@@ -77,14 +79,9 @@ class QueryBuilder
private $_dql;
/**
- * @var array The query parameters.
- */
- private $_params = array();
-
- /**
- * @var array The parameter type map of this query.
+ * @var \Doctrine\Common\Collections\ArrayCollection The query parameters.
*/
- private $_paramTypes = array();
+ private $parameters = array();
/**
* @var integer The index of the first result to retrieve.
@@ -109,6 +106,7 @@ class QueryBuilder
public function __construct(EntityManager $em)
{
$this->_em = $em;
+ $this->parameters = new ArrayCollection();
}
/**
@@ -218,8 +216,10 @@ public function getDQL()
*/
public function getQuery()
{
+ $parameters = clone $this->parameters;
+
return $this->_em->createQuery($this->getDQL())
- ->setParameters($this->_params, $this->_paramTypes)
+ ->setParameters($parameters)
->setFirstResult($this->_firstResult)
->setMaxResults($this->_maxResults);
}
@@ -355,10 +355,7 @@ public function getRootEntities()
*/
public function setParameter($key, $value, $type = null)
{
- $key = trim($key, ':');
-
- $this->_paramTypes[$key] = $type ?: Query\ParameterTypeInferer::inferType($value);
- $this->_params[$key] = $value;
+ $this->parameters->add(new Query\Parameter($key, $value, $type));
return $this;
}
@@ -371,20 +368,18 @@ public function setParameter($key, $value, $type = null)
* ->select('u')
* ->from('User', 'u')
* ->where('u.id = :user_id1 OR u.id = :user_id2')
- * ->setParameters(array(
- * 'user_id1' => 1,
- * 'user_id2' => 2
- ));
+ * ->setParameters(new ArrayCollection(array(
+ * new Parameter('user_id1', 1),
+ * new Parameter('user_id2', 2)
+ )));
* </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.
*/
- public function setParameters(array $params, array $types = array())
+ public function setParameters(ArrayCollection $parameters)
@stof
stof added a note

you should typehint the interface

@stof
stof added a note

btw, why not allowing an array here whereas the query allows it ? It is inconsistent

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
{
- foreach ($params as $key => $value) {
- $this->setParameter($key, $value, isset($types[$key]) ? $types[$key] : null);
- }
+ $this->parameters = $parameters;
return $this;
}
@@ -396,18 +391,25 @@ public function setParameters(array $params, array $types = array())
*/
public function getParameters()
{
- return $this->_params;
+ return $this->parameters;
}
/**
* Gets a (previously set) query parameter of the query being constructed.
*
* @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;
+ foreach ($this->parameters->getIterator() as $parameter) {
@stof
stof added a note

no need of the explicit getIterator()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
+ if ($parameter->getName() === $key) {
+ return $parameter;
+ }
+ }
+
+ return null;
}
/**
View
10 tests/Doctrine/Tests/ORM/Functional/HydrationCacheTest.php
@@ -14,6 +14,7 @@ class HydrationCacheTest extends OrmFunctionalTestCase
public function setUp()
{
$this->useModelSet('cms');
+
parent::setUp();
$user = new CmsUser;
@@ -72,14 +73,17 @@ public function testHydrationParametersSerialization()
$user = new CmsUser();
$user->id = 1;
- $dql = "SELECT u FROM Doctrine\Tests\Models\Cms\CmsUser u WHERE u.id = ?1";
+ $dql = "SELECT u FROM Doctrine\Tests\Models\Cms\CmsUser u WHERE u.id = ?1";
$query = $this->_em->createQuery($dql)
- ->setParameter(1, $user)
- ->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache));
+ ->setParameter(1, $user)
+ ->setHydrationCacheProfile(new QueryCacheProfile(null, null, $cache));
$query->getResult();
+
$c = $this->getCurrentQueryCount();
+
$query->getResult();
+
$this->assertEquals($c, $this->getCurrentQueryCount(), "Should not execute query. Its cached!");
}
}
View
20 tests/Doctrine/Tests/ORM/Functional/NativeQueryTest.php
@@ -2,8 +2,12 @@
namespace Doctrine\Tests\ORM\Functional;
+use Doctrine\Common\Collections\ArrayCollection;
+
use Doctrine\ORM\Query\ResultSetMapping;
use Doctrine\ORM\Query\ResultSetMappingBuilder;
+use Doctrine\ORM\Query\Parameter;
+
use Doctrine\Tests\Models\CMS\CmsUser;
use Doctrine\Tests\Models\CMS\CmsPhonenumber;
use Doctrine\Tests\Models\CMS\CmsAddress;
@@ -191,6 +195,10 @@ public function testJoinedOneToOneNativeQuery()
public function testFluentInterface()
{
+ $parameters = new ArrayCollection;
+ $parameters->add(new Parameter(1, 'foo'));
+ $parameters->add(new Parameter(2, 'bar'));
+
$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);
@@ -199,7 +207,7 @@ public function testFluentInterface()
->expireResultCache(true)
->setHint('foo', 'bar')
->setParameter(1, 'foo')
- ->setParameters(array(2 => 'bar'))
+ ->setParameters($parameters)
->setResultCacheDriver(null)
->setResultCacheLifetime(3500);
@@ -362,7 +370,7 @@ public function testBasicNativeNamedQueryWithSqlResultSetMapping()
$repository = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsAddress');
$query = $repository->createNativeNamedQuery('find-all');
$result = $query->getResult();
-
+
$this->assertCount(1, $result);
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsAddress', $result[0]);
$this->assertEquals($addr->id, $result[0]->id);
@@ -396,7 +404,7 @@ public function testBasicNativeNamedQueryWithResultClass()
$result = $repository->createNativeNamedQuery('fetchIdAndUsernameWithResultClass')
->setParameter(1, 'FabioBatSilva')->getResult();
-
+
$this->assertEquals(1, count($result));
$this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $result[0]);
$this->assertNull($result[0]->name);
@@ -511,7 +519,7 @@ public function testMixedNativeNamedQueryNormalJoin()
$user2->name = 'test tester';
$user2->username = 'test';
$user2->status = 'tester';
-
+
$phone1 = new CmsPhonenumber;
$phone2 = new CmsPhonenumber;
$phone3 = new CmsPhonenumber;
@@ -528,7 +536,7 @@ public function testMixedNativeNamedQueryNormalJoin()
$this->_em->flush();
$this->_em->clear();
-
+
$repository = $this->_em->getRepository('Doctrine\Tests\Models\CMS\CmsUser');
$result = $repository->createNativeNamedQuery('fetchUserPhonenumberCount')
@@ -583,7 +591,7 @@ public function testNativeNamedQueryInheritance()
$this->_em->clear();
-
+
$result = $repository->createNativeNamedQuery('fetchAllWithResultClass')
->getResult();
View
5 tests/Doctrine/Tests/ORM/Functional/QueryCacheTest.php
@@ -19,10 +19,13 @@ class QueryCacheTest extends \Doctrine\Tests\OrmFunctionalTestCase
*/
private $cacheDataReflection;
- protected function setUp() {
+ protected function setUp()
+ {
$this->cacheDataReflection = new \ReflectionProperty("Doctrine\Common\Cache\ArrayCache", "data");
$this->cacheDataReflection->setAccessible(true);
+
$this->useModelSet('cms');
+
parent::setUp();
}
View
39 tests/Doctrine/Tests/ORM/Functional/QueryTest.php
@@ -2,11 +2,16 @@
namespace Doctrine\Tests\ORM\Functional;
+use Doctrine\Common\Collections\ArrayCollection;
+
use Doctrine\DBAL\Connection;
-use Doctrine\Tests\Models\CMS\CmsUser,
- Doctrine\Tests\Models\CMS\CmsArticle;
+
use Doctrine\ORM\Mapping\ClassMetadata;
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';
@@ -151,7 +156,20 @@ public function testInvalidInputParameterThrowsException()
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->setParameters(array(1 => 'jwage', 2 => 'active'));
+
$users = $q->getResult();
}
@@ -176,7 +194,7 @@ public function testIterateResultAsArrayAndParams()
$articleId = $article1->id;
$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();
foreach ($articles AS $article) {
@@ -520,10 +538,10 @@ public function testParameterOrder()
$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->setParameters(array(
- 'b' => array($user1->id, $user2->id, $user3->id),
- 'a' => 'developer',
- ));
+ $query->setParameters(new ArrayCollection(array(
+ new Parameter('b', array($user1->id, $user2->id, $user3->id)),
+ new Parameter('a', 'developer')
+ )));
$result = $query->getResult();
$this->assertEquals(3, count($result));
@@ -639,7 +657,7 @@ public function testQueryWithHiddenAsSelectExpression()
/**
* @group DDC-1651
*/
- public function testSetParameterBindingSingleIdentifierObjectConverted()
+ public function testSetParameterBindingSingleIdentifierObject()
{
$userC = new CmsUser;
$userC->name = 'Jonathan';
@@ -653,7 +671,10 @@ public function testSetParameterBindingSingleIdentifierObjectConverted()
$q = $this->_em->createQuery("SELECT DISTINCT u from Doctrine\Tests\Models\CMS\CmsUser u WHERE u.id = ?1");
$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
37 tests/Doctrine/Tests/ORM/Query/QueryTest.php
@@ -3,6 +3,9 @@
namespace Doctrine\Tests\ORM\Query;
use Doctrine\Common\Cache\ArrayCache;
+use Doctrine\Common\Collections\ArrayCollection;
+
+use Doctrine\ORM\Query\Parameter;
class QueryTest extends \Doctrine\Tests\OrmTestCase
{
@@ -16,21 +19,34 @@ protected function setUp()
public function testGetParameters()
{
$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()
{
$query = $this->_em->createQuery("select u from Doctrine\Tests\Models\CMS\CmsUser u where u.username = ?1");
$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()
{
$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()
@@ -40,7 +56,7 @@ public function testFree()
$query->free();
- $this->assertEquals(array(), $query->getParameters());
+ $this->assertEquals(0, count($query->getParameters()));
}
public function testClone()
@@ -54,7 +70,7 @@ public function testClone()
$cloned = clone $query;
$this->assertEquals($dql, $cloned->getDql());
- $this->assertEquals(array(), $cloned->getParameters());
+ $this->assertEquals(0, count($cloned->getParameters()));
$this->assertFalse($cloned->getHint('foo'));
}
@@ -68,7 +84,7 @@ public function testFluentQueryInterface()
->setHint('foo', 'bar')
->setHint('bar', 'baz')
->setParameter(1, 'bar')
- ->setParameters(array(2 => 'baz'))
+ ->setParameters(new ArrayCollection(array(new Parameter(2, 'baz'))))
->setResultCacheDriver(null)
->setResultCacheId('foo')
->setDql('foo')
@@ -129,7 +145,7 @@ public function testIterateWithDistinct()
/**
* @group DDC-1697
*/
- public function testKeyValueParameters()
+ public function testCollectionParameters()
{
$cities = array(
0 => "Paris",
@@ -142,8 +158,9 @@ public function testKeyValueParameters()
->setParameter('cities', $cities);
$parameters = $query->getParameters();
+ $parameter = $parameters->first();
- $this->assertArrayHasKey('cities', $parameters);
- $this->assertEquals($cities, $parameters['cities']);
+ $this->assertEquals('cities', $parameter->getName());
+ $this->assertEquals($cities, $parameter->getValue());
}
}
View
35 tests/Doctrine/Tests/ORM/QueryBuilderTest.php
@@ -19,8 +19,12 @@
namespace Doctrine\Tests\ORM;
+use Doctrine\Common\Collections\ArrayCollection;
+
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';
@@ -385,7 +389,9 @@ public function testSetParameter()
->where('u.id = :id')
->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()
@@ -395,9 +401,13 @@ public function testSetParameters()
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->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 @@ public function testGetParameters()
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->where('u.id = :id');
- $qb->setParameters(array('id' => 1));
- $this->assertEquals(array('id' => 1), $qb->getParameters());
+ $parameters = new ArrayCollection();
+ $parameters->add(new Parameter('id', 1));
+
+ $qb->setParameters($parameters);
+
+ $this->assertEquals($parameters, $qb->getParameters());
}
public function testGetParameter()
@@ -419,8 +433,12 @@ public function testGetParameter()
->from('Doctrine\Tests\Models\CMS\CmsUser', 'u')
->where('u.id = :id');
- $qb->setParameters(array('id' => 1));
- $this->assertEquals(1, $qb->getParameter('id'));
+ $parameters = new ArrayCollection();
+ $parameters->add(new Parameter('id', 1));
+
+ $qb->setParameters($parameters);
+
+ $this->assertEquals($parameters->first(), $qb->getParameter('id'));
}
public function testMultipleWhere()
@@ -536,6 +554,7 @@ public function testMultipleIsolatedQueryConstruction()
$qb->setParameter('name', 'romanb');
$q1 = $qb->getQuery();
+
$this->assertEquals('SELECT u FROM Doctrine\Tests\Models\CMS\CmsUser u WHERE u.name = :name', $q1->getDql());
$this->assertEquals(1, count($q1->getParameters()));
Something went wrong with that request. Please try again.