Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DDC-2794] Arbitrary Join count walkers solution #1092

Merged
merged 7 commits into from
Sep 22, 2014
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
27 changes: 10 additions & 17 deletions lib/Doctrine/ORM/Tools/Pagination/CountWalker.php
Original file line number Diff line number Diff line change
Expand Up @@ -50,31 +50,24 @@ public function walkSelectStatement(SelectStatement $AST)
throw new \RuntimeException('Cannot count query that uses a HAVING clause. Use the output walkers for pagination');
}

$rootComponents = array();
foreach ($this->_getQueryComponents() as $dqlAlias => $qComp) {
$isParent = array_key_exists('parent', $qComp)
&& $qComp['parent'] === null
&& $qComp['nestingLevel'] == 0
;
if ($isParent) {
$rootComponents[] = array($dqlAlias => $qComp);
}
}
if (count($rootComponents) > 1) {
$queryComponents = $this->_getQueryComponents();
// Get the root entity and alias from the AST fromClause
$from = $AST->fromClause->identificationVariableDeclarations;
if (count($from) > 1) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

empty newline before this line

throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction");
}
$root = reset($rootComponents);
$parentName = key($root);
$parent = current($root);
$identifierFieldName = $parent['metadata']->getSingleIdentifierFieldName();

$rootAlias = $from[0]->rangeVariableDeclaration->aliasIdentificationVariable;
$rootClass = $queryComponents[$rootAlias]['metadata'];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

align = signs

$identifierFieldName = $rootClass->getSingleIdentifierFieldName();

$pathType = PathExpression::TYPE_STATE_FIELD;
if (isset($parent['metadata']->associationMappings[$identifierFieldName])) {
if (isset($rootClass->associationMappings[$identifierFieldName])) {
$pathType = PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION;
}

$pathExpression = new PathExpression(
PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $parentName,
PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $rootAlias,
$identifierFieldName
);
$pathExpression->type = $pathType;
Expand Down
25 changes: 11 additions & 14 deletions lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php
Original file line number Diff line number Diff line change
Expand Up @@ -60,37 +60,34 @@ class LimitSubqueryWalker extends TreeWalkerAdapter
*/
public function walkSelectStatement(SelectStatement $AST)
{
$parent = null;
$parentName = null;
$queryComponents = $this->_getQueryComponents();
// Get the root entity and alias from the AST fromClause
$from = $AST->fromClause->identificationVariableDeclarations;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Align = signs

$rootAlias = $from[0]->rangeVariableDeclaration->aliasIdentificationVariable;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The AST doesn't ensure that the array is indexed starting from 0, so this should probably be reset($from)

$rootClass = $queryComponents[$rootAlias]['metadata'];
$selectExpressions = array();

foreach ($this->_getQueryComponents() as $dqlAlias => $qComp) {
foreach ($queryComponents as $dqlAlias => $qComp) {
// Preserve mixed data in query for ordering.
if (isset($qComp['resultVariable'])) {
$selectExpressions[] = new SelectExpression($qComp['resultVariable'], $dqlAlias);
continue;
}

if ($qComp['parent'] === null && $qComp['nestingLevel'] == 0) {
$parent = $qComp;
$parentName = $dqlAlias;
continue;
}
}

$identifier = $parent['metadata']->getSingleIdentifierFieldName();
if (isset($parent['metadata']->associationMappings[$identifier])) {
$identifier = $rootClass->getSingleIdentifierFieldName();
if (isset($rootClass->associationMappings[$identifier])) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

empty newline before this line

throw new \RuntimeException("Paginating an entity with foreign key as identifier only works when using the Output Walkers. Call Paginator#setUseOutputWalkers(true) before iterating the paginator.");
}

$this->_getQuery()->setHint(
self::IDENTIFIER_TYPE,
Type::getType($parent['metadata']->getTypeOfField($identifier))
Type::getType($rootClass->getTypeOfField($identifier))
);

$pathExpression = new PathExpression(
PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION,
$parentName,
$rootAlias,
$identifier
);
$pathExpression->type = PathExpression::TYPE_STATE_FIELD;
Expand Down
27 changes: 10 additions & 17 deletions lib/Doctrine/ORM/Tools/Pagination/WhereInWalker.php
Original file line number Diff line number Diff line change
Expand Up @@ -71,30 +71,23 @@ class WhereInWalker extends TreeWalkerAdapter
*/
public function walkSelectStatement(SelectStatement $AST)
{
$rootComponents = array();
foreach ($this->_getQueryComponents() as $dqlAlias => $qComp) {
$isParent = array_key_exists('parent', $qComp)
&& $qComp['parent'] === null
&& $qComp['nestingLevel'] == 0
;
if ($isParent) {
$rootComponents[] = array($dqlAlias => $qComp);
}
}
if (count($rootComponents) > 1) {
$queryComponents = $this->_getQueryComponents();
// Get the root entity and alias from the AST fromClause
$from = $AST->fromClause->identificationVariableDeclarations;
if (count($from) > 1) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

empty newline before this line

throw new \RuntimeException("Cannot count query which selects two FROM components, cannot make distinction");
}
$root = reset($rootComponents);
$parentName = key($root);
$parent = current($root);
$identifierFieldName = $parent['metadata']->getSingleIdentifierFieldName();

$rootAlias = $from[0]->rangeVariableDeclaration->aliasIdentificationVariable;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Align = signs.

$rootClass = $queryComponents[$rootAlias]['metadata'];
$identifierFieldName = $rootClass->getSingleIdentifierFieldName();

$pathType = PathExpression::TYPE_STATE_FIELD;
if (isset($parent['metadata']->associationMappings[$identifierFieldName])) {
if (isset($rootClass->associationMappings[$identifierFieldName])) {
$pathType = PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION;
}

$pathExpression = new PathExpression(PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $parentName, $identifierFieldName);
$pathExpression = new PathExpression(PathExpression::TYPE_STATE_FIELD | PathExpression::TYPE_SINGLE_VALUED_ASSOCIATION, $rootAlias, $identifierFieldName);
$pathExpression->type = $pathType;

$count = $this->_getQuery()->getHint(self::HINT_PAGINATOR_ID_COUNT);
Expand Down
16 changes: 16 additions & 0 deletions tests/Doctrine/Tests/ORM/Tools/Pagination/CountWalkerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -90,5 +90,21 @@ public function testCountQuery_HavingException()

$query->getSql();
}

/**
* Arbitrary Join
*/
public function testCountQueryWithArbitraryJoin()
{
$query = $this->entityManager->createQuery(
'SELECT p FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost p LEFT JOIN Doctrine\Tests\ORM\Tools\Pagination\Category c WITH p.category = c');
$query->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\CountWalker'));
$query->setHint(CountWalker::HINT_DISTINCT, true);
$query->setFirstResult(null)->setMaxResults(null);

$this->assertEquals(
"SELECT count(DISTINCT b0_.id) AS sclr_0 FROM BlogPost b0_ LEFT JOIN Category c1_ ON (b0_.category_id = c1_.id)", $query->getSql()
);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -67,5 +67,36 @@ public function testLimitSubqueryWithSortOnAssociation()
$limitQuery->getSql()
);
}

/**
* Arbitrary Join
*/
public function testLimitSubqueryWithArbitraryJoin()
{
$dql = 'SELECT p, c FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost p JOIN Doctrine\Tests\ORM\Tools\Pagination\Category c WITH p.category = c';
$query = $this->entityManager->createQuery($dql);
$limitQuery = clone $query;

$limitQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker'));

$this->assertEquals(
"SELECT DISTINCT m0_.id AS id_0 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON (m0_.category_id = c1_.id)",
$limitQuery->getSql()
);
}

public function testLimitSubqueryWithSortWithArbitraryJoin()
{
$dql = 'SELECT p, c FROM Doctrine\Tests\ORM\Tools\Pagination\MyBlogPost p JOIN Doctrine\Tests\ORM\Tools\Pagination\Category c WITH p.category = c ORDER BY p.title';
$query = $this->entityManager->createQuery($dql);
$limitQuery = clone $query;

$limitQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\LimitSubqueryWalker'));

$this->assertEquals(
"SELECT DISTINCT m0_.id AS id_0, m0_.title AS title_1 FROM MyBlogPost m0_ INNER JOIN Category c1_ ON (m0_.category_id = c1_.id) ORDER BY m0_.title ASC",
$limitQuery->getSql()
);
}
}

29 changes: 29 additions & 0 deletions tests/Doctrine/Tests/ORM/Tools/Pagination/WhereInWalkerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -121,5 +121,34 @@ public function testWhereInQuery_WhereNot()
"SELECT u0_.id AS id_0, g1_.id AS id_1 FROM User u0_ INNER JOIN user_group u2_ ON u0_.id = u2_.user_id INNER JOIN groups g1_ ON g1_.id = u2_.group_id WHERE (NOT 1 = 2) AND u0_.id IN (?)", $whereInQuery->getSql()
);
}

/**
* Arbitrary Join
*/
public function testWhereInQueryWithArbitraryJoin_NoWhere()
{
$whereInQuery = $this->entityManager->createQuery(
'SELECT p FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost p JOIN Doctrine\Tests\ORM\Tools\Pagination\Category c WITH p.category = c'
);
$whereInQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\WhereInWalker'));
$whereInQuery->setHint(WhereInWalker::HINT_PAGINATOR_ID_COUNT, 10);

$this->assertEquals(
"SELECT b0_.id AS id_0, b0_.author_id AS author_id_1, b0_.category_id AS category_id_2 FROM BlogPost b0_ INNER JOIN Category c1_ ON (b0_.category_id = c1_.id) WHERE b0_.id IN (?)", $whereInQuery->getSql()
);
}

public function testWhereInQueryWithArbitraryJoin_SingleWhere()
{
$whereInQuery = $this->entityManager->createQuery(
'SELECT p FROM Doctrine\Tests\ORM\Tools\Pagination\BlogPost p JOIN Doctrine\Tests\ORM\Tools\Pagination\Category c WITH p.category = c WHERE 1 = 1'
);
$whereInQuery->setHint(Query::HINT_CUSTOM_TREE_WALKERS, array('Doctrine\ORM\Tools\Pagination\WhereInWalker'));
$whereInQuery->setHint(WhereInWalker::HINT_PAGINATOR_ID_COUNT, 10);

$this->assertEquals(
"SELECT b0_.id AS id_0, b0_.author_id AS author_id_1, b0_.category_id AS category_id_2 FROM BlogPost b0_ INNER JOIN Category c1_ ON (b0_.category_id = c1_.id) WHERE 1 = 1 AND b0_.id IN (?)", $whereInQuery->getSql()
);
}
}