Laravelを勉強していると必ず登場する、
Validation(バリデーション)
今回はバリデーションについて、
- そもそも何なのか
- なぜ必要なのか
- Controllerに直接書く方法
- Form Requestを使う方法
- Form Requestの便利な機能
まで、RPGの世界に例えながら整理してみます。
バリデーションを一言で
門番です。
もう少し正確に言うと、
アプリケーションに入ってくるデータが、
決められたルールを守っているか確認する仕組みです。
バリデーションの恩恵
RPGの世界にあるギルドで考えてみます。
👦 Request(村人)
「畑の作物を食い荒らす魔物がいて困っているんです!」
「ギルドに討伐依頼を出しに来ました!」
村人は依頼書を書きます。
{
"enemy": "",
"place": "むら"
}
そしてギルドへ。
🚶♂️...
👩🦰 Validation(ギルド受付)
「ご依頼ありがとうございます!」
「まずは依頼内容を確認しますね!」
依頼書を確認すると……
enemy → 空っぽ
place → むら
受付:
「敵の名前が書かれていませんね……」
「これでは勇者に依頼を出せないので、enemyを書いてからもう一度お願いします!」
なぜ受付の人はこれを判断できたのでしょうか?
例えば、ギルド側でこんなルールが決められていたとします。
enemy→ 必須
place→ 必須
もしValidationをせずに、そのまま依頼ボード(DB)へ保存すると、
依頼名:ゴブリン討伐
敵:
場所:むら
のような不完全な依頼が登録される可能性があります。
勇者:
「敵……誰やねん。」
逆に、
敵:ゴブリン
場所:
なら、
勇者:「どこ行ったらええねん。」
となります。
村人の状況を考えると
少し厳しいようですが、結果的には
- ユーザー
- 開発者
- DB
- アプリケーション
みんなを守ってくれています。
LaravelでValidationを書く方法
Laravelでは、大きく分けて2つの書き方があります。
- Controllerに直接Validationを書く
- Form RequestにValidationを分離する
| 方法 | メリット | デメリット |
|---|---|---|
| Controller内Validation | すぐ書ける・小さい処理なら分かりやすい | Controllerが長くなりやすい |
| Form Request | Validationを分離できる・Controllerがスッキリする | ファイルが増える・小さい処理では少し大げさ |
RPGで考えると
Controller内Validation
勇者:
「入場チェックもする!」
「戦闘もする!」
「姫様も救う!」
「王様も助ける!」
「宝箱も開ける!」
勇者:「仕事が多すぎるーー」
Controllerも同じで、色々な処理を書きすぎると役割が増えすぎます。
Form Request
そこで、
👩🦰 門番:
「入力チェックは私が担当します!」
⚔️ 勇者:
「じゃあ俺は戦闘に集中する!」
という役割分担をします。
Request
↓
Form Request
「入力チェックは任せろ!」
↓
Controller
「本来の処理をやるぞ!」
これがForm Requestのイメージです。
いろんな条件で制限をかけられる
こういった条件でデータを制限していきます。
一つだけ使うことも、組み合わせて使うこともできます。
| Validation | 意味 |
|---|---|
required |
必須 |
nullable |
nullを許可 |
string |
文字列 |
integer |
整数 |
numeric |
数値 |
boolean |
真偽値 |
email |
メールアドレス形式 |
min:3 |
最小値・最小文字数など |
max:255 |
最大値・最大文字数など |
confirmed |
確認用フィールドと一致 |
unique:quests,title |
DB上で重複していないか確認 |
exists:users,id |
DBに値が存在するか確認 |
Controllerに直接Validationを書く
例えば、
title
→ 必須
→ 文字列
→ 最大255文字
level
→ 必須
→ 整数
→ 1以上
というルールを設定するとして
public function store(Request $request)
{
$validated = $request->validate([
'title' => 'required|string|max:255',
'level' => 'required|integer|min:1',
]);
Quest::create($validated);
}
$request->validate() の中にバリデーションを書く
$request->validate([
'title' => 'required|string|max:255',
'level' => 'required|integer|min:1',
]);
例えば、
{
"title": "ドラゴン討伐",
"level": 20
}
なら通過します。
しかし、
{
"title": "",
"level": 0
}
なら、
title
→ required違反
level
→ min:1違反
となりValidationエラーになります。
Form Requestとは
Form Requestは、
ValidationやRequestに関係する処理を専用クラスへ切り出す仕組み
です。
基本的には、
app/
└── Http/
└── Requests/
に作成します。
Form Requestを作成する
Artisanコマンドを使います。
php artisan make:request StoreQuestRequest
すると、
app/
└── Http/
└── Requests/
└── StoreQuestRequest.php
が作成されます。
Form Requestを書く
<?php
// app/Http/Requestsに属していることを表す
namespace App\Http\Requests;
// Laravel標準のFormRequestクラスを読み込む
use Illuminate\Foundation\Http\FormRequest;
// Quest作成用のForm Request
class StoreQuestRequest extends FormRequest
{
// このRequestを実行していいユーザーか判定
public function authorize(): bool
{
// trueなら許可
// falseなら拒否
return true;
}
// Validationルール
public function rules(): array
{
return [
// 必須・文字列・最大255文字
'title' => 'required|string|max:255',
// 必須・整数・1以上
'level' => 'required|integer|min:1',
];
}
}
Controller側
作成したForm RequestをControllerで使います。
use App\Http\Requests\StoreQuestRequest;
public function store(StoreQuestRequest $request)
{
// Validationを通過したデータだけ取得してDBへ保存
Quest::create(
$request->validated()
);
}
ここで重要なのが、
StoreQuestRequest $request
Laravelは、
Controller
「StoreQuestRequestが必要です」
↓
Laravel
「了解」
↓
StoreQuestRequestを用意
↓
Validation実行
↓
成功したらControllerへ
という流れで処理してくれます。
$request->validated() とは?
Form Requestでは、
$request->validated()
をよく使います。
これは、
Validation対象になっていて、Validationを通過したデータを取得する
ためのメソッドです。
例えばRequestが、
{
"title": "魔王討伐",
"level": 50,
"cheat_code": "999999"
}
だったとして、
Form Requestが、
public function rules(): array
{
return [
'title' => 'required|string',
'level' => 'required|integer',
];
}
なら、
$request->validated();
で扱う中心になるのは、
[
'title' => '魔王討伐',
'level' => 50,
]
です。
cheat_codeはValidationルールの対象にしていません。
そのため、DB保存などでは、
$request->all()を何でもそのまま渡すより、
$request->validated()
を利用する方が意図が明確になります。
Form Requestの便利機能一覧
Form RequestにはValidation以外にも便利なメソッドがあります。
| メソッド | 何をする? | ざっくり例 |
|---|---|---|
authorize() |
Requestを実行していいか確認 | 管理者だけ許可 |
rules() |
Validationルールを定義 | `required |
messages() |
エラー文を変更 | 「クエスト名は必須です」 |
attributes() |
項目名を分かりやすくする |
title → クエスト名
|
prepareForValidation() |
Validation前に値を整える | trim() |
passedValidation() |
Validation成功後に処理 | 値の追加加工 |
withValidator() |
複雑な追加Validation | 2項目を比較 |
authorize()
このRequestを実行してよいか確認します。
public function authorize(): bool
{
return true;
}
trueなら許可。falseなら拒否されます。
public function authorize(): bool
{
return auth()->user()->level >= 100;
}
とすれば、
level 100以上
→ 許可
level 100未満
→ 拒否
という使い方もできます。
rules()
Validationの中心です。
public function rules(): array
{
return [
'title' => 'required|string|max:255',
'level' => 'required|integer|min:1',
];
}
RPGでいうなら、ギルドの受付条件一覧です。
messages()
Validationエラーのメッセージを変更できます。
public function messages(): array
{
return [
'title.required' => 'クエスト名を入力してください。',
'level.min' => '推奨レベルは1以上にしてください。',
];
}
例として
title.requiredは
title
↓
required違反に対応しています。
attributes()
項目名を人間向けの名前に変更できます。
public function attributes(): array
{
return [
'title' => 'クエスト名',
'level' => '推奨レベル',
];
}
つまり、titleというプログラム上の名前を、
クエスト名としてエラー表示などに利用できます。
prepareForValidation()
Validationする前に入力値を整えることができます。
例えば、
{
"title": " 魔王討伐 "
}
のように余計な空白が入っているとします。
protected function prepareForValidation(): void
{
$this->merge([
'title' => trim($this->title),
]);
}
これによって、
" 魔王討伐 "
↓
"魔王討伐"
にしてからValidationできます。
RPGでいうと、ギルド受付の前に依頼書を整える係です。
passedValidation()
Validation成功後に処理を追加できます。
protected function passedValidation(): void
{
$this->merge([
'title' => '【QUEST】' . $this->title,
]);
}
例えば、魔王討伐が 【QUEST】魔王討伐になります。
withValidator()
rules()だけでは書きづらい追加チェックをしたい場合に使えます。
例えば、
開始レベル:20
終了レベル:10
という入力が来たとします。
終了レベルが開始レベルより小さいので、おかしいですね。
public function withValidator($validator): void
{
$validator->after(function ($validator) {
if ($this->end_level <= $this->start_level) {
$validator->errors()->add(
'end_level',
'終了レベルは開始レベルより大きくしてください。'
);
}
});
}
このように、
複数の値を使った少し複雑な条件などを追加できます。
Form Request全部入り
全部組み合わせると、例えばこんな形になります。
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class StoreQuestRequest extends FormRequest
{
// 実行権限を確認
public function authorize(): bool
{
return true;
}
// Validation前にデータを整える
protected function prepareForValidation(): void
{
$this->merge([
'title' => trim($this->title),
]);
}
// Validationルール
public function rules(): array
{
return [
'title' => 'required|string|max:255',
'level' => 'required|integer|min:1',
];
}
// エラーメッセージ
public function messages(): array
{
return [
'title.required' => 'クエスト名を入力してください。',
'level.required' => '推奨レベルを入力してください。',
];
}
// 項目名
public function attributes(): array
{
return [
'title' => 'クエスト名',
'level' => '推奨レベル',
];
}
// Validation成功後
protected function passedValidation(): void
{
//
}
}
Controllerは、
use App\Http\Requests\StoreQuestRequest;
public function store(StoreQuestRequest $request)
{
Quest::create(
$request->validated()
);
return redirect('/quests');
}
だけで済みます。
かなりスッキリしました。
Form Requestの処理イメージ
minとmaxには注意
ここは少し重要です。
'level' => 'integer|min:3'
なら、
数値として3以上です。
一方、
'title' => 'string|min:3'
なら、
文字列として最低3文字になります。
integer + min
→ 数値の大きさ
string + min
→ 文字数
この変化に注意して使います。
結局どちらを使えばいい?
小さなValidationなら、
$request->validate();
で十分です。
例えば、
public function store(Request $request)
{
$validated = $request->validate([
'title' => 'required',
]);
Quest::create($validated);
}
くらいなら非常に読みやすいです。
Validationルールが多い
独自メッセージがある
権限チェックがある
入力値の整形がある
Controllerが長くなってきた
という場合は、
Form Requestに切り出す
ことを検討すると良さそうです。
まとめ
Validationとは
アプリケーションに入ってくるデータがルールを守っているか確認する門番
Laravelでは、
小規模なら
Controller
↓
$request->validate()
でもValidationできます。
処理が大きくなってきたら、
Form Request
↓
Validationを専用クラスへ分離
↓
Controllerがスッキリ
RPG風にまとめるなら、
村人
↓
依頼書を持ってギルドへ
ギルド受付(Validation)
↓
依頼内容をチェック
問題あり
↓
受付拒否
問題なし
↓
受付完了
Controller(勇者)
↓
本来の仕事へ
DB(依頼ボード)
↓
正しい依頼だけ保存
こんな感じです。
勇者に「どこ行ったらええねん」と言わせないためにも、Validationは大事です。
