1.

入力データの保持

▶
この記事の要点
  • バリデーションエラー後に入力値を再表示するには Blade で {{ old('name') }} を使う
  • 初期値を出したい編集画面では {{ old('name', $user->name) }}
  • $request->validate() は失敗時に入力値を自動でセッションへ保存(フラッシュ)して元の画面へ戻す
  • Validator を手動で使うときは back()->withErrors($v)->withInput() が必要
  • チェックボックス・セレクトは @checked / @selected(Laravel 9 以降)で復元
  • パスワード系の項目は既定でフラッシュされないので再表示されない

結論:value に old() を書く

Laravel でバリデーションエラーがあった場合に、入力値を再度表示させるには、フォームの value 属性に old() ヘルパーを書きます。

<input type="url" name="url" value="{{ old('url') }}" required="required">

入力フォームの値を保持したい場合は {{ old(name) }} を使用します。デフォルト値を設定したい場合は {{ old(name, デフォルト値) }} としてください。old の第 1 引数には、input の name 属性と同じ文字列を指定します。

仕組み:入力値はセッションに一時保存される

フォーム送信 → バリデーション失敗 → 元の画面へリダイレクト、という流れでは、リダイレクトによってリクエストが 1 回切れるため、送信された値はそのままでは失われます。Laravel はこれを次のように解決しています。

  1. バリデーションに失敗すると、送信された入力値をセッションにフラッシュ(次の 1 リクエストだけ有効な一時保存)する
  2. エラーメッセージも同様にフラッシュし、直前の画面へリダイレクトする
  3. リダイレクト先の画面で old('キー') を呼ぶと、フラッシュされた値が取り出せる
  4. その次のリクエストではフラッシュデータは消える

コントローラで $request->validate([...]) や Form Request を使っている場合、この 1〜2 の処理は自動で行われます。

public function store(Request $request)
{
    $validated = $request->validate([
        'url'   => ['required', 'url'],
        'title' => ['required', 'max:100'],
    ]);
    // 失敗した場合はここに来ず、入力値付きで前の画面へ戻る

    Bookmark::create($validated);
    return redirect()->route('bookmarks.index');
}

Validator を手動で使う場合

Validator::make() で自分で判定するときは、withInput() を付けないと入力値がフラッシュされず、old() が空になります。

use Illuminate\Support\Facades\Validator;

$validator = Validator::make($request->all(), [
    'url' => ['required', 'url'],
]);

if ($validator->fails()) {
    return back()
        ->withErrors($validator)
        ->withInput();          // これがないと old() が空になる
}

バリデーション以外の理由(外部 API エラーなど)で入力画面に戻すときも、同じく ->withInput() を付ければ値を保持できます。一部だけ戻したいときは withInput($request->except('card_number')) のように指定します。

入力部品ごとの書き方

テキストエリア

<textarea name="body">{{ old('body') }}</textarea>

編集画面(DB の値を初期表示)

<input type="text" name="title" value="{{ old('title', $bookmark->title) }}">

セレクトボックス

<select name="category">
@foreach ($categories as $category)
    <option value="{{ $category->id }}" @selected(old('category', $bookmark->category_id) == $category->id)>
        {{ $category->name }}
    </option>
@endforeach
</select>

チェックボックス・ラジオボタン

<input type="checkbox" name="public" value="1" @checked(old('public', $bookmark->public))>

<input type="radio" name="type" value="a" @checked(old('type') === 'a')>
<input type="radio" name="type" value="b" @checked(old('type') === 'b')>

@checked / @selected ディレクティブは Laravel 9 以降で使えます。それより前のバージョンでは {{ old('public') ? 'checked' : '' }} のように書きます。

配列形式の入力

name="tags[]" や name="items[0][name]" のような配列入力は、ドット記法で取り出します。

<input type="text" name="items[0][name]" value="{{ old('items.0.name') }}">

エラーメッセージと一緒に表示する

<input type="url" name="url" value="{{ old('url') }}"
       class="@error('url') is-invalid @enderror">
@error('url')
    <div class="invalid-feedback">{{ $message }}</div>
@enderror

old() が効かないときの原因

  • name 属性と old のキーが違う: name="user_name" なのに old('name') になっているなど
  • セッションが使えないルート: routes/api.php のルートには web ミドルウェアグループ(セッション)が適用されないため、フラッシュされない
  • 手動 Validator で withInput() を付けていない
  • リダイレクトせずに view を返している: 失敗時に return view(...) するとフラッシュデータは次のリクエスト用なので、その画面では読めない
  • パスワード項目: password、password_confirmation、current_password は安全のため既定でフラッシュ対象外。再入力してもらう仕様が正しい
  • 空欄にした値が初期値に戻る: Laravel は空文字を null に変換するミドルウェアが既定で有効なため、編集画面で項目を空にして他の項目でエラーになると、old('title', $bookmark->title) が DB の値を表示することがある

確認方法

フラッシュされた値が実際にあるかは、リダイレクト先のビューやコントローラで確認できます。

dd(session()->getOldInput());   // フラッシュされた全入力値
dd($errors->all());             // Blade 内で利用できるエラー一覧

わざと不正な値を送信し、エラーメッセージが表示され、かつ入力欄に送信した値が残っていれば正しく動作しています。

関連

Post Share
子ページ

子ページはありません

同階層のページ
  1. 入力データの保持
  2. 必須チェック
  3. 文字列の長さチェック
  4. 存在チェック
  5. 一意チェック
  6. 日本語化