Skip to content

Commit e38932b

Browse files
authored
Merge pull request #12 from Icinga/utilize-ipl-stdlib-filter
Utilize \ipl\Stdlib\Filter
2 parents c168d01 + 31606ad commit e38932b

File tree

3 files changed

+66
-66
lines changed

3 files changed

+66
-66
lines changed

src/Behaviors.php

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,11 @@
33
namespace ipl\Orm;
44

55
use ArrayIterator;
6-
use Icinga\Data\Filter\Filter;
7-
use Icinga\Data\Filter\FilterExpression;
86
use ipl\Orm\Contract\PersistBehavior;
97
use ipl\Orm\Contract\PropertyBehavior;
108
use ipl\Orm\Contract\RetrieveBehavior;
119
use ipl\Orm\Contract\RewriteFilterBehavior;
10+
use ipl\Stdlib\Filter;
1211
use IteratorAggregate;
1312

1413
class Behaviors implements IteratorAggregate
@@ -125,18 +124,18 @@ public function persistProperty($value, $key)
125124
}
126125

127126
/**
128-
* Rewrite the given filter expression
127+
* Rewrite the given filter condition
129128
*
130-
* @param FilterExpression $expression
129+
* @param Filter\Condition $condition
131130
* @param string $relation Absolute path of the model
132131
*
133-
* @return Filter|null
132+
* @return Filter\Rule|null
134133
*/
135-
public function rewriteCondition(FilterExpression $expression, $relation = null)
134+
public function rewriteCondition(Filter\Condition $condition, $relation = null)
136135
{
137136
$filter = null;
138137
foreach ($this->rewriteFilterBehaviors as $behavior) {
139-
$replacement = $behavior->rewriteCondition($filter ?: $expression, $relation);
138+
$replacement = $behavior->rewriteCondition($filter ?: $condition, $relation);
140139
if ($replacement !== null) {
141140
$filter = $replacement;
142141
}

src/Compat/FilterProcessor.php

Lines changed: 53 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,21 @@
44

55
use AppendIterator;
66
use ArrayIterator;
7-
use Icinga\Data\Filter\Filter;
8-
use Icinga\Data\Filter\FilterAnd;
9-
use Icinga\Data\Filter\FilterChain;
10-
use Icinga\Data\Filter\FilterExpression;
11-
use Icinga\Data\Filter\FilterOr;
127
use ipl\Orm\Query;
138
use ipl\Orm\Relation;
149
use ipl\Orm\UnionQuery;
1510
use ipl\Sql\Expression;
11+
use ipl\Sql\Filter\Exists;
12+
use ipl\Sql\Filter\NotExists;
13+
use ipl\Stdlib\Filter;
1614

1715
class FilterProcessor extends \ipl\Sql\Compat\FilterProcessor
1816
{
1917
protected $baseJoins = [];
2018

2119
protected $madeJoins = [];
2220

23-
public static function apply(Filter $filter, Query $query)
21+
public static function apply(Filter\Rule $filter, Query $query)
2422
{
2523
if ($query instanceof UnionQuery) {
2624
foreach ($query->getUnions() as $union) {
@@ -30,11 +28,11 @@ public static function apply(Filter $filter, Query $query)
3028
return;
3129
}
3230

33-
if (! $filter->isEmpty()) {
31+
if ($filter instanceof Filter\Condition || ! $filter->isEmpty()) {
3432
$filter = clone $filter;
35-
if (! $filter->isChain()) {
33+
if (! $filter instanceof Filter\Chain) {
3634
// TODO: Quickfix, there's probably a better solution?
37-
$filter = Filter::matchAll($filter);
35+
$filter = Filter::all($filter);
3836
}
3937

4038
$processor = new static();
@@ -58,24 +56,24 @@ public static function apply(Filter $filter, Query $query)
5856
}
5957
}
6058

61-
protected function requireAndResolveFilterColumns(Filter $filter, Query $query)
59+
protected function requireAndResolveFilterColumns(Filter\Rule $filter, Query $query)
6260
{
63-
if ($filter instanceof FilterExpression) {
64-
if ($filter->getExpression() === '*') {
61+
if ($filter instanceof Filter\Condition) {
62+
if ($filter->getValue() === '*') {
6563
// Wildcard only filters are ignored so stop early here to avoid joining a table for nothing
6664
return;
6765
}
6866

6967
$column = $filter->getColumn();
70-
if (isset($filter->metaData)) {
71-
$column = $filter->metaData['relationCol'];
68+
if (isset($filter->relationCol)) {
69+
$column = $filter->relationCol;
7270
}
7371

7472
$resolver = $query->getResolver();
7573
$baseTable = $query->getModel()->getTableName();
7674
$column = $resolver->qualifyPath($column, $baseTable);
7775

78-
$filter->metaData['column'] = $column;
76+
$filter->columnPath = $column;
7977

8078
list($relationPath, $columnName) = preg_split('/\.(?=[^.]+$)/', $column);
8179

@@ -95,10 +93,10 @@ protected function requireAndResolveFilterColumns(Filter $filter, Query $query)
9593
$subjectBehaviors = $resolver->getBehaviors($subject);
9694

9795
// Prepare filter as if it were final to allow full control for rewrite filter behaviors
98-
$filter->setExpression($subjectBehaviors->persistProperty($filter->getExpression(), $columnName));
96+
$filter->setValue($subjectBehaviors->persistProperty($filter->getValue(), $columnName));
9997
$filter->setColumn($resolver->getAlias($subject) . '.' . $columnName);
100-
$filter->metaData['relationCol'] = $columnName;
101-
$filter->metaData['relationPath'] = $path;
98+
$filter->relationCol = $columnName;
99+
$filter->relationPath = $path;
102100

103101
$rewrittenFilter = $subjectBehaviors->rewriteCondition($filter, $path . '.');
104102
if ($rewrittenFilter !== null) {
@@ -111,69 +109,71 @@ protected function requireAndResolveFilterColumns(Filter $filter, Query $query)
111109
$this->madeJoins[$relationPath][] = $filter;
112110
}
113111
} else {
114-
/** @var FilterChain $filter */
112+
/** @var Filter\Chain $filter */
115113

116114
$subQueryFilters = [];
117-
foreach ($filter->filters() as $child) {
118-
/** @var Filter $child */
115+
foreach ($filter as $child) {
116+
/** @var Filter\Rule $child */
119117
$rewrittenFilter = $this->requireAndResolveFilterColumns($child, $query);
120118
if ($rewrittenFilter !== null) {
121-
$filter->replaceById($child->getId(), $rewrittenFilter);
119+
$filter->replace($child, $rewrittenFilter);
122120
$child = $rewrittenFilter;
123121
}
124122

125123
// We optimize only single expressions
126-
if (isset($child->metaData) && $child instanceof FilterExpression) {
127-
$relationPath = $child->metaData['relationPath'];
124+
if (isset($child->relationPath) && $child instanceof Filter\Condition) {
125+
$relationPath = $child->relationPath;
128126
if (
129127
$relationPath !== $query->getModel()->getTableName()
130128
&& ! isset($query->getWith()[$relationPath])
131129
) {
132130
if (! $query->getResolver()->isDistinctRelation($relationPath)) {
133-
if (isset($child->metaData['original'])) {
134-
$column = $child->metaData['original']->metaData['column'];
135-
$sign = $child->metaData['original']->getSign();
131+
if (isset($child->original)) {
132+
$column = $child->original->columnPath;
133+
$child = $child->original;
136134
} else {
137135
$column = $child->getColumn();
138-
$sign = $child->getSign();
139136
}
140137

141-
$subQueryFilters[$sign][$column][] = $child;
138+
$subQueryFilters[get_class($child)][$column][] = $child;
142139
}
143140
}
144141
}
145142
}
146143

147-
foreach ($subQueryFilters as $sign => $filterCombinations) {
144+
foreach ($subQueryFilters as $conditionClass => $filterCombinations) {
148145
foreach ($filterCombinations as $column => $filters) {
149146
// The relation path must be the same for all entries
150-
$relationPath = $filters[0]->metaData['relationPath'];
147+
$relationPath = $filters[0]->relationPath;
151148

152149
// In case the parent query also selects the relation we may not require a subquery.
153150
// Otherwise we form a cartesian product and get unwanted results back.
154151
$selectedByParent = isset($query->getWith()[$relationPath]);
155152

156153
// Though, only single equal comparisons or those chained with an OR may be evaluated on the base
157-
if ($selectedByParent && $sign !== '!=' && (count($filters) === 1 || $filter instanceof FilterOr)) {
154+
if (
155+
$selectedByParent && $conditionClass !== Filter\Unequal::class
156+
&& (count($filters) === 1 || $filter instanceof Filter\Any)
157+
) {
158158
continue;
159159
}
160160

161161
$relation = $query->getResolver()->resolveRelation($relationPath);
162162
$subQuery = $query->createSubQuery($relation->getTarget(), $relationPath);
163163
$subQuery->columns([new Expression('1')]);
164164

165-
if ($sign === '!=' || $filter instanceof FilterAnd) {
165+
if ($conditionClass === Filter\Unequal::class || $filter instanceof Filter\All) {
166166
$targetKeys = join(',', array_values(
167167
$subQuery->getResolver()->qualifyColumns(
168168
(array) $subQuery->getModel()->getKeyName(),
169169
$subQuery->getResolver()->getAlias($subQuery->getModel())
170170
)
171171
));
172172

173-
if ($sign !== '!=' || $filter instanceof FilterOr) {
173+
if ($conditionClass !== Filter\Unequal::class || $filter instanceof Filter\Any) {
174174
// Unequal (!=) comparisons chained with an OR are considered an XOR
175-
$count = count(array_unique(array_map(function (FilterExpression $f) {
176-
return $f->getExpression();
175+
$count = count(array_unique(array_map(function (Filter\Condition $f) {
176+
return $f->getValue();
177177
}, $filters)));
178178
} else {
179179
// Unequal (!=) comparisons are transformed to equal (=) ones. If chained with an AND
@@ -186,22 +186,24 @@ protected function requireAndResolveFilterColumns(Filter $filter, Query $query)
186186
}
187187

188188
foreach ($filters as $i => $child) {
189-
if ($sign === '!=') {
189+
if ($conditionClass === Filter\Unequal::class) {
190190
// Unequal comparisons must be negated since the sub-query is an inverse of the outer one
191-
if ($child->isExpression()) {
192-
$filters[$i] = $child->setSign('=');
193-
$filters[$i]->metaData = $child->metaData;
191+
if ($child instanceof Filter\Condition) {
192+
$negation = Filter::equal($child->getColumn(), $child->getValue());
193+
$negation->relationCol = $child->relationCol;
194+
$negation->columnPath = $child->columnPath;
195+
$negation->relationPath = $child->relationPath;
196+
$filters[$i] = $negation;
194197
} else {
195-
$childId = $child->getId(); // Gets re-indexed otherwise
196-
$filters[$i] = Filter::not($child)->setId($childId);
198+
$filters[$i] = Filter::none($child);
197199
}
198200
}
199201

200202
// Remove joins solely used for filter conditions
201203
foreach ($this->madeJoins as $joinPath => &$madeBy) {
202204
$madeBy = array_filter($madeBy, function ($relationFilter) use ($child) {
203-
return $child->getId() !== $relationFilter->getId()
204-
&& ! $child->hasId($relationFilter->getId());
205+
return $child !== $relationFilter
206+
&& ($child instanceof Filter\Condition || ! $child->has($relationFilter));
205207
});
206208

207209
if (empty($madeBy)) {
@@ -213,16 +215,16 @@ protected function requireAndResolveFilterColumns(Filter $filter, Query $query)
213215
}
214216
}
215217

216-
$filter->removeId($child->getId());
218+
$filter->remove($child);
217219
}
218220

219-
static::apply(Filter::matchAny($filters), $subQuery);
221+
static::apply(Filter::any(...$filters), $subQuery);
220222

221-
$filter->addFilter(new FilterExpression(
222-
'',
223-
($sign === '!=' ? 'NOT ' : '') . 'EXISTS',
224-
$subQuery->assembleSelect()->resetOrderBy()
225-
));
223+
if ($conditionClass === Filter\Unequal::class) {
224+
$filter->add(new NotExists($subQuery->assembleSelect()->resetOrderBy()));
225+
} else {
226+
$filter->add(new Exists($subQuery->assembleSelect()->resetOrderBy()));
227+
}
226228
}
227229
}
228230
}

src/Contract/RewriteFilterBehavior.php

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,22 +2,21 @@
22

33
namespace ipl\Orm\Contract;
44

5-
use Icinga\Data\Filter\Filter;
6-
use Icinga\Data\Filter\FilterExpression;
75
use ipl\Orm\Behavior;
6+
use ipl\Stdlib\Filter;
87

98
interface RewriteFilterBehavior extends Behavior
109
{
1110
/**
12-
* Rewrite the given filter expression
11+
* Rewrite the given filter condition
1312
*
14-
* The expression can either be adjusted directly or replaced by an entirely new filter. The result must be
15-
* returned otherwise (NULL is returned) the original expression is kept as is.
13+
* The condition can either be adjusted directly or replaced by an entirely new rule. The result must be
14+
* returned otherwise (NULL is returned) the original condition is kept as is.
1615
*
17-
* @param FilterExpression $expression
16+
* @param Filter\Condition $condition
1817
* @param string $relation The absolute path (with a trailing dot) of the model
1918
*
20-
* @return Filter|null
19+
* @return Filter\Rule|null
2120
*/
22-
public function rewriteCondition(FilterExpression $expression, $relation = null);
21+
public function rewriteCondition(Filter\Condition $condition, $relation = null);
2322
}

0 commit comments

Comments
 (0)