274
288

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

Laravelのバリデーションにはフォームリクエストを使おう

Last updated at Posted at 2016-11-07

追記(2018-02-09)

Laravel5.2の時代に書いたものです。
時間あるときに5.5にアップデートします。。。

フォームリクエストとは

  • フォームを含む各リクエストに対して、それぞれ固有のバリデーションを設定できる。
  • バリデーションエラー時には、入力値とエラー情報を付与して前のページに自動的にリダイレクトする。
  • バリデーションをパスした時に初めてコントローラー内の処理に移る。

詳しくは公式ドキュメントのフォームリクエストの項を参照。
バリデーション 5.2 Laravel

メリット

  • バリデーションルールやエラー時の処理をコントローラーから完全に分離できる。
  • 使いまわしが効く。
  • 可読性が高い。
  • カスタマイズしやすい。

チュートリアル

###App\Http\Requests配下にRequestを拡張したクラスを作る。
手動で作ってもいいが、コマンドの方が楽。
php artisan make:request CreateUserRequest

CreateUserRequest.php
namespace App\Http\Requests;
use App\Http\Requests\Request;

/**
 * 新規ユーザ登録のバリデーションを行うクラス
 */
class CreateUserRequest extends Request
{
    /**
     * 認証関係の判定を行う場合はここに処理を記述する。
     * 特にない場合は常にtrueを返しておく。
     */ 
    public function authorize() {
        return true;
    }

        /**
     * バリデーションルールを記述
     */
    public function rules() {
        return [
            'username' => 'required|max:20',
            'email' => 'required|email|max:100',
            'password' => 'required|min:6|confirmed',
        ];
    }
}

'フィールド名' => 'ルール'の形式で記述する。
'email' => 'required|email|max:100'
これでname='email'が指定されている入力フィールドに「必須」「メール形式」「最大100文字」の制限がかかる。

###コントローラーの引数に拡張クラスをあてる

UserController.php
public function postCreate(CreateUserRequest $req) {
    /**
     * 拡張クラスに書いたルールでリクエストが自動的に検証される
      * バリデーションをパスするとこの後の処理が実行される 
      */ 
    $this->userService->createUser($req->all());
    return view('...');
}

エラーメッセージを表示する

バリデーションエラーが起きた場合、エラーメッセージと入力値がフラッシュデータとして保存され、入力を行ったページにリダイレクトする以下のようなレスポンスが生成される。

return redirect()->back()->withErrors($validator)->withInput() 

(※AJAXリクエストの場合はJSONを含んだ422ステータスコードのHTTPレスポンスが返される)

入力された値は{{ old('username') }}
エラーメッセージは{{ $errors->first('username') }}
のように書けば取得できる。

※注意点としてルートに 'web' ミドルウェアを適用しないと、$errorsが使えない。これがLaravelマジックとも言われる所以で、エラーメッセージをフラッシュデータとして$errorsに保存してビューに引き渡す処理は
Illuminate\View\Middleware\ShareErrorsFromSessionミドルウェアで行われている。そのため、フォームリクエストの恩恵を受けさせるためにはルーティングにwebミドルウェアを指定する必要がある。

routes.php

Route::group(['middleware' => ['web']], function () {
Route::post('/user/create', 'UserController@postCreate');
});


- すべてのエラーの取得 ` {{ $errors->all() }} `
- 特定のエラーの取得 ` {{ $errors->first('username') }} `
(※配列形式で結果が返ってくるため、`first()`で最初のものを取得している。)
- エラーの存在チェック ` {{ $errors->has('username') }} `
- エラー数の取得` {{ count($errors) }} `

#### エラーメッセージの表示例

```php:user_create.blade.php
/** エラーをすべて表示 */
@if(count($errors) > 0)
    <ul>
        @foreach ($errors->all() as $error)
            <li>{{ $error }}</li>
        @endforeach
    </ul>
@endif

/** 特定のエラーの表示 */
@if($errors->has('username'))
    <div class="error">
        <p>{{ $errors->first('username') }}</p>
    </div>
@endif

※エラーメッセージをカスタマイズする場合はmessages()をオーバーライドする。

public function messages() {
    return [
        'username.required' => 'ユーザー名を入力してください。',
        'email.email'  => 'メールアドレスとして正しい形式ではありません。',
    ];
}

authorize()について

Requestの拡張クラスにあったauthorizeというメソッド。これは権限に関する判定を行うもの。使わなければtrueを返しておく。falseを返した場合は403ステータスコードのレスポンスが返される。

(例)管理者権限を持っているユーザにのみ許可

CreateUserRequest.php
public function authorize() {
    if(!session()->has('isAdmin')) {
        return false;
    }
    return true;
}

チュートリアル終わり。

##いろんなルール

複数入力の判定

ワイルドカード*で指定できる。

'username.*' => 'required|max:12' 

username[0], username[1], ...に適用される。

また、以下のような入力値があった場合

user = [
    [0] = [
        'username' => 'foo',
        'email' => 'foo',
        'password' => 'foo'
    ],
    [1] = [
        'username' => 'bar',
        'email' => 'bar',
        'password' => 'bar'
    ],
    [2] = [
        'username' => 'fizz',
        'email' => 'fizz',
        'password' => 'fizz'
    ]
]   

これで対応できる。

'user.*.username' => 'required|max:12' 
'user.*.email' => 'required|email|max:100' 
'user.*.password' => 'required|min:8|confirmed' 

フィールド存在時のみバリデーションをかけたい

sometimesを指定すると、その名前の入力値が存在しない時はバリデーションが無視される。ワイルドカードとの合わせ技で割と役立つ。

'article.*.hashtag' => 'sometimes|min:3|max:10' 

(例)ハッシュタグが入力されている記事のみ、ハッシュタグの文字数制限をかけるバリデーション。sometimesがないと、ハッシュタグが何も入力されていない記事で「最低3文字」の制限を満たせずエラーが起きてしまう。

一意性(unique)

unique:テーブル名、カラム名で指定。
カラム名は省略でき、その場合はフィールド名と同じカラムが探索される。

'email' => 'unique:users,email_address'

###パスワードなど確認用のフィールド(confirmed)
メールアドレスとかパスワードとか、確認用として入力した値が一致しているかを検証する。name="xxx"のフィールドとname="xxx_confirmation"のフィールドが同一の値か検証される。

'email' => 'confirmed'
<!-- 2つの入力値が同一でないとバリデーションエラー -->
メールアドレス<input type="text" name="email">
メールアドレス(確認用)<input type="text" name="email_confirmation">

セレクトボックスが未選択かどうかを検証

not_inを使います。これ、地味に役立ちます。例えば以下の、職業を選択させるフィールドに必須のバリデーションをかけたい場合。

<select name="job">
   <option value="0">選択してください</option>
    <option value="1">学生</option>
    <option value="2">会社員</option>
    <option value="3">主婦</option>
</select>
'job' => 'required'

にしてしまうと、「選択してください」が選択された状態でもエラーにならずに処理が通ってしまう。「選択してください」以外を選ばせたい場合は以下のようにする。

'job' => 'not_in: 0'

「0以外を入力してくれよ」というバリデーション。「0」の時はエラーが起こる。エラーメッセージを分かりやすくするために、もう一手間かけてみる。カスタムリクエストクラスでmessages()メソッドをオーバーライドする。

public function messages() {
    return [
        'job.not_in' => '職業が選択されていません。',
    ]
}

jobフィールドでnot_inのエラーが発生した場合、「職業が選択されていません。」というメッセージが返される。全部のメッセージを記述する必要はなく、カスタマイズしたいメッセージだけオーバーライドすればOK。

###ファイルアップロード
拡張子:mimes:png,jpg,gif,pdfなどなど複数指定可。
ファイルサイズ: max:3000 単位はキロバイト。

###bail
bailルールを指定すると、バリデーションエラーが起きた時点で残りの判定が行われなくなる。

'username' => 'bail|required|min:4|max:12'

もっと知りたい方は

公式のドキュメントもいいですが、
Laravel5.2 バリデーション

ルールについては以下の記事が詳しくまとめられていてとてもタメになります。
Laravelのバリデーションで指定できる内容をざっくりまとめ直しました。

次回はエラーメッセージのカスタマイズについて詳しく書こうかと。

274
288
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
274
288

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?