Skip to content

Commit 0b38817

Browse files
committed
Merge pull request #4 from vincentchalamon/filter_nested
Fix from GitHub comments
2 parents 2cc6f44 + d067ebd commit 0b38817

File tree

9 files changed

+156
-315
lines changed

9 files changed

+156
-315
lines changed

Doctrine/Orm/Extension/PaginationExtension.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
use Dunglas\ApiBundle\Api\ResourceInterface;
1818
use Dunglas\ApiBundle\Doctrine\Orm\Paginator;
1919
use Dunglas\ApiBundle\Doctrine\Orm\QueryResultExtensionInterface;
20-
use Dunglas\ApiBundle\Doctrine\Orm\Util\QueryUtils;
20+
use Dunglas\ApiBundle\Doctrine\Orm\Util\QueryChecker;
2121
use Symfony\Component\HttpFoundation\Request;
2222
use Symfony\Component\HttpFoundation\RequestStack;
2323

@@ -155,7 +155,7 @@ private function useOutputWalkers(QueryBuilder $queryBuilder)
155155
*
156156
* @see https://github.com/doctrine/doctrine2/blob/900b55d16afdcdeb5100d435a7166d3a425b9873/lib/Doctrine/ORM/Tools/Pagination/CountWalker.php#L50
157157
*/
158-
if (QueryUtils::hasHavingClause($queryBuilder)) {
158+
if (QueryChecker::hasHavingClause($queryBuilder)) {
159159
return true;
160160
}
161161

@@ -164,7 +164,7 @@ private function useOutputWalkers(QueryBuilder $queryBuilder)
164164
*
165165
* @see https://github.com/doctrine/doctrine2/blob/900b55d16afdcdeb5100d435a7166d3a425b9873/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php#L87
166166
*/
167-
if (QueryUtils::hasRootEntityWithForeignKeyIdentifier($queryBuilder, $this->managerRegistry)) {
167+
if (QueryChecker::hasRootEntityWithForeignKeyIdentifier($queryBuilder, $this->managerRegistry)) {
168168
return true;
169169
}
170170

@@ -174,8 +174,8 @@ private function useOutputWalkers(QueryBuilder $queryBuilder)
174174
* @see https://github.com/doctrine/doctrine2/blob/900b55d16afdcdeb5100d435a7166d3a425b9873/lib/Doctrine/ORM/Tools/Pagination/LimitSubqueryWalker.php#L149
175175
*/
176176
if (
177-
QueryUtils::hasMaxResults($queryBuilder)
178-
&& QueryUtils::hasOrderByOnToManyJoin($queryBuilder, $this->managerRegistry)
177+
QueryChecker::hasMaxResults($queryBuilder)
178+
&& QueryChecker::hasOrderByOnToManyJoin($queryBuilder, $this->managerRegistry)
179179
) {
180180
return true;
181181
}

Doctrine/Orm/Filter/AbstractFilter.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
use Doctrine\Common\Persistence\ManagerRegistry;
1515
use Doctrine\Common\Persistence\Mapping\ClassMetadata;
1616
use Dunglas\ApiBundle\Api\ResourceInterface;
17-
use Dunglas\ApiBundle\Util\RequestUtils;
17+
use Dunglas\ApiBundle\Util\RequestParser;
1818
use Symfony\Component\HttpFoundation\Request;
1919

2020
/**
@@ -180,7 +180,7 @@ protected function extractProperties(Request $request)
180180
}
181181

182182
if ($needsFixing) {
183-
$request = RequestUtils::getFixedRequest($request);
183+
$request = RequestParser::parseAndDuplicateRequest($request);
184184
}
185185

186186
return $request->query->all();

Doctrine/Orm/Filter/DateFilter.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
use Doctrine\Common\Persistence\ManagerRegistry;
1515
use Doctrine\ORM\QueryBuilder;
1616
use Dunglas\ApiBundle\Api\ResourceInterface;
17-
use Dunglas\ApiBundle\Doctrine\Orm\Util\QueryUtils;
17+
use Dunglas\ApiBundle\Doctrine\Orm\Util\QueryNameGenerator;
1818
use Symfony\Component\HttpFoundation\RequestStack;
1919

2020
/**
@@ -88,7 +88,7 @@ public function apply(ResourceInterface $resource, QueryBuilder $queryBuilder)
8888
$parentAlias = $alias;
8989

9090
foreach ($propertyParts['associations'] as $association) {
91-
$alias = QueryUtils::generateJoinAlias($association);
91+
$alias = QueryNameGenerator::generateJoinAlias($association);
9292
$queryBuilder->join(sprintf('%s.%s', $parentAlias, $association), $alias);
9393
$parentAlias = $alias;
9494
}
@@ -138,7 +138,7 @@ public function apply(ResourceInterface $resource, QueryBuilder $queryBuilder)
138138
*/
139139
private function addWhere(QueryBuilder $queryBuilder, $alias, $field, $operator, $value, $nullManagement)
140140
{
141-
$valueParameter = QueryUtils::generateParameterName(sprintf('%s_%s', $field, $operator));
141+
$valueParameter = QueryNameGenerator::generateParameterName(sprintf('%s_%s', $field, $operator));
142142
$baseWhere = sprintf('%s.%s %s :%s', $alias, $field, self::PARAMETER_BEFORE === $operator ? '<=' : '>=', $valueParameter);
143143

144144
if (null === $nullManagement || self::EXCLUDE_NULL === $nullManagement) {

Doctrine/Orm/Filter/OrderFilter.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
use Doctrine\Common\Persistence\ManagerRegistry;
1515
use Doctrine\ORM\QueryBuilder;
1616
use Dunglas\ApiBundle\Api\ResourceInterface;
17-
use Dunglas\ApiBundle\Doctrine\Orm\Util\QueryUtils;
17+
use Dunglas\ApiBundle\Doctrine\Orm\Util\QueryNameGenerator;
1818
use Symfony\Component\HttpFoundation\Request;
1919
use Symfony\Component\HttpFoundation\RequestStack;
2020

@@ -94,7 +94,7 @@ public function apply(ResourceInterface $resource, QueryBuilder $queryBuilder)
9494
$parentAlias = $alias;
9595

9696
foreach ($propertyParts['associations'] as $association) {
97-
$alias = QueryUtils::generateJoinAlias($association);
97+
$alias = QueryNameGenerator::generateJoinAlias($association);
9898
$queryBuilder->join(sprintf('%s.%s', $parentAlias, $association), $alias);
9999
$parentAlias = $alias;
100100
}

Doctrine/Orm/Filter/SearchFilter.php

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
use Doctrine\ORM\QueryBuilder;
1616
use Dunglas\ApiBundle\Api\IriConverterInterface;
1717
use Dunglas\ApiBundle\Api\ResourceInterface;
18-
use Dunglas\ApiBundle\Doctrine\Orm\Util\QueryUtils;
18+
use Dunglas\ApiBundle\Doctrine\Orm\Util\QueryNameGenerator;
1919
use Dunglas\ApiBundle\Exception\InvalidArgumentException;
2020
use Symfony\Component\HttpFoundation\RequestStack;
2121
use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
@@ -113,7 +113,7 @@ public function apply(ResourceInterface $resource, QueryBuilder $queryBuilder)
113113
$parentAlias = $alias;
114114

115115
foreach ($propertyParts['associations'] as $association) {
116-
$alias = QueryUtils::generateJoinAlias($association);
116+
$alias = QueryNameGenerator::generateJoinAlias($association);
117117
$queryBuilder->join(sprintf('%s.%s', $parentAlias, $association), $alias);
118118
$parentAlias = $alias;
119119
}
@@ -145,8 +145,8 @@ public function apply(ResourceInterface $resource, QueryBuilder $queryBuilder)
145145
$value = $this->getFilterValueFromUrl($value);
146146

147147
$association = $field;
148-
$associationAlias = QueryUtils::generateJoinAlias($association);
149-
$valueParameter = QueryUtils::generateParameterName($association);
148+
$associationAlias = QueryNameGenerator::generateJoinAlias($association);
149+
$valueParameter = QueryNameGenerator::generateParameterName($association);
150150

151151
$queryBuilder
152152
->join(sprintf('%s.%s', $alias, $association), $associationAlias)
@@ -170,8 +170,8 @@ public function apply(ResourceInterface $resource, QueryBuilder $queryBuilder)
170170
$values = array_map([$this, 'getFilterValueFromUrl'], $values);
171171

172172
$association = $field;
173-
$associationAlias = QueryUtils::generateJoinAlias($association);
174-
$valuesParameter = QueryUtils::generateParameterName($association);
173+
$associationAlias = QueryNameGenerator::generateJoinAlias($association);
174+
$valuesParameter = QueryNameGenerator::generateParameterName($association);
175175

176176
$queryBuilder
177177
->join(sprintf('%s.%s', $alias, $association), $associationAlias)
@@ -196,7 +196,7 @@ public function apply(ResourceInterface $resource, QueryBuilder $queryBuilder)
196196
*/
197197
private function addWhereByStrategy($strategy, QueryBuilder $queryBuilder, $alias, $field, $value)
198198
{
199-
$valueParameter = QueryUtils::generateParameterName($field);
199+
$valueParameter = QueryNameGenerator::generateParameterName($field);
200200

201201
switch ($strategy) {
202202
case null:

Doctrine/Orm/Util/QueryChecker.php

Lines changed: 128 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,128 @@
1+
<?php
2+
3+
/*
4+
* This file is part of the DunglasApiBundle package.
5+
*
6+
* (c) Kévin Dunglas <dunglas@gmail.com>
7+
*
8+
* For the full copyright and license information, please view the LICENSE
9+
* file that was distributed with this source code.
10+
*/
11+
12+
namespace Dunglas\ApiBundle\Doctrine\Orm\Util;
13+
14+
use Doctrine\Common\Persistence\ManagerRegistry;
15+
use Doctrine\ORM\QueryBuilder;
16+
17+
/**
18+
* Utility functions for working with Doctrine ORM query.
19+
*
20+
* @author Teoh Han Hui <teohhanhui@gmail.com>
21+
* @author Vincent Chalamon <vincentchalamon@gmail.com>
22+
*/
23+
abstract class QueryChecker
24+
{
25+
/**
26+
* Determines whether the query builder uses a HAVING clause.
27+
*
28+
* @param QueryBuilder $queryBuilder
29+
*
30+
* @return bool
31+
*/
32+
public static function hasHavingClause(QueryBuilder $queryBuilder)
33+
{
34+
return !empty($queryBuilder->getDQLPart('having'));
35+
}
36+
37+
/**
38+
* Determines whether the query builder has any root entity with foreign key identifier.
39+
*
40+
* @param QueryBuilder $queryBuilder
41+
* @param ManagerRegistry $managerRegistry
42+
*
43+
* @return bool
44+
*/
45+
public static function hasRootEntityWithForeignKeyIdentifier(QueryBuilder $queryBuilder, ManagerRegistry $managerRegistry)
46+
{
47+
foreach ($queryBuilder->getRootEntities() as $rootEntity) {
48+
$rootMetadata = $managerRegistry
49+
->getManagerForClass($rootEntity)
50+
->getClassMetadata($rootEntity)
51+
;
52+
53+
if ($rootMetadata->containsForeignIdentifier) {
54+
return true;
55+
}
56+
}
57+
58+
return false;
59+
}
60+
61+
/**
62+
* Determines whether the query builder has the maximum number of results specified.
63+
*
64+
* @param QueryBuilder $queryBuilder
65+
*
66+
* @return bool
67+
*/
68+
public static function hasMaxResults(QueryBuilder $queryBuilder)
69+
{
70+
return null !== $queryBuilder->getMaxResults();
71+
}
72+
73+
/**
74+
* Determines whether the query builder has ORDER BY on entity joined through
75+
* to-many association.
76+
*
77+
* @param QueryBuilder $queryBuilder
78+
* @param ManagerRegistry $managerRegistry
79+
*
80+
* @return bool
81+
*/
82+
public static function hasOrderByOnToManyJoin(QueryBuilder $queryBuilder, ManagerRegistry $managerRegistry)
83+
{
84+
if (
85+
empty($orderByParts = $queryBuilder->getDQLPart('orderBy'))
86+
|| empty($joinParts = $queryBuilder->getDQLPart('join'))
87+
) {
88+
return false;
89+
}
90+
91+
$orderByAliases = [];
92+
foreach ($orderByParts as $orderBy) {
93+
$parts = QueryNameGenerator::getOrderByParts($orderBy);
94+
95+
foreach ($parts as $part) {
96+
if (false !== ($pos = strpos($part, '.'))) {
97+
$alias = substr($part, 0, $pos);
98+
99+
$orderByAliases[$alias] = true;
100+
}
101+
}
102+
}
103+
104+
if (!empty($orderByAliases)) {
105+
foreach ($joinParts as $rootAlias => $joins) {
106+
foreach ($joins as $join) {
107+
$alias = QueryNameGenerator::getJoinAlias($join);
108+
109+
if (isset($orderByAliases[$alias])) {
110+
$relationship = QueryNameGenerator::getJoinRelationship($join);
111+
112+
$relationshipParts = explode('.', $relationship);
113+
$parentAlias = $relationshipParts[0];
114+
$association = $relationshipParts[1];
115+
116+
$parentMetadata = QueryNameGenerator::getClassMetadataFromJoinAlias($parentAlias, $queryBuilder, $managerRegistry);
117+
118+
if ($parentMetadata->isCollectionValuedAssociation($association)) {
119+
return true;
120+
}
121+
}
122+
}
123+
}
124+
}
125+
126+
return false;
127+
}
128+
}

0 commit comments

Comments
 (0)