diff --git a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php index 8631dc297..b2c6b9f5e 100644 --- a/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php +++ b/lib/Doctrine/ORM/Internal/Hydration/AbstractHydrator.php @@ -235,7 +235,7 @@ abstract class AbstractHydrator continue 2; } - if (isset ($this->_rsm->newObjectMappings[$key])) { + if (isset($this->_rsm->newObjectMappings[$key])) { $cache[$key]['isNewObjectParameter'] = true; $cache[$key]['argIndex'] = $this->_rsm->newObjectMappings[$key]['argIndex']; $cache[$key]['objIndex'] = $this->_rsm->newObjectMappings[$key]['objIndex']; @@ -243,7 +243,7 @@ abstract class AbstractHydrator } } - if (isset ($cache[$key]['isNewObjectParameter'])) { + if (isset($cache[$key]['isNewObjectParameter'])) { $argIndex = $cache[$key]['argIndex']; $objIndex = $cache[$key]['objIndex']; $rowData['newObjects'][$objIndex]['class'] = $cache[$key]['class']; diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 94a435a37..49cc7f6b5 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -1630,7 +1630,7 @@ class Parser } /** - * NewObjectExpression ::= "NEW" IdentificationVariable "(" SelectExpression {"," SelectExpression}* ")" + * NewObjectExpression ::= "NEW" IdentificationVariable "(" NewObjectArg {"," NewObjectArg}* ")" * @return \Doctrine\ORM\Query\AST\NewObjectExpression */ public function NewObjectExpression() @@ -1643,19 +1643,19 @@ class Parser if ( ! class_exists($className, true)) { $this->semanticalError("Class '$className' is not defined.", $this->_lexer->token); } - + $class = new \ReflectionClass($className); if($class->getConstructor() === null) { $this->semanticalError("Class '$className' has not a valid contructor.", $this->_lexer->token); } $this->match(Lexer::T_OPEN_PARENTHESIS); - - $args[] = $this->SelectExpression(); + + $args[] = $this->NewObjectArg(); while ($this->_lexer->isNextToken(Lexer::T_COMMA)) { $this->match(Lexer::T_COMMA); - $args[] = $this->SelectExpression(); + $args[] = $this->NewObjectArg(); } $this->match(Lexer::T_CLOSE_PARENTHESIS); @@ -1664,10 +1664,41 @@ class Parser $this->semanticalError("Number of arguments does not match definition.", $this->_lexer->token); } - $expression = new AST\NewObjectExpression($className, $args); + return new AST\NewObjectExpression($className, $args);; + } - // @TODO : Defer NewObjectExpression validation ? - return $expression; + /** + * @return \Doctrine\ORM\Query\AST\SimpleSelectExpression + */ + public function NewObjectArg() + { + $peek = $this->_lexer->glimpse(); + + switch (true) { + case ($peek['type'] === Lexer::T_DOT): + $expression = $this->StateFieldPathExpression(); + + return new AST\SimpleSelectExpression($expression); + + case ($peek['type'] !== Lexer::T_OPEN_PARENTHESIS): + $expression = $this->IdentificationVariable(); + + return new AST\SimpleSelectExpression($expression); + + case ($this->_isFunction()): + // SUM(u.id) + COUNT(u.id) + if ($this->_isMathOperator($this->_peekBeyondClosingParenthesis())) { + return new AST\SimpleSelectExpression($this->ScalarExpression()); + } + // COUNT(u.id) + if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) { + return new AST\SimpleSelectExpression($this->AggregateExpression()); + } + // IDENTITY(u) + return new AST\SimpleSelectExpression($this->FunctionDeclaration()); + } + + $this->semanticalError("Unsupported expression"); } /** diff --git a/lib/Doctrine/ORM/Query/SqlWalker.php b/lib/Doctrine/ORM/Query/SqlWalker.php index 2fff87ff0..5ffdccc16 100644 --- a/lib/Doctrine/ORM/Query/SqlWalker.php +++ b/lib/Doctrine/ORM/Query/SqlWalker.php @@ -1229,25 +1229,28 @@ class SqlWalker implements TreeWalker break; case ($expr instanceof AST\NewObjectExpression): - $sqlSelectExpressions = array(); - $this->_rsm->newObjectMappings['className'] = $expr->className; - $sqlSelectExpressions = array(); - $objIndex = $this->newObjectCounter ++; - foreach ($expr->args as $key => $e) { + $sqlSelectExpressions = array(); + $objIndex = $this->newObjectCounter ++; + foreach ($expr->args as $argIndex => $e) { + + $resultAlias = $this->scalarResultCounter++; + $columnAlias = $this->getSQLColumnAlias('sclr') . $resultAlias; $resultAliasMap = $this->scalarResultAliasMap; - $sqlSelectExpressions[] = $this->walkSelectExpression($e); - $scalarResultAliasMap = array_diff($this->scalarResultAliasMap, $resultAliasMap); - foreach ($scalarResultAliasMap as $aliasMap) { - $this->_rsm->newObjectMappings[$aliasMap] = array( - 'className' => $expr->className, - 'objIndex' => $objIndex, - 'argIndex' => $key - ); - } + $sqlSelectExpressions[] = $this->walkSimpleSelectExpression($e) . ' AS ' . $columnAlias; + + + $this->scalarResultAliasMap[$resultAlias] = $columnAlias; + $this->rsm->addScalarResult($columnAlias, $resultAlias, 'string'); + + $this->rsm->newObjectMappings[$columnAlias] = array( + 'className' => $expr->className, + 'objIndex' => $objIndex, + 'argIndex' => $argIndex + ); } - $sql .= implode(', ', $sqlSelectExpressions); + $sql .= implode(',', $sqlSelectExpressions); break; default: diff --git a/tests/Doctrine/Tests/Models/CMS/CmsAddressDTO.php b/tests/Doctrine/Tests/Models/CMS/CmsAddressDTO.php new file mode 100644 index 000000000..2a4f220f8 --- /dev/null +++ b/tests/Doctrine/Tests/Models/CMS/CmsAddressDTO.php @@ -0,0 +1,17 @@ +country = $country; + $this->city = $city; + $this->zip = $zip; + } +} \ No newline at end of file diff --git a/tests/Doctrine/Tests/Models/CMS/CmsUserDTO.php b/tests/Doctrine/Tests/Models/CMS/CmsUserDTO.php index 78398f863..974c69e7d 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsUserDTO.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsUserDTO.php @@ -6,12 +6,12 @@ class CmsUserDTO { public $name; public $email; - public $city; + public $address; - public function __construct($name = null, $email = null, $city = null) + public function __construct($name = null, $email = null, $address = null) { - $this->name = $name; - $this->email = $email; - $this->city = $city; + $this->name = $name; + $this->email = $email; + $this->address = $address; } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php new file mode 100644 index 000000000..6291427c0 --- /dev/null +++ b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php @@ -0,0 +1,158 @@ +useModelSet('cms'); + parent::setUp(); + + $this->loadFixtures(); + } + + private function loadFixtures() + { + $u1 = new CmsUser; + $u2 = new CmsUser; + $u3 = new CmsUser; + + $u1->setEmail(new CmsEmail()); + $u1->setAddress(new CmsAddress()); + + $u2->setEmail(new CmsEmail()); + $u2->setAddress(new CmsAddress()); + + $u3->setEmail(new CmsEmail()); + $u3->setAddress(new CmsAddress()); + + $u1->name = 'Test 1'; + $u1->username = '1test'; + $u1->status = 'developer'; + $u1->email->email = 'email@test1.com'; + $u1->address->zip = '111111111'; + $u1->address->city = 'Some City 1'; + $u1->address->country = 'Some Country 2'; + + $u2->name = 'Test 2'; + $u2->username = '2test'; + $u2->status = 'developer'; + $u2->email->email = 'email@test2.com'; + $u2->address->zip = '222222222'; + $u2->address->city = 'Some City 2'; + $u2->address->country = 'Some Country 2'; + + $u3->name = 'Test 3'; + $u3->username = '3test'; + $u3->status = 'developer'; + $u3->email->email = 'email@test3.com'; + $u3->address->zip = '33333333'; + $u3->address->city = 'Some City 3'; + $u3->address->country = 'Some Country 3'; + + $this->_em->persist($u1); + $this->_em->persist($u2); + $this->_em->persist($u3); + + $this->_em->flush(); + $this->_em->clear(); + + $this->fixtures = array($u1, $u2, $u3); + } + + public function testShouldSupportsBasicUsage() + { + $dql = " + SELECT + new Doctrine\Tests\Models\CMS\CmsUserDTO( + u.name, + e.email, + a.city + ) + FROM + Doctrine\Tests\Models\CMS\CmsUser u + JOIN + u.email e + JOIN + u.address a + ORDER + BY u.name"; + + $query = $this->_em->createQuery($dql); + $result = $query->getResult(); + + $this->assertCount(3, $result); + + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[0]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[1]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[2]); + + $this->assertEquals($this->fixtures[0]->name, $result[0]->name); + $this->assertEquals($this->fixtures[1]->name, $result[1]->name); + $this->assertEquals($this->fixtures[2]->name, $result[2]->name); + + $this->assertEquals($this->fixtures[0]->email->email, $result[0]->email); + $this->assertEquals($this->fixtures[1]->email->email, $result[1]->email); + $this->assertEquals($this->fixtures[2]->email->email, $result[2]->email); + + $this->assertEquals($this->fixtures[0]->address->city, $result[0]->address); + $this->assertEquals($this->fixtures[1]->address->city, $result[1]->address); + $this->assertEquals($this->fixtures[2]->address->city, $result[2]->address); + } + + public function testShouldSupportNestedOperators() + { + $this->markTestIncomplete(); + $dql = " + SELECT + new Doctrine\Tests\Models\CMS\CmsUserDTO( + u.name, + e.email, + new Doctrine\Tests\Models\CMS\CmsUserDTO(a.country, a.city, a.zip) + ) + FROM + Doctrine\Tests\Models\CMS\CmsUser u + JOIN + u.email e + JOIN + u.address a + ORDER + BY u.name"; + + $query = $this->_em->createQuery($dql); + $result = $query->getResult(); + + $this->assertCount(3, $result); + + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[0]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[1]); + $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUserDTO', $result[2]); + + } + + public function testShouldSupportAggregateFunctions() + { + $this->markTestIncomplete(); + } +} \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Functional/QueryTest.php b/tests/Doctrine/Tests/ORM/Functional/QueryTest.php index 780c7e8a2..906b5e68f 100644 --- a/tests/Doctrine/Tests/ORM/Functional/QueryTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/QueryTest.php @@ -780,65 +780,4 @@ class QueryTest extends \Doctrine\Tests\OrmFunctionalTestCase $this->assertInstanceOf('Doctrine\Tests\Models\CMS\CmsUser', $users[2]); $this->assertNull($users[3]); } - - /** - * @group DDC-1574 - */ - public function testSupportsNewOperator() - { - $u1 = new CmsUser; - $u2 = new CmsUser; - $u3 = new CmsUser; - - $u1->setEmail(new \Doctrine\Tests\Models\CMS\CmsEmail()); - $u1->setAddress(new \Doctrine\Tests\Models\CMS\CmsAddress()); - - $u2->setEmail(new \Doctrine\Tests\Models\CMS\CmsEmail()); - $u2->setAddress(new \Doctrine\Tests\Models\CMS\CmsAddress()); - - $u3->setEmail(new \Doctrine\Tests\Models\CMS\CmsEmail()); - $u3->setAddress(new \Doctrine\Tests\Models\CMS\CmsAddress()); - - $u1->name = 'Test 1'; - $u1->username = '1test'; - $u1->status = 'developer'; - $u1->email->email = 'email@test1.com'; - $u1->address->zip = '111111111'; - $u1->address->city = 'Some City 1'; - $u1->address->country = 'Some Country 2'; - - $u2->name = 'Test 2'; - $u2->username = '2test'; - $u2->status = 'developer'; - $u2->email->email = 'email@test2.com'; - $u2->address->zip = '111111111'; - $u2->address->city = 'Some City 2'; - $u2->address->country = 'Some Country 2'; - - $u3->name = 'Fabio Silva'; - $u3->username = 'FabioBatSilva'; - $u3->status = 'developer'; - $u3->email->email = 'fabio.bat.silva@gmail.com'; - $u3->address->zip = '33333333'; - $u3->address->city = 'Some City 3'; - $u3->address->country = 'Some Country 3'; - - $this->_em->persist($u1); - $this->_em->persist($u2); - $this->_em->persist($u3); - - $this->_em->flush(); - $this->_em->clear(); - - $query = $this->_em->createQuery("SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.city) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a ORDER BY u.name"); - $result = $query->getResult(); - - $this->assertCount(3, $result); - - $this->markTestIncomplete(); - - $this->assertTrue($result[0] instanceof \Doctrine\Tests\Models\CMS\CmsUserDTO); - $this->assertTrue($result[1] instanceof \Doctrine\Tests\Models\CMS\CmsUserDTO); - $this->assertTrue($result[2] instanceof \Doctrine\Tests\Models\CMS\CmsUserDTO); - } -} \ No newline at end of file +} diff --git a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php index 1e812f366..b9ec036ee 100644 --- a/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php +++ b/tests/Doctrine/Tests/ORM/Query/SelectSqlGenerationTest.php @@ -1562,7 +1562,7 @@ class SelectSqlGenerationTest extends \Doctrine\Tests\OrmTestCase { $this->assertSqlGeneration( 'SELECT new Doctrine\Tests\Models\CMS\CmsUserDTO(u.name, e.email, a.city) FROM Doctrine\Tests\Models\CMS\CmsUser u JOIN u.email e JOIN u.address a', - 'SELECT c0_.name AS name0, c1_.email AS email1, c2_.city AS city2 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id' + 'SELECT c0_.name AS sclr01, c1_.email AS sclr12, c2_.city AS sclr23 FROM cms_users c0_ INNER JOIN cms_emails c1_ ON c0_.email_id = c1_.id INNER JOIN cms_addresses c2_ ON c0_.id = c2_.user_id' ); }