From 976a53e32174a99ed37f0aba00f75463ed0bff34 Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Mon, 11 Mar 2024 18:08:49 +0100 Subject: [PATCH] Transform custom variable filters as late as possible fixes #865 --- .../Model/Behavior/FlattenedObjectVars.php | 33 ++++++++++++++++--- library/Icingadb/Model/CustomvarFlat.php | 15 +++++++++ 2 files changed, 43 insertions(+), 5 deletions(-) diff --git a/library/Icingadb/Model/Behavior/FlattenedObjectVars.php b/library/Icingadb/Model/Behavior/FlattenedObjectVars.php index b1c308a35..bd3966b78 100644 --- a/library/Icingadb/Model/Behavior/FlattenedObjectVars.php +++ b/library/Icingadb/Model/Behavior/FlattenedObjectVars.php @@ -11,6 +11,7 @@ use ipl\Orm\Contract\QueryAwareBehavior; use ipl\Orm\Contract\RewriteColumnBehavior; use ipl\Orm\Query; +use ipl\Stdlib\Data; use ipl\Stdlib\Filter; class FlattenedObjectVars implements RewriteColumnBehavior, QueryAwareBehavior @@ -32,11 +33,33 @@ public function rewriteCondition(Filter\Condition $condition, $relation = null) $column = $condition->metaData()->get('columnName'); if ($column !== null) { $relation = substr($relation, 0, -5) . 'customvar_flat.'; - $nameFilter = Filter::like($relation . 'flatname', $column); - $class = get_class($condition); - $valueFilter = new $class($relation . 'flatvalue', $condition->getValue()); - - return Filter::all($nameFilter, $valueFilter); + $condition + ->setColumn($relation . $column) + ->metaData() + ->set('requiresTransformation', true) + ->set('columnPath', $relation . $column) + ->set('relationPath', substr($relation, 0, -1)); + + // The ORM's FilterProcessor only optimizes filter conditions that are in the same level (chain). + // Previously, this behavior transformed a single condition to an ALL chain and hence the semantics + // of the level changed, since the FilterProcessor interpreted the conditions separately from there on. + // To not change the semantics of the condition it is required to delay the transformation of the condition + // until the subquery is created. Though, since the FilterProcessor only applies behaviors once, this + // hack is required. (The entire filter, metadata and optimization is total garbage.) + $oldMetaData = $condition->metaData(); + $reflection = new \ReflectionClass($condition); + $reflection->getProperty('metaData')->setAccessible(true); + $reflection->getProperty('metaData')->setValue($condition, new class () extends Data { + public function set($name, $value) + { + if ($name === 'behaviorsApplied') { + return $this; + } + + return parent::set($name, $value); + } + }); + $condition->metaData()->merge($oldMetaData); } } diff --git a/library/Icingadb/Model/CustomvarFlat.php b/library/Icingadb/Model/CustomvarFlat.php index 99d8acae1..1b79cc3c1 100644 --- a/library/Icingadb/Model/CustomvarFlat.php +++ b/library/Icingadb/Model/CustomvarFlat.php @@ -6,9 +6,12 @@ use ipl\Orm\Behavior\Binary; use ipl\Orm\Behaviors; +use ipl\Orm\Contract\RewriteFilterBehavior; use ipl\Orm\Model; use ipl\Orm\Query; use ipl\Orm\Relations; +use ipl\Stdlib\Filter; +use ipl\Stdlib\Filter\Condition; use Traversable; class CustomvarFlat extends Model @@ -42,6 +45,18 @@ public function createBehaviors(Behaviors $behaviors) 'customvar_id', 'flatname_checksum' ])); + $behaviors->add(new class implements RewriteFilterBehavior { + public function rewriteCondition(Condition $condition, $relation = null) + { + if ($condition->metaData()->has('requiresTransformation')) { + $nameFilter = Filter::like($relation . 'flatname', $condition->metaData()->get('columnName')); + $class = get_class($condition); + $valueFilter = new $class($relation . 'flatvalue', $condition->getValue()); + + return Filter::all($nameFilter, $valueFilter); + } + } + }); } public function createRelations(Relations $relations)