From f2adf08061868bd5dea84ae3e1d27ff30a7f9d02 Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Tue, 2 Apr 2024 13:38:57 +0200 Subject: [PATCH 1/3] Drop annotations support --- src/Annotation/Locale.php | 27 ------ src/Annotation/Translatable.php | 26 ------ src/Annotation/TranslationCollection.php | 23 ----- src/Attribute/Locale.php | 3 +- src/Attribute/Translatable.php | 3 +- src/Attribute/TranslationCollection.php | 3 +- src/Doctrine/PolyglotListener.php | 4 +- src/Doctrine/TranslatableClassMetadata.php | 83 ++++++------------- src/Entity/BaseTranslation.php | 5 +- src/Translatable.php | 4 +- .../TranslatableClassMetadataTest.php | 4 +- tests/Functional/FunctionalTestBase.php | 3 +- 12 files changed, 35 insertions(+), 153 deletions(-) delete mode 100644 src/Annotation/Locale.php delete mode 100644 src/Annotation/Translatable.php delete mode 100644 src/Annotation/TranslationCollection.php diff --git a/src/Annotation/Locale.php b/src/Annotation/Locale.php deleted file mode 100644 index a210023..0000000 --- a/src/Annotation/Locale.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Webfactory\Bundle\PolyglotBundle\Annotation; - -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; -use Webfactory\Bundle\PolyglotBundle\Attribute\Locale as Attribute; - -/** - * @Annotation - * @NamedArgumentConstructor - * @Target({"CLASS","PROPERTY"}) - */ -final class Locale extends Attribute -{ - public function __construct(string $primary = null) - { - trigger_deprecation('webfactory/polyglot-bundle', '3.1.0', 'The %s annotation has been deprecated and will be removed in the 4.0 release. Use the %s attribute instead.', self::class, parent::class); - parent::__construct($primary); - } -} diff --git a/src/Annotation/Translatable.php b/src/Annotation/Translatable.php deleted file mode 100644 index ab1d3db..0000000 --- a/src/Annotation/Translatable.php +++ /dev/null @@ -1,26 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Webfactory\Bundle\PolyglotBundle\Annotation; - -use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor; -use Webfactory\Bundle\PolyglotBundle\Attribute\Translatable as Attribute; - -/** - * @Annotation - * @NamedArgumentConstructor - */ -final class Translatable extends Attribute -{ - public function __construct(string $translationFieldname = null) - { - trigger_deprecation('webfactory/polyglot-bundle', '3.1.0', 'The %s annotation has been deprecated and will be removed in the 4.0 release. Use the %s attribute instead.', self::class, parent::class); - parent::__construct($translationFieldname); - } -} diff --git a/src/Annotation/TranslationCollection.php b/src/Annotation/TranslationCollection.php deleted file mode 100644 index 6839601..0000000 --- a/src/Annotation/TranslationCollection.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Webfactory\Bundle\PolyglotBundle\Annotation; - -use Webfactory\Bundle\PolyglotBundle\Attribute\TranslationCollection as Attribute; - -/** - * @Annotation - */ -final class TranslationCollection extends Attribute -{ - public function __construct() - { - trigger_deprecation('webfactory/polyglot-bundle', '3.1.0', 'The %s annotation has been deprecated and will be removed in the 4.0 release. Use the %s attribute instead.', self::class, parent::class); - } -} diff --git a/src/Attribute/Locale.php b/src/Attribute/Locale.php index 68f7e81..015b88f 100644 --- a/src/Attribute/Locale.php +++ b/src/Attribute/Locale.php @@ -11,9 +11,8 @@ use Attribute; -/** @final */ #[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_PROPERTY)] -class Locale +final class Locale { private ?string $primary; diff --git a/src/Attribute/Translatable.php b/src/Attribute/Translatable.php index f176963..6e76267 100644 --- a/src/Attribute/Translatable.php +++ b/src/Attribute/Translatable.php @@ -11,9 +11,8 @@ use Attribute; -/** @final */ #[Attribute] -class Translatable +final class Translatable { private ?string $translationFieldname; diff --git a/src/Attribute/TranslationCollection.php b/src/Attribute/TranslationCollection.php index d140c7e..e4bd419 100644 --- a/src/Attribute/TranslationCollection.php +++ b/src/Attribute/TranslationCollection.php @@ -11,8 +11,7 @@ use Attribute; -/** @final */ #[Attribute] -class TranslationCollection +final class TranslationCollection { } diff --git a/src/Doctrine/PolyglotListener.php b/src/Doctrine/PolyglotListener.php index b83b02d..f822fbe 100644 --- a/src/Doctrine/PolyglotListener.php +++ b/src/Doctrine/PolyglotListener.php @@ -9,7 +9,6 @@ namespace Webfactory\Bundle\PolyglotBundle\Doctrine; -use Doctrine\Common\Annotations\Reader; use Doctrine\Common\EventSubscriber; use Doctrine\ORM\EntityManager; use Doctrine\ORM\Event\PostFlushEventArgs; @@ -50,7 +49,6 @@ final class PolyglotListener implements EventSubscriber private array $ejectedTranslatables = []; public function __construct( - private readonly Reader $annotationReader, private readonly DefaultLocaleProvider $defaultLocaleProvider, private readonly LoggerInterface $logger = null ?? new NullLogger(), ) { @@ -165,7 +163,7 @@ private function loadTranslationMetadataForClass($className, EntityManager $em): } // Load/parse - $meta = TranslatableClassMetadata::parseFromClass($className, $this->annotationReader, $metadataFactory); + $meta = TranslatableClassMetadata::parseFromClass($className, $metadataFactory); if (null !== $meta) { $meta->setLogger($this->logger); diff --git a/src/Doctrine/TranslatableClassMetadata.php b/src/Doctrine/TranslatableClassMetadata.php index 7aab9fb..86c58b0 100644 --- a/src/Doctrine/TranslatableClassMetadata.php +++ b/src/Doctrine/TranslatableClassMetadata.php @@ -9,7 +9,6 @@ namespace Webfactory\Bundle\PolyglotBundle\Doctrine; -use Doctrine\Common\Annotations\Reader; use Doctrine\ORM\EntityManager; use Doctrine\ORM\Mapping\ClassMetadataFactory; use Doctrine\ORM\Mapping\ClassMetadataInfo; @@ -17,14 +16,13 @@ use ReflectionClass; use ReflectionProperty; use RuntimeException; -use Webfactory\Bundle\PolyglotBundle\Annotation; use Webfactory\Bundle\PolyglotBundle\Attribute; use Webfactory\Bundle\PolyglotBundle\Locale\DefaultLocaleProvider; use Webfactory\Bundle\PolyglotBundle\Translatable; /** - * For an entity class that contains @Translatable annotations, this class holds metadata - * like which fields in the class are @Translatable_s, which field holds the collection + * For an entity class that contains #[Translatable] attributes, this class holds metadata + * like which fields in the class are #[Translatable]s, which field holds the collection * of translations etc. There need only be one instance of this class for every * entity class with translations. */ @@ -83,20 +81,20 @@ private function __construct( ) { } - public static function parseFromClass(string $class, Reader $reader, ClassMetadataFactory $classMetadataFactory): ?self + public static function parseFromClass(string $class, ClassMetadataFactory $classMetadataFactory): ?self { /** @var ClassMetadataInfo $cm */ $cm = $classMetadataFactory->getMetadataFor($class); $tm = new static($class); - $tm->findPrimaryLocale($cm, $reader); - $tm->findTranslationsCollection($cm, $reader, $classMetadataFactory); - $tm->findTranslatedProperties($cm, $reader, $classMetadataFactory); + $tm->findPrimaryLocale($cm); + $tm->findTranslationsCollection($cm, $classMetadataFactory); + $tm->findTranslatedProperties($cm, $classMetadataFactory); if ($tm->isClassWithoutTranslations()) { return null; } - $tm->assertAnnotationsAreComplete($class); + $tm->assertAttributesAreComplete($class); return $tm; } @@ -157,30 +155,30 @@ private function isClassWithoutTranslations(): bool && 0 === \count($this->translatedProperties); } - private function assertAnnotationsAreComplete(string $class): void + private function assertAttributesAreComplete(string $class): void { if (null === $this->translationClass) { - throw new RuntimeException(sprintf('Unable to find the translations for %s. There should be a one-to-may collection holding the translation entities, and it should be marked with %s.', $class, Annotation\TranslationCollection::class)); + throw new RuntimeException(sprintf('Unable to find the translations for %s. There should be a one-to-may collection holding the translation entities, and it should be marked with %s.', $class, Attribute\TranslationCollection::class)); } if (null === $this->translationLocaleProperty) { - throw new RuntimeException('The @Polyglot\Locale annotation at the language property of the translation class is missing or incorrect'); + throw new RuntimeException('The #[Polyglot\Locale] attribute at the language property of the translation class is missing or incorrect'); } if (null === $this->translationMappingProperty) { - throw new RuntimeException('The attribute referenced in the mappedBy-Attribute of the @ORM\OneToMany(..., mappedBy="...") is missing or incorrect'); + throw new RuntimeException('The property referenced in the mappedBy-property of the #[ORM\OneToMany(..., mappedBy: "...")] is missing or incorrect'); } if (0 === \count($this->translatedProperties)) { - throw new RuntimeException('No translatable attributes annotated with @Polyglot\Translatable were found'); + throw new RuntimeException('No translatable properties attributed with #[Polyglot\Translatable] were found'); } if (null === $this->primaryLocale) { - throw new RuntimeException(sprintf('Class %s uses translations, so it needs to provide the primary locale with the %s annotation at the class level. This can either be at the class itself, or in one of its parent classes.', $class, Annotation\Locale::class)); + throw new RuntimeException(sprintf('Class %s uses translations, so it needs to provide the primary locale with the %s attribute at the class level. This can either be at the class itself, or in one of its parent classes.', $class, Attribute\Locale::class)); } } - private function findTranslatedProperties(ClassMetadataInfo $cm, Reader $reader, ClassMetadataFactory $classMetadataFactory): void + private function findTranslatedProperties(ClassMetadataInfo $cm, ClassMetadataFactory $classMetadataFactory): void { if (!$this->translationClass) { return; @@ -194,33 +192,24 @@ private function findTranslatedProperties(ClassMetadataInfo $cm, Reader $reader, continue; } - $foundAttributeOrAnnotation = null; $reflectionProperty = $cm->getReflectionClass()->getProperty($fieldName); $attributes = $reflectionProperty->getAttributes(Attribute\Translatable::class); - if ($attributes) { - $foundAttributeOrAnnotation = $attributes[0]->newInstance(); - } else { - $foundAttributeOrAnnotation = $reader->getPropertyAnnotation($reflectionProperty, Annotation\Translatable::class); - - if ($foundAttributeOrAnnotation) { - trigger_deprecation('webfactory/polyglot-bundle', '3.1.0', 'Using the %s annotation on the %s::%s property is deprecated. Use the %s attribute instead.', Annotation\Translatable::class, $reflectionProperty->class, $reflectionProperty->name, Attribute\Translatable::class); - } + if (! $attributes) { + continue; } - if ($foundAttributeOrAnnotation) { - $translationFieldname = $foundAttributeOrAnnotation->getTranslationFieldname() ?: $fieldName; - $translationFieldReflectionProperty = $translationClassMetadata->getReflectionProperty($translationFieldname); + $attribute = $attributes[0]->newInstance(); + $translationFieldname = $attribute->getTranslationFieldname() ?: $fieldName; + $translationFieldReflectionProperty = $translationClassMetadata->getReflectionProperty($translationFieldname); - $this->translatedProperties[$fieldName] = $reflectionProperty; - $this->translationFieldMapping[$fieldName] = $translationFieldReflectionProperty; - } + $this->translatedProperties[$fieldName] = $reflectionProperty; + $this->translationFieldMapping[$fieldName] = $translationFieldReflectionProperty; } } - private function findTranslationsCollection(ClassMetadataInfo $cm, Reader $reader, ClassMetadataFactory $classMetadataFactory): void + private function findTranslationsCollection(ClassMetadataInfo $cm, ClassMetadataFactory $classMetadataFactory): void { - $found = false; foreach ($cm->associationMappings as $fieldName => $mapping) { if (isset($mapping['declared'])) { // The association is inherited from a parent class @@ -230,26 +219,19 @@ private function findTranslationsCollection(ClassMetadataInfo $cm, Reader $reade $reflectionProperty = $cm->getReflectionProperty($fieldName); if ($reflectionProperty->getAttributes(Attribute\TranslationCollection::class)) { - $found = true; - } elseif ($reader->getPropertyAnnotation($reflectionProperty, Annotation\TranslationCollection::class)) { - trigger_deprecation('webfactory/polyglot-bundle', '3.1.0', 'Using the %s annotation on the %s::%s property is deprecated. Use the %s attribute instead.', Annotation\TranslationCollection::class, $reflectionProperty->class, $reflectionProperty->name, Attribute\TranslationCollection::class); - $found = true; - } - - if ($found) { $this->translationsCollectionProperty = $reflectionProperty; $translationEntityMetadata = $classMetadataFactory->getMetadataFor($mapping['targetEntity']); $this->translationClass = $translationEntityMetadata->getReflectionClass(); $this->translationMappingProperty = $translationEntityMetadata->getReflectionProperty($mapping['mappedBy']); - $this->parseTranslationsEntity($reader, $translationEntityMetadata); + $this->parseTranslationsEntity($translationEntityMetadata); return; } } } - private function findPrimaryLocale(ClassMetadataInfo $cm, Reader $reader): void + private function findPrimaryLocale(ClassMetadataInfo $cm): void { foreach (array_merge([$cm->name], $cm->parentClasses) as $class) { $reflectionClass = new ReflectionClass($class); @@ -259,18 +241,10 @@ private function findPrimaryLocale(ClassMetadataInfo $cm, Reader $reader): void return; } - - $annotation = $reader->getClassAnnotation($reflectionClass, Annotation\Locale::class); - if (null !== $annotation) { - trigger_deprecation('webfactory/polyglot-bundle', '3.1.0', 'Using the %s annotation on the %s class is deprecated. Use the %s attribute instead.', Annotation\Locale::class, $reflectionClass->name, Attribute\Locale::class); - $this->primaryLocale = $annotation->getPrimary(); - - return; - } } } - private function parseTranslationsEntity(Reader $reader, ClassMetadataInfo $cm): void + private function parseTranslationsEntity(ClassMetadataInfo $cm): void { foreach ($cm->fieldMappings as $fieldName => $mapping) { $reflectionProperty = $cm->getReflectionProperty($fieldName); @@ -280,13 +254,6 @@ private function parseTranslationsEntity(Reader $reader, ClassMetadataInfo $cm): return; } - - if ($reader->getPropertyAnnotation($reflectionProperty, Annotation\Locale::class)) { - $this->translationLocaleProperty = $reflectionProperty; - trigger_deprecation('webfactory/polyglot-bundle', '3.1.0', 'Using the %s annotation on the %s::%s property is deprecated. Use the %s attribute instead.', Annotation\Locale::class, $reflectionProperty->class, $reflectionProperty->name, Attribute\Locale::class); - - return; - } } } diff --git a/src/Entity/BaseTranslation.php b/src/Entity/BaseTranslation.php index d2cb1b7..c2530c6 100644 --- a/src/Entity/BaseTranslation.php +++ b/src/Entity/BaseTranslation.php @@ -10,7 +10,7 @@ namespace Webfactory\Bundle\PolyglotBundle\Entity; use Doctrine\ORM\Mapping as ORM; -use Webfactory\Bundle\PolyglotBundle\Annotation as Polyglot; +use Webfactory\Bundle\PolyglotBundle\Attribute as Polyglot; /** * @ORM\MappedSuperclass @@ -28,9 +28,8 @@ class BaseTranslation /** * @ORM\Column - * - * @Polyglot\Locale */ + #[Polyglot\Locale] protected $locale; /** diff --git a/src/Translatable.php b/src/Translatable.php index 80dc358..bf68b17 100644 --- a/src/Translatable.php +++ b/src/Translatable.php @@ -28,11 +28,11 @@ * Die Klasse heisst "Translatable", damit sie in Klienten "nett" initialisiert werden * kann: * - * use Webfactory\Bundle\PolyglotBundle\Annotation as Polyglot; + * use Webfactory\Bundle\PolyglotBundle\Attribute as Polyglot; * use Webfactory\Bundle\PolyglotBundle\Translatable; * * class MyClass { ... - * // @ Polyglot\Translatable + * #[Polyglot\Translatable] * private $aField; * public function __construct() {... * $aField = new Translatable(); diff --git a/tests/Doctrine/TranslatableClassMetadataTest.php b/tests/Doctrine/TranslatableClassMetadataTest.php index d32f876..e977b9c 100644 --- a/tests/Doctrine/TranslatableClassMetadataTest.php +++ b/tests/Doctrine/TranslatableClassMetadataTest.php @@ -2,7 +2,6 @@ namespace Webfactory\Bundle\PolyglotBundle\Tests\Doctrine; -use Doctrine\Common\Annotations\AnnotationReader; use PHPUnit\Framework\TestCase; use Webfactory\Bundle\PolyglotBundle\Doctrine\TranslatableClassMetadata; use Webfactory\Bundle\PolyglotBundle\Tests\TestEntity; @@ -26,13 +25,12 @@ public function can_be_serialized_and_retrieved(): void private function createMetadata(): TranslatableClassMetadata { - $reader = new AnnotationReader(); $infrastructure = new ORMInfrastructure([ TestEntity::class, TestEntityTranslation::class, ]); $entityManager = $infrastructure->getEntityManager(); - return TranslatableClassMetadata::parseFromClass(TestEntity::class, $reader, $entityManager->getMetadataFactory()); + return TranslatableClassMetadata::parseFromClass(TestEntity::class, $entityManager->getMetadataFactory()); } } diff --git a/tests/Functional/FunctionalTestBase.php b/tests/Functional/FunctionalTestBase.php index 1fcd543..312b68f 100644 --- a/tests/Functional/FunctionalTestBase.php +++ b/tests/Functional/FunctionalTestBase.php @@ -2,7 +2,6 @@ namespace Webfactory\Bundle\PolyglotBundle\Tests\Functional; -use Doctrine\Common\Annotations\AnnotationReader; use Doctrine\DBAL\Types\Type; use Doctrine\ORM\EntityManagerInterface; use PHPUnit\Framework\TestCase; @@ -28,7 +27,7 @@ protected function setupOrmInfrastructure(array $classes): void $this->defaultLocaleProvider = new DefaultLocaleProvider('en_GB'); $this->entityManager->getEventManager()->addEventSubscriber( - new PolyglotListener(new AnnotationReader(), $this->defaultLocaleProvider) + new PolyglotListener($this->defaultLocaleProvider) ); } From 930e7880bfadc64a8f3238fd402db7fdf0864ee6 Mon Sep 17 00:00:00 2001 From: mpdude Date: Tue, 2 Apr 2024 11:40:23 +0000 Subject: [PATCH 2/3] Fix CS with PHP-CS-Fixer --- src/Doctrine/TranslatableClassMetadata.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Doctrine/TranslatableClassMetadata.php b/src/Doctrine/TranslatableClassMetadata.php index 86c58b0..a18a240 100644 --- a/src/Doctrine/TranslatableClassMetadata.php +++ b/src/Doctrine/TranslatableClassMetadata.php @@ -195,7 +195,7 @@ private function findTranslatedProperties(ClassMetadataInfo $cm, ClassMetadataFa $reflectionProperty = $cm->getReflectionClass()->getProperty($fieldName); $attributes = $reflectionProperty->getAttributes(Attribute\Translatable::class); - if (! $attributes) { + if (!$attributes) { continue; } From abc4e52db36c1a040eddf90832b8b9bf0e171a4a Mon Sep 17 00:00:00 2001 From: Matthias Pigulla Date: Thu, 4 Apr 2024 14:22:50 +0200 Subject: [PATCH 3/3] Update the changelog --- CHANGELOG.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9a03514..a1e871b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,11 @@ To get the diff for a specific change, go to https://github.com/webfactory/polyglot-bundle/commit/XXX where XXX is the change hash. To get the diff between two versions, go to https://github.com/webfactory/polyglot-bundle/compare/{oldversion}...{newversion}. +## Version 4.0.0 + +* Support for annotation-based configuration of translation properties has been removed. Switch to attribute-based configuration, which has been added in 3.1.0. +* Classes from the `Webfactory\Bundle\PolyglotBundle\Attribute` namespace are now `final`. + ## Version 3.1.0 * The annotations `\Webfactory\Bundle\PolyglotBundle\Annotation\Locale`, `\Webfactory\Bundle\PolyglotBundle\Annotation\Translatable` and `\Webfactory\Bundle\PolyglotBundle\Annotation\TranslationCollection` have been deprecated. Replace them with the corresponding PHP attributes from the `\Webfactory\Bundle\PolyglotBundle\Attribute` namespace.