Symfony Advent Calendar 2021 4日目の記事です。
昨日は @ttskch さんの UniqueEntity制約はpersistしただけの既存エンティティとの重複は検出してくれないので工夫が必要 でした。

Symfony6は "php": ">=8.0.2" という攻めた必須要件で、PHP8.1を使っているとEnumの恩恵を受けることもできるようになりました!
というわけで、早速PHP8.1.0とSymfony6の環境でEnumフォームタイプで遊んでみようと思います。(EnumFormType自体はSymfony5.4から利用可能です)
※ 以下、コードの詳細は遊んだプルリク https://github.com/77web/php8.1-Symfony6-playground/pull/1 のコミットログでも確認できます。

EnumType

EnumTypeはChoiceTypeを継承しているのでmultiple, expanded等ChoiceTypeでおなじみのオプションも使えます。 https://github.com/77web/php8.1-Symfony6-playground/blob/c125e99ac14ff4143e2446c6a48ad3d9b136f13b/src/Form/ContactType.php

<?php
class ContactType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('name')
            ->add('age', EnumType::class, [
                'class' => Age::class,
            ])
            ->add('interests', EnumType::class, [
                'class' => Interest::class,
                'multiple' => true,
                'expanded' => true,
            ])
            ->add('opinion')
        ;
    }
    // ...
}

EnumTypeを含むフォームへのsubmit

https://github.com/77web/php8.1-Symfony6-playground/blob/c125e99ac14ff4143e2446c6a48ad3d9b136f13b/tests/Form/ContactTypeTest.php

BackedでないEnumに対してはEnumに定義されたcase上の順番(0index)の番号、BackedEnumに対してはBackedな値(Enumのインスタンスのvalueに当たるもの)をsubmitします。

<?php

$form->submit([
    'name' => '77web',
    'age' => 3, // Age::AGE_40_TO_49はAgeの4番目のcase
    'interests' => ['php', 'angular', 'aws'], // BackedEnumなのでvalue
    'opinion' => 'I love Symfony',
]);

$form->getData() で実際にセットされた値を確認すると、ちゃんと各Enumのインスタンスがセットされていることがわかります。

<?php

$this->assertEquals(Age::AGE_40_TO_49, $data->getAge());
$this->assertEquals([Interest::PHP, Interest::FRONTEND, Interest::INFRA], $data->getInterests());

EnumTypeを使ったフォームのレンダリング

HTMLになると通常のChoiceTypeと同じです。

multiple => true, expanded => falseにしたときはselect multiple multiple => true, expanded => trueにしたときはcheckbox
スクリーンショット 2021-12-04 0 15 10 スクリーンショット 2021-12-04 0 15 23

先ほど見たようにselect,radio,inputの値はsubmitしたい値なので、BackedでないEnumでは連番、BackedEnumではvalueとなります。

BackedでないEnumのselectのHTML出力

スクリーンショット 2021-12-04 0 40 50

BackedEnumのcheckboxのHTML出力

スクリーンショット 2021-12-04 0 45 22

まとめ

PHP8.1のEnum+Symfony6のEnumType、 いままで自作の EnumTrait で色々むちゃしていたところがスッキリできそうです。遊びでコードを書いているだけでも、とても良い開発者体験を味わうことができました。 ☺️ 早くプロダクションコードをPHP8.1にして、自作のEnumTraitを駆逐したいです :grin:

明日も @ttskch さんの記事です!お楽しみに!