Provides a bridge between different enumeration implementations and Doctrine. Any type of PHP enumerable (e.g. Eloquent\Enumeration or myclabs/php-enum can be used with this adaptor.
A default, string casting, serializer is used if no serializer is provided.
All enumerations are stored using the DBs native varchar format. If you wish to use a custom
DB type, extend and re-implement the getSQLDeclaration()
method.
In your frameworks application service provider / bundle boot method, register your enums and the appropriate callables to create / serialize as needed. A default serializer that casts the enumerable to a string will be used if none is provided.
The callbacks will receive:
- value - the current value either a PHP type, or the database type (for constructor)
- platform - the Doctrine AbstractPlatform instance
For example, in a Symfony project, in your AppBundle class:
<?php
use Somnambulist\Components\Doctrine\Types\EnumerationBridge;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class AppBundle extends Bundle
{
public function boot()
{
EnumerationBridge::registerEnumType(Action::class, function ($value) {
if (Action::isValid($value)) {
return new Action($value);
}
throw new InvalidArgumentException(sprintf(
'The value "%s" is not valid for the enum "%s". Expected one of ["%s"]',
$value,
Action::class,
implode('", "', Action::toArray())
));
});
}
}
In Laravel, add to your AppServiceProvider (register
and boot
should both work):
<?php
use Somnambulist\Components\Doctrine\Types\EnumerationBridge;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
EnumerationBridge::registerEnumType(Action::class, function ($value) {
if (Action::isValid($value)) {
return new Action($value);
}
throw new InvalidArgumentException(sprintf(
'The value "%s" is not valid for the enum "%s". Expected one of ["%s"]',
$value,
Action::class,
implode('", "', Action::toArray())
));
});
}
}
When registering the type, you can either use the fully qualified class name, or an alias / short
string. The only limitation is that it should be unique for each enumeration. In the above example
we could register the enumeration as http_action
instead.
Note: the bridge will check if the type has already been registered and skip it if that is the case. If you wish to replace an existing type then you should use
Type::overrideType()
, however that will only work if the type has already been registered.
Note: when using short aliases you MUST explicitly set the class in the constructor for hydrating the object. This means that constructors cannot be shared with other types.
Multiple enumerations can be registered at once by calling registerEnumTypes()
and passing an
array of enum name and either an array of callables (constructor, serializer) or just the
constructor:
<?php
use Somnambulist\Components\Doctrine\Types\EnumerationBridge;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class AppBundle extends Bundle
{
public function boot()
{
EnumerationBridge::registerEnumTypes(
[
'gender' => [
function ($value) {
if (Gender::isValidValue($value)) {
return Gender::memberByValue($value);
}
throw new InvalidArgumentException(sprintf(
'The value "%s" is not valid for the enum "%s"', $value, Gender::class
));
},
function ($value, $platform) {
return is_null($value) ? 'default' : $value->value();
}
]
]
);
}
}
In your Doctrine mapping files simply set the type on the field:
<entity>
<field name="name" type="string" length="255"/>
<field name="gender" type="gender" length="10"/>
<field name="action" type="AppBundle\Enumerable\Action" length="10"/>
</entity>
The type should be set to whatever you used when registering. If this is the class name, use that; if you used a short name - use that instead. It is recommended to use short names as it is easier to manage them than figuring out the full class name (that does not usually auto-complete).
The following value-object constructors are provided in the library in the Doctrine\Enumerations
namespace:
- CountryConstructor
- CurrencyConstructor
- NullableTypedEnumeratorConstructor - used when your enumeration allows a value of
null
- TypedEnumeratorConstructor - enforces typing of the value to string or int before checking
- TypedMultitonConstructor - instantiates by key not value
When using Country or Currency the custom serializer should be registered to correctly convert the VO to the ISO code for storage. These would be setup as follows:
<?php
use Somnambulist\Components\Doctrine\Enumerations\Constructors\CountryConstructor;
use Somnambulist\Components\Doctrine\Enumerations\Constructors\CurrencyConstructor;
use Somnambulist\Components\Doctrine\Enumerations\Serializers\CountrySerializer;
use Somnambulist\Components\Doctrine\Enumerations\Serializers\CurrencySerializer;
use Somnambulist\Components\Doctrine\Types\EnumerationBridge;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class AppBundle extends Bundle
{
public function boot()
{
EnumerationBridge::registerEnumType('country', new CountryConstructor(), new CountrySerializer());
EnumerationBridge::registerEnumType('currency', new CurrencyConstructor(), new CurrencySerializer());
}
}
Note: the first argument of
registerEnumType
is the alias/name for how to refer to this type. If you use the fully qualified class name via the::class
constant, then the Doctrine mapping must reference this type:
<field name="currency" type="Somnambulist\Components\Models\Types\Money\Currency" length="3" nullable="false"/>
vs:
<field name="currency" type="currency" length="3" nullable="false"/>
By default short aliases are registered by this library.