Enum php 8.1, et les librairie symfony. (Doctrine, graphql-php, api platform)

Yann Dardot
Reparcar
Published in
4 min readAug 1, 2022

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

--

--

Yann Dardot
Reparcar
Editor for

Développeur jusqu’au plus profond de mon âme.