OpenWork Tech Blog

社員クチコミサービスを運営しているオープンワークエンジニアによるテックブログです。

PHP8.1、Symfony5.4でMyCLabs\Enum\EnumからEnum型に置き換える方法

Webアプリエンジニアの大橋です。

弊社では今年の年明けにPHP8.1、Symfony5.4にバージョンアップしたので Webアプリエンジニアチーム内で新しい機能について勉強会で紹介し合ったり、実際に使ってみたコードを共有しました。
その後は機能追加のついでや改善の一環として新しい機能を取り入れています。

今回はその中でもMyCLabs\Enum\EnumからEnum型に置き換えた方法について書きます。

MyCLabs\Enum\EnumからEnum型に置き換える

まずはEnumを作り直します。

before

バージョンアップ前はMyCLabs\Enum\Enumでこのように実装していました。

use MyCLabs\Enum\Enum;

/**
 * @method static PurposeType RECRUITING()
 * @method static PurposeType INFORMATION_GATHERING()
 * @method static PurposeType OTHER()
 */
class PurposeType extends Enum
{
    private const RECRUITING = 1;
    private const INFORMATION_GATHERING = 2;
    private const OTHER = 3;

    public static function getDescriptionListIndexedByValue(): array
    {
        return [
            self::RECRUITING => '採用活動で利用するため',
            self::INFORMATION_GATHERING => '利用を検討しており、情報収集している',
            self::OTHER => 'その他',
        ];
    }

    public function getDescription(): string
    {
        return self::getDescriptionListIndexedByValue()[$this->getValue()] ?? '';
    }

after

Enum型に置き換えるとこうなりました。

enum PurposeType: int   // 戻り値の定義が必要
{
    case Recruiting = 1;
    case InformationGathering = 2;
    case Other = 3;    // caseを追加する可能性がある場合は99等がおすすめ

    public function getDescription(): string
    {
        return match ($this) {
            self::Recruiting => '採用活動で利用するため',
            self::InformationGathering => '利用を検討しており、情報収集している',
            self::Other => 'その他',
        };
    }
}

matchと組み合わせて使うと便利です。
ここで注意する必要があるのがOtherのように最後に表示したい特殊なcaseがある場合です。
この後説明するFormでの表示順は、定義した順になるので、もし他にもcaseを追加する可能性がある場合、Otherのようなcaseには99など必ず最後になる値を設定することをお勧めします。

ChoiceTypeをEnumTypeに置き換える

次はこのEnum型をFormで使ってみます。
今まではChoiceTypeを使っていましたが、これをEnumTypeに置き換えます。  

use Symfony\Component\Form\AbstractType;
- use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
+ use Symfony\Component\Form\Extension\Core\Type\EnumType;

class PurposeTypeForm extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder)
    {
        $builder
            ->add(
                'purposeType',
-               ChoiceType::class,
+               EnumType::class,
                [
-                    'choices' => array_flip(
-                        PurposeType::getDescriptionListIndexedByValue()
-                    ),
+                    'class' => PurposeType::class,
+                    'choice_label' => static function (PurposeType $purposeType): string {
+                        return $purposeType->getDescription();
+                    },
                ]
            )
       ;
    }

choicesやarray_flip()を使わなくて済むようになりました。

一部のcaseのみ表示したい場合は?

EnumTypeはChoiceTypeを継承している為、choice_filterが使えます。

EntityのプロパティにenumTypeを指定する

次はDoctrineのEntityのプロパティにEnum型を使います。
スキーマの更新は不要でしたが、enumTypeに指定したEnum型に定義されていないcaseは許可しないので注意してください。

class RegistrationPurpose
{
    /**
-     * @see PurposeType
-     * @ORM\Column(name="purpose_type", type="smallint", nullable=false)
+     * @ORM\Column(name="purpose_type", enumType=PurposeType::class, type="smallint", nullable=false)
     */
-    private int $purposeType;
+    private PurposeType $purposeType;
}

QueryBuilderでもEnum型を使えるか?

setParameter()を使用するとEnum型もint型(プリミティブ型)もどちらも使えましたが、 直接指定の場合はEnum型は使えませんでした。

MyCLabs\Enum\Enumの使い方との違い

MyCLabs\Enum\Enumの使い方とEnum型での使い方の違いを表にしてみました。

MyCLabs\Enum\EnumEnum型
new SampleType($value)SampleType::from($value) または SampleType::tryFrom($value)
SampleType::OTHER()SampleType::Other
SampleType::values()SampleType::cases()
$sampleType->getValue()$sampleType->value
$sampleType->equals(self::Other)$sampleType === self::Other

置き換えの進め方

MyCLabs\Enum\Enumもまだ使えるのでEnum型への置き換えは必須としていませんが、私たちは以下のようなスタンスで対応をしています。

  • 新しく作成するコードは新しい機能を使う
  • 何かのついでに対応できそうな場合は古いコードも新しい機能に置き換える
  • リファクタの1つとして対応する

新しく入社したメンバーが「どちらの方法で実装すればいいのだろう?」と迷うことがないようにしていきたいと思います。

バージョンアップの詳しい話はこちら techblog.openwork.co.jp オープンワークの採用情報はこちら www.openwork.co.jp