diff --git a/src/Doctrine/TranslatableClassMetadata.php b/src/Doctrine/TranslatableClassMetadata.php index 3e6b77c..6ec22db 100644 --- a/src/Doctrine/TranslatableClassMetadata.php +++ b/src/Doctrine/TranslatableClassMetadata.php @@ -207,7 +207,7 @@ private function findTranslatedProperties(ClassMetadata $cm, ClassMetadataFactor } /* Class-level #[TranslatedProperty] attributes */ - foreach ($reflectionClass->getAttributes(Attribute\TranslatedProperty::class) as $classAttribute) { + foreach (self::getAttributesIncludingParents($reflectionClass, Attribute\TranslatedProperty::class) as $classAttribute) { $attribute = $classAttribute->newInstance(); $propertyName = $attribute->getPropertyName(); @@ -219,7 +219,7 @@ private function findTranslatedProperties(ClassMetadata $cm, ClassMetadataFactor continue; } - $candidates[$propertyName] = $attribute->getTranslationFieldname(); + $candidates[$propertyName] ??= $attribute->getTranslationFieldname(); } /* Register all collected candidates */ @@ -229,6 +229,20 @@ private function findTranslatedProperties(ClassMetadata $cm, ClassMetadataFactor } } + private static function getAttributesIncludingParents(ReflectionClass $rc, ?string $attributeName = null, int $flags = 0): array + { + $attributes = []; + + do { + $attributes = array_merge( + $attributes, + $rc->getAttributes($attributeName, $flags) + ); + } while ($rc = $rc->getParentClass()); + + return $attributes; + } + private function findTranslationsCollection(ClassMetadata $cm, ClassMetadataFactory $classMetadataFactory): void { foreach ($cm->associationMappings as $fieldName => $mapping) { @@ -256,14 +270,10 @@ private function findTranslationsCollection(ClassMetadata $cm, ClassMetadataFact private function findPrimaryLocale(ClassMetadata $cm): void { - foreach (array_merge([$cm->name], $cm->parentClasses) as $class) { - $reflectionClass = new ReflectionClass($class); - - foreach ($reflectionClass->getAttributes(Attribute\Locale::class) as $attribute) { - $this->primaryLocale = $attribute->newInstance()->getPrimary(); + $attributes = self::getAttributesIncludingParents($cm->getReflectionClass(), Attribute\Locale::class); - return; - } + if ($attributes) { + $this->primaryLocale = $attributes[0]->newInstance()->getPrimary(); } } diff --git a/tests/Doctrine/TranslatableClassMetadataTest.php b/tests/Doctrine/TranslatableClassMetadataTest.php index 0b010cf..a21d9c2 100644 --- a/tests/Doctrine/TranslatableClassMetadataTest.php +++ b/tests/Doctrine/TranslatableClassMetadataTest.php @@ -4,6 +4,7 @@ use Doctrine\Persistence\Mapping\RuntimeReflectionService; use Webfactory\Bundle\PolyglotBundle\Doctrine\TranslatableClassMetadata; +use Webfactory\Bundle\PolyglotBundle\Tests\Fixtures\Entity\EntityInheritance\EntityInheritance_MappedSuperclassChainEntityLocaleOverride; use Webfactory\Bundle\PolyglotBundle\Tests\Fixtures\Entity\TestEntity; use Webfactory\Bundle\PolyglotBundle\Tests\Functional\DatabaseFunctionalTestCase; @@ -22,6 +23,21 @@ public function can_be_serialized_and_retrieved(): void self::assertEquals($metadata, $unserialized); } + /** + * @test + */ + public function subclass_locale_takes_priority_over_parent_locale(): void + { + $metadata = TranslatableClassMetadata::parseFromClass( + EntityInheritance_MappedSuperclassChainEntityLocaleOverride::class, + $this->entityManager->getMetadataFactory() + ); + + // EntityInheritance_MappedSuperclassChain (parent) declares en_GB, + // EntityInheritance_MappedSuperclassChainEntityLocaleOverride (child) declares de_DE. + self::assertSame('de_DE', $metadata->sleep()->primaryLocale); + } + private function createMetadata(): TranslatableClassMetadata { $entityManager = $this->entityManager; diff --git a/tests/Fixtures/Entity/EntityInheritance/EntityInheritance_MappedSuperclassChain.php b/tests/Fixtures/Entity/EntityInheritance/EntityInheritance_MappedSuperclassChain.php new file mode 100644 index 0000000..8529180 --- /dev/null +++ b/tests/Fixtures/Entity/EntityInheritance/EntityInheritance_MappedSuperclassChain.php @@ -0,0 +1,35 @@ +translations = new ArrayCollection(); + } + + public function setText(TranslatableInterface $text): void + { + $this->text = $text; + } +} diff --git a/tests/Fixtures/Entity/EntityInheritance/EntityInheritance_MappedSuperclassChainEntity.php b/tests/Fixtures/Entity/EntityInheritance/EntityInheritance_MappedSuperclassChainEntity.php new file mode 100644 index 0000000..7bc6e53 --- /dev/null +++ b/tests/Fixtures/Entity/EntityInheritance/EntityInheritance_MappedSuperclassChainEntity.php @@ -0,0 +1,16 @@ +setTranslation('Basistext', 'de_DE'); + $entity->setText($t); + + self::import([$entity]); + + $loaded = $this->entityManager->find(EntityInheritance_MappedSuperclassChainEntity::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_MappedSuperclassChainEntity(); + $entity->setText(new Translatable('base text')); + self::import([$entity]); + + $loaded = $entityManager->find(EntityInheritance_MappedSuperclassChainEntity::class, $entity->getId()); + $loaded->getText()->setTranslation('Basistext', 'de_DE'); + $entityManager->flush(); + + $entityManager->clear(); + $reloaded = $entityManager->find(EntityInheritance_MappedSuperclassChainEntity::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_MappedSuperclassChainEntity(); + $t = new Translatable('old text'); + $t->setTranslation('alter Text', 'de_DE'); + $entity->setText($t); + self::import([$entity]); + + $loaded = $entityManager->find(EntityInheritance_MappedSuperclassChainEntity::class, $entity->getId()); + $loaded->getText()->setTranslation('new text'); + $loaded->getText()->setTranslation('neuer Text', 'de_DE'); + $entityManager->flush(); + + $entityManager->clear(); + $reloaded = $entityManager->find(EntityInheritance_MappedSuperclassChainEntity::class, $entity->getId()); + + self::assertSame('new text', $reloaded->getText()->translate('en_GB')); + self::assertSame('neuer Text', $reloaded->getText()->translate('de_DE')); + } +}