Laravel の Enum をInertia.jsでスマートに扱う方法 – これで煩わしい定数定義にサヨナラ!

こんにちは。フリーランス・コンサルタント&エンジニアの 九保すこひ です。

さてさて、最近はLaravel + InertiaReactVue 3)の開発が多くなってきたのですが、やはりInertiaを使うとAPI開発がすっとばせるので、とても快適に開発できています。

ただ、1点「うーん…」と思うことがありました。

それが・・・・・・

「Enum が JavaScript 側で参照できない😅」

というものです。

そりゃあ、EnumPHPでセットしているものなので当然なんですが、毎回毎回ビューのパラメータにセットするのがメンドクサイんですよね…

こんなカンジですね。
↓↓↓

Inertia::render('Test', ['enum_values' => $enum_values]);

ちなみに、以前公開した「Laravel + Inertia + React で多言語化する」と同じようにページにアクセスするたびに自動でInertiaへデータを送ってもいいのですが、これも1点だけ「うーん…」なことがあったりします。

それは・・・・・・

「動的にデータ取得しているので、入力補完が効かない😫」

というものです。

つまり、(私が使っている)phpstormでデータを入力する時に毎回「えーっと、あれの値はなんだっけか??」とEnumファイルを見にいかないといけなくなってしまうわけです。

(例えば、「Food.」と入力した時点でカレーとかピザとかの候補が出てほしいんです)

そこで❗

一計を案じて、「Laravel側のEnumが自動的にInertia側に渡されるようにする方法」を考えてみました。しかも入力補完にも対応してます。

ぜひ何かの参考になりましたら嬉しいです。😄✨

※ ちなみにPHPEnum8.1から使える機能です。お気をつけください!

「神戸の名店ホルモン焼き
めちゃくちゃウマかった❗
ホルモン1本60円👍」

開発環境: PHP 8.1、Laravel 10.x、Vite、Inertia.js、React 18

Enum(列挙型)をつくる

では、まずはInertia側へ渡したいEnumをつくります。
今回はわかりやすく「食べ物」に関するデータでつくってみましょう。

app/Enums/Food.php

<?php

namespace App\Enums;

enum Food: int
{
    case Pizza = 1;     // ピザ
    case Pasta = 2;     // パスタ
    case Burger = 3;    // ハンバーガー
    case Curry = 4;     // カレー
    case Ramen = 5;     // ラーメン
}

ちなみにEnumとは、シンプルに言うと「定数(変更されないデータ)」をグループで管理できる機能です。

Enum のデータを JavaScript 化する Artisan コマンドをつくる

続いて、先ほどつくったEnumJavaScriptでも読み込めるようにするArtisanコマンドをつくります。

以下のコマンドを実行してください。

php artisan make:command EnumToInertiaCommand

すると、Artisanコマンド用のファイルが作成されるので中身を以下のように変更します。

app/Console/Commands/EnumToInertiaCommand.php

<?php

namespace App\Console\Commands;

use App\Enums\Food;
use Illuminate\Console\Command;

class EnumToInertiaCommand extends Command
{
    protected $signature = 'enum_to_inertia';
    protected $description = 'A command to convert enums to JavaScript';

    // ここに変換したい Enum を追加する
    private $converting_enums = [
        Food::class,
    ];

    public function handle(): void
    {
        $js_code = '';

        foreach ($this->converting_enums as $converting_enum) {

            $enum_name = last(
                explode('\\', $converting_enum)
            );
            $cases = call_user_func($converting_enum .'::cases');
            $js_code .= 'window.'. $enum_name .' = {' . "\n";

            foreach ($cases as $case) {

                $case_name = $case->name;
                $case_value = $case->value;
                $js_value = (is_string($case_value)) // 文字列の場合はクォートで囲む
                    ? "'". $case_value ."'"
                    : $case_value;
                $js_code .= '    "'. $case_name .'": '. $js_value .',' . "\n";

            }

            $js_code .= '};'. "\n";

        }

        $path = resource_path('js/Constants.js');
        file_put_contents($path, $js_code);

        $this->info('Done!');
    }
}

この中では、$converting_enumsにセットされた全てのEnumからデータを取得し、JavaScriptのコードを生成。そして、それをファイルConstants.jsへ保存するようにしています。

※ なお、今回はテストなのでkey-value形式のみですが、JavaScriptではオブジェクトの順番が保持できない(確約されていない)ので、実際にはコレクション形式のデータも合わせて生成するようにするといいでしょう。

コマンドがビルド時に自動実行されるようにする

では、先ほどのArtisanコマンドがViteのビルド時に自動的に実行されるようにします。

package.json

{
    "private": true,
    "scripts": {
        "dev": "vite",
        "build": "php artisan enum_to_inertia && vite build"
    },

    // 省略

ここでは、初期状態では以下のようになっている部分にphp artisan enum_to_inertia && を追加しています。

"build": "vite build"

こうすることで、ビルド時に以下2つの流れで実行されるようになります。

  1. Enum のデータを JavaScript 化
  2. 通常の Vite のビルド

ちなみに、今回はビルド時だけJavaScript化されるようにしていますが、devでも同じように実行したい場合は以下のようにするといいでしょう。(時間がかかる可能性もあるので今回はビルド時のみにしました)

"dev": "php artisan enum_to_inertia && vite",

これで自動的にEnumJavaScript化することができるようになりました。

定数の読み込み

続いて、先ほどのJavaScript化した定数ファイルはそのままではViteで有効になっていないので、app.jsxへのセットしておきましょう。

import './bootstrap';
import '../css/app.css';

import { createRoot } from 'react-dom/client';
import { createInertiaApp } from '@inertiajs/react';
import { resolvePageComponent } from 'laravel-vite-plugin/inertia-helpers';
import './Constants'; // 👈 ここを追加しました

const appName = window.document.getElementsByTagName('title')[0]?.innerText || 'Laravel';

// 省略

さぁ、これで作業は全て完了です。
お疲れ様でした😄

テストしてみる

では、実際にテストしてみましょう❗

まずはEnumの定数の中身を確認するためのページをササッとつくります。

routes/web.php

use Inertia\Inertia;

// 省略

Route::get('constants', fn() => Inertia::render('Constants'));

そして、ビューです。

resources/js/Pages/Constants.jsx

export default function Constants() {

    const FoodKeys = Object.keys(Food);

    return (
        <div className="p-5">
            {FoodKeys.map((key) => (
                <div key={key}>{key}: {Food[key]}</div>
            ))}
        </div>
    );

}

さぁ、これで準備は整いました。
以下のコマンドでViteのビルドを実行してみましょう。

npm run build

すると・・・・・・

はい❗
Enum → JavaScript」化したConstants.jsが作成されました。

まずは成功です😄👍

ちなみに中身はこうなっていました。

window.Food = {
    "Pizza": 1,
    "Pasta": 2,
    "Burger": 3,
    "Curry": 4,
    "Ramen": 5,
};

これで、グローバルな変数としてFoodが使えるようになりますね。

では、先ほどつくったテストページを実行してみましょう❗

うまくいくでしょうか・・・・・・

はい❗
うまくEnumから取得したデータを表示することができました。

では、今回重要な点として冒頭でも書いた「入力補完」がうまくいくかを最後にチェックしてみましょう。

テストページの途中に「Food.」と入力してみます。

すると・・・・・・

はい❗
Enumの入力補完がうまく働きました。

すべて成功です😄✨

企業様へのご提案

今回のように、PHP側のデータをJavaScript化することで、PHPのデータをJavaScript(今回はReact)内で利用することができ、さらに入力補完も効くようにしているので、開発効率もあがること間違いなしです。

もしそういった効率的な開発を行いたい場合はぜひお問い合わせからご相談ください。

お待ちしております!

開発のご依頼お待ちしております
開発のご依頼はこちらから: お問い合わせ
どうぞよろしくお願いいたします! by 九保すこひ

おわりに

ということで、今回はLaravelEnumJavaScript化し、定数としてReact内から参照できるようにしてみました。

ちなみに、今回はReactでしたがやり方としては同じなのでVuesvelteでも使えるんじゃないでしょうか。

また、今回のアイデアはうろ覚えなのですが、たしかlaravel-ide-helperが同じような仕組みで実装してるんじゃなかったっけ…🤔と思ったことが発端でした。(つまり、やり方としてはパクリです。天才たち、いつもありがとう!)

なお、今回の件で一点だけ気をつけていただきたいのが、「外に出してはいけないデータを定数化してしまう」ことです。

例えば、Laravelのコンフィグにはキーなどが含まれているので安易に定数化することはしないでください。

便利さの裏にはセキュリティ上の懸念も存在するということですね。

ではでは〜❗

「シャウエッセンに
旨みひろがる香り白だしをかける
…神ですな」

このエントリーをはてなブックマークに追加       follow us in feedly