Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Drop annotations support #42

Merged
merged 5 commits into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
27 changes: 0 additions & 27 deletions src/Annotation/Locale.php

This file was deleted.

26 changes: 0 additions & 26 deletions src/Annotation/Translatable.php

This file was deleted.

23 changes: 0 additions & 23 deletions src/Annotation/TranslationCollection.php

This file was deleted.

3 changes: 1 addition & 2 deletions src/Attribute/Locale.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@

use Attribute;

/** @final */
#[Attribute(Attribute::TARGET_CLASS | Attribute::TARGET_PROPERTY)]
class Locale
final class Locale
{
private ?string $primary;

Expand Down
3 changes: 1 addition & 2 deletions src/Attribute/Translatable.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@

use Attribute;

/** @final */
#[Attribute]
class Translatable
final class Translatable
{
private ?string $translationFieldname;

Expand Down
3 changes: 1 addition & 2 deletions src/Attribute/TranslationCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@

use Attribute;

/** @final */
#[Attribute]
class TranslationCollection
final class TranslationCollection
{
}
4 changes: 1 addition & 3 deletions src/Doctrine/PolyglotListener.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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(),
) {
Expand Down Expand Up @@ -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);
Expand Down
83 changes: 25 additions & 58 deletions src/Doctrine/TranslatableClassMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,22 +9,20 @@

namespace Webfactory\Bundle\PolyglotBundle\Doctrine;

use Doctrine\Common\Annotations\Reader;
use Doctrine\ORM\EntityManager;
use Doctrine\ORM\Mapping\ClassMetadataFactory;
use Doctrine\ORM\Mapping\ClassMetadataInfo;
use Psr\Log\LoggerInterface;
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.
*/
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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;
Expand All @@ -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
Expand All @@ -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);
Expand All @@ -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);
Expand All @@ -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;
}
}
}

Expand Down
3 changes: 0 additions & 3 deletions src/Entity/BaseTranslation.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@
namespace Webfactory\Bundle\PolyglotBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Webfactory\Bundle\PolyglotBundle\Annotation as PolyglotAnnotation;
use Webfactory\Bundle\PolyglotBundle\Attribute as Polyglot;

/**
Expand All @@ -33,8 +32,6 @@ class BaseTranslation

/**
* @ORM\Column
*
* @PolyglotAnnotation\Locale
*/
#[Polyglot\Locale]
#[ORM\Column]
Expand Down
4 changes: 2 additions & 2 deletions src/Translatable.php
Original file line number Diff line number Diff line change
Expand Up @@ -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();
Expand Down
4 changes: 1 addition & 3 deletions tests/Doctrine/TranslatableClassMetadataTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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());
}
}
Loading
Loading