Skip to content

Commit

Permalink
Allow using PHP attributes for configuration (#40)
Browse files Browse the repository at this point in the history
This PR introduces PHP attributes that can be used instead of the
current annotations. At the same time, annotations are being deprecated.

Annotations support will be removed in the next major version of this
bundle.
  • Loading branch information
mpdude authored Mar 28, 2024
1 parent 5d1fcc6 commit 212f4e5
Show file tree
Hide file tree
Showing 17 changed files with 182 additions and 107 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/lock-symfony-version.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#!/bin/sh

sed --in-place --regexp-extended --expression='/symfony\/error-handler/b; /symfony\/phpunit-bridge/b; s/"(symfony\/.*)": ".*"/"\1": "'$VERSION'"/' composer.json
sed --in-place --regexp-extended --expression='/symfony\/deprecation-contracts/b; /symfony\/error-handler/b; /symfony\/phpunit-bridge/b; s/"(symfony\/.*)": ".*"/"\1": "'$VERSION'"/' composer.json
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,12 @@

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 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.
* Using annotations to configure entity classes for this bundle has been deprecated. Switch to PHP 8 attributes.
* Attribute classes will be made `final` in the next major release.

## Version 3.0.0

* Dropped support for PHP versions below 8.1, and for Symfony versions before 5.4.
Expand Down
3 changes: 2 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,14 @@
"php": "8.1.*|8.2.*|8.3.*",
"doctrine/annotations": "^1.12",
"doctrine/collections": "^1.0",
"doctrine/event-manager": "^1.0",
"doctrine/dbal": "^2.3|^3.0",
"doctrine/event-manager": "^1.0",
"doctrine/orm": "^2.10",
"doctrine/persistence": "^1.3.8 | ^2.1",
"psr/log": "^1.0",
"symfony/config": "^5.4|^6.4|^7.0",
"symfony/dependency-injection": "^5.4|^6.4|^7.0",
"symfony/deprecation-contracts": "^2.0|^3.0",
"symfony/event-dispatcher": "^5.4|^6.4|^7.0",
"symfony/http-kernel": "^5.4|^6.4|^7.0"
},
Expand Down
13 changes: 4 additions & 9 deletions src/Annotation/Locale.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,23 +10,18 @@
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
final class Locale extends Attribute
{
private ?string $primary;

public function __construct(string $primary = null)
{
$this->primary = $primary;
}

public function getPrimary(): ?string
{
return $this->primary;
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);
}
}
13 changes: 4 additions & 9 deletions src/Annotation/Translatable.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,17 @@
namespace Webfactory\Bundle\PolyglotBundle\Annotation;

use Doctrine\Common\Annotations\Annotation\NamedArgumentConstructor;
use Webfactory\Bundle\PolyglotBundle\Attribute\Translatable as Attribute;

/**
* @Annotation
* @NamedArgumentConstructor
*/
final class Translatable
final class Translatable extends Attribute
{
private ?string $translationFieldname;

public function __construct(string $translationFieldname = null)
{
$this->translationFieldname = $translationFieldname;
}

public function getTranslationFieldname(): ?string
{
return $this->translationFieldname;
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);
}
}
8 changes: 7 additions & 1 deletion src/Annotation/TranslationCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,15 @@

namespace Webfactory\Bundle\PolyglotBundle\Annotation;

use Webfactory\Bundle\PolyglotBundle\Attribute\TranslationCollection as Attribute;

/**
* @Annotation
*/
final class TranslationCollection
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);
}
}
29 changes: 29 additions & 0 deletions src/Attribute/Locale.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

/*
* (c) webfactory GmbH <info@webfactory.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Webfactory\Bundle\PolyglotBundle\Attribute;

use Attribute;

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

public function __construct(string $primary = null)
{
$this->primary = $primary;
}

public function getPrimary(): ?string
{
return $this->primary;
}
}
29 changes: 29 additions & 0 deletions src/Attribute/Translatable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<?php

/*
* (c) webfactory GmbH <info@webfactory.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Webfactory\Bundle\PolyglotBundle\Attribute;

use Attribute;

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

public function __construct(string $translationFieldname = null)
{
$this->translationFieldname = $translationFieldname;
}

public function getTranslationFieldname(): ?string
{
return $this->translationFieldname;
}
}
18 changes: 18 additions & 0 deletions src/Attribute/TranslationCollection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?php

/*
* (c) webfactory GmbH <info@webfactory.de>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Webfactory\Bundle\PolyglotBundle\Attribute;

use Attribute;

/** @final */
#[Attribute]
class TranslationCollection
{
}
60 changes: 42 additions & 18 deletions src/Doctrine/TranslatableClassMetadata.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
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;

Expand Down Expand Up @@ -193,15 +194,22 @@ private function findTranslatedProperties(ClassMetadataInfo $cm, Reader $reader,
continue;
}

$foundAttributeOrAnnotation = null;
$reflectionProperty = $cm->getReflectionClass()->getProperty($fieldName);
$attributes = $reflectionProperty->getAttributes(Attribute\Translatable::class);

$annotation = $reader->getPropertyAnnotation(
$reflectionProperty,
Annotation\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 ($annotation) {
$translationFieldname = $annotation->getTranslationFieldname() ?: $fieldName;
if ($foundAttributeOrAnnotation) {
$translationFieldname = $foundAttributeOrAnnotation->getTranslationFieldname() ?: $fieldName;
$translationFieldReflectionProperty = $translationClassMetadata->getReflectionProperty($translationFieldname);

$this->translatedProperties[$fieldName] = $reflectionProperty;
Expand All @@ -212,19 +220,24 @@ private function findTranslatedProperties(ClassMetadataInfo $cm, Reader $reader,

private function findTranslationsCollection(ClassMetadataInfo $cm, Reader $reader, ClassMetadataFactory $classMetadataFactory): void
{
$found = false;
foreach ($cm->associationMappings as $fieldName => $mapping) {
if (isset($mapping['declared'])) {
// The association is inherited from a parent class
continue;
}

$annotation = $reader->getPropertyAnnotation(
$cm->getReflectionProperty($fieldName),
Annotation\TranslationCollection::class
);
$reflectionProperty = $cm->getReflectionProperty($fieldName);

if ($annotation) {
$this->translationsCollectionProperty = $cm->getReflectionClass()->getProperty($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();
Expand All @@ -239,8 +252,17 @@ private function findTranslationsCollection(ClassMetadataInfo $cm, Reader $reade
private function findPrimaryLocale(ClassMetadataInfo $cm, Reader $reader): void
{
foreach (array_merge([$cm->name], $cm->parentClasses) as $class) {
$annotation = $reader->getClassAnnotation(new ReflectionClass($class), Annotation\Locale::class);
$reflectionClass = new ReflectionClass($class);

foreach ($reflectionClass->getAttributes(Attribute\Locale::class) as $attribute) {
$this->primaryLocale = $attribute->newInstance()->getPrimary();

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;
Expand All @@ -253,13 +275,15 @@ private function parseTranslationsEntity(Reader $reader, ClassMetadataInfo $cm):
foreach ($cm->fieldMappings as $fieldName => $mapping) {
$reflectionProperty = $cm->getReflectionProperty($fieldName);

$annotation = $reader->getPropertyAnnotation(
$reflectionProperty,
Annotation\Locale::class
);
if ($reflectionProperty->getAttributes(Attribute\Locale::class)) {
$this->translationLocaleProperty = $reflectionProperty;

return;
}

if ($annotation) {
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
14 changes: 5 additions & 9 deletions tests/Functional/CascadePersistTranslationsTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Webfactory\Bundle\PolyglotBundle\Annotation as Polyglot;
use Webfactory\Bundle\PolyglotBundle\Attribute as Polyglot;
use Webfactory\Bundle\PolyglotBundle\Translatable;
use Webfactory\Bundle\PolyglotBundle\TranslatableInterface;

Expand Down Expand Up @@ -46,9 +46,8 @@ public function adding_and_persisting_translations(): void

/**
* @ORM\Entity
*
* @Polyglot\Locale(primary="en_GB")
*/
#[Polyglot\Locale(primary: 'en_GB')]
class CascadePersistTranslationsTest_Entity
{
/**
Expand All @@ -64,16 +63,14 @@ class CascadePersistTranslationsTest_Entity
* (!) There is *not* cascade="persist" configuration here.
*
* @ORM\OneToMany(targetEntity="CascadePersistTranslationsTest_Translation", mappedBy="entity")
*
* @Polyglot\TranslationCollection
*/
#[Polyglot\TranslationCollection]
protected Collection $translations;

/**
* @ORM\Column(type="string")
*
* @Polyglot\Translatable
*/
#[Polyglot\Translatable]
protected string|TranslatableInterface $text;

public function __construct()
Expand Down Expand Up @@ -104,9 +101,8 @@ class CascadePersistTranslationsTest_Translation

/**
* @ORM\Column
*
* @Polyglot\Locale
*/
#[Polyglot\Locale]
private string $locale;

/**
Expand Down
Loading

0 comments on commit 212f4e5

Please sign in to comment.