somnambulist/doctrine-enum-bridge

This package is abandoned and no longer maintained. The author suggests using the somnambulist/domain package instead.

Allows any enumeration library to be used with Doctrine type system.

1.2.0 2017-10-24 00:09 UTC

This package is auto-updated.

Last update: 2020-02-03 17:24:20 UTC


README

This repository has been archived. Please update to the combined package.s

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.

Requirements

  • PHP 7+
  • Doctrine 2.5+

Installation

Install using composer, or checkout / pull the files from github.com.

  • composer require somnambulist/doctrine-enum-bridge

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)
  • name - the bound name given to the enumeration this could be the FQCN
  • platform - the Doctrine AbstractPlatform instance

For example, in a Symfony project, in your AppBundle class:

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

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

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.

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:

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:

fields:
    name:
        type: string
        length: 255
    
    gender:
        type: gender
    
    action:
        type: AppBundle\Enumerable\Action

Share a Constructor

For enumerables that have the same signature / method names, a constructor callable can be shared.

To share a constructor callable, use the FQCN as the name of the enumerable or you could use a hash map of aliases to Enumerables:

class AppBundle extends Bundle
{
    public function boot()
    {
        $constructor = function ($value, $class) {
            // constructor method should handle nulls
            if (is_null($value)) {
                return null;
            }
        
            if ($class::isValid($value)) {
                return new $class($value);
            }
        
            throw new InvalidArgumentException(sprintf(
                'The value "%s" is not valid for the enum "%s". Expected one of ["%s"]',
                $value,
                $class,
                implode('", "', $class::toArray())
           ));
        }
    
        EnumerationBridge::registerEnumType(Action::class, $constructor);
        EnumerationBridge::registerEnumType(Country::class, $constructor);
        EnumerationBridge::registerEnumType(Currency::class, $constructor);
    }
}

Because each enumerable can be mapped to its own construct / serializer handlers, complex multitions from the Eloquent\Enumerable library can be handled by this bridge.

Links