From ccd86f482ceb80145b458c5f4be41593acd02cc7 Mon Sep 17 00:00:00 2001 From: Laurent Bientz Date: Sun, 22 Mar 2020 08:55:18 +0100 Subject: [PATCH] Decouple label building from Twig extension (#138) It can be reused from PHP --- README.md | 49 ++++++++++++++- .../Compiler/TranslatorCompilerPass.php | 2 +- .../Greg0ireEnumExtension.php | 2 + .../Symfony/Resources/config/services.xml | 14 +++++ src/Bridge/Symfony/Translator/GetLabel.php | 62 +++++++++++++++++++ src/Bridge/Twig/Extension/EnumExtension.php | 45 +++++--------- .../Greg0ireEnumExtensionTest.php | 4 +- .../Twig/Extension/EnumExtensionTest.php | 3 +- 8 files changed, 148 insertions(+), 33 deletions(-) create mode 100644 src/Bridge/Symfony/Resources/config/services.xml create mode 100644 src/Bridge/Symfony/Translator/GetLabel.php diff --git a/README.md b/README.md index 16b7036..0a3e5f7 100644 --- a/README.md +++ b/README.md @@ -148,6 +148,50 @@ final class MyEnum extends AbstractEnum } ``` +### Get label from a service + +If you want to get the constant label behind an enum value, you can instantiate +the `GetLabel` class and invoke it. + +```php +use Greg0ire\Enum\Bridge\Symfony\Translator\GetLabel; + +$label = new GetLabel(); +$label($value, 'Your\Enum\Class'); +``` + +To enable translation, require the `symfony/translation` component +and pass a `Symfony\Contracts\Translation\TranslationInterface` instance on the +`GetLabel` constructor + +```php +use Greg0ire\Enum\Bridge\Symfony\Translator\GetLabel; +use Symfony\Contracts\Translation\TranslationInterface; + +$label = new GetLabel($translator); +$label($value, 'Your\Enum\Class'); +``` + +If you're using Symfony, tag the service and simply inject it. +If translations are enabled, the `TranslatorInterface` will be automatically injected. + +```yaml +services: + # ... + Greg0ire\Enum\Bridge\Symfony\Translator\Label: "@greg0ire_enum.symfony.translator.label" +``` + +```php +public function index(GetLabel $label) +{ + $label($value, 'Your\Enum\Class'); + $label($value, 'Your\Enum\Class', 'another_domain'); // Change the translation domain + $label($value, 'Your\Enum\Class', false); // Disable translation. In this case the class prefix wont be added + $label($value, 'Your\Enum\Class', false, true); // Disable translation but keep class prefix + $label($value, 'Your\Enum\Class', false, true, '.'); // Disable translation but keep class prefix with a custom separator +} +``` + ### Integration with other libraries `greg0ire/enum` integrates with other libraries. The list is available in the @@ -230,8 +274,11 @@ You have to require the `twig/twig` package to get it working. The `enum_label` filter will try to return the constant label corresponding to the given value. +This filter relies on the `Greg0ire\Enum\Bridge\Symfony\Translator\GetLabel` service. + It will try to translate it if possible. To enable translation, require the `symfony/translation` component -and pass a `Symfony\Contracts\Translation\TranslationInterface` instance on the `EnumExtension` constructor. +and pass a `Symfony\Contracts\Translation\TranslationInterface` instance on the `GetLabel` constructor. +`GetLabel` instance will be injected on the `EnumExtension` constructor. If translation is not available, you will have the default label with class prefixing. diff --git a/src/Bridge/Symfony/DependencyInjection/Compiler/TranslatorCompilerPass.php b/src/Bridge/Symfony/DependencyInjection/Compiler/TranslatorCompilerPass.php index c5f3e18..8e22664 100644 --- a/src/Bridge/Symfony/DependencyInjection/Compiler/TranslatorCompilerPass.php +++ b/src/Bridge/Symfony/DependencyInjection/Compiler/TranslatorCompilerPass.php @@ -18,7 +18,7 @@ final class TranslatorCompilerPass implements CompilerPassInterface public function process(ContainerBuilder $container): void { if (class_exists(AbstractExtension::class) && $container->hasDefinition('translator.default')) { - $container->getDefinition('greg0ire_enum.twig.extension.enum') + $container->getDefinition('greg0ire_enum.symfony.translator.label') ->addArgument(new Reference('translator.default')); } } diff --git a/src/Bridge/Symfony/DependencyInjection/Greg0ireEnumExtension.php b/src/Bridge/Symfony/DependencyInjection/Greg0ireEnumExtension.php index 6313c2b..c254696 100644 --- a/src/Bridge/Symfony/DependencyInjection/Greg0ireEnumExtension.php +++ b/src/Bridge/Symfony/DependencyInjection/Greg0ireEnumExtension.php @@ -23,5 +23,7 @@ public function load(array $configs, ContainerBuilder $container): void if (class_exists(AbstractExtension::class)) { $loader->load('twig.xml'); } + + $loader->load('services.xml'); } } diff --git a/src/Bridge/Symfony/Resources/config/services.xml b/src/Bridge/Symfony/Resources/config/services.xml new file mode 100644 index 0000000..c390272 --- /dev/null +++ b/src/Bridge/Symfony/Resources/config/services.xml @@ -0,0 +1,14 @@ + + + + + + + + + + + + diff --git a/src/Bridge/Symfony/Translator/GetLabel.php b/src/Bridge/Symfony/Translator/GetLabel.php new file mode 100644 index 0000000..830ef3f --- /dev/null +++ b/src/Bridge/Symfony/Translator/GetLabel.php @@ -0,0 +1,62 @@ +translator = $translator; + } + + /** + * Displays the label corresponding to a specific value of an enumeration. + * + * @param mixed $value Must exists in the enumeration class specified with $class + * @param string $class The enum class name + * @param string|bool $translationDomain the translation domain to use if the translator if available. + * string: Use the specified one + * null: Use the default one + * false: Do not use the translator + * @param bool $classPrefixed Prefix the label with the enum class. Defaults to true if the translator + * is available and enabled, false otherwise. + * @param string $namespaceSeparator namespace separator to use with the class prefix. + * This takes effect only if $classPrefixed is true + */ + public function __invoke( + $value, + string $class, + $translationDomain = null, + ?bool $classPrefixed = null, + ?string $namespaceSeparator = null + ): string { + // Determine if the translator can be used or not. + $useTranslation = $this->translator instanceof TranslatorInterface + && (is_null($translationDomain) || is_string($translationDomain)); + + // If not defined, guess the default behavior. + if (is_null($classPrefixed)) { + $classPrefixed = $useTranslation; + } + + $label = array_search( + $value, + call_user_func([$class, 'getConstants'], 'strtolower', $classPrefixed, $namespaceSeparator) + ); + + if ($useTranslation) { + $translatedLabel = $this->translator->trans($label, [], $translationDomain); + + return $translatedLabel ?: $label; + } + + return $label; + } +} diff --git a/src/Bridge/Twig/Extension/EnumExtension.php b/src/Bridge/Twig/Extension/EnumExtension.php index f6d750c..dc8b5fa 100644 --- a/src/Bridge/Twig/Extension/EnumExtension.php +++ b/src/Bridge/Twig/Extension/EnumExtension.php @@ -3,6 +3,7 @@ namespace Greg0ire\Enum\Bridge\Twig\Extension; use Greg0ire\Enum\AbstractEnum; +use Greg0ire\Enum\Bridge\Symfony\Translator\GetLabel; use Symfony\Contracts\Translation\TranslatorInterface; use Twig\Extension\AbstractExtension; use Twig\TwigFilter; @@ -14,16 +15,24 @@ final class EnumExtension extends AbstractExtension { /** - * @var TranslatorInterface + * @var GetLabel */ - private $translator; + private $label; - /** - * @param TranslatorInterface $translator - */ - public function __construct(TranslatorInterface $translator = null) + public function __construct($label) { - $this->translator = $translator; + if ($label instanceof TranslatorInterface) { + @trigger_error(sprintf( + 'Providing a %s instance to %s is deprecated and will not be supported in 5.0. Please provide a %s instance instead.', + TranslatorInterface::class, + __METHOD__, + GetLabel::class + ), E_USER_DEPRECATED); + $this->label = new GetLabel($label); + + return; + } + $this->label = $label; } /** @@ -67,27 +76,7 @@ public function label( ?bool $classPrefixed = null, ?string $namespaceSeparator = null ): string { - // Determine if the translator can be used or not. - $useTranslation = $this->translator instanceof TranslatorInterface - && (is_null($translationDomain) || is_string($translationDomain)); - - // If not defined, guess the default behavior. - if (is_null($classPrefixed)) { - $classPrefixed = $useTranslation; - } - - $label = array_search( - $value, - call_user_func([$class, 'getConstants'], 'strtolower', $classPrefixed, $namespaceSeparator) - ); - - if ($useTranslation) { - $translatedLabel = $this->translator->trans($label, [], $translationDomain); - - return $translatedLabel ?: $label; - } - - return $label; + return ($this->label)($value, $class, $translationDomain, $classPrefixed, $namespaceSeparator); } /** diff --git a/tests/Bridge/Symfony/DependencyInjection/Greg0ireEnumExtensionTest.php b/tests/Bridge/Symfony/DependencyInjection/Greg0ireEnumExtensionTest.php index 212e26d..21120f9 100644 --- a/tests/Bridge/Symfony/DependencyInjection/Greg0ireEnumExtensionTest.php +++ b/tests/Bridge/Symfony/DependencyInjection/Greg0ireEnumExtensionTest.php @@ -60,7 +60,7 @@ public function testLoadWithoutATranslator() ); } - public function testTwigExtensionHasTheTranslator() + public function testLabelServiceHasTheTranslator() { $this->registerService('translator.logging.inner', \stdClass::class); $this->registerService('logger', \stdClass::class); @@ -74,7 +74,7 @@ public function testTwigExtensionHasTheTranslator() $compilerPass->process($this->container); $this->assertContainerBuilderHasServiceDefinitionWithArgument( - 'greg0ire_enum.twig.extension.enum', + 'greg0ire_enum.symfony.translator.label', 0, new Reference('translator.default') ); diff --git a/tests/Bridge/Twig/Extension/EnumExtensionTest.php b/tests/Bridge/Twig/Extension/EnumExtensionTest.php index 95a56de..30d3f97 100644 --- a/tests/Bridge/Twig/Extension/EnumExtensionTest.php +++ b/tests/Bridge/Twig/Extension/EnumExtensionTest.php @@ -2,6 +2,7 @@ namespace Greg0ire\Enum\Tests\Bridge\Twig\Extension; +use Greg0ire\Enum\Bridge\Symfony\Translator\GetLabel; use Greg0ire\Enum\Bridge\Twig\Extension\EnumExtension; use Greg0ire\Enum\Tests\Fixtures\FooEnum; use Greg0ire\Enum\Tests\Fixtures\FooInterface; @@ -32,7 +33,7 @@ final class EnumExtensionTest extends TestCase protected function setUp(): void { $this->translator = $this->createMock(TranslatorInterface::class); - $this->extension = new EnumExtension($this->translator); + $this->extension = new EnumExtension(new GetLabel($this->translator)); } public function testEnvironment()