Laravel-enumを使おう!

7 min read読了の目安(約6400字

こんにちは

今回は定数をいい感じに管理する1つの手法としてLarave-enumをご紹介したいと思います。
enumは特にlaravel独自の概念ではなく、php以外のプログラミング言語にもある概念です。

Enumはいつ使うのか?

enumが活躍するのは、識別用の数字と意味を持つ文字が対を成すときで、例えば所属している部署情報をデータベースに格納する際にデータベースに "管理部", "人事部"という文字をいれることはほぼありません。

だいたいはデータベースに「1」とか「2」とか識別用の数字を割り当てて格納し、
Webサイトで文字を表示する際に、「ええ、1っていうことは"管理部"という文字を出力すればいいんだな」「ええと、2だから"人事部"っていう言葉を返せば良いんだな」というふうに変換していることが多いのではないでしょうか?

こういうときにenumが役立ちます。では早速使い方を見ていきましょう。

Laravel-enumの使い方

今回は管理部と人事部と情報システム部の3つの部署があると仮定して、「管理部が1番」「人事部が2番」「情報システム部が3番」ということにします。

Laravel-enumを追加する

$ composer require bensampo/laravel-enum

enumファイルを作成する

今回は部署用のenumファイルを作成するので、 DepartmentType と名付けてみました。

$ php artisan make:enum DepartmentType
Enum created successfully

app/Enums/DepartmentType.php に新しいファイルができました。

産まれたてのenumファイルはこんな感じになっています。

<?php

namespace App\Enums;

use BenSampo\Enum\Enum;

/**
 * @method static static OptionOne()
 * @method static static OptionTwo()
 * @method static static OptionThree()
 */
final class DepartmentType extends Enum
{
    const OptionOne =   0;
    const OptionTwo =   1;
    const OptionThree = 2;
}

定数なのに定数名がアッパーケースになっていないのが困ったところですが、まあそれは後で直してあげましょう。

enumファイルを編集する

まずは産まれたてのenumの定数部分をそれぞれの部署に置き換えてみました。
1が管理部で、2が人事部で、3が情報システム部です。

<?php

namespace App\Enums;

use BenSampo\Enum\Enum;

final class DepartmentType extends Enum
{
    const MANAGEMENT =   1; // 修正
    const HUMAN_RESOURCE =   2; // 修正
    const INFORMATION_SYSTEM = 3; // 修正
}

実はこれである程度動作するのです。 php artisan tinker で試してみましょう。

>>> DepartmentType::getDescription(1)
=> "Management"

>>> DepartmentType::getDescription(2)
=> "Human resource"

>>> DepartmentType::getDescription(3)
=> "Information system"

>>> DepartmentType::getValue("MANAGEMENT")
=> 1

>>> DepartmentType::getValue("HUMAN_RESOURCE")
=> 2

>>> DepartmentType::getValue("INFORMATION_SYSTEM")
=> 3

DepartmentType::getDescription(1) と打つと、 constで定義した定数名の"Management"が返ってきました!

ただ、日本で使う分にはこれでは不十分ですよね、 Managementではなく「管理部」と表示されてほしいところです。

では、現在は 「1」と「Management」が対応づいているところを「1」と「管理部」が対応づくように設定してみましょう。

getDescriptionとgetValueをオーバーライドする

言語ファイルを作成してローカライゼーションする方法もあるのですが、
今回はgetDescriptionとgetValueをオーバーライドする方法を解説してみます。

一番楽で直感的に分かりやすい方法かなと思います。

では、enumファイルを次のように書き換えます。

<?php

namespace App\Enums;

use BenSampo\Enum\Enum;

final class DepartmentType extends Enum
{
    const MANAGEMENT =   1;
    const HUMAN_RESOURCE =   2;
    const INFORMATION_SYSTEM = 3;

    // ここから先を追加
    public static function getDescription($value): string
    {
        if ($value === self::MANAGEMENT) {
            return '管理部';
        }

        if ($value === self::HUMAN_RESOURCE) {
            return '人事部';
        }

        if ($value === self::INFORMATION_SYSTEM) {
            return '情報システム部';
        }

        return parent::getDescription($value);
    }

    public static function getValue(string $key)
    {
        if ($key === '管理部') {
            return self::MANAGEMENT;
        }

        if ($key === '人事部') {
            return self::HUMAN_RESOURCE;
        }

        if ($key === '情報システム部') {
            return self::INFORMATION_SYSTEM;
        }

        return parent::getValue($key);
    }
}

このif文の書き方は個人的に見やすくて好きなだけなので、switch文使ったり、ちゃんとelse if使ったりお好きにどうぞ。

では、こちらで試してみましょう。

>>> DepartmentType::getDescription(1)
=> "管理部"

>>> DepartmentType::getDescription(2)
=> "人事部"

>>> DepartmentType::getDescription(3)
=> "情報システム部"

>>> DepartmentType::getValue("管理部")
=> 1

>>> DepartmentType::getValue("人事部")
=> 2

>>> DepartmentType::getValue("情報システム部")
=> 3

いい感じに出力されていますね!

では、ここからは個人的におすすめするlaravel-enumの便利な使い方を紹介していきます。

キーとバリューのペアの配列を出力する(asSelectArray)

asSelectrrayを使うとキーとバリューのペアで配列を出力してくれます。

フロントエンド側でセレクトボックスを使うときに、項目をasSelectArrayを使って渡してあげると、超簡単にセレクトボックスができます。

>>> DepartmentType::asSelectArray()
=> [
     1 => "管理部",
     2 => "人事部",
     3 => "情報システム部",
   ]

Enumに定義されている値かどうかを確認する(hasValue)

>>> DepartmentType::hasValue(1)
=> true
>>> DepartmentType::hasValue(5)
=> false

これを使えば、バリデーションっぽい実装ができます。

public function store(Request $request)
{
    $name = $request->input('name');
    if (!DepartmentType::hasValue($name)) {
        // 送られてきた値がEnumの定義の範囲外の場合の処理
    }
}

そんなことよりもっとスマートにバリデーションする

送られてきた値ががEnumの定義の範囲内かどうかのバリデーションは実は簡単に実装できます。

上と同じことをやろうとした場合、このようになります。

public function store(Request $request)
{
    $this->validate($request, [
        'department' => 'enum_value:' . Department::class,,
    ]);
}

これで普通にバリデーションができます。

今回は定数の値でバリデーションをかけていますが、キーでバリデーションを書けることもできます。詳しいことを知りたい場合は公式サイトを見るのが一番!

https://github.com/BenSampo/laravel-enum#instance-properties

マイグレーションだってEnumで簡単にできちゃう

今回の例では、 DepartmentTypeのenumを使用してこのように記述できます。
(ちなみに僕はマイグレーションの際にEnum型は使いません。ちなみに調べてみると、mysqlにenum型を使うのはだいぶ宜しくないみたいなので、良い子は真似しないでください)

<?php

use App\Enums\DepartmentType;
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class CreateDepartmentsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('departments', function (Blueprint $table) {
            $table->id();
            $table->enum('type', DepartmentType::getValues())
                ->default(DepartmentType::MANAGEMENT);
            $table->timestamps();
        });
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::dropIfExists('departments');
    }
}

mysqlで定義を確認するとこのようになっていました。

mysql> describe departments;
+------------+---------------------+------+-----+---------+----------------+
| Field      | Type                | Null | Key | Default | Extra          |
+------------+---------------------+------+-----+---------+----------------+
| id         | bigint(20) unsigned | NO   | PRI | NULL    | auto_increment |
| type       | enum('1','2','3')   | NO   |     | 1       |                |
| created_at | timestamp           | YES  |     | NULL    |                |
| updated_at | timestamp           | YES  |     | NULL    |                |
+------------+---------------------+------+-----+---------+----------------+

これだと、もっとtype増やしたい!っていうときにちょっと面倒なので、
僕はEnum型を使っていませんが、こういうのもありますよっていう紹介として捉えていただけると良いかと思います!

終わりに

laravel-enumは導入が簡単ですし学習コストも低くて、おすすめです!

定数は全部configディレクトリ配下にばんばん入れちゃっている人も、この際larave-enumを使ってみたらいかがでしょうか!

というわけで、ではまた!