🐤

Laravel Enum 機能を試す

2021/11/22に公開

まえがき

Laravel 8.69~8.71辺りで、PHP8.1からの新機能の Enum への対応が始まっているので、それを試してみました。

PHP8.1 Enum の機能自体については、下記のリンクを辿って下さい。
PHP マニュアル 列挙型(Enum)
PHP 8.1: Enums(英語)

執筆時点の Laravel では、Validation、cast、Insert などで Enum がサポートされました。
Validation は、あまり試し甲斐がないので、以下では cast、そして最後に DB へ登録する所まで試してみました。

以下、参考リンク
[8.x] Add an Enum validation rule #39437
[8.x] Allow model attributes to be casted to/from an Enum #39315
[8.x] Accept enums for insert update and where #39492
[8.x] Enum casts accept backed values #39608

本題

ということで、まずはサンプル用 Enum ファイルを作成します。

app/Enums/UserStatus.php

<?php

namespace App\Enums;

enum UserStatus: int
{
    case Pending = 1;
    case Valid = 2;
    case Locked = 3;

    public function getStatus(): string
    {
        return match($this)
        {
            self::Pending => 'pending',
            self::Valid => 'valid',
            self::Locked => 'locked',
        };
    }

    public function isLocked(): bool
    {
        return $this === self::Locked;
    }
}

User用マイグレーションファイル

    public function up()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->timestamp('email_verified_at')->nullable();
            $table->string('password');
+           $table->unsignedTinyInteger('status');
            $table->rememberToken();
            $table->timestamps();
        });
    }

DBは、Enum型とかではなく、普通に tinyint にしています。

Userモデル

+use App\Enums\UserStatus;

class User extends Authenticatable
{
    // 途中、略

    protected $casts = [
        'email_verified_at' => 'datetime',
+       'status' => UserStatus::class,
    ];

web.php

Route::get('/', function () {
    $user = new User();
    $user->status = UserStatus::Pending; // まずはこれを試す。
    // $user->status = 2;
    // $user->status = '3';
    // $user->status = 5;

    dump($user->status === UserStatus::Pending);
    dump($user->status === UserStatus::Valid);
    dump($user->status->isLocked());
    dd($user->status->getStatus());
});

と言うことで、「まずはこれを試す。」という箇所を試すと、以下の結果となります。

true
false
false
"pending"

続けて今後は、$user->status = 2; を有効化して試します。(Enumに数字や文字列を付加したりするのは、Backed Enum とか呼ばれたりしていますが…)

false
true
false
"valid"

こちらも大丈夫で、2番目が true になりました。

では今度は、$user->status = '3'; を有効化して試してみます、意図的に文字列にしていますが…

false
false
true
"locked"

こちらも問題無しで、3番目が true。

今度は、$user->status = 5; を試してみます。(5番は存在しない)

ValueError
5 is not a valid backing value for enum "App\Enums\UserStatus"

見事エラーになりました。

今度は、DBに保存してみます。とりあえず、以下の感じで。

Route::get('/', function () {
    $user = new User();
    $user->status = UserStatus::Pending;

    $user->name = 'taro';
    $user->email = 'aaa@example.net';
    $user->password = bcrypt('aaaa');
    $user->save();

    return view('welcome');
});

画像無くてすいませんが、DBには1件無事保存され、'status' 欄には、1と登録されました。

雑感

いつかお世話になるかも知れません。

おかしな箇所等ありましたらコメント下さい。

Discussion