如何使用 Symfony Doctrine 持久化枚举(实体字段类型:"enum")

How to persist enum with Symfony Doctrine (entity field type:"enum")

我有一个要记录在数据库中的枚举(感谢 php 8.1)。该字段的类型为 Enum。通过管理员 SQL 查询,没有问题。

UPDATE `users` SET `grade` = 1, WHERE `id` = '51';

在这种情况下,在 Adminer 中,我可以在成绩列中看到 'A5'(值为 1 的枚举名称)

但是使用 Doctrine 和 symfony 形式,数据不会持久化(仅在这个字段上)。 我没有错误。

if ($form->isSubmitted() && $form->isValid()){

        $entityManager = $this->getDoctrine()->getManager();
        $entityManager->persist($user);
        $entityManager->flush();

这是 ProfileType 中的“添加”

            ->add('grade', ChoiceType::class, [
            'data' => $user ? $user->getGrade() : GradeEnum::D1,
            'placeholder' => 'Choose an option',
            'required' => false,
            'choices' => [
                GradeEnum::D1->name => GradeEnum::D1,
                GradeEnum::D2->name => GradeEnum::D2,
                GradeEnum::C1->name => GradeEnum::C1,
                GradeEnum::C2->name => GradeEnum::C2,
                GradeEnum::B1->name => GradeEnum::B1,
                GradeEnum::B2->name => GradeEnum::B2,
                GradeEnum::A1->name => GradeEnum::A1,
                GradeEnum::A2->name => GradeEnum::A2,
                GradeEnum::A3->name => GradeEnum::A3,
                GradeEnum::A4->name => GradeEnum::A4,
                GradeEnum::A5->name => GradeEnum::A5,
                GradeEnum::A5plus->name => GradeEnum::A5plus,
            ],
            'attr' => [
                'class' => 'mb-3'
            ],
            'label' => 'Grade'
        ])

我转储了表格。我得到一个枚举。

App\Enum\GradeEnum {#332
 +name: "A5"
 +value: 1
}

如果我尝试将此字段中的数据保存为另一种类型(如整数),则会出错。这个是正常的。但为什么学说不坚持枚举?

是的,从 8.1 版开始,学说不支持 php 枚举,但您可以创建自己的学说类型。 Defining a custom Doctrine type

在你的情况下,它应该是 link 这个:

<?php

namespace App\DBAL;

use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
use App\Enum\GradeEnum;
use InvalidArgumentException;
use JetBrains\PhpStorm\Pure;

class GradeType extends Type
{
    protected string $name;
    protected array $values = [
        GradeEnum::A5plus,
        GradeEnum::A5,
        GradeEnum::A4,
        GradeEnum::A3,
        GradeEnum::A2,
        GradeEnum::A1,
        GradeEnum::B2,
        GradeEnum::B1,
        GradeEnum::C2,
        GradeEnum::C1,
        GradeEnum::D2,
        GradeEnum::D1,
    ];

    const GRADE = 'grade';

    public function getName(): string
    {
        return self::GRADE;
    }

    public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): string
    {
        $values = array_map(function ($val) {
            /** @var GradeEnum $val */
            return "'{$val->name}'";
        }, $this->values);

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

    #[Pure]
    public function convertToPHPValue($value, AbstractPlatform $platform): ?GradeEnum
    {
        if (null === $value) {
            return null;
        }
        return GradeEnum::getGradeFromString($value);
    }

    public function convertToDatabaseValue($value, AbstractPlatform $platform)
    {
        /** @var GradeEnum $value */
        if ($value !== null) {
            if (!in_array($value, $this->values)) {
                throw new InvalidArgumentException("Invalid '" . $this->name . "' value.");
            } else {
                return $value->name;
            }
        }

        return null;
    }

    public function canRequireSQLConversion(): bool
    {
        return true;
    }

    public function requiresSQLCommentHint(AbstractPlatform $platform): bool
    {
        return true;
    }
}
<?php

namespace App\Enum;

enum GradeEnum: int
{
    case A5plus = 0;
    case A5 = 1;
    case A4 = 2;
    case A3 = 3;
    case A2 = 4;
    case A1 = 5;
    case B2 = 6;
    case B1 = 7;
    case C2 = 8;
    case C1 = 9;
    case D2 = 10;
    case D1 = 11;

    public static function getGradeFromString(string $grade): GradeEnum {
        return match ($grade) {
            self::A5plus->name => self::A5plus,
            self::A5->name => self::A5,
            self::A4->name => self::A4,
            self::A3->name => self::A3,
            self::A2->name => self::A2,
            self::A1->name => self::A1,
            self::B1->name => self::B1,
            self::B2->name => self::B2,
            self::C1->name => self::C1,
            self::C2->name => self::C2,
            self::D1->name => self::D1,
            self::D2->name => self::D2,
        };
    }
}

不要忘记将您的类型添加到 doctrine.yaml

doctrine:
    dbal:
        types:
            grade: App\DBAL\GradeType
        mapping_types:
            enum: string
            grade: grade

祝你有个愉快的一天!

从 2.11 版开始,ORM 支持 php 8 个枚举

enum Suit: string {
    case Hearts = 'H';
    case Diamonds = 'D';
    case Clubs = 'C';
    case Spades = 'S';
}

#[Entity]
class Card
{
    /** ... */

    #[Column(type: 'string', enumType: Suit::class)]
    public $suit;
} 

您可以在 offical blog post 中阅读更多相关信息。 官方文档应该很快就会跟进。