Blogブログ

TORAT | 2021.10.12

【Laravel8】バリデーション機能の使い方

  • Laravel
  • PHP

バリデーションとは

入力されたデータが、条件や形式と合っているかチェックするものです。

「バリデーション」という言葉を知らなくても、画像のようなエラーメッセージを見たことはあるかと思います。このように正しく入力しないと進めないのは、バリデーションと呼ばれる入力チェックが行われているからなんです。

バリデーションを書いてみよう

記述場所はコントローラー・フォームリクエストの2通りありますが、当記事ではコントローラーに記述する方法について見ていきます。

コントローラーの記述方法も2通りあります。
・validateメソッドを使用
・Validatorファサードを使用

validateメソッドは手軽ですが応用があまり効きません。Validatorファサードは少し記述が増えますが、エラー時のリダイレクト先を指定など独自の処理を行うことが可能です。

validateメソッドで書く

validateメソッドのエラー時は、自動でリクエスト元(入力ページ)にリダイレクトします。

validateメソッドを使用した書き方は、2通りあります。

// パイプ区切り
$request->validate([
  'email' => 'required|email|unique:users',
  'name' => 'required',
]);
// 配列
$request->validate([
  'email' => ['required', 'email', 'unique:users'],
  'name' => ['required'],
]);

例では、「required(必須入力)」「email(メールアドレスのフォーマットかどうか)」「unique:users(usersテーブルに登録済みではないか)」をチェックしています。

どちらの書き方も同じように動きますが、正規表現でパイプ「 | 」を使用するときは配列で書く必要があります。下の例では、正規表現「/^(090|080|070)-\d{4}-\d{4}$/」で携帯の電話番号かどうかをチェックしています。

// パイプ区切り(エラーになる)
$request->validate([
   'tel' => 'required|regex:/^(090|080|070)-\d{4}-\d{4}$/'],
]);
// 配列
$request->validate([
   'tel' => ['required','regex:/^(090|080|070)-\d{4}-\d{4}$/'],
]);

Validatorファサードで書く

$validator = Validator::make($request->all(), [
  'email' => 'required|email|unique:users',
  'name' => 'required',
]);

if ($validator->fails()) {
  return redirect("/input")
    ->withErrors($validator)
    ->withInput();
}

例はパイプ区切りで記述していますが、配列でも大丈夫です。

Validationファサードは、リダイレクト先を指定する必要があります。また、エラーメッセージを表示するにはwithErrorsでエラーを渡す必要があります。withInputは入力済みの値を渡し、リダイレクト先のviewで {{ old(‘email’) }} で取得することができます。

エラーメッセージを表示させよう

エラーメッセージの表示

foreachで発生しているエラーすべてを表示します。

// すべてのエラーメッセージを表示
@if ($errors->any())
    <div class="alert alert-danger">
        <ul>
            @foreach ($errors->all() as $error)
                <li>{{ $error }}</li>
            @endforeach
        </ul>
    </div>
@endif

error(‘ ’)で特定のエラーだけ表示することが可能です。

// 特定のエラーメッセージを表示
<label for="title">Post Title</label>

<input id="title" type="text" name="title" class="@error('title') is-invalid @enderror">

@error('title')
    <div class="alert alert-danger">{{ $message }}</div>
@enderror

エラーメッセージのカスタマイズ

エラーメッセージは、何も指定しなければ validation.php の内容が表示されます。そのため、すべての箇所共通でメッセージを変えたい場合は validation.php の内容を変更すればOKです。

// resources/lang/ja/validation.php
'distinct'     => ':attributeには異なった値を指定してください。',
'email'     => '有効なメールアドレスを指定してください。',
'ends_with'  => ':attributeには、:valuesのどれかで終わる値を指定してください。',

特定の箇所だけメッセージを変えたいような場合はコントローラー内で記述します。

// validateメソッドの場合
$request->validate([
  'email' => ['required', 'email', 'unique:users'],
],
[
  'email.required' => 'メールアドレスの入力は必須です',
  'email.email' => 'メールアドレスの形式が正しくありません',
  'email.unique' => '既に登録されているメールアドレスです',
]);
// Validatorファサードの場合
$validator = Validator::make($request->all(), [
  'email' => 'required|email|unique:users',
  'name' => 'required',
],
[
  'email.required' => 'メールアドレスの入力は必須です',
  'email.email' => 'メールアドレスの形式が正しくありません',
  'email.unique' => '既に登録されているメールアドレスです',
]);

if ($validator->fails()) {
  return redirect("/input")
    ->withErrors($validator)
    ->withInput();
}

ルールごとにメッセージをカスタマイズできます。uniqueのメッセージだけカスタマイズしたい場合は、uniqueだけ記述すればrequiredとemailはデフォルトのメッセージを表示できます。

開発中に直面した問題

バリデーションエラー時に無限ループ

Cメソッド内でBのバリデーションに失敗した際、無限ループが起こってしまいました。

public function A(Request $request) {

}

public function B(Request $request) {
// Aのバリデーション →エラー時はGETでAに戻る
}

public function C(Request $request) {
// Bのバリデーション →エラー時はGETでBに戻る
}

原因は、CでバリデーションエラーになったときにBにAの値を渡せていなかったため、Bでも必ずバリデーションエラーになり無限ループ…という状態でした。

そこで、$_SERVER[‘REQUEST_METHOD’] を用いてブラウザからのリクエストがGETかPOSTかを判別し、GETの場合はAのバリデーションを行わないようにすることで解決しました。

public function A(Request $request) {

}

public function B(Request $request) {
   if ($_SERVER["REQUEST_METHOD"] === "POST") {
      // Aのバリデーション
   else {
      // バリデーションは行わない
   }
}

public function C(Request $request) {
// Bのバリデーション
}

バリデーションエラー時にThe payload is invalid.エラー

// 修正前
// app/Http/Controllers/AuthController.php
public function input(Request $request) {
            $encrypt_email = $request->input('email');  // バリデーションエラー時はnull
            $decrypt_email = Crypt::decryptString($encrypt_email);
            // nullの値を復号化(decrypt)しようとしてるためエラー発生
            // 省略
}

public function confirm(Request $request) {
        $validator = Validator::make($request->all(), [
            // 省略
        ]);

        if ($validator->fails()) {
             return redirect('/input')
                        ->withErrors($validator)
                        ->withInput();
        }
       // 省略
}

confirmメソッドからinputメソッドにリダイレクトした際、$encrypt_emailがnullのため暗号化できないよというエラーでした。

こちらも同じくGET/POSTで条件分岐を行いました。
リダイレクト先にパラメーターで$encrypt_emailを持たせ、バリデーションエラー時はパラメーターの値を取るようにすることで解決しました。

// 修正後
// app/Http/Controllers/AuthController.php
public function input(Request $request) {
        if ($_SERVER["REQUEST_METHOD"] === "POST") {
            $encrypt_email = $request->input('email');
            $decrypt_email = Crypt::decryptString($encrypt_email);   
        } else {
            $encrypt_email = $request->query('e');  // バリデーションエラー時はパラメーターで値を取得
            $decrypt_email = Crypt::decryptString($encrypt_email);
        }
  // 省略
}

public function confirm(Request $request) {
     $encrypt_email = $request->input('email');
        $validator = Validator::make($request->all(), [
            // 省略
        ]);

        if ($validator->fails()) {
             return redirect('/input?e=$encrypt_email') // パラメーターで$encrypt_emailを渡す
                        ->withErrors($validator)
                        ->withInput();
        }
       // 省略
}

さいごに

エラーの解決、そんなに難しいことはしていないですが苦戦しました。。
(途中諦めて全部if文で書こうとしました。笑)
バリデーションは苦手意識がありましたが、今回ちゃんと学び、エラーも乗り越えたことで克服できた気がします!

この記事を書いた人

TORAT 管理者

関連記事

Recommend愛されているブログ