Une implémentation robuste pour utiliser les enums PHP 8.1+ comme types personnalisés dans Doctrine ORM avec PostgreSQL.
Ce projet fournit un ensemble de classes permettant d'utiliser les enums PHP comme types natifs dans PostgreSQL via Doctrine. La solution garantit l'intégrité des données en créant des types enum personnalisés dans PostgreSQL qui correspondent directement aux valeurs de vos enums PHP.
- ✅ Support complet des backed enums et des enums classiques
- ✅ Validation côté base de données via des contraintes PostgreSQL natives
- ✅ Conversion automatique entre les représentations PHP et SQL
- ✅ Architecture extensible suivant les principes SOLID
- ✅ Tests unitaires et d'intégration complets
- PHP 8.1+
- Symfony 6.0+
- Doctrine ORM
- PostgreSQL
-
Clonez ce dépôt ou copiez les fichiers dans votre projet
-
Enregistrez les types personnalisés dans le fichier de configuration Doctrine :
# config/packages/doctrine.yaml
doctrine:
dbal:
types:
task_priority_enum: App\Doctrine\Type\TaskPriorityEnumType
- Exécutez les migrations pour créer les types enum dans PostgreSQL
<?php
declare(strict_types=1);
namespace App\Enum;
enum TaskPriorityEnum: string
{
case LOW = 'basse';
case MEDIUM = 'normale';
case HIGH = 'haute';
case CRITICAL = 'critique';
}
<?php
declare(strict_types=1);
namespace App\Doctrine\Type;
use App\Enum\TaskPriorityEnum;
class TaskPriorityEnumType extends AbstractEnumType
{
public const TYPE_NAME = 'task_priority_enum';
public static function getTypeName(): string
{
return self::TYPE_NAME;
}
protected static function getEnumClass(): string
{
return TaskPriorityEnum::class;
}
}
<?php
namespace App\Entity;
use App\Doctrine\Type\TaskPriorityEnumType;
use App\Enum\TaskPriorityEnum;
use Doctrine\ORM\Mapping as ORM;
#[ORM\Entity]
class Task
{
#[ORM\Column(type: TaskPriorityEnumType::TYPE_NAME)]
private ?TaskPriorityEnum $priority = null;
public function getPriority(): ?TaskPriorityEnum
{
return $this->priority;
}
public function setPriority(TaskPriorityEnum $priority): self
{
$this->priority = $priority;
return $this;
}
}
La migration doit créer le type enum PostgreSQL avant de créer les tables qui l'utilisent :
<?php
declare(strict_types=1);
namespace DoctrineMigrations;
use App\Doctrine\Type\TaskPriorityEnumType;
use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;
final class Version20250319040536 extends AbstractMigration
{
public function getDescription(): string
{
return 'Task / TaskPriorityEnumType';
}
public function up(Schema $schema): void
{
// Créer d'abord le type enum
$this->addSql(TaskPriorityEnumType::getCreateTypeSQL());
// Puis créer la table qui l'utilise
$this->addSql('CREATE TABLE task (
id INT GENERATED BY DEFAULT AS IDENTITY NOT NULL,
name VARCHAR(255) NOT NULL,
created_at TIMESTAMP(0) WITHOUT TIME ZONE NOT NULL,
priority task_priority_enum NOT NULL,
PRIMARY KEY(id)
)');
}
public function down(Schema $schema): void
{
$this->addSql('DROP TABLE task');
$this->addSql(TaskPriorityEnumType::getDropTypeSQL());
}
}
Le système repose sur trois composants principaux :
- AbstractEnumType : Classe abstraite gérant la conversion entre PHP et SQL
- EnumTypeHelper : Classe utilitaire pour générer le SQL nécessaire aux types enum PostgreSQL
- Types concrets (ex: TaskPriorityEnumType) : Implémentations spécifiques pour chaque enum
- En lecture, les valeurs de la base de données sont converties en instances d'enum PHP
- En écriture, les instances d'enum PHP sont converties en chaînes pour la base de données
- PostgreSQL valide que les valeurs correspondent bien aux valeurs autorisées par le type enum
Le projet inclut des tests unitaires et d'intégration pour garantir le bon fonctionnement.
# Installer les dépendances de développement
composer require --dev phpunit/phpunit symfony/test-pack
# Exécuter tous les tests
./vendor/bin/phpunit
# Exécuter uniquement les tests unitaires
./vendor/bin/phpunit --testsuite Unit
# Exécuter uniquement les tests d'intégration
./vendor/bin/phpunit --testsuite Integration
Les contributions sont les bienvenues ! N'hésitez pas à ouvrir une issue ou une pull request.
Ce projet suit les principes SOLID et les bonnes pratiques de Clean Code :
- Single Responsibility : Chaque classe a une responsabilité unique
- Open/Closed : Le système est ouvert à l'extension mais fermé à la modification
- Liskov Substitution : Les sous-types sont substituables à leurs types de base
- Interface Segregation : Les interfaces sont spécifiques à leurs clients
- Dependency Inversion : Dépendance vers les abstractions, non les implémentations
Ce projet est sous licence MIT. Voir le fichier LICENSE pour plus d'informations.