From b19e4a6440b0b16a4063752a2b2e5394640596dc Mon Sep 17 00:00:00 2001 From: "Fabio B. Silva" Date: Mon, 9 Jul 2012 18:56:16 -0300 Subject: [PATCH] support arithmetic expression and aggregate functions --- lib/Doctrine/ORM/Query/Parser.php | 29 ++-- .../Doctrine/Tests/Models/CMS/CmsUserDTO.php | 10 +- .../Tests/ORM/Functional/NewOperatorTest.php | 137 +++++++++++++++++- 3 files changed, 155 insertions(+), 21 deletions(-) diff --git a/lib/Doctrine/ORM/Query/Parser.php b/lib/Doctrine/ORM/Query/Parser.php index 4b577af96..376e51369 100644 --- a/lib/Doctrine/ORM/Query/Parser.php +++ b/lib/Doctrine/ORM/Query/Parser.php @@ -1683,20 +1683,25 @@ class Parser case ($this->_lexer->lookahead['type'] === Lexer::T_NEW): return $this->NewObjectExpression(); - case ($this->_isFunction()): - // SUM(u.id) + COUNT(u.id) - if ($this->_isMathOperator($this->_peekBeyondClosingParenthesis())) { - return new AST\SimpleSelectExpression($this->ScalarExpression()); + default: + if ( ! ($this->_isFunction() || $this->_isAggregateFunction($this->_lexer->lookahead))) { + $this->syntaxError(); } - // 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"); + // We may be in an ArithmeticExpression (find the matching ")" and inspect for Math operator) + $this->_lexer->peek(); // "(" + $peek = $this->_peekBeyondClosingParenthesis(); + + if ($this->_isMathOperator($peek)) { + return $this->SimpleArithmeticExpression(); + } + + if ($this->_isAggregateFunction($this->_lexer->lookahead['type'])) { + return $this->AggregateExpression(); + } + + return $this->FunctionDeclaration(); + } } /** diff --git a/tests/Doctrine/Tests/Models/CMS/CmsUserDTO.php b/tests/Doctrine/Tests/Models/CMS/CmsUserDTO.php index 974c69e7d..0a07d26c1 100644 --- a/tests/Doctrine/Tests/Models/CMS/CmsUserDTO.php +++ b/tests/Doctrine/Tests/Models/CMS/CmsUserDTO.php @@ -7,11 +7,13 @@ class CmsUserDTO public $name; public $email; public $address; + public $phonenumbers; - public function __construct($name = null, $email = null, $address = null) + public function __construct($name = null, $email = null, $address = null, $phonenumbers = null) { - $this->name = $name; - $this->email = $email; - $this->address = $address; + $this->name = $name; + $this->email = $email; + $this->address = $address; + $this->phonenumbers = $phonenumbers; } } \ No newline at end of file diff --git a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php index 76fa8656b..3d563cb22 100644 --- a/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php +++ b/tests/Doctrine/Tests/ORM/Functional/NewOperatorTest.php @@ -40,12 +40,18 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase $u1->setEmail(new CmsEmail()); $u1->setAddress(new CmsAddress()); + $u1->addPhonenumber(new CmsPhonenumber()); $u2->setEmail(new CmsEmail()); $u2->setAddress(new CmsAddress()); + $u2->addPhonenumber(new CmsPhonenumber()); + $u2->addPhonenumber(new CmsPhonenumber()); $u3->setEmail(new CmsEmail()); $u3->setAddress(new CmsAddress()); + $u3->addPhonenumber(new CmsPhonenumber()); + $u3->addPhonenumber(new CmsPhonenumber()); + $u3->addPhonenumber(new CmsPhonenumber()); $u1->name = 'Test 1'; $u1->username = '1test'; @@ -54,6 +60,7 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase $u1->address->zip = '111111111'; $u1->address->city = 'Some City 1'; $u1->address->country = 'Some Country 2'; + $u1->phonenumbers[0]->phonenumber = "(11) 1111-1111"; $u2->name = 'Test 2'; $u2->username = '2test'; @@ -62,6 +69,8 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase $u2->address->zip = '222222222'; $u2->address->city = 'Some City 2'; $u2->address->country = 'Some Country 2'; + $u2->phonenumbers[0]->phonenumber = "(22) 1111-1111"; + $u2->phonenumbers[1]->phonenumber = "(22) 2222-2222"; $u3->name = 'Test 3'; $u3->username = '3test'; @@ -70,6 +79,9 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase $u3->address->zip = '33333333'; $u3->address->city = 'Some City 3'; $u3->address->country = 'Some Country 3'; + $u3->phonenumbers[0]->phonenumber = "(33) 1111-1111"; + $u3->phonenumbers[1]->phonenumber = "(33) 2222-2222"; + $u3->phonenumbers[2]->phonenumber = "(33) 3333-3333"; $this->_em->persist($u1); $this->_em->persist($u2); @@ -96,8 +108,8 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase u.email e JOIN u.address a - ORDER - BY u.name"; + ORDER BY + u.name"; $query = $this->_em->createQuery($dql); $result = $query->getResult(); @@ -141,8 +153,8 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase u.email e JOIN u.address a - ORDER - BY u.name"; + ORDER BY + u.name"; $query = $this->_em->createQuery($dql); $result = $query->getResult(); @@ -157,6 +169,121 @@ class NewOperatorTest extends \Doctrine\Tests\OrmFunctionalTestCase public function testShouldSupportAggregateFunctions() { - $this->markTestIncomplete(); + $dql = " + SELECT + new Doctrine\Tests\Models\CMS\CmsUserDTO( + u.name, + e.email, + a.city, + COUNT(p) + ) + FROM + Doctrine\Tests\Models\CMS\CmsUser u + JOIN + u.email e + JOIN + u.address a + JOIN + u.phonenumbers p + GROUP BY + u + 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); + + $this->assertEquals( + (count($this->fixtures[0]->phonenumbers)), + $result[0]->phonenumbers + ); + + $this->assertEquals( + (count($this->fixtures[1]->phonenumbers)), + $result[1]->phonenumbers + ); + + $this->assertEquals( + (count($this->fixtures[2]->phonenumbers)), + $result[2]->phonenumbers + ); + } + + public function testShouldSupportArithmeticExpression() + { + $dql = " + SELECT + new Doctrine\Tests\Models\CMS\CmsUserDTO( + u.name, + e.email, + a.city, + COUNT(p) + u.id + ) + FROM + Doctrine\Tests\Models\CMS\CmsUser u + JOIN + u.email e + JOIN + u.address a + JOIN + u.phonenumbers p + GROUP BY + u + 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); + + $this->assertEquals( + (count($this->fixtures[0]->phonenumbers) + $this->fixtures[0]->id), + $result[0]->phonenumbers + ); + + $this->assertEquals( + (count($this->fixtures[1]->phonenumbers) + $this->fixtures[1]->id), + $result[1]->phonenumbers + ); + + $this->assertEquals( + (count($this->fixtures[2]->phonenumbers) + $this->fixtures[2]->id), + $result[2]->phonenumbers + ); } } \ No newline at end of file