Enum php 8.1, et les librairie symfony. (Doctrine, graphql-php, api platform)
A quoi ressemble les enum php 8.1 ?
pour tous nos exemples nous allons utiliser le même enum, l’important ici est d’avoir un enum “string” c’est-à-dire ou la valeur de chaque cas sera un string
<?php
namespace App\Entity\Enum;enum MyEnum: string
{
case CASE1 = 'CASE1';
case CASE2 = 'CASE2';
case CASE3 = 'CASE3';
case CASE4 = 'CASE4';
}
Doctrine et les enums.
Doctrine ne gère pas nativement les enum, néanmoins depuis la version 2.11 de doctrine une option a été rajouté dans le type string “enumType” ce qui va nous être utile
avec doctrine deux choix s’offre à nous pour faire marcher les enums
modifier le type string
commençons par créer une entité avec un attribut en enum:
<?php
namespace App\Entity;use App\Entity\Enum\MyEnum;
use App\Repository\MyEntityRepository;
use Doctrine\ORM\Mapping as ORM;#[ORM\Entity(repositoryClass: MyEntityRepository::class)]
class MyEntity
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column(type: 'integer')]
private int $id; #[ORM\Column(type: 'string', enumType: MyEnum::class )]
private MyEnum $awesomeEnum;
public function getId(): ?int
{
return $this->id;
}
public function getAwesomeEnum(): ?string
{
return $this->awesomeEnum?->value;
} public function setAwesomeEnum(string|MyEnum $awesomeEnum): self
{
if (is_string($awesomeEnum)) {
$awesomeEnum = MyEnum::from($awesomeEnum);
}
$this->awesomeEnum = $awesomeEnum; return $this;
}
}
dans notre entitée on a donc utilisé le nouvel enumType ainsi que typer notre attribut de notre enum et changer un peu nos getter et setter
ensuite pour modifier le type string nous devons créer un listener qui va s’occuper de modifier la column definition pour le transformer en enum sql lors des migrations, pour cela on va simplement regarder si on a le nouvel enumType sur la définition de la colonne et dans ce cas changer la column definition
<?php
declare(strict_types=1);namespace App\EventListener;use Doctrine\DBAL\Schema\Column;
use Doctrine\ORM\Tools\Event\GenerateSchemaEventArgs;class EnumTypeListener
{
public function postGenerateSchema(GenerateSchemaEventArgs $eventArgs)
{
foreach ($eventArgs->getSchema()->getTables() as $table) {
foreach ($table->getColumns() as $column) {
if (array_key_exists('enumType', $column->getCustomSchemaOptions())) {
$this->changeStringToEnum($column);
}
}
}
} private function changeStringToEnum(Column $column)
{
$enum = $column->getCustomSchemaOptions()['enumType'];
$enumCases = array_map(fn ($case) => "'$case->value'", $enum::cases());
$column->setColumnDefinition(sprintf('ENUM(%s)', implode(',', $enumCases)));
}
}
pour finir il faut que doctrine ce qu’est le type enum en base de données pour cela dans package/doctrine.yaml:
doctrine:
dbal:
mapping_types:
enum: string
et voilà nous avons de manière simple fait comprendre a doctrine les enums.
Utilisation avec overblog/graphql-bundle
Avec graphql, nous avons besoin de créer un nouveau type pour chaque enum, pour cela commençons par créer un service qui nous servira pour chacun de nos types
ce service transforme notre php 8.1 enum en type enum graphql
<?php
namespace App\Entity\Enum;use GraphQL\Type\Definition\EnumType;class GenericEnumType extends EnumType
{ public function __construct(string $enumClass)
{
$config = [];
$config['values'] = $this->enumToValues($enumClass);
$config['name'] = str_replace('App\Entity\Enum\\', '', $enumClass);
parent::__construct($config);
}
private function enumToValues(string $enumClass)
{
$values = [];
foreach ($enumClass::cases() as $constantValue) {
$values[$constantValue->name] = [
'value' => $constantValue->value,
'name' => $constantValue->name,
];
}
return $values;
}
}
Maintenant nous devons créer un service par enum pour créer nos types graphql, pour cela dans le service.yaml il nous suffit d’ajouter:
MyEnum:
class: App\Entity\Enum\GenericEnumType
arguments:
- App\Entity\Enum\MyEnum
tags:
- { name: overblog_graphql.type, alias: 'MyEnum' }
une fois le type créer on peut l’utiliser dans notre entité
#[GQL\Field(type: 'MyEnum')]
#[ORM\Column(type: 'string', enumType: MyEnum::class)]
private MyEnum $awesomeEnum;
Api platform et les enums
Pour api platform cela est très similaire a overblog/graphql-bundle avec de petites modifications sur la déclaration des services.
en premier créons notre classe qui va nous servir pour nos types enum graphql,
<?php
namespace App\Entity\Enum;use GraphQL\Type\Definition\EnumType;
use ApiPlatform\Core\GraphQl\Type\Definition\TypeInterface;class GenericEnumType extends EnumType implements TypeInterface
{public function __construct(string $enumClass)
{
$config = [];
$config['values'] = $this->enumToValues($enumClass);
$config['name'] = str_replace('App\Entity\Enum\\', '', $enumClass);
parent::__construct($config);
}
private function enumToValues(string $enumClass)
{
$values = [];
foreach ($enumClass::cases() as $constantValue) {
$values[$constantValue->name] = [
'value' => $constantValue->value,
'name' => $constantValue->name,
];
}
return $values;
} public function getName(): string
{
return $this->name;
}
}
ensuite pour la déclaration de nos services utilisons cette classe pour créer les types
App\Entity\Enum\MyEnum:
class: App\Entity\Enum\GenericEnumType
arguments:
- App\Entity\Enum\MyEnum
tags:
- { name: api_platform.graphql.type }
pour finir nous devons créer un type converter, pour que nos types soient compris par api platform
<?php
namespace App\Type;use ApiPlatform\Core\GraphQl\Type\TypeConverterInterface;
use GraphQL\Type\Definition\Type as GraphQLType;
use Symfony\Component\PropertyInfo\Type;final class TypeConverter implements TypeConverterInterface
{
private $defaultTypeConverter; public function __construct(TypeConverterInterface $defaultTypeConverter)
{
$this->defaultTypeConverter = $defaultTypeConverter;
} /**
* {@inheritdoc}
*/
public function convertType(Type $type, bool $input, ?string $queryName, ?string $mutationName, ?string $subscriptionName, string $resourceClass, string $rootResource, ?string $property, int $depth): string|GraphQLType|null
{
if (enum_exists($resourceClass)) {
return str_replace('App\Entity\Enum\\', '', $resourceClass);
}
return $this->defaultTypeConverter->convertType($type, $input, $queryName, $mutationName, $subscriptionName, $resourceClass, $rootResource, $property, $depth);
} /**
* {@inheritdoc}
*/
public function resolveType(string $type): ?GraphQLType
{
return $this->defaultTypeConverter->resolveType($type);
}
}
Bravo vous avez configuré vos enums
Conclusion
les enums php 8.1 étant encore nouveau ceux si ne sont pas encore gérer par la plupart des librairie, mais elle le seront dans l’avenir, ces solution sont donc temporaire en attendant que les librairie ce mettent à jours