現在Laravelの教材を鋭意制作中です!完成まで今しばらくお待ちください

#27 確認画面の作成と問題点

それでは最後の仕上げにふさわしい「確認画面」、しっかり整えていきましょう!

Webアプリでフォームを送信するとき、ユーザーが「これで間違いないかな?」と確認できる画面があると安心です。

たとえば「お問い合わせフォーム」や「商品注文フォーム」などでは、名前・メールアドレス・内容などを送信前にもう一度確認できる画面があることで、ユーザーの信頼感や使いやすさがグッと上がります。

また、間違いに気づいて戻ることもできるので、UX(ユーザー体験)としてとても親切な設計になります。

確認画面の目的

確認画面を作成する目的は主に3つあります。

  1. 入力内容をユーザーに再確認してもらう
  2. 誤入力があれば「戻る」で修正できる
  3. 問題なければ「送信」で保存処理へ進む

これにより、誤ったデータ送信や「うっかりミス」を防ぐことができます。

確認画面を作成する場合、フォームの入力 → 確認画面 → データの送信が基本的な流れとなり、確認画面をはさむフォームでは、3つのステップが必要です。

入力フォーム → 確認画面 → 完了画面(送信処理)

それぞれに役割とURLがあるのがポイントとなります。

すでに入力フォームと完了画面(thanksページ)は作成済みですので、今回は真ん中の確認画面を作成する章となります。

1. pullとブランチの作成

もう、何も言わなくても流れるように作業が定着できたのではないですか?作業に入る前に前回mergeしたメインブランチを更新しておきましょう。

# ブランチをpullする
git pull origin main

以上で完了です。mainブランチが前章で作成されたブランチと同じになれば、次に進みます。

git switch -C make-confirm

今回は確認画面の作成を行うので、make-confirmと言う名前のブランチを作成しました。ブランチ名は任意となりますので、お好きなブランチ名を付けて構いません。

2.ContactController:confirm()の作成

public function confirm(ContactRequest $request)
{
  $contact = $request->validated();
  return view('confirm', compact('contact'));
}

この confirm メソッドは、ユーザーが入力した情報を一度検証し、確認画面に表示するための処理です。

FormtRequest で作成したContactRequestをconfirm()メソッドとstore()メソッドで使いまわしております。

そして$request->validated()で バリデーションを通過した安全なデータだけを $contact に代入します。

万が一エラーがあった場合は、自動で元の画面に戻してくれるのでとても安心です。

2.contact.blade.phpの修正

<form action="{{ route('contact.confirm') }}" method="POST">

<form action="{{ route('contact.store') }}" method="POST">の部分を上記に変更してください。

3.confirm.blade.phpの作成

@extends('layouts.app')

@section('title', '内容確認')

@section('content')

<h2>入力内容の確認</h2>

<table border="1" cellpadding="8" cellspacing="0">
    <tr>
      <th>お名前</th>
      <td>{{ $contact['name'] }}</td>
    </tr>
    <tr>
      <th>メールアドレス</th>
      <td>{{ $contact['email'] }}</td>
    </tr>
    <tr>
      <th>年齢</th>
      <td>{{ $contact['age'] }}歳</td>
    </tr>
    <tr>
      <th>性別</th>
      <td>{{ $contact['gender'] }}</td>
    </tr>
    <tr>
      <th>興味のある分野</th>
      <td>{{ isset($contact['interests']) ? (is_array($contact['interests']) ? implode(', ', $contact['interests']) : $contact['interests']) : 'なし' }}</td>
    </tr>
    <tr>
      <th>お問い合わせ内容</th>
      <td>{!! nl2br(e($contact['message'])) !!}</td>
    </tr>
</table><br>

<form action="{{ route('contact.store') }}" method="POST">
    @csrf
    @foreach ($contact as $key => $value)
        @if (is_array($value))
            @foreach ($value as $v)
                <input type="hidden" name="{{ $key }}[]" value="{{ $v }}">
            @endforeach
        @else
            <input type="hidden" name="{{ $key }}" value="{{ $value }}">
        @endif
    @endforeach
    <button type="submit">送信する</button>
</form>

<form action="{{ route('contact') }}" method="GET" style="margin-top: 10px;">
    <button type="submit">戻って修正する</button>
</form>


@endsection

コードの解説

<td>{{ $contact['name'] }}</td>

確認画面なので、フォームから送られたデータをそのままテーブルタグに出力しています。

<td>{{ isset($contact['interests']) ? (is_array($contact['interests']) ? implode(', ', $contact['interests']) : $contact['interests']) : 'なし' }}</td>

このコードはちょっと三項演算子を2つ使用しているので、わかりにくいですが、if文にすると見やすいかと思いますので、if文に変換してみましょう。

<td>
  @if (isset($contact['interests']))
    @if (is_array($contact['interests']))
      {{ implode(', ', $contact['interests']) }}
    @else
      {{ $contact['interests'] }}
    @endif
  @else
    なし
  @endif
</td>

まず、@if (isset($contact['interests']))で要素があるのかどうかをチェックしています。要素がない場合は「なし」が表示されます。

そして@if (is_array($contact['interests']))で配列がある場合は、implode(', ', $contact['interests'])によって配列を,で区切って一つのデータに変換しています。

配列がない場合、つまりデータが1つだけの場合はそのまま{{ $contact['interests'] }}として表示されています。

<form action="/contact/store" method="POST">
    @csrf
    /* 中略 */
</form>

この <form> /contact/store にデータを POST で送信します。

その際、画面上には表示せず、<input type="hidden"> によって $contact の情報を埋め込んで送信する仕組みです。

@foreach ($contact as $key => $value)
...
@endforeach

配列 $contact の内容を、キーと値のセットで1件ずつ処理するために使っています。

この構文によって $contact の中身を1件ずつ分解して処理できます。

$contactには入力内容(例:氏名、メール、興味など)が格納された連想配列入っています。

$key には 各項目の名前(例:name, email, interests)が入ります。

$value には項目に対応する値(例:taro, eample@example.com, [PHP,Laravel])などが入ります。

@if (is_array($contact['interests']))
  {{ implode(', ', $contact['interests']) }}
@else
  {{ $contact['interests'] }}
@endif

フォームの「興味・関心」などの項目は、チェックボックスを複数選べる形式にすると、複数の値が配列として送られてくる可能性があります。

配列の場合はimplode(', ', ...) で「カンマ区切り文字列」に変換して表示します(例:['PHP', 'Laravel']PHP, Laravel

配列じゃない場合はそのまま文字列として表示します(例:"Laravel"Laravel

<form action="{{ route('contact') }}" method="GET" style="margin-top: 10px;">
    <button type="submit">戻って修正する</button>
</form>

通常、戻るリンクなら <a href="{{ route('contact') }}"> のように記述されがちですが、以下の理由でフォームにしています。

    1. デザイン上ボタンを使いたい(HTMLボタンはクリック感やスタイルで好まれる)
    2. GETリクエストで状態保持や検索条件付きのページに戻りたい
    3. 将来的に hidden input などを追加してデータを持ち回りたい場合の柔軟性

4.確認画面から戻ると old() が空になる

このセクションについては、今回のアプリ開発では取り扱いません。ステップアップとして掲載しています。

この時「確認画面から『戻って修正する』ボタンを押してお問い合わせフォームに戻ると old() が空になる」、という問題が発生します。

old()前のリクエスト(POST)のデータをセッションに保持しているものなので、「戻る」操作で GET リクエストになると、セッションに old() データが残っていないため、結果としてフォームの値が空になってしまう。

この問題に対して、対処するには3つの方法が考えられます。

方法①:確認画面→戻るも POST にする(フォーム経由)

<form action="{{ route('contact') }}" method="POST" style="margin-top: 10px;">
  @csrf
  <!-- hidden fields -->
</form>

ただしこの方法を実装する場合は web.php に POST ルートを追加する必要があります。

Route::post('/contact', [ContactController::class, 'create']);

これなら old() が有効な状態で戻れて、UXも維持できます。

方法②:セッション or hidden で値を保持する

確認画面で、全データを hidden に仕込んで送り直す構成にすると、old() に頼らずともフォームに復元可能です。

つまり「oldがなくても戻せる状態を自前で作る」ってことですね。

方法③:確認画面では JavaScript の履歴で戻る

超シンプルに onclick="history.back();" を使えば、ブラウザ履歴に頼って戻すこともできます。

ただ、この方法は Laravel らしさが失われるのと、入力内容が変わっていた場合の整合性が取れないので注意が必要です。

5.Git push

以上が完了したら、ここでGitを共有しておきましょう。

git add -A
git commit -m "コメントを入力する"
git push origin make-confirm

git pushが無事完了したら、GitHub上で操作を行います。

pull requestが来ているので、mergeしましょう。

次章: