Skip to content

Latest commit

 

History

History
191 lines (153 loc) · 6.71 KB

doctrine-enum-bridge.md

File metadata and controls

191 lines (153 loc) · 6.71 KB

Doctrine Enum Bridge

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.

Usage

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.

Register Multiple 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();
                    }
                ]
            ]
        );
    }
}

Usage in Doctrine Mapping Files

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).

Built-in Enumeration Constructors

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.

Links