From 90989a4e88d68e7c3504e47587ede43cc42f1ef9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=A1clav=20Pel=C3=AD=C5=A1ek?= Date: Fri, 21 Jun 2024 23:48:35 +0200 Subject: [PATCH] Implement entity lookup in hierarchy, closes #101 --- src/Configurator.php | 51 +++++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 20 deletions(-) diff --git a/src/Configurator.php b/src/Configurator.php index 8c40402..1b38aa4 100644 --- a/src/Configurator.php +++ b/src/Configurator.php @@ -112,7 +112,7 @@ public function initFields(EntitySchema $entity, \ReflectionClass $class, string } $field = $this->initField($property->getName(), $column, $class, $columnPrefix); - $field->setEntityClass($property->getDeclaringClass()->getName()); + $field->setEntityClass($this->findOwningEntity($class, $property->getDeclaringClass())->getName()); $entity->getFields()->set($property->getName(), $field); } } @@ -120,9 +120,9 @@ public function initFields(EntitySchema $entity, \ReflectionClass $class, string public function initRelations(EntitySchema $entity, \ReflectionClass $class): void { foreach ($class->getProperties() as $property) { - // ignore properties declared by parent class + // ignore properties declared by parent entties // otherwise all the relation columns declared in parent would be duplicated across all child tables in JTI - if ($this->propertyBelongsToOtherEntity($class, $property->getDeclaringClass())) { + if ($this->findOwningEntity($class, $property->getDeclaringClass())->getName() !== $class->getName()) { continue; } @@ -420,26 +420,37 @@ private function isOnInsertGeneratedField(Field $field): bool }; } - private function propertyBelongsToOtherEntity(\ReflectionClass $currentClass, \ReflectionClass $declaringClass): bool + /** + * Function to find an owning entity class in the inheritance hierarchy. + * + * Entity classes may extend a base class and this function is needed route the properties from declaring class to the entity class. + * The function stops only when the declaring class is truly found, it does not naively stop on first entity. + * This behaviour makes it also functional in cases of Joined Table Inheritance on theoretically any number of nesting levels. + */ + private function findOwningEntity(\ReflectionClass $currentClass, \ReflectionClass $declaringClass): \ReflectionClass { - // if the current class is the same as declaring class, than the property belongs to current Entity - if ($currentClass->getName() === $declaringClass->getName()) { - return false; - } - - $parentClass = $currentClass->getParentClass(); + // latest found entityClass before declaringClass + $latestEntityClass = $currentClass; + + do { + // we found declaringClass in the hierarchy + // in most cases the execution will stop here in first loop + if ($currentClass->getName() === $declaringClass->getName()) { + return $latestEntityClass; + } - // not possible to happen for logical reasons, but defensively check anyway - if (!$parentClass instanceof \ReflectionClass) { - return false; - } + $currentClass = $currentClass->getParentClass(); - // if a parent class in hierarchy is an Entity on its own, the property belongs to that Entity - if (\count($parentClass->getAttributes(Entity::class)) > 0) { - return true; - } + // not possible to happen for logical reasons, but defensively check anyway + if (!$currentClass instanceof \ReflectionClass) { + return $latestEntityClass; + } - // continue until we find a declaringClass or Entity attribute - return $this->propertyBelongsToOtherEntity($parentClass, $declaringClass); + // if a currentClass in hierarchy is an entity on its own, the property belongs to that entity + if (\count($currentClass->getAttributes(Entity::class)) > 0) { + $latestEntityClass = $currentClass; + } + } while (true); // the inheritance hierarchy cannot be infinite } } +