From b5a4caae4c6f4a59f24ae3506194ea47e6e608cf Mon Sep 17 00:00:00 2001 From: Johannes Meyer Date: Mon, 16 Sep 2024 16:10:40 +0200 Subject: [PATCH] db: Cleanup parent/child relations in matching models The previous relations didn't work as expected. Now, filtering with `child.host.name` or `parent.service.name` works fine. --- library/Icingadb/Model/DependencyEdge.php | 14 ++++++++---- library/Icingadb/Model/DependencyNode.php | 25 +++++++++++----------- library/Icingadb/Model/Host.php | 15 +++++++++---- library/Icingadb/Model/RedundancyGroup.php | 19 ++++++++++------ library/Icingadb/Model/Service.php | 15 +++++++++---- 5 files changed, 58 insertions(+), 30 deletions(-) diff --git a/library/Icingadb/Model/DependencyEdge.php b/library/Icingadb/Model/DependencyEdge.php index d6c6599c2..7674ce3ed 100644 --- a/library/Icingadb/Model/DependencyEdge.php +++ b/library/Icingadb/Model/DependencyEdge.php @@ -17,8 +17,8 @@ * @property string $from_node_id * @property ?string $dependency_id * - * @property DependencyNode|Query $from - * @property DependencyNode|Query $to + * @property DependencyNode|Query $child + * @property DependencyNode|Query $parent * @property (?Dependency)|Query $dependency */ class DependencyEdge extends Model @@ -53,11 +53,17 @@ public function createBehaviors(Behaviors $behaviors): void public function createRelations(Relations $relations): void { - $relations->belongsTo('from', DependencyNode::class) + $relations->belongsTo('child', DependencyNode::class) ->setCandidateKey('from_node_id'); - $relations->belongsTo('to', DependencyNode::class) + $relations->belongsTo('parent', DependencyNode::class) ->setCandidateKey('to_node_id'); $relations->belongsTo('dependency', Dependency::class) ->setJoinType('LEFT'); + + // "from" and "to" are only necessary for sub-query filters. + $relations->belongsTo('from', DependencyNode::class) + ->setCandidateKey('from_node_id'); + $relations->belongsTo('to', DependencyNode::class) + ->setCandidateKey('to_node_id'); } } diff --git a/library/Icingadb/Model/DependencyNode.php b/library/Icingadb/Model/DependencyNode.php index 995e9be7e..79a244bc9 100644 --- a/library/Icingadb/Model/DependencyNode.php +++ b/library/Icingadb/Model/DependencyNode.php @@ -4,6 +4,7 @@ namespace Icinga\Module\Icingadb\Model; +use Icinga\Module\Icingadb\Model\Behavior\ReRoute; use ipl\Orm\Behavior\Binary; use ipl\Orm\Behaviors; use ipl\Orm\Model; @@ -23,8 +24,6 @@ * @property (?RedundancyGroup)|Query $redundancy_group * @property (?DependencyEdge)|Query $from * @property (?DependencyEdge)|Query $to - * @property (?DependencyNode)|Query $child - * @property (?DependencyNode)|Query $parent */ class DependencyNode extends Model { @@ -56,6 +55,10 @@ public function createBehaviors(Behaviors $behaviors): void 'service_id', 'redundancy_group_id' ])); + $behaviors->add(new ReRoute([ + 'child' => 'to.from', + 'parent' => 'from.to' + ])); } public function createRelations(Relations $relations): void @@ -74,15 +77,13 @@ public function createRelations(Relations $relations): void ->setForeignKey('to_node_id') ->setJoinType('LEFT'); - $relations->belongsToMany('child', self::class) - ->through(DependencyEdge::class) - ->setForeignKey('to_node_id') - ->setTargetForeignKey('from_node_id') - ->setJoinType('LEFT'); - $relations->belongsToMany('parent', self::class) - ->through(DependencyEdge::class) - ->setForeignKey('from_node_id') - ->setTargetForeignKey('to_node_id') - ->setJoinType('LEFT'); + // TODO: This self join is only a work-around as when selecting nodes and filtering by child or parent, + // the ORM wants to join the base table as usual in case a sub-query is used. Though, in this case + // resolving e.g. child to "to.from" is reversed in a sub-query to "from.to" and the ORM does not + // detect that "to" is already the link to the base table. + // Given the path "dependency_node.to.from.host", the sub-query uses "host.from.to.dependency_node". + // "to.dependency_node" is the crucial part, as "dependency_node" is said self-join. + $relations->hasOne('dependency_node', self::class) + ->setForeignKey('id'); } } diff --git a/library/Icingadb/Model/Host.php b/library/Icingadb/Model/Host.php index a68275950..1dc83be0e 100644 --- a/library/Icingadb/Model/Host.php +++ b/library/Icingadb/Model/Host.php @@ -181,8 +181,8 @@ public function createBehaviors(Behaviors $behaviors) ])); $behaviors->add(new ReRoute([ - 'child' => 'dependency_node.child', - 'parent' => 'dependency_node.parent', + 'child' => 'to.from', + 'parent' => 'from.to', 'servicegroup' => 'service.servicegroup', 'user' => 'notification.user', 'usergroup' => 'notification.usergroup' @@ -237,8 +237,6 @@ public function createDefaults(Defaults $defaults) public function createRelations(Relations $relations) { - $relations->belongsTo('dependency_node', DependencyNode::class) - ->setJoinType('LEFT'); $relations->belongsTo('environment', Environment::class); $relations->belongsTo('eventcommand', Eventcommand::class); $relations->belongsTo('checkcommand', Checkcommand::class); @@ -274,5 +272,14 @@ public function createRelations(Relations $relations) $relations->hasMany('notification', Notification::class)->setJoinType('LEFT'); $relations->hasMany('notification_history', NotificationHistory::class); $relations->hasMany('service', Service::class)->setJoinType('LEFT'); + + $relations->belongsToMany('from', DependencyEdge::class) + ->setTargetCandidateKey('from_node_id') + ->setTargetForeignKey('id') + ->through(DependencyNode::class); + $relations->belongsToMany('to', DependencyEdge::class) + ->setTargetCandidateKey('to_node_id') + ->setTargetForeignKey('id') + ->through(DependencyNode::class); } } diff --git a/library/Icingadb/Model/RedundancyGroup.php b/library/Icingadb/Model/RedundancyGroup.php index c8a76e7bf..d4e20140d 100644 --- a/library/Icingadb/Model/RedundancyGroup.php +++ b/library/Icingadb/Model/RedundancyGroup.php @@ -21,6 +21,8 @@ * * @property (?RedundancyGroupState)|Query $state * @property Dependency|Query $dependency + * @property DependencyEdge|Query $from + * @property DependencyEdge|Query $to */ class RedundancyGroup extends Model { @@ -48,20 +50,25 @@ public function createBehaviors(Behaviors $behaviors): void 'id' ])); $behaviors->add(new ReRoute([ - 'child' => 'dependency_node.child', - 'parent' => 'dependency_node.parent' + 'child' => 'to.from', + 'parent' => 'from.to' ])); } public function createRelations(Relations $relations): void { - $relations->belongsTo('dependency_node', DependencyNode::class) - ->setForeignKey('redundancy_group_id') - ->setCandidateKey('id'); - $relations->hasOne('state', RedundancyGroupState::class) ->setJoinType('LEFT'); $relations->hasMany('dependency', Dependency::class); + + $relations->belongsToMany('from', DependencyEdge::class) + ->setTargetCandidateKey('from_node_id') + ->setTargetForeignKey('id') + ->through(DependencyNode::class); + $relations->belongsToMany('to', DependencyEdge::class) + ->setTargetCandidateKey('to_node_id') + ->setTargetForeignKey('id') + ->through(DependencyNode::class); } } diff --git a/library/Icingadb/Model/Service.php b/library/Icingadb/Model/Service.php index 1383823ab..5a0d52843 100644 --- a/library/Icingadb/Model/Service.php +++ b/library/Icingadb/Model/Service.php @@ -170,8 +170,8 @@ public function createBehaviors(Behaviors $behaviors) ])); $behaviors->add(new ReRoute([ - 'child' => 'dependency_node.child', - 'parent' => 'dependency_node.parent', + 'child' => 'to.from', + 'parent' => 'from.to', 'user' => 'notification.user', 'usergroup' => 'notification.usergroup' ])); @@ -224,8 +224,6 @@ public function createDefaults(Defaults $defaults) public function createRelations(Relations $relations) { - $relations->belongsTo('dependency_node', DependencyNode::class) - ->setJoinType('LEFT'); $relations->belongsTo('environment', Environment::class); $relations->belongsTo('host', Host::class)->setJoinType('LEFT'); $relations->belongsTo('checkcommand', Checkcommand::class); @@ -263,5 +261,14 @@ public function createRelations(Relations $relations) $relations->hasMany('history', History::class); $relations->hasMany('notification', Notification::class)->setJoinType('LEFT'); $relations->hasMany('notification_history', NotificationHistory::class); + + $relations->belongsToMany('from', DependencyEdge::class) + ->setTargetCandidateKey('from_node_id') + ->setTargetForeignKey('id') + ->through(DependencyNode::class); + $relations->belongsToMany('to', DependencyEdge::class) + ->setTargetCandidateKey('to_node_id') + ->setTargetForeignKey('id') + ->through(DependencyNode::class); } }