From 905e05cd361e9702325e3ea26224b95ac4f7d2e0 Mon Sep 17 00:00:00 2001 From: Guilherme Blanco Date: Wed, 11 May 2011 21:40:27 -0300 Subject: [PATCH] [DDC-1067][DDC-1145] Fixed bug with multiple froms and inclusion of joins. Added support for index by in QueryBuilder. This break BC only if users are using base support (->add). --- lib/Doctrine/ORM/Query/Expr/Join.php | 15 ++-- lib/Doctrine/ORM/QueryBuilder.php | 70 +++++++++++++++---- tests/Doctrine/Tests/ORM/QueryBuilderTest.php | 44 ++++++++++++ 3 files changed, 108 insertions(+), 21 deletions(-) diff --git a/lib/Doctrine/ORM/Query/Expr/Join.php b/lib/Doctrine/ORM/Query/Expr/Join.php index a1a3955c6..54ad6ead7 100644 --- a/lib/Doctrine/ORM/Query/Expr/Join.php +++ b/lib/Doctrine/ORM/Query/Expr/Join.php @@ -45,20 +45,23 @@ class Join private $_alias; private $_conditionType; private $_condition; + private $_indexBy; - public function __construct($joinType, $join, $alias = null, $conditionType = null, $condition = null) + public function __construct($joinType, $join, $alias = null, $conditionType = null, $condition = null, $indexBy = null) { - $this->_joinType = $joinType; - $this->_join = $join; - $this->_alias = $alias; + $this->_joinType = $joinType; + $this->_join = $join; + $this->_alias = $alias; $this->_conditionType = $conditionType; - $this->_condition = $condition; + $this->_condition = $condition; + $this->_indexBy = $indexBy; } public function __toString() { return strtoupper($this->_joinType) . ' JOIN ' . $this->_join . ($this->_alias ? ' ' . $this->_alias : '') - . ($this->_condition ? ' ' . strtoupper($this->_conditionType) . ' ' . $this->_condition : ''); + . ($this->_condition ? ' ' . strtoupper($this->_conditionType) . ' ' . $this->_condition : '') + . ($this->_indexBy ? ' INDEX BY ' . $this->_indexBy : ''); } } \ No newline at end of file diff --git a/lib/Doctrine/ORM/QueryBuilder.php b/lib/Doctrine/ORM/QueryBuilder.php index eed9acf9d..74782702d 100644 --- a/lib/Doctrine/ORM/QueryBuilder.php +++ b/lib/Doctrine/ORM/QueryBuilder.php @@ -282,8 +282,13 @@ class QueryBuilder */ public function setParameters(array $params, array $types = array()) { - $this->_paramTypes = $types; - $this->_params = $params; + foreach ($params as $key => $value) { + if (isset($types[$key])) { + $this->setParameter($key, $value, $types[$key]); + } else { + $this->setParameter($key, $value); + } + } return $this; } @@ -370,7 +375,13 @@ class QueryBuilder $isMultiple = is_array($this->_dqlParts[$dqlPartName]); if ($append && $isMultiple) { - $this->_dqlParts[$dqlPartName][] = $dqlPart; + if (is_array($dqlPart)) { + $key = key($dqlPart); + + $this->_dqlParts[$dqlPartName][$key][] = $dqlPart[$key]; + } else { + $this->_dqlParts[$dqlPartName][] = $dqlPart; + } } else { $this->_dqlParts[$dqlPartName] = ($isMultiple) ? array($dqlPart) : $dqlPart; } @@ -523,11 +534,12 @@ class QueryBuilder * @param string $alias The alias of the join * @param string $conditionType The condition type constant. Either ON or WITH. * @param string $condition The condition for the join + * @param string $indexBy The index for the join * @return QueryBuilder This QueryBuilder instance. */ - public function join($join, $alias, $conditionType = null, $condition = null) + public function join($join, $alias, $conditionType = null, $condition = null, $indexBy = null) { - return $this->innerJoin($join, $alias, $conditionType, $condition); + return $this->innerJoin($join, $alias, $conditionType, $condition, $indexBy); } /** @@ -547,12 +559,15 @@ class QueryBuilder * @param string $alias The alias of the join * @param string $conditionType The condition type constant. Either ON or WITH. * @param string $condition The condition for the join + * @param string $indexBy The index for the join * @return QueryBuilder This QueryBuilder instance. */ - public function innerJoin($join, $alias, $conditionType = null, $condition = null) + public function innerJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null) { - return $this->add('join', new Expr\Join( - Expr\Join::INNER_JOIN, $join, $alias, $conditionType, $condition + $rootAlias = substr($join, 0, strpos($join, '.')); + + return $this->add('join', array( + $rootAlias => new Expr\Join(Expr\Join::INNER_JOIN, $join, $alias, $conditionType, $condition, $indexBy) ), true); } @@ -574,12 +589,15 @@ class QueryBuilder * @param string $alias The alias of the join * @param string $conditionType The condition type constant. Either ON or WITH. * @param string $condition The condition for the join + * @param string $indexBy The index for the join * @return QueryBuilder This QueryBuilder instance. */ - public function leftJoin($join, $alias, $conditionType = null, $condition = null) + public function leftJoin($join, $alias, $conditionType = null, $condition = null, $indexBy = null) { - return $this->add('join', new Expr\Join( - Expr\Join::LEFT_JOIN, $join, $alias, $conditionType, $condition + $rootAlias = substr($join, 0, strpos($join, '.')); + + return $this->add('join', array( + $rootAlias => new Expr\Join(Expr\Join::LEFT_JOIN, $join, $alias, $conditionType, $condition, $indexBy) ), true); } @@ -865,14 +883,36 @@ class QueryBuilder private function _getDQLForSelect() { - return 'SELECT' - . $this->_getReducedDQLQueryPart('select', array('pre' => ' ', 'separator' => ', ')) - . $this->_getReducedDQLQueryPart('from', array('pre' => ' FROM ', 'separator' => ', ')) - . $this->_getReducedDQLQueryPart('join', array('pre' => ' ', 'separator' => ' ')) + $dql = 'SELECT' . $this->_getReducedDQLQueryPart('select', array('pre' => ' ', 'separator' => ', ')); + + $fromParts = $this->getDQLPart('from'); + $joinParts = $this->getDQLPart('join'); + $fromClauses = array(); + + // Loop through all FROM clauses + if ( ! empty($fromParts)) { + $dql .= ' FROM '; + + foreach ($fromParts as $from) { + $fromClause = (string) $from; + + if (isset($joinParts[$from->getAlias()])) { + foreach ($joinParts[$from->getAlias()] as $join) { + $fromClause .= ' ' . ((string) $join); + } + } + + $fromClauses[] = $fromClause; + } + } + + $dql .= implode(', ', $fromClauses) . $this->_getReducedDQLQueryPart('where', array('pre' => ' WHERE ')) . $this->_getReducedDQLQueryPart('groupBy', array('pre' => ' GROUP BY ', 'separator' => ', ')) . $this->_getReducedDQLQueryPart('having', array('pre' => ' HAVING ')) . $this->_getReducedDQLQueryPart('orderBy', array('pre' => ' ORDER BY ', 'separator' => ', ')); + + return $dql; } private function _getReducedDQLQueryPart($queryPartName, $options = array()) diff --git a/tests/Doctrine/Tests/ORM/QueryBuilderTest.php b/tests/Doctrine/Tests/ORM/QueryBuilderTest.php index bc885afa5..119a40dcd 100644 --- a/tests/Doctrine/Tests/ORM/QueryBuilderTest.php +++ b/tests/Doctrine/Tests/ORM/QueryBuilderTest.php @@ -144,6 +144,50 @@ class QueryBuilderTest extends \Doctrine\Tests\OrmTestCase $this->assertValidQueryBuilder($qb, 'SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.articles a'); } + public function testLeftJoinWithIndexBy() + { + $qb = $this->_em->createQueryBuilder() + ->select('u', 'a') + ->from('Doctrine\Tests\Models\CMS\CmsUser', 'u') + ->leftJoin('u.articles', 'a', null, null, 'a.name'); + + $this->assertValidQueryBuilder($qb, 'SELECT u, a FROM Doctrine\Tests\Models\CMS\CmsUser u LEFT JOIN u.articles a INDEX BY a.name'); + } + + public function testMultipleFrom() + { + $qb = $this->_em->createQueryBuilder() + ->select('u', 'g') + ->from('Doctrine\Tests\Models\CMS\CmsUser', 'u') + ->from('Doctrine\Tests\Models\CMS\CmsGroup', 'g'); + + $this->assertValidQueryBuilder($qb, 'SELECT u, g FROM Doctrine\Tests\Models\CMS\CmsUser u, Doctrine\Tests\Models\CMS\CmsGroup g'); + } + + public function testMultipleFromWithJoin() + { + $qb = $this->_em->createQueryBuilder() + ->select('u', 'g') + ->from('Doctrine\Tests\Models\CMS\CmsUser', 'u') + ->from('Doctrine\Tests\Models\CMS\CmsGroup', 'g') + ->innerJoin('u.articles', 'a', 'ON', 'u.id = a.author_id'); + + $this->assertValidQueryBuilder($qb, 'SELECT u, g FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.articles a ON u.id = a.author_id, Doctrine\Tests\Models\CMS\CmsGroup g'); + } + + public function testMultipleFromWithMultipleJoin() + { + $qb = $this->_em->createQueryBuilder() + ->select('u', 'g') + ->from('Doctrine\Tests\Models\CMS\CmsUser', 'u') + ->from('Doctrine\Tests\Models\CMS\CmsArticle', 'a') + ->innerJoin('u.groups', 'g') + ->leftJoin('u.address', 'ad') + ->innerJoin('a.comments', 'c'); + + $this->assertValidQueryBuilder($qb, 'SELECT u, g FROM Doctrine\Tests\Models\CMS\CmsUser u INNER JOIN u.groups g LEFT JOIN u.address ad, Doctrine\Tests\Models\CMS\CmsArticle a INNER JOIN a.comments c'); + } + public function testWhere() { $qb = $this->_em->createQueryBuilder()