diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 3d3fb5f5e..e5b9f69ca 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -125,6 +125,11 @@ class Parser */ private $_customOutputWalker; + /** + * @var array + */ + private $_identVariableExpressions = array(); + /** * Creates a new query parser object. * @@ -297,6 +302,21 @@ class Parser } } + // Fix order of identification variables. + // They have to appear in the select clause in the same order as the + // declarations (from ... x join ... y join ... z ...) appear in the query + // as the hydration process relies on that order for proper operation. + if ( count($this->_identVariableExpressions) > 1) { + foreach ($this->_queryComponents as $dqlAlias => $qComp) { + if (isset($this->_identVariableExpressions[$dqlAlias])) { + $expr = $this->_identVariableExpressions[$dqlAlias]; + $key = array_search($expr, $AST->selectClause->selectExpressions); + unset($AST->selectClause->selectExpressions[$key]); + $AST->selectClause->selectExpressions[] = $expr; + } + } + } + if ($this->_customOutputWalker) { $outputWalker = new $this->_customOutputWalker( $this->_query, $this->_parserResult, $this->_queryComponents @@ -1628,6 +1648,7 @@ class Parser public function SelectExpression() { $expression = null; + $identVariable = null; $fieldAliasIdentificationVariable = null; $peek = $this->_lexer->glimpse(); @@ -1639,7 +1660,7 @@ class Parser $expression = $this->ScalarExpression(); } else { $supportsAlias = false; - $expression = $this->IdentificationVariable(); + $expression = $identVariable = $this->IdentificationVariable(); } } else if ($this->_lexer->lookahead['value'] == '(') { if ($peek['type'] == Lexer::T_SELECT) { @@ -1666,6 +1687,7 @@ class Parser } else if ($this->_lexer->lookahead['type'] == Lexer::T_PARTIAL) { $supportsAlias = false; $expression = $this->PartialObjectExpression(); + $identVariable = $expression->identificationVariable; } else if ($this->_lexer->lookahead['type'] == Lexer::T_INTEGER || $this->_lexer->lookahead['type'] == Lexer::T_FLOAT) { // Shortcut: ScalarExpression => SimpleArithmeticExpression @@ -1694,7 +1716,11 @@ class Parser } } - return new AST\SelectExpression($expression, $fieldAliasIdentificationVariable); + $expr = new AST\SelectExpression($expression, $fieldAliasIdentificationVariable); + if (!$supportsAlias) { + $this->_identVariableExpressions[$identVariable] = $expr; + } + return $expr; } /** diff --git a/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC736Test.php b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC736Test.php new file mode 100644 index 000000000..0c8b764a2 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/Ticket/DDC736Test.php @@ -0,0 +1,46 @@ +useModelSet('ecommerce'); + parent::setUp(); + } + + /** + * @group DDC-736 + */ + public function testFetchJoinInitializesPreviouslyUninitializedCollectionOfManagedEntity() + { + $cust = new ECommerceCustomer; + $cust->setName('roman'); + + $cart = new ECommerceCart; + $cart->setPayment('cash'); + $cart->setCustomer($cust); + + $this->_em->persist($cust); + $this->_em->persist($cart); + $this->_em->flush(); + $this->_em->clear(); + + $result = $this->_em->createQuery("select c, c.name, ca, ca.payment from Doctrine\Tests\Models\ECommerce\ECommerceCart ca join ca.customer c") + ->getSingleResult(/*\Doctrine\ORM\Query::HYDRATE_ARRAY*/); + + $cart2 = $result[0]; + unset($result[0]); + + $this->assertInstanceOf('Doctrine\Tests\Models\ECommerce\ECommerceCart', $cart2); + $this->assertNotInstanceOf('Doctrine\ORM\Proxy\Proxy', $cart2->getCustomer()); + $this->assertInstanceOf('Doctrine\Tests\Models\ECommerce\ECommerceCustomer', $cart2->getCustomer()); + $this->assertEquals(array('name' => 'roman', 'payment' => 'cash'), $result); + } +}