Skip to content

Commit

Permalink
Merge pull request #34 from Soullivaneuh/twig
Browse files Browse the repository at this point in the history
Twig integration
  • Loading branch information
greg0ire authored Jun 23, 2016
2 parents fa27a9c + 34dffca commit 0dd3b3a
Show file tree
Hide file tree
Showing 12 changed files with 441 additions and 13 deletions.
57 changes: 57 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ Additionally, you may get all the constants in your class as a hash:
```php
DaysOfWeek::getConstants();
DaysOfWeek::getConstants('strtolower'); // Will combine your values with `DaysOfWeek::getKeys($callback)`.
DaysOfWeek::getConstants('strtolower', true); // Values combine with `DaysOfWeek::getClassPrefixedKeys($callback)`.
DaysOfWeek::getConstants('strtolower', true, '.'); // Same with `DaysOfWeek::getClassPrefixedKeys($callback, $separator)`.
```

You may also get all the keys in your class as an array:
Expand All @@ -58,6 +60,14 @@ DaysOfWeek::getKeys();
DaysOfWeek::getKeys('strtolower'); // Will call `array_map` with the given callback.
```

Or the key with the enum class prefix:

```php
DaysOfWeek::getClassPrefixedKeys();
DaysOfWeek::getClassPrefixedKeys('strtolower'); // Will call `array_map` with the given callback.
DaysOfWeek::getClassPrefixedKeys('strtolower', '.'); // Replace the namespace separator ('_' by default).
```

### Advanced usage

If you need to get the constants from a class you cannot modify, or from an
Expand Down Expand Up @@ -194,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
45 changes: 42 additions & 3 deletions src/AbstractEnum.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,25 +2,35 @@

namespace Greg0ire\Enum;

use Doctrine\Common\Inflector\Inflector;

/**
* @author Grégoire Paris <[email protected]>
* @author Sullivan Senechal <[email protected]>
*/
abstract class AbstractEnum
{
/**
* @var string
*/
public static $defaultNamespaceSeparator = '_';

private static $constCache = [];

/**
* Uses reflection to find the constants defined in the class and cache
* them in a local property for performance, before returning them.
*
* @param callable|null $keysCallback
* @param bool $classPrefixed True if you want the enum class prefix on each keys, false otherwise.
* @param string $namespaceSeparator Only relevant if $classPrefixed is set to true.
*
* @return array a hash with your constants and their value. Useful for
* building a choice widget
*/
final public static function getConstants($keysCallback = null)
final public static function getConstants($keysCallback = null, $classPrefixed = false, $namespaceSeparator = null)
{
$namespaceSeparator = $namespaceSeparator ?: static::$defaultNamespaceSeparator;
$enumTypes = static::getEnumTypes();
$enums = [];

Expand All @@ -44,8 +54,13 @@ final public static function getConstants($keysCallback = null)
}
}

if (is_callable($keysCallback)) {
return array_combine(static::getKeys($keysCallback), $enums);
if (is_callable($keysCallback) || $classPrefixed) {
return array_combine(
$classPrefixed
? static::getClassPrefixedKeys($keysCallback, $namespaceSeparator)
: static::getKeys($keysCallback),
$enums
);
}

return $enums;
Expand All @@ -69,6 +84,30 @@ final public static function getKeys($callback = null)
return $keys;
}

/**
* @param callable|null $callback A callable function compatible with array_map
* @param string|null $namespaceSeparator Choose which character should replace namespaces separation.
* Example: With Foo\BarMagic enum class with '.' separator,
* it will be converted to foo.bar_magic.YOUR_KEY
*
* @return string[]
*/
final public static function getClassPrefixedKeys($callback = null, $namespaceSeparator = null)
{
$namespaceSeparator = $namespaceSeparator ?: static::$defaultNamespaceSeparator;
$classKey = str_replace('\\', $namespaceSeparator, Inflector::tableize(static::class));

$keys = static::getKeys(function ($key) use ($namespaceSeparator, $classKey) {
return $classKey.$namespaceSeparator.$key;
});

if (is_callable($callback)) {
return array_map($callback, $keys);
}

return $keys;
}

/**
* Checks whether a constant with this name is defined.
*
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'));
}
}
}
}
12 changes: 3 additions & 9 deletions src/Bridge/Symfony/Form/Type/EnumType.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

namespace Greg0ire\Enum\Bridge\Symfony\Form\Type;

use Doctrine\Common\Inflector\Inflector;
use Greg0ire\Enum\AbstractEnum;
use Greg0ire\Enum\Bridge\Symfony\Validator\Constraint\Enum;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Exception\LogicException;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
Expand Down Expand Up @@ -37,13 +35,9 @@ public function configureOptions(OptionsResolver $resolver)
$resolver->setDefault('choices', function (Options $options) {
$class = $options['class'];

$keys = call_user_func([$class, 'getKeys'], 'strtolower');
if ($options['prefix_label_with_class']) {
array_walk($keys, function (&$key) use ($class) {
$classKey = str_replace('\\', '_', Inflector::tableize($class));
$key = $classKey.'_'.$key;
});
}
$keys = $options['prefix_label_with_class']
? call_user_func([$class, 'getClassPrefixedKeys'], 'strtolower')
: call_user_func([$class, 'getKeys'], 'strtolower');

$choices = array_combine($keys, call_user_func([$class, 'getConstants']));
// SF <3.1 BC
Expand Down
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';
}
}
Loading

0 comments on commit 0dd3b3a

Please sign in to comment.