🎻

[Symfony] エンティティのプロパティが定数をとるときに僕がよくやる実装

2020/07/26に公開

僕がよくやる実装を紹介していくシリーズです😇

エンティティのプロパティが定数をとるとき

例えば、 ユーザー部署 という文字列プロパティを持っていて、そこには 営業部 開発部 総務部 という3つの値しか入らないことを想定しているとします。

厳密にやるなら 部署 をエンティティにしたほうがいいのかもしれませんが、

  • そう頻繁には変更されなさそう
  • アプリ利用者が自分で変更できる必要もない

ようなものなら、定数にしてしまったほうがコードがスッキリします。

僕は試したことがないのですが、myclabs/php-enum のようなEnumっぽい機能を提供してくれるライブラリもあるので、こういうものを活用するのもいいかもしれません。

エンティティの実装

具体的にはこんな感じです。

use Symfony\Component\Validator\Constraints as Assert;

class User
{
    const DIVISION_SALES = '営業部';
    const DIVISION_DEVELOPMENT = '開発部':
    const DIVISION_GENERAL = '総務部';
    
    public static function getValidDivisions(): array
    {
        return [
            self::DIVISION_SALES,
            self::DIVISION_DEVELOPMENT,
            self::DIVISION_GENERAL,
        ];
    }

    /**
     * Assert\Choice(callback="getValidDivisions")
     */
    private $division;
}

Choice 制約の callback オプション を使って、適切な部署名しか入力できないようにバリデーションを設定しています。

もっとアグレッシブにやるなら

public function setDivision(string $division): self
{
    if (!in_array($division, self::getValidDivisions()) {
        throw new \LogicException('Invalid division');
    }
    
    $this->division = $division;
}

のようにsetterで弾くようにしてしまってもいいかもしれません。

FormTypeの実装

部署を入力するためのFormTypeも使い回せるように独立して作っておきます。

// src/Form/User/DivisionChoiceType.php

class DivisionChoiceType extends ChoiceType
{
    public function configureOptions(OptionsResolver $resolver)
    {
        parent::configureOptions($resolver);

        $resolver->setDefaults([
            'choices' => array_combine(User::getValidDivisions(), User::getValidDivisions()),
            'multiple' => false,
        ]);
    }
}

こうしておけば、他のFormTypeから簡単に呼び出せて便利です。

// src/Form/UserType.php
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('divisions', DivisionChoiceType::class, [
            'required' => false,
            'label' => '部署',
        ])
}

UserType からしか使われないようならわざわざ独立したFormTypeにしておく必要はないかもしれませんが、場合によっては他のエンティティが 対象とする部署 のような形で 部署 の情報を持つような仕様も全然あるでしょうし、EasyAdminBundle を使っていて管理画面のフォームもテキストフィールドではなく選択式にしたいという場合にも使えます。

参考:[Symfony] EasyAdminBundleで普通のテキストプロパティを選択形式にする

まとめ

こんな感じで実装しておくと、部署のバリエーションや表示順の情報は User クラスの定数定義と getValidDivisions() メソッドに閉じ込められるので、とても簡単に変更に対応できます。

  class User
  {
+     const DIVISION_DEVELOPMENT = '開発部':
      const DIVISION_SALES = '営業部';
-     const DIVISION_DEVELOPMENT = '開発部':
+     const DIVISION_PLANNING = '企画部';
      const DIVISION_GENERAL = '総務部';
      
      public static function getValidDivisions(): array
      {
          return [
+             self::DIVISION_DEVELOPMENT,
              self::DIVISION_SALES,
-             self::DIVISION_DEVELOPMENT,
+             self::DIVISION_PLANNING,
              self::DIVISION_GENERAL,
          ];
      }
GitHubで編集を提案

Discussion