From d856e013f5d29ce1357eab39e7650e4338887cb4 Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Fri, 27 Mar 2026 11:36:37 +0100 Subject: [PATCH 1/4] Fix translations collection detection for hierarchies with mapped superclasses --- src/Doctrine/TranslatableClassMetadata.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/Doctrine/TranslatableClassMetadata.php b/src/Doctrine/TranslatableClassMetadata.php index 8ee6936..3e6b77c 100644 --- a/src/Doctrine/TranslatableClassMetadata.php +++ b/src/Doctrine/TranslatableClassMetadata.php @@ -232,8 +232,10 @@ private function findTranslatedProperties(ClassMetadata $cm, ClassMetadataFactor private function findTranslationsCollection(ClassMetadata $cm, ClassMetadataFactory $classMetadataFactory): void { foreach ($cm->associationMappings as $fieldName => $mapping) { - if (isset($mapping['declared'])) { - // The association is inherited from a parent class + if (isset($mapping['inherited'])) { + // "inherited" means that there is another (inheritance parent) entity class containing this + // field (https://github.com/doctrine/orm/blob/580a95ce3f5f016547d15ecc6cc94dd85453bed5/src/Mapping/AssociationMapping.php#L34-L46). + // Since PolyglotListener::getTranslationMetadatas() loops over these parent classes as well, we can skip the field here. continue; } From bedea55958490b641113bd01a373d8355a321027 Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Fri, 27 Mar 2026 13:22:01 +0100 Subject: [PATCH 2/4] Add a test --- src/Doctrine/PolyglotListener.php | 3 +- ...nheritance_MappedSuperclassTranslation.php | 26 ++++++ ...tance_MappedSuperclassWithTranslations.php | 52 +++++++++++ ...pedSuperclassWithTranslationsInterface.php | 7 ++ ...appedSuperclassWithTranslations_Entity.php | 15 ++++ .../MappedSuperclassWithTranslationsTest.php | 90 +++++++++++++++++++ 6 files changed, 192 insertions(+), 1 deletion(-) create mode 100644 tests/Fixtures/Entity/EntityInheritance/EntityInheritance_MappedSuperclassTranslation.php create mode 100644 tests/Fixtures/Entity/EntityInheritance/EntityInheritance_MappedSuperclassWithTranslations.php create mode 100644 tests/Fixtures/Entity/EntityInheritance/EntityInheritance_MappedSuperclassWithTranslationsInterface.php create mode 100644 tests/Fixtures/Entity/EntityInheritance/EntityInheritance_MappedSuperclassWithTranslations_Entity.php create mode 100644 tests/Functional/MappedSuperclassWithTranslationsTest.php diff --git a/src/Doctrine/PolyglotListener.php b/src/Doctrine/PolyglotListener.php index 58bc292..5c8f273 100644 --- a/src/Doctrine/PolyglotListener.php +++ b/src/Doctrine/PolyglotListener.php @@ -117,7 +117,8 @@ private function getTranslationMetadatas(object $entity, EntityManager $em): arr $this->translatableClassMetadatasByClass[$class] = []; $classMetadata = $em->getClassMetadata($class); - foreach (array_merge([$classMetadata->name], $classMetadata->parentClasses) as $className) { + $candidates = array_merge([$classMetadata->name], $classMetadata->parentClasses); + foreach ($candidates as $className) { if ($tm = $this->loadTranslationMetadataForClass($className, $em)) { $this->translatableClassMetadatasByClass[$class][] = $tm; } diff --git a/tests/Fixtures/Entity/EntityInheritance/EntityInheritance_MappedSuperclassTranslation.php b/tests/Fixtures/Entity/EntityInheritance/EntityInheritance_MappedSuperclassTranslation.php new file mode 100644 index 0000000..f1b0068 --- /dev/null +++ b/tests/Fixtures/Entity/EntityInheritance/EntityInheritance_MappedSuperclassTranslation.php @@ -0,0 +1,26 @@ +translations = new ArrayCollection(); + } + + public function getId(): ?int + { + return $this->id; + } + + public function setText(TranslatableInterface $text): void + { + $this->text = $text; + } + + public function getText(): TranslatableInterface|string|null + { + return $this->text; + } +} diff --git a/tests/Fixtures/Entity/EntityInheritance/EntityInheritance_MappedSuperclassWithTranslationsInterface.php b/tests/Fixtures/Entity/EntityInheritance/EntityInheritance_MappedSuperclassWithTranslationsInterface.php new file mode 100644 index 0000000..b97506e --- /dev/null +++ b/tests/Fixtures/Entity/EntityInheritance/EntityInheritance_MappedSuperclassWithTranslationsInterface.php @@ -0,0 +1,7 @@ +addResolveTargetEntity( + EntityInheritance_MappedSuperclassWithTranslationsInterface::class, + EntityInheritance_MappedSuperclassWithTranslations_Entity::class, + [] + ); + + $this->entityManager->getEventManager()->addEventSubscriber($resolveTargetEntity); + + self::setupSchema([ + EntityInheritance_MappedSuperclassWithTranslations::class, + EntityInheritance_MappedSuperclassWithTranslations_Entity::class, + EntityInheritance_MappedSuperclassTranslation::class, + ]); + } + + public function testPersistAndReloadEntity(): void + { + $entity = new EntityInheritance_MappedSuperclassWithTranslations_Entity(); + $t = new Translatable('base text'); + $t->setTranslation('Basistext', 'de_DE'); + $entity->setText($t); + + self::import([$entity]); + + $loaded = $this->entityManager->find(EntityInheritance_MappedSuperclassWithTranslations_Entity::class, $entity->getId()); + + self::assertSame('base text', $loaded->getText()->translate('en_GB')); + self::assertSame('Basistext', $loaded->getText()->translate('de_DE')); + } + + public function testAddTranslation(): void + { + $entityManager = $this->entityManager; + + $entity = new EntityInheritance_MappedSuperclassWithTranslations_Entity(); + $entity->setText(new Translatable('base text')); + self::import([$entity]); + + $loaded = $entityManager->find(EntityInheritance_MappedSuperclassWithTranslations_Entity::class, $entity->getId()); + $loaded->getText()->setTranslation('Basistext', 'de_DE'); + $entityManager->flush(); + + $entityManager->clear(); + $reloaded = $entityManager->find(EntityInheritance_MappedSuperclassWithTranslations_Entity::class, $entity->getId()); + + self::assertSame('base text', $reloaded->getText()->translate('en_GB')); + self::assertSame('Basistext', $reloaded->getText()->translate('de_DE')); + } + + public function testUpdateTranslations(): void + { + $entityManager = $this->entityManager; + + $entity = new EntityInheritance_MappedSuperclassWithTranslations_Entity(); + $t = new Translatable('old text'); + $t->setTranslation('alter Text', 'de_DE'); + $entity->setText($t); + self::import([$entity]); + + $loaded = $entityManager->find(EntityInheritance_MappedSuperclassWithTranslations_Entity::class, $entity->getId()); + $loaded->getText()->setTranslation('new text'); + $loaded->getText()->setTranslation('neuer Text', 'de_DE'); + $entityManager->flush(); + + $entityManager->clear(); + $reloaded = $entityManager->find(EntityInheritance_MappedSuperclassWithTranslations_Entity::class, $entity->getId()); + + self::assertSame('new text', $reloaded->getText()->translate('en_GB')); + self::assertSame('neuer Text', $reloaded->getText()->translate('de_DE')); + } +} From e7d3d3237d798eaefd3be484f08ee6aa8aef5cab Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Fri, 27 Mar 2026 13:24:42 +0100 Subject: [PATCH 3/4] Revert unnecessary change --- src/Doctrine/PolyglotListener.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/Doctrine/PolyglotListener.php b/src/Doctrine/PolyglotListener.php index 5c8f273..58bc292 100644 --- a/src/Doctrine/PolyglotListener.php +++ b/src/Doctrine/PolyglotListener.php @@ -117,8 +117,7 @@ private function getTranslationMetadatas(object $entity, EntityManager $em): arr $this->translatableClassMetadatasByClass[$class] = []; $classMetadata = $em->getClassMetadata($class); - $candidates = array_merge([$classMetadata->name], $classMetadata->parentClasses); - foreach ($candidates as $className) { + foreach (array_merge([$classMetadata->name], $classMetadata->parentClasses) as $className) { if ($tm = $this->loadTranslationMetadataForClass($className, $em)) { $this->translatableClassMetadatasByClass[$class][] = $tm; } From 6dee7ba9227455fb82cedd6fbcd36e8ae7e0991b Mon Sep 17 00:00:00 2001 From: mpdude <1202333+mpdude@users.noreply.github.com> Date: Fri, 27 Mar 2026 12:25:10 +0000 Subject: [PATCH 4/4] Fix CS with PHP-CS-Fixer --- ...tityInheritance_MappedSuperclassWithTranslations_Entity.php | 3 --- tests/Functional/MappedSuperclassWithTranslationsTest.php | 1 - 2 files changed, 4 deletions(-) diff --git a/tests/Fixtures/Entity/EntityInheritance/EntityInheritance_MappedSuperclassWithTranslations_Entity.php b/tests/Fixtures/Entity/EntityInheritance/EntityInheritance_MappedSuperclassWithTranslations_Entity.php index 0dc15f1..d409875 100644 --- a/tests/Fixtures/Entity/EntityInheritance/EntityInheritance_MappedSuperclassWithTranslations_Entity.php +++ b/tests/Fixtures/Entity/EntityInheritance/EntityInheritance_MappedSuperclassWithTranslations_Entity.php @@ -2,11 +2,8 @@ namespace Webfactory\Bundle\PolyglotBundle\Tests\Fixtures\Entity\EntityInheritance; -use Doctrine\Common\Collections\ArrayCollection; -use Doctrine\Common\Collections\Collection; use Doctrine\ORM\Mapping as ORM; use Webfactory\Bundle\PolyglotBundle\Attribute as Polyglot; -use Webfactory\Bundle\PolyglotBundle\TranslatableInterface; #[Polyglot\Locale(primary: 'en_GB')] #[ORM\Entity] diff --git a/tests/Functional/MappedSuperclassWithTranslationsTest.php b/tests/Functional/MappedSuperclassWithTranslationsTest.php index 46224a3..2e85930 100644 --- a/tests/Functional/MappedSuperclassWithTranslationsTest.php +++ b/tests/Functional/MappedSuperclassWithTranslationsTest.php @@ -2,7 +2,6 @@ namespace Webfactory\Bundle\PolyglotBundle\Tests\Functional; -use Doctrine\ORM\Tools\ResolveTargetEntity; use Doctrine\ORM\Tools\ResolveTargetEntityListener; use Webfactory\Bundle\PolyglotBundle\Tests\Fixtures\Entity\EntityInheritance\EntityInheritance_MappedSuperclassTranslation; use Webfactory\Bundle\PolyglotBundle\Tests\Fixtures\Entity\EntityInheritance\EntityInheritance_MappedSuperclassWithTranslations;