/
NestedTreeComparisonExpression.php
124 lines (109 loc) · 4.06 KB
/
NestedTreeComparisonExpression.php
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
<?php
namespace Oro\Bundle\ApiBundle\Collection\QueryVisitorExpression;
use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\ORM\Query\Expr;
use Doctrine\ORM\Query\QueryException;
use Doctrine\ORM\QueryBuilder;
use Gedmo\Tree\TreeListener;
use Oro\Bundle\ApiBundle\Collection\QueryExpressionVisitor;
use Oro\Bundle\ApiBundle\Model\Range;
use Oro\Component\DoctrineUtils\ORM\QueryBuilderUtil;
/**
* Represents NESTED_TREE and NESTED_TREE_WITH_ROOT comparison expression.
* The NESTED_TREE expression returns all child nodes for a given node, independs on the nesting level.
* The NESTED_TREE_WITH_ROOT expression returns a given node and all child nodes for this node,
* independs on the nesting level.
* @see \Gedmo\Tree\Entity\Repository\NestedTreeRepository::childrenQueryBuilder
*/
class NestedTreeComparisonExpression implements ComparisonExpressionInterface
{
/** @var TreeListener */
private $treeListener;
/** @var ManagerRegistry */
private $doctrine;
/** @var bool */
private $includeRoot;
/**
* @param TreeListener $treeListener
* @param ManagerRegistry $doctrine
* @param bool $includeRoot
*/
public function __construct(
TreeListener $treeListener,
ManagerRegistry $doctrine,
bool $includeRoot = false
) {
$this->treeListener = $treeListener;
$this->doctrine = $doctrine;
$this->includeRoot = $includeRoot;
}
/**
* {@inheritdoc}
*/
public function walkComparisonExpression(
QueryExpressionVisitor $visitor,
string $field,
string $expression,
string $parameterName,
$value
) {
if (null === $value) {
// the filter like NESTED_TREE for NULL does not have a sense
throw new QueryException(\sprintf('The value for "%s" must not be NULL.', $field));
}
if ($value instanceof Range) {
throw new QueryException(\sprintf('The value for "%s" must not be a range.', $field));
}
$visitor->addParameter($parameterName, $value);
$subquery = $visitor->createSubquery($field ?: null);
$this->buildSubquery($subquery, $visitor->buildPlaceholder($parameterName));
return $visitor->getExpressionBuilder()->exists($subquery->getDQL());
}
/**
* @param QueryBuilder $subquery
* @param string $parameterPlaceholder
*/
private function buildSubquery(QueryBuilder $subquery, string $parameterPlaceholder): void
{
$entityClass = QueryBuilderUtil::getSingleRootEntity($subquery);
$subqueryAlias = QueryBuilderUtil::getSingleRootAlias($subquery);
$criteriaAlias = $subqueryAlias . '_criteria';
$subquery->innerJoin(
$entityClass,
$criteriaAlias,
Expr\Join::WITH,
$subquery->expr()->eq($criteriaAlias, $parameterPlaceholder)
);
$config = $this->treeListener->getConfiguration(
$this->doctrine->getManagerForClass($entityClass),
$entityClass
);
$rightFieldName = $config['right'];
$leftFieldName = $config['left'];
$expressions = [
$subquery->expr()->lt(
$subqueryAlias . '.' . $rightFieldName,
$criteriaAlias . '.' . $rightFieldName
),
$subquery->expr()->gt(
$subqueryAlias . '.' . $leftFieldName,
$criteriaAlias . '.' . $leftFieldName
)
];
if (isset($config['root'])) {
$rootFieldName = $config['root'];
$expressions[] = $subquery->expr()->eq(
$subqueryAlias . '.' . $rootFieldName,
$criteriaAlias . '.' . $rootFieldName
);
}
$whereExpression = new Expr\Andx($expressions);
if ($this->includeRoot) {
$whereExpression = $subquery->expr()->orX(
$whereExpression,
$subquery->expr()->eq($subqueryAlias, $parameterPlaceholder)
);
}
$subquery->andWhere($whereExpression);
}
}