Mysql Enums

The type system of Doctrine ORM consists of flyweights, which means there is only one instance of any given type. Additionally types do not contain state. Both assumptions make it rather complicated to work with the Enum Type of MySQL that is used quite a lot by developers.

Doctrine ORM の型システムは flyweight で構成されています。つまり、特定の型のインスタンスは 1 つだけです。さらに、型には状態が含まれません。両方の仮定により、開発者がかなり頻繁に使用する MySQL の Enum タイプの操作がかなり複雑になります。

When using Enums with a non-tweaked Doctrine ORM application you will get errors from the Schema-Tool commands due to the unknown database type “enum”. By default Doctrine does not map the MySQL enum type to a Doctrine type. This is because Enums contain state (their allowed values) and Doctrine types don’t.

微調整されていない Doctrine ORM アプリケーションで Enum を使用すると、不明なデータベース タイプ「enum」が原因で Schema-Tool コマンドからエラーが発生します。デフォルトでは、Doctrine は MySQL の enum タイプを Doctrine タイプにマップしません。 state (許可された値) であり、Doctrinetypes はそうではありません。

This cookbook entry shows two possible solutions to work with MySQL enums. But first a word of warning. The MySQL Enum type has considerable downsides:

このクックブック エントリは、MySQL 列挙型を操作するための 2 つの可能な解決策を示しています。ただし、最初に警告の言葉があります。 MySQL Enum 型にはかなりの欠点があります。

  • Adding new values requires to rebuild the whole table, which can take hours depending on the size.

    新しい値を追加するには、テーブル全体を再構築する必要があり、サイズによっては数時間かかる場合があります。

  • Enums are ordered in the way the values are specified, not in their “natural” order.

    列挙型は、「自然な」順序ではなく、値が指定された方法で順序付けられます。

  • Enums validation mechanism for allowed values is not necessarily good, specifying invalid values leads to an empty enum for the default MySQL error settings. You can easily replicate the “allow only some values” requirement in your Doctrine entities.

    許可された値の列挙型検証メカニズムは必ずしも適切ではありません。無効な値を指定すると、デフォルトの MySQL エラー設定の列挙型が空になります。 Doctrineエンティティで「一部の値のみを許可する」要件を簡単に複製できます。

Solution 1: Mapping to Varchars

You can map ENUMs to varchars. You can register MySQL ENUMs to map to Doctrine varchars. This way Doctrine always resolves ENUMs to Doctrine varchars. It will even detect this match correctly when using SchemaTool update commands.

ENUM を varchar にマップできます。 MySQL ENUM を登録して、Doctrinevarchars にマップできます。このように、Doctrine は常に ENUM を Doctrine varchar に解決します。 SchemaTool 更新コマンドを使用すると、この一致も正しく検出されます。

<?php
$conn = $em->getConnection();
$conn->getDatabasePlatform()->registerDoctrineTypeMapping('enum', 'string');

In this case you have to ensure that each varchar field that is an enum in the database only gets passed the allowed values. You can easily enforce this in your entities:

この場合、データベース内の列挙型である各 varchar フィールドに許可された値のみが渡されるようにする必要があります。エンティティでこれを簡単に強制できます。

<?php
/** @Entity */
class Article
{
    const STATUS_VISIBLE = 'visible';
    const STATUS_INVISIBLE = 'invisible';

    /** @Column(type="string") */
    private $status;

    public function setStatus($status)
    {
        if (!in_array($status, array(self::STATUS_VISIBLE, self::STATUS_INVISIBLE))) {
            throw new \InvalidArgumentException("Invalid status");
        }
        $this->status = $status;
    }
}

If you want to actively create enums through the Doctrine Schema-Tool by using the columnDefinition attribute.

columnDefinition 属性を使用して、Doctrine Schema-Tool で積極的に列挙型を作成したい場合。

<?php
/** @Entity */
class Article
{
    /** @Column(type="string", columnDefinition="ENUM('visible', 'invisible')") */
    private $status;
}

In this case however Schema-Tool update will have a hard time not to request changes for this column on each call.

ただし、この場合、Schema-Tool の更新では、呼び出しごとにこの列の変更を要求しないようにするのが難しくなります。

Solution 2: Defining a Type

You can make a stateless ENUM type by creating a type class for each unique set of ENUM values. For example for the previous enum type:

ENUM 値の一意のセットごとに型クラスを作成することにより、ステートレス ENUM 型を作成できます。たとえば、前の列挙型の場合:

<?php
namespace MyProject\DBAL;

use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Platforms\AbstractPlatform;

class EnumVisibilityType extends Type
{
    const ENUM_VISIBILITY = 'enumvisibility';
    const STATUS_VISIBLE = 'visible';
    const STATUS_INVISIBLE = 'invisible';

    public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
    {
        return "ENUM('visible', 'invisible')";
    }

    public function convertToPHPValue($value, AbstractPlatform $platform)
    {
        return $value;
    }

    public function convertToDatabaseValue($value, AbstractPlatform $platform)
    {
        if (!in_array($value, array(self::STATUS_VISIBLE, self::STATUS_INVISIBLE))) {
            throw new \InvalidArgumentException("Invalid status");
        }
        return $value;
    }

    public function getName()
    {
        return self::ENUM_VISIBILITY;
    }

    public function requiresSQLCommentHint(AbstractPlatform $platform)
    {
        return true;
    }
}

You can register this type with Type::addType('enumvisibility', 'MyProject\DBAL\EnumVisibilityType');. Then in your entity you can just use this type:

このタイプを Type::addType('enumvisibility', 'MyProject\DBAL\EnumVisibilityType'); で登録できます。次に、エンティティでこのタイプを使用できます。

<?php
/** @Entity */
class Article
{
    /** @Column(type="enumvisibility") */
    private $status;
}

You can generalize this approach easily to create a base class for enums:

このアプローチを簡単に一般化して、列挙型の基本クラスを作成できます。

<?php
namespace MyProject\DBAL;

use Doctrine\DBAL\Types\Type;
use Doctrine\DBAL\Platforms\AbstractPlatform;

abstract class EnumType extends Type
{
    protected $name;
    protected $values = array();

    public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform)
    {
        $values = array_map(function($val) { return "'".$val."'"; }, $this->values);

        return "ENUM(".implode(", ", $values).")";
    }

    public function convertToPHPValue($value, AbstractPlatform $platform)
    {
        return $value;
    }

    public function convertToDatabaseValue($value, AbstractPlatform $platform)
    {
        if (!in_array($value, $this->values)) {
            throw new \InvalidArgumentException("Invalid '".$this->name."' value.");
        }
        return $value;
    }

    public function getName()
    {
        return $this->name;
    }

    public function requiresSQLCommentHint(AbstractPlatform $platform)
    {
        return true;
    }
}

With this base class you can define an enum as easily as:

この基本クラスを使用すると、列挙型を次のように簡単に定義できます。

<?php
namespace MyProject\DBAL;

class EnumVisibilityType extends EnumType
{
    protected $name = 'enumvisibility';
    protected $values = array('visible', 'invisible');
}

Table Of Contents

Previous topic

Working with DateTime Instances

DateTime インスタンスの操作

Next topic

Advanced field value conversion using custom mapping types

カスタム マッピング タイプを使用した高度なフィールド値の変換

This Page

Fork me on GitHub