Aller au contenu
Sylvain Joffraud l4w
Retour

Remplacer un array de constantes par un enum PHP 8.1

Un tunnel applicatif a 7 étapes. Au départ, l’information vivait dans le contrôleur sous forme d’arrays de constantes :

final class TunnelController
{
    private const STEPS = [
        1 => 'Coordonnées',
        2 => 'Bénéficiaires',
        3 => 'Votre contrat',
        4 => 'Récapitulatif',
        5 => 'Pièces justificatives',
        6 => 'Signature',
        7 => 'Paiement',
    ];

    private const STEP_SLUGS = [
        1 => 'coordonnees',
        2 => 'beneficiaires',
        // … le même mapping, en double, ailleurs
    ];
}

Ça « marche ». Et ça pose exactement les problèmes qu’un enum PHP 8.1 résout d’un coup.

Ce qui cloche avec l’array de constantes

L’enum backed, avec ses comportements attachés

Un enum PHP 8.1 backed (adossé à un int) regroupe la valeur, ses libellés et sa logique de résolution au même endroit :

enum Step: int
{
    case Coordonnees          = 1;
    case Beneficiaires        = 2;
    case VotreContrat         = 3;
    case Recapitulatif        = 4;
    case PiecesJustificatives = 5;
    case Signature            = 6;
    case Paiement             = 7;

    public function slug(): string
    {
        return match ($this) {
            self::Coordonnees          => 'coordonnees',
            self::Beneficiaires        => 'beneficiaires',
            self::VotreContrat         => 'votre-contrat',
            self::Recapitulatif        => 'recapitulatif',
            self::PiecesJustificatives => 'pieces-justificatives',
            self::Signature            => 'signature',
            self::Paiement             => 'paiement',
        };
    }

    public function title(): string
    {
        return match ($this) {
            self::Coordonnees          => 'Coordonnées',
            self::Beneficiaires        => 'Bénéficiaires',
            self::VotreContrat         => 'Votre contrat',
            self::Recapitulatif        => 'Récapitulatif',
            self::PiecesJustificatives => 'Pièces justificatives',
            self::Signature            => 'Signature',
            self::Paiement             => 'Paiement',
        };
    }

    public static function fromSlug(string $slug): ?self
    {
        foreach (self::cases() as $step) {
            if ($step->slug() === $slug) {
                return $step;
            }
        }

        return null;
    }
}

Le gain n’est pas cosmétique :

Côté routing, la résolution slug → étape (et son rejet propre des slugs inconnus) tient dans fromSlug() :

$step = Step::fromSlug($request->attributes->get('slug'));
if ($step === null) {
    throw $this->createNotFoundException();
}
$this->render($step->title(), $step->slug());

Le piège à éviter : tout fourrer dans l’enum

Un enum n’est pas une fourre-tout. Ici, je n’ai pas modélisé la 8ᵉ « étape » de confirmation finale dedans : elle n’a ni formulaire ni état de session, elle est exposée par une route distincte. La forcer dans l’enum aurait obligé à des cas spéciaux (slug() qui renvoie quoi ? title() ?) pour une chose qui n’est pas vraiment une étape du même genre. L’enum modélise les 7 étapes homogènes ; l’exception reste dehors. Un enum gagne sa valeur à rester un ensemble cohérent.

Lessons learned

#php #symfony #enum #refactoring #kiss


Partager cet article :

Article précédent
State machine dans l’agrégat : transitions protégées