Description
Hi, thanks so much for your package. I do not have time to actually make a PR at the moment, but I wanted to share what I did to support native MySQL ENUM types.
I have my own AbstractEnumType class:
<?php
namespace App\DBAL;
use BackedEnum;
use Doctrine\DBAL\Platforms\AbstractPlatform;
use Doctrine\DBAL\Types\Type;
abstract class AbstractEnumType extends Type
{
abstract public static function getEnumsClass(): string;
public function getName(): string // the name of the type.
{
return static::getEnumsClass();
}
public function getSQLDeclaration(array $fieldDeclaration, AbstractPlatform $platform): mixed
{
/** @var BackedEnum $class */
$class = static::getEnumsClass();
$values = array_map(
/** @var BackedEnum $val */
function ($val) {
return "'" . $val->value . "'";
},
$class::cases()
);
return "ENUM(" . implode(", ", $values) . ")";
}
public function convertToDatabaseValue($value, AbstractPlatform $platform): mixed
{
if ($value instanceof \BackedEnum) {
return $value->value;
}
return null;
}
public function convertToPHPValue($value, AbstractPlatform $platform): mixed
{
if (false === enum_exists($this->getEnumsClass(), true)) {
throw new \LogicException("This class should be an enum");
}
/** @var BackedEnum $class */
$class = static::getEnumsClass();
return $class::tryFrom($value);
}
/**
* @codeCoverageIgnore
*/
public function requiresSQLCommentHint(AbstractPlatform $platform): bool
{
return true;
}
}
When I make new enum like
<?php
namespace App\Entity\Enum;
use Elao\Enum\Attribute\EnumCase;
use Elao\Enum\ReadableEnumInterface;
use Elao\Enum\ReadableEnumTrait;
enum AdminRole: string implements ReadableEnumInterface
{
use ReadableEnumTrait;
#[EnumCase('Admin')]
case ADMIN = 'ROLE_ADMIN';
#[EnumCase('Super Admin')]
case SUPER_ADMIN = 'ROLE_SUPER_ADMIN';
#[EnumCase('CSR')]
case CSR = 'ROLE_CSR';
#[EnumCase('Manager')]
case MANAGER = 'ROLE_MANAGER';
#[EnumCase('Accounting')]
case ACCOUNTING = 'ROLE_ACCOUNTING';
#[EnumCase('SEO')]
case SEO = 'ROLE_SEO';
}
I then make also a "wrapper" in DBAL folder:
<?php
namespace App\DBAL;
use App\Entity\Enum\AdminRole;
class AdminRoleType extends AbstractEnumType
{
public static function getEnumsClass(): string // the enums class to convert
{
return AdminRole::class;
}
}
This wrapper just returns the PHP enum which it links.
In config/services.yaml
I put:
services:
_instanceof:
App\DBAL\AbstractEnumType:
tags: ['app.doctrine_enum_type']
and then in Kernel.php, I register all "wrapper" classes as valid types:
<?php
namespace App;
use App\DBAL\AbstractEnumType;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
class Kernel extends BaseKernel implements CompilerPassInterface
{
use MicroKernelTrait;
public function process(ContainerBuilder $container): void
{
$typesDefinition = [];
if ($container->hasParameter('doctrine.dbal.connection_factory.types')) {
$typesDefinition = $container->getParameter('doctrine.dbal.connection_factory.types');
}
$taggedEnums = $container->findTaggedServiceIds('app.doctrine_enum_type');
foreach ($taggedEnums as $enumType => $definition) {
/** @var $enumType AbstractEnumType */
$typesDefinition[$enumType::getEnumsClass()] = ['class' => $enumType];
}
$container->setParameter('doctrine.dbal.connection_factory.types', $typesDefinition);
}
}
And thats it!. In Entity I use:
#[ORM\Column(type: AdminRole::class)]
private AdminRole $admin_role = AdminRole::CSR;
and the SQL generated by Doctrine migrations is:
CREATE TABLE admin (id INT AUTO_INCREMENT NOT NULL, email VARCHAR(180) NOT NULL, admin_role ENUM('ROLE_ADMIN', 'ROLE_SUPER_ADMIN', 'ROLE_CSR', 'ROLE_MANAGER', 'ROLE_ACCOUNTING', 'ROLE_SEO') NOT NULL COMMENT '(DC2Type:App\\Entity\\Enum\\AdminRole)', password VARCHAR(255) NOT NULL, token VARCHAR(255) DEFAULT NULL, UNIQUE INDEX UNIQ_880E0D76E7927C74 (email), PRIMARY KEY(id)) DEFAULT CHARACTER SET utf8mb4 COLLATE `utf8mb4_unicode_ci` ENGINE = InnoDB
Adding new native ENUM type consists of only creating the php enum, then making the wrapper class.
So I was thinking this might be interesting to extend in your own AbstractEnumType doctrine bundle, which now uses either INT
or VARCHAR
. And if nothing else, it might help others who want native MySQL ENUMs
Thanks for your work!
Activity