九保すこひ@フリーランスエンジニア|累計300万PVのブログ運営中
さてさて、「プログラミング言語」と一口に言っても世の中にはPHP
以外にもPyhon
やJava
などたくさんのものが存在しています。
そして、そんな他の言語を使ったことがある人なら、(きっと)思うこと。
それは・・・・・・
あっちの言語だったらできたのにな…😅
です。
結構前ですが、私はAndroid
アプリを開発していたのですが、そのとき「これ便利だな」と感じていたのが今回テーマの
Enum(いーなむ)
です。
Enum
とは、簡単にいうと「先にグループのメンバーを決めておく」仕組みです。
例えば、十二支で見てみましょう。
- 子
- 丑
- 寅
- 卯
- 辰
- 巳
- 午
- 未
- 申
- 酉
- 戌
- 亥
もちろんですが、この中には「パンダ」や「カモノハシ」は入ることはできません。もうグループ・メンバーが決定しているからです。
では、なぜグループ化するのかというと「予期せぬ値を扱わないようにできる」というメリットがあるからですね。
そして、長らくPHP
にはこのEnum
の機能がついていなかったのですが、実は次期バージョン8.1
(2021.11.25 リリース予定)から使えるようになります。
そこで❗
今回は、Laravel + Enum
でセレクトボックスをつくり、さらに、それを送信したときのバリデーションまで実装してみることにしました。
ぜひ何かの参考になりましたら嬉しいです。
※ なお、PHP 8.1
のリリースの関係で記事公開の順番を変更させていただきました。「Laravel + Leaflet 地図で駅名検索できるようにする」は来週公開させていただきます。もし楽しみにしていただいていたらゴメンナサイ。m(_ _)m
「もうすぐ8.1リリースですよ👍」
開発環境: PHP 8.1.0(RC6)、Laravel 8.70
目次
選択肢について
今回Enum
でつくる選択肢は「書籍のカテゴリ」で、以下のとおりです。
- 本
- 雑誌
- 新聞
では実際に作業を進めていきましょう❗
Enumをつくる
では、早速Enum
をつくりましょう。
とはいっても、Enum
はよく開発でもつかう「クラス」をもっとシンプルにしたものと考えて問題ないので、そんなに複雑ではありません。
実際のコードはこうなります。
app/Enums/Category.php
<?php namespace App\Enums; enum Category: int { // 基本情報 case Book = 1; case Magazine = 2; case Newspaper = 3; // 日本語を追加 public function label(): string { return match($this) { Category::Book => '本', Category::Magazine => '雑誌', Category::Newspaper => '新聞', }; } }
まず、Enum
で必ず必要になってくるのが「名前」で、今回では以下の3つになります。
- Book
- Magazine
- Newspaper
これだけでもEnum
としては使えるのですが、category_id
としてデータベースに保存することを想定しているので、今回はそれぞれ1
〜3
のIDを追加しています。
なお、
enum Category: int
と書いているのは、このID
が数値だからです。もし文字列を使いたい場合はstringになりますので、気をつけてください。(enum
では、TypeScript
みたいに型が重要になってきます)
そして、日本語環境ではセレクトボックスの選択肢に「Book」や「Magazine」と表示されるのはあまり使い勝手がいいとはいえません。
そのため、それぞれ日本語名を追加します。
それが、label()
の部分です。(なお、label
という名前は好きに変更してOKですよ👍)
そして、以下のようにすると日本語名を取得できるようになります。
$label = Category::Book->label(); // 本
コントローラーをつくる
では、Enum
のテスト用にコントローラーをつくります。
以下のコマンドを実行してください。
php artisan make:controller EnumController
すると、ファイルが作成されるので中身を以下のようにしてください。
app/Http/Controllers/EnumController.php
<?php namespace App\Http\Controllers; use App\Enums\Category; use Illuminate\Http\Request; use Illuminate\Support\Arr; use Illuminate\Validation\Rule; class EnumController extends Controller { public function create() { $categories = Category::cases(); return view('enum.create', compact('categories')); } public function store(Request $request) { $categories = Category::cases(); $category_ids = Arr::pluck($categories, 'value'); $request->validate([ 'category_id' => ['required', Rule::in($category_ids)] ]); return 'バリデーション通過しました!'; } }
まず、create()
では、先ほどつくったenum
をビューに渡しています。
また、store()
では選択されたID
が「事前にセットした3つの中に入っているか?」をチェックしています。
ビューをつくる
では、先ほど指定したビューをつくっていきましょう。
resources/views/enum/create.blade.php
<html> <head> <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.1/dist/css/bootstrap.min.css" rel="stylesheet"> </head> <body> <div id="app" class="p-5"> <h1 class="mb-5">Enum(PHP 8.1 以上) を使って<br>選択肢をつくるサンプル</h1> <div class="row"> <div class="col-4"> <form action="/enum" method="POST"> @csrf <select name="category_id" class="form-control"> <option value="">▼ 以下から選択</option> @foreach($categories as $category) <option value="{{ $category->value }}">{{ $category->label() }}</option> @endforeach </select> @error('category_id') <div class="alert alert-danger">{{ $message }}</div> @enderror <button type="submit" class="btn btn-primary mt-3">送信する</button> </form> </div> </div> </div> </body> </html>
この中で重要なのが、セレクトボックスをループでつくっているところです。
つまり、今回の場合で言うと、以下のようにすることでそれぞれの値を取得することができます。
- $category->name: Book、Magazine、Newspaper
- $category->value: 1、2、3
- $category->label(): 本、雑誌、新聞
そして、これを使えばJavaScript
側にも配列やオブジェクトでEnum
の中身を使えるようになるというわけですね。
ルートをつくる
では、コントローラーでつくったメソッドをルートとして登録しておきましょう。
use App\Http\Controllers\EnumController; // 省略 Route::get('enum/create', [EnumController::class, 'create']); Route::post('enum', [EnumController::class, 'store']);
これで作業は完了です❗
お疲れ様でした😄✨
ちなみに: JavaScript 側へセットするには
ちなみに、例えばVue 3
にEnum
の中身をセットする場合は次のようになります。
Vue.createApp({ data() { return { categories: [ @foreach($categories as $category) { id: {{ $category->value }}, name: '{{ $category->name }}', label: '{{ $category->label() }}' }, @endforeach ] } }, }).mount('#app');
また、以下のようにkey-value
形式のオブジェクトにすることもできます。
Vue.createApp({ data() { return { categories: { @foreach($categories as $category) {{ $category->value }}: '{{ $category->label() }}', @endforeach } } }, }).mount('#app');
テストしてみる
では、実際にテストしてみましょう❗
「https://******/enum/create」へブラウザからアクセスしてください。
すると、セレクトボックスが表示されるので、この中から「本」を選択肢、送信してみましょう。
すると・・・・・・
はい❗
うまくバリデーションを通過できました。
ただ、これだけでは本当にちゃんとEnum
以外のものを拒否してくれるのかわからないので、次に選択肢にない「技術書」をわざとセレクトボックスに追加して送信してみましょう。
<select name="category_id" class="form-control"> <option value="">▼ 以下から選択</option> @foreach($categories as $category) <option value="{{ $category->value }}">{{ $category->label() }}</option> @endforeach <!-- ⚠ これは Enum には入っていない選択肢です! --> <option value="999">技術書</option> </select>
では、もう一度リロードして選択しなおします。
どうなったでしょうか・・・・・・
はい❗
バリデーション・メッセージは英語のままですが、うまくエラーを表示してくれました。
成功です😄✨
企業様へのご提案
今回のように、「これしかありえない選択肢」を使うことで、より想定外の出来事が起きにくいコードを書くことができます。
また、Enum
は使い回しもしやすいですので、開発の効率化にも役立つかと思います。
もしそういったご依頼がございましたら、お気軽にお問い合わせからご連絡ください。
どうぞよろしくお願いいたします。m(_ _)m
おわりに
ということで、今回はPHP 8.1
から使えるようになるEnum
をLaravel
で使ってみました。
これまでグループ化された定数を使う場合、以下のようにモデルの中に入れるパターンや、Laravel
のconfig
フォルダの中にセットするパターンが多かったかもしれませんが、これからはEnum
を使うのもありじゃないでしょうか。
class Category extends Model { use HasFactory; const BOOK = 1; const MAGAZINE = 2; const NEWSPAPER = 3; const CATEGORIES = [ self::BOOK => '本', self::MAGAZINE => '雑誌', self::NEWSPAPER => '新聞', ]; }
ぜひ、みなさんも一度試してみてくださいね。
ではでは〜❗
「この標識に勇気づけられました。
挫折禁止 😂✨」