Skip to content

Commit

Permalink
Twig integration
Browse files Browse the repository at this point in the history
Add an optional Twig integration to have an extension for enum labels.
  • Loading branch information
soullivaneuh committed Jun 23, 2016
1 parent b78bc42 commit 34dffca
Show file tree
Hide file tree
Showing 9 changed files with 347 additions and 1 deletion.
47 changes: 47 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,53 @@ $view = $this->factory->create(EnumType::class, null, array(
))->createView();
```

#### Twig extension

This package comes with an `enum_label` filter, available thanks to the `EnumExtension` Twig class.
You have to require the `twig/twig` package to get it working.

The filter will try to return the constant label corresponding to the given value.

It will try to translate it if possible. To enable translation, require the `symfony/translation` component
and pass a `Symfony\Component\Translation\TranslationInterface` instance on the `EnumExtension` constructor.

If translation is not available, you will have the default label with class prefixing.

Usage:

```twig
{{ value|enum_label('Your\\Enum\\Class') }}
{{ value|enum_label('Your\\Enum\\Class', 'another_domain') }} {# Change the translation domain #}
{{ value|enum_label('Your\\Enum\\Class', false) }} {# Disable translation. In this case the class prefix wont be added #}
{{ value|enum_label('Your\\Enum\\Class', false, true) }} {# Disable translation but keep class prefix #}
{{ value|enum_label('Your\\Enum\\Class', false, true, '.') }} {# Disable translation but keep class prefix with a custom separator #}
```

##### Twig extension as a service

On Symfony projects, the extension can be autoloaded.
First, you have to require the `symfony/framework-bundle` and `symfony/twig-bundle` packages, or use Symfony fullstack.

Then, register the bundle in the kernel of your application:

``` php
// app/AppKernel.php

public function registerBundles()
{
$bundles = array(
// ...
new Greg0ire\Enum\Bridge\Symfony\Bundle\Greg0ireEnumBundle(),
);

// ...

return $bundles
}
```

That's all. You can now directly use the filter.

## Contributing

see [CONTRIBUTING.md][1]
Expand Down
6 changes: 5 additions & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,14 @@
"doctrine/inflector": "^1.0"
},
"require-dev": {
"matthiasnoback/symfony-dependency-injection-test": "^0.7.6",
"phpunit/phpunit": "^4.1",
"sllh/php-cs-fixer-styleci-bridge": "^2.0",
"symfony/form": "^2.7 || ^3.0",
"symfony/validator": "^2.7 || ^3.0"
"symfony/framework-bundle": "^2.7 || ^3.0",
"symfony/twig-bundle": "^2.7 || ^3.0",
"symfony/validator": "^2.7 || ^3.0",
"twig/twig": "^1.24"
},
"suggest": {
"symfony/form": "To use enum form type",
Expand Down
20 changes: 20 additions & 0 deletions src/Bridge/Symfony/Bundle/Greg0ireEnumBundle.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<?php

namespace Greg0ire\Enum\Bridge\Symfony\Bundle;

use Greg0ire\Enum\Bridge\Symfony\DependencyInjection\Greg0ireEnumExtension;
use Symfony\Component\HttpKernel\Bundle\Bundle;

/**
* @author Sullivan Senechal <[email protected]>
*/
final class Greg0ireEnumBundle extends Bundle
{
/**
* {@inheritdoc}
*/
protected function getContainerExtensionClass()
{
return Greg0ireEnumExtension::class;
}
}
33 changes: 33 additions & 0 deletions src/Bridge/Symfony/DependencyInjection/Greg0ireEnumExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<?php

namespace Greg0ire\Enum\Bridge\Symfony\DependencyInjection;

use Symfony\Bundle\FrameworkBundle\Translation\Translator;
use Symfony\Component\Config\FileLocator;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\DependencyInjection\Extension\Extension;
use Symfony\Component\DependencyInjection\Loader\XmlFileLoader;
use Symfony\Component\DependencyInjection\Reference;

/**
* @author Sullivan Senechal <[email protected]>
*/
final class Greg0ireEnumExtension extends Extension
{
/**
* {@inheritdoc}
*/
public function load(array $configs, ContainerBuilder $container)
{
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));

if (class_exists(\Twig_Extension::class)) {
$loader->load('twig.xml');

if (class_exists(Translator::class)) {
$container->getDefinition('greg0ire_enum.twig.extension.enum')
->addArgument(new Reference('translator.default'));
}
}
}
}
11 changes: 11 additions & 0 deletions src/Bridge/Symfony/Resources/config/twig.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" ?>

<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://symfony.com/schema/dic/services http://symfony.com/schema/dic/services/services-1.0.xsd">
<services>
<service id="greg0ire_enum.twig.extension.enum" class="Greg0ire\Enum\Bridge\Twig\Extension\EnumExtension">
<tag name="twig.extension"/>
</service>
</services>
</container>
83 changes: 83 additions & 0 deletions src/Bridge/Twig/Extension/EnumExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
<?php

namespace Greg0ire\Enum\Bridge\Twig\Extension;

use Symfony\Component\Translation\TranslatorInterface;

/**
* @author Sullivan Senechal <[email protected]>
*/
final class EnumExtension extends \Twig_Extension
{
/**
* @var TranslatorInterface
*/
private $translator;

/**
* @param TranslatorInterface $translator
*/
public function __construct(TranslatorInterface $translator = null)
{
$this->translator = $translator;
}

/**
* {@inheritdoc}
*/
public function getFilters()
{
return [
new \Twig_SimpleFilter('enum_label', [$this, 'label']),
];
}

/**
* 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.
*
* @return string
*/
public function label($value, $class, $translationDomain = null, $classPrefixed = null, $namespaceSeparator = null)
{
// 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;
}

/**
* {@inheritdoc}
*/
public function getName()
{
return 'greg0ire_enum';
}
}
38 changes: 38 additions & 0 deletions test/Bridge/Symfony/Bundle/Greg0ireEnumBundleTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace Greg0ire\Enum\Tests\Bridge\Symfony\Bundle;

use Greg0ire\Enum\Bridge\Symfony\Bundle\Greg0ireEnumBundle;
use Greg0ire\Enum\Bridge\Symfony\DependencyInjection\Greg0ireEnumExtension;
use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractContainerBuilderTestCase;

/**
* @author Sullivan Senechal <[email protected]>
*/
final class Greg0ireEnumBundleTest extends AbstractContainerBuilderTestCase
{
/**
* @var Greg0ireEnumBundle
*/
private $bundle;

/**
* {@inheritdoc}
*/
protected function setUp()
{
parent::setUp();

$this->bundle = new Greg0ireEnumBundle();
}

public function testBuild()
{
$this->bundle->build($this->container);
}

public function testGetContainerExtension()
{
$this->assertInstanceOf(Greg0ireEnumExtension::class, $this->bundle->getContainerExtension());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?php

namespace Greg0ire\Enum\Tests\Bridge\Symfony\DependencyInjection;

use Greg0ire\Enum\Bridge\Symfony\DependencyInjection\Greg0ireEnumExtension;
use Greg0ire\Enum\Bridge\Twig\Extension\EnumExtension;
use Matthias\SymfonyDependencyInjectionTest\PhpUnit\AbstractExtensionTestCase;
use Symfony\Component\DependencyInjection\Reference;

/**
* @author Sullivan Senechal <[email protected]>
*/
final class Greg0ireEnumExtensionTest extends AbstractExtensionTestCase
{
public function testLoad()
{
$this->load();

$this->assertContainerBuilderHasService('greg0ire_enum.twig.extension.enum', EnumExtension::class);
$this->assertContainerBuilderHasServiceDefinitionWithArgument(
'greg0ire_enum.twig.extension.enum',
0,
new Reference('translator.default')
);
}

/**
* {@inheritdoc}
*/
protected function getContainerExtensions()
{
return [
new Greg0ireEnumExtension(),
];
}
}
74 changes: 74 additions & 0 deletions test/Bridge/Twig/Extension/EnumExtensionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
<?php

namespace Greg0ire\Enum\Tests\Bridge\Twig\Extension;

use Greg0ire\Enum\Bridge\Twig\Extension\EnumExtension;
use Greg0ire\Enum\Tests\Fixtures\FooEnum;
use Greg0ire\Enum\Tests\Fixtures\FooInterface;
use Symfony\Component\Translation\TranslatorInterface;

/**
* @author Sullivan Senechal <[email protected]>
*/
final class EnumExtensionTest extends \PHPUnit_Framework_TestCase
{
/**
* @var TranslatorInterface|\PHPUnit_Framework_MockObject_MockObject
*/
private $translator;

/**
* @var EnumExtension
*/
private $extension;

/**
* {@inheritdoc}
*/
protected function setUp()
{
$this->translator = $this->getMock(TranslatorInterface::class);
$this->extension = new EnumExtension($this->translator);
}

public function testEnvironment()
{
$twig = new \Twig_Environment();
$twig->addExtension($this->extension);

$this->assertTrue($twig->hasExtension('greg0ire_enum'));
$this->assertInstanceOf(\Twig_SimpleFilter::class, $twig->getFilter('enum_label'));
}

/**
* @dataProvider getLabels
*/
public function testLabel($value, $class, $classPrefix, $separator, $expectedResult)
{
$this->assertSame(
$expectedResult,
$this->extension->label($value, $class, false, $classPrefix, $separator)
);
}

public function getLabels()
{
return [
[FooInterface::CHUCK, FooEnum::class, false, null, 'chuck'],
[FooInterface::CHUCK, FooEnum::class, true, null, 'greg0ire_enum_tests_fixtures_foo_enum_chuck'],
[FooInterface::CHUCK, FooEnum::class, true, '.', 'greg0ire.enum.tests.fixtures.foo_enum.chuck'],
];
}

public function testLabelWithTranslator()
{
$this->translator->expects($this->once())
->method('trans')->with('greg0ire_enum_tests_fixtures_foo_enum_chuck', [], 'test');

$this->assertSame(
'greg0ire_enum_tests_fixtures_foo_enum_chuck',
$this->extension->label(FooInterface::CHUCK, FooEnum::class, 'test'),
'Without any available translation, the filter should just return the key.'
);
}
}

0 comments on commit 34dffca

Please sign in to comment.