greg0ire / enum
work around the missing enum type in php
Installs: 354 212
Dependents: 11
Suggesters: 0
Security: 0
Stars: 45
Watchers: 4
Forks: 4
Open Issues: 2
Requires
- php: ^7.1 || ^8.0
- doctrine/inflector: ^1.4 || ^2.0
Requires (Dev)
- matthiasnoback/symfony-dependency-injection-test: ^4.0
- symfony/config: ^4.2 || ^5.0 || ^6.0
- symfony/dependency-injection: ^4.2 || ^5.0 || ^6.0
- symfony/form: ^4.2.5 || ^5.0 || ^6.0
- symfony/framework-bundle: ^4.2 || ^5.0 || ^6.0
- symfony/http-kernel: ^4.2 || ^5.1.5 || ^6.0
- symfony/options-resolver: ^4.2 || ^5.0 || ^6.0
- symfony/phpunit-bridge: ^5.0 || ^6.0
- symfony/translation: ^4.2 || ^5.0 || ^6.0
- symfony/translation-contracts: ^2.0
- symfony/twig-bundle: ^4.2 || ^5.0 || ^6.0
- symfony/validator: ^4.3.6 || ^5.0 || ^6.0
- twig/twig: ^2.4.2 || ^3.0
Suggests
- symfony/form: To use enum form type
- symfony/validator: To use enum validator
Conflicts
- symfony/form: <3.2 || >=7.0 <999
- symfony/validator: <3.2 || >=7.0 <999
README
This package holds a simple class that may be used as an ancestor for your enum classes.
Installation
composer require greg0ire/enum
Usage
Basic usage
Extend the Greg0ire\Enum\AbstractEnum
, define your enum key values as constants,
and Bob's your uncle. You can make the class abstract or final, as you see fit.
use Greg0ire\Enum\AbstractEnum; final class DaysOfWeek extends AbstractEnum { const Sunday = 0; const Monday = 1; const Tuesday = 2; const Wednesday = 3; const Thursday = 4; const Friday = 5; const Saturday = 6; }
Then, you may use the DaysOfWeek class for input validation:
DaysOfWeek::isValidName('Humpday'); // false DaysOfWeek::isValidName('Monday'); // true DaysOfWeek::isValidName('monday'); // false DaysOfWeek::isValidName(0); // false DaysOfWeek::isValidValue(0); // true DaysOfWeek::isValidValue(5); // true DaysOfWeek::isValidValue(7); // false DaysOfWeek::isValidValue('Friday'); // false
Both methods have an assert*
counterpart that will throw a
Greg0ire\Enum\Exception\InvalidEnumValue
exception:
DaysOfWeek::assertValidName(0); // InvalidEnumName
DaysOfWeek::assertValidValue('Friday'); // InvalidEnumValue
Additionally, you may get all the constants in your class as a hash:
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:
DaysOfWeek::getKeys(); DaysOfWeek::getKeys('strtolower'); // Will call `array_map` with the given callback.
Or the key with the enum class prefix:
DaysOfWeek::getClassPrefixedKeys(); DaysOfWeek::getClassPrefixedKeys('strtolower'); // Will call `array_map` with the given callback. DaysOfWeek::getClassPrefixedKeys('strtolower', '.'); // Replace the namespace separator ('_' by default).
If you would like to get the keys from a value:
$key = DaysOfWeek::getKeysFromValue(1); // Monday will be assigned to $key
If you have many keys with the same value you will get an array, and a value otherwise.
Advanced usage
If you need to get the constants from a class you cannot modify, or from an
interface, or even from several classes / interfaces, you may override
AbstractEnum::getEnumTypes()
.
For example, if you have the following class and interface :
namespace Vendor\Namespace; class ClassFromAVendor { const SOMETHING = 'something'; const SOMETHING_ELSE = 'something_else'; }
namespace My\Namespace; interface SomeInterface { const ANOTHER_CONST = 'another_const'; }
You can get all three constants by creating this Enum :
use Greg0ire\Enum\AbstractEnum; use My\Namespace\SomeInterface; use Vendor\Namespace\ClassFromAVendor; final class MyEnum extends AbstractEnum { protected static function getEnumTypes() { return [ClassFromAVendor::class, SomeInterface::class]; } }
Alternatively, you can specify a prefix for each type to avoid getting FQCNs in the hash keys.
use Greg0ire\Enum\AbstractEnum; use My\Namespace\SomeInterface; use Vendor\Namespace\ClassFromAVendor; final class MyEnum extends AbstractEnum { protected static function getEnumTypes() { return [ 'prefix1' => ClassFromAVendor::class, 'prefix2' => SomeInterface::class, ]; } }
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.
use Greg0ire\Enum\Bridge\Symfony\Translator\GetLabel; $label = new GetLabel(); $label(Your\Enum\Class::VALUE, Your\Enum\Class::class);
To enable translation, require the symfony/translation
component
and pass a Symfony\Contracts\Translation\TranslationInterface
instance on the
GetLabel
constructor
use Greg0ire\Enum\Bridge\Symfony\Translator\GetLabel; use Symfony\Contracts\Translation\TranslationInterface; $label = new GetLabel($translator); $label(Your\Enum\Class::VALUE, Your\Enum\Class::class);
If you're using Symfony, alias the service and simply inject it.
If translations are enabled, the TranslatorInterface
will be automatically injected.
services: # ... Greg0ire\Enum\Bridge\Symfony\Translator\GetLabel: "@greg0ire_enum.symfony.translator.get_label"
public function index(GetLabel $label) { $label(Your\Enum\Class::VALUE, Your\Enum\Class::class); $label(Your\Enum\Class::VALUE, Your\Enum\Class::class, 'another_domain'); // Change the translation domain $label(Your\Enum\Class::VALUE, Your\Enum\Class::class, false); // Disable translation. In this case the class prefix wont be added $label(Your\Enum\Class::VALUE, Your\Enum\Class::class, false, true); // Disable translation but keep class prefix $label(Your\Enum\Class::VALUE, Your\Enum\Class::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
suggest
section of the Composer dependency manifest.
Symfony validator
This package provides a "ready to use" symfony validator.
You have to require the symfony/validator
package to get it working.
use Greg0ire\Enum\Bridge\Symfony\Validator\Constraint\Enum; use Symfony\Component\Validator\Validation; use Your\Namespace\EnumClass; $validator = Validation::createValidator(); $violations = $validator->validateValue(42, new Enum(EnumClass::class)); // You can also show the constants keys on the error message: $violations = $validator->validateValue(42, new Enum(['class' => EnumClass::class, 'showKeys' => true])); // Enum constraint inherits from Choice constraint. You can use inherited options too: $violations = $validator->validateValue(42, new Enum(['class' => EnumClass::class, 'strict' => true]));
Another example with annotations:
use Doctrine\Common\Annotations\AnnotationRegistry; use Greg0ire\Enum\Bridge\Symfony\Validator\Constraint\Enum as EnumAssert; use Symfony\Component\Validator\Validation; class MyClass { /** * @EnumAssert("Your\Namespace\EnumClass") */ private $dummy; public function __construct($dummy) { $this->dummy = $dummy } } AnnotationRegistry::registerLoader('class_exists'); $validator = Validation::createValidatorBuilder() ->enableAnnotationMapping() ->getValidator(); $object = new MyClass(42); $violations = $validator->validate($object);
Note: You will have to get doctrine/annotations
and doctrine/cache
packages to get it working.
Symfony form
This package provides a "ready to use" symfony form type.
You have to require the symfony/form
package to get it working.
use Greg0ire\Enum\Bridge\Symfony\Form\Type\EnumType; use Symfony\Component\Form\Forms; use Your\Namespace\EnumClass; $formFactory = Forms::createFormFactory(); $view = $this->factory->create(EnumType::class, null, array( 'class' => EnumClass::class, ))->createView();
Twig extension
This package comes with an EnumExtension
Twig class. It contains a filter and some functions.
You have to require the twig/twig
package to get it working.
Filter
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 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.
Usage:
{{ 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 #}
Functions
The 3 available twig functions are ports of some AbstractEnum
methods that can be useful in a twig template:
enum_get_constants
=>AbstractEnum::getConstants
enum_get_keys
=>AbstractEnum::getKeys
enum_get_class_prefixed_keys
=>AbstractEnum::getClassPrefixedKeys
The arguments are exactly the same except you have to specify the targeted class first (as enum_label
filter).
Here is a concrete example with enum_get_constants
function:
{% for enum_key, enum_value in enum_get_constants('Your\\Enum\\Class') %} {{ enum_key }} -> {{ enum_value }} {% endfor %}
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:
// app/AppKernel.php public function registerBundles() { $bundles = [ // ... new Greg0ire\Enum\Bridge\Symfony\Bundle\Greg0ireEnumBundle(), ]; // ... return $bundles }
That's all. You can now directly use the filter.
Contributing
see CONTRIBUTING.md
Credits
This is a shameless rip-off of this Stack Overflow answer, with one
modification: the getConstants
method has been made public so that it is
available for building choice widgets, for instance. If you want to give credit
to someone for this, give it to Brian Cline