フォーム作成(Laravel)

Laravelにおいて、フォームを作成する方法を掲載する。

もちろん、基本的なフォームだけでなく、
認証ユーザーの情報を変更するためのコンソールなどを
作成する際にも、同様の機能を使う事になる。

投稿画面の作成

投稿画面の基本的なHTMLコードを作成する。

Laravelにおいても、「フォームヘルパー」という、
フォームタグなどを生成する関数が存在するが、使用は必須ではない
むしろ関数を覚えるのが面倒、関数を回す分の処理が気になる、
フォームタグの根本のほうを理解したいという人は、普通にHTMLで書いてもよい。

また、Laravelにおけるフォーム作成の記事では、
同じビューファイルで、投稿・確認を担っている構造もあるが、
複数の要素を1要素に混載すると、わかりにくくなる原因にもなり、
また場合によっては、確認画面の構造自体、
投稿画面のレイアウトと異なる構造にする事もあるので、
基本的には、投稿・確認のビューファイルは別々に作ってもよい。

以下が、「/report」ページにおいて作成するフォームという場合での例である。

<form method="POST" action="/report">
    {{ csrf_field() }}

    <h3>タイトル</h3>
    <input name="title" type="text" value="{!! $formDefaultDatas['title'] !!}">

    <h3>コメント</h3>
    <textarea name="comment" cols=50 rows=4 >{!! $formDefaultDatas['comment'] !!}</textarea>

    <button type="submit">
        確認する
    </button>
</form>

今回は単純に「タイトル」と「コメント」の2要素のみのフォームとする。

「$formDefaultDatas」という配列には、
コントローラーで、初期値を格納するために用意した配列。
確認画面に「戻る」動作を行った際にも、この配列に格納すれば、
確認画面から戻った際に、入力が維持された状態にできる。

初期値が必要なくても、空文字を代入して用意しておけば、取り回しがラクになる。
フォームの部品には、初期値を「value」属性に入れたり、
「t<extarea>」のように、タグで囲む場合もあり、要素により違いがあり、
「value」属性も配慮するとなると、処理が増える。
HTMLコードを生で見られた際に、「value=""」の有無を特に気にする事がなければ、
初期値にだけ配慮しておくとよい。

ビューで出力する際には、「{!! !!}」で囲めば、特殊文字がエスケープされる事がない。
「<textarea>」などで行われた改行も、そのまま反映できる。
ただ、この出力には「echo」が使われているため、未定義やNullを参照しないようには注意しよう。

「{{ csrf_field() }}」については、このページの最後に記述する。

確認画面

確認画面に遷移する際には、入力された情報はセッションに保存しておくとよい。

フォームからのPOST情報を取り出すには、
コントローラーのメソッド仮引数に、「Request」のオブジェクトを定義し、
そのオブジェクトの「input()」メソッドから取り出すといった形となる。

public function postIndex(Request $request){

    // フォーム情報をすべて抽出
    $formData = $request->input();

    // フォーム情報をセッションに丸ごと格納
    session()->put('formData',$formData);
    
    // ビューに渡すための配列を定義
    $viewData = [];
    /*
    * ビューに渡すデータを整形する処理
    */
    
    // ビューのテンプレート指定および変数・配列を渡す
    return view('report',['viewData' => $viewData]);
}

フォームの情報は、連想配列で格納されている。
フォーム要素の「name」属性で指定した値が添え字になっている。
また、セッションにも、そのままの形で格納できる。

このフォーム情報をビューに渡して、表示のための処理を行う事も可能だが、
ここはコントローラーらしく、ビューに渡す情報をコントロールしてみたいものである。

ビューに渡すための情報は、フォームから取得したデータそのものを変更するのではなく、
改めて別途配列を作って、ビューに渡すとよい。

「性」と「名」の要素のある氏名や、「時」「分」の要素のある時間などを、
コントローラー内で結合・整形して、
ビュー上では「{{ }}」で出力すればいいようにしておくとよい。

「t<extarea>」から取得した改行テキストの場合は、
PHP関数「nl2br()」を使って整形するとよい。
ただし、ビューで出力する際には、エスケープされると「
」タグが見えてしまうため、
「{!! !!}」で出力すればよい。

確認画面ビューテンプレートのコード

確認画面のコードは、コントローラーで整形したコードを出力すればよい。

確認画面からさらに保存画面に遷移するには、以下のコードのみでよい。

    <form method="POST" action="/report/save">
        <a class="btn" href="/report">
            戻る
        </a>
        {{ csrf_field() }}
        <button type="submit">
            送信する
        </button>
    </form>

各投稿情報は、セッションに保存されたものを、
そのままデータベースに格納すればよい。
ここでは、保存用のコントローラーにPOSTメソッドでアクセスする要素を置いておくとよい。

これだけなら一見、「<a>」タグで遷移してもいいと思うかもしれないが、
GETメソッドで保存画面に遷移するのは少々危険でもある。
CSRFトークンを保存が完了するまで使うという意味でも、
POSTメソッドで送ったほうがよい。

「戻る」ボタンは、普通に「<a>」タグでのリンクでもよい。
戻った先での修正内容を反映させるには、セッションに格納しておいた内容を、
初期値として用意しておいた配列に代入するだけで、反映できるはずである。
戻るリンクの見た目は、後で画像やCSSでボタンに加工するとよいだろう。

保存画面

保存画面に遷移したら、やる事はデータベースに保存して、
「投稿を保存しました」などのメッセージを表示するだけである。
データベース保存に関しては、モデルの使い方などを参照してほしい。

最後に、CSRFトークンを更新しておくとよい。

session()->regenerateToken();

これを行っておけば、ブラウザバックしても、二重投稿が行われない。
もしブラウザバックを行った場合は、トークンエラーが発生するが、
動きとしては、それで正常である。

ただ、このトークン更新は、本当にデータを保存した後に行ったほうがよい。
画面遷移ごとに更新するのも堅牢ではあるが、
その場合、確認画面からのブラウザバックができずに不便になる。
「戻る」ボタンを設置してあるとはいえ、このぐらいは許可しておいてもよいだろう。
もしくは、ブラウザバックが使えない事を説明した前提で
トークン更新を毎回行うという手もある。

CSRF対策

{{ csrf_field() }}

「<form>」タグに、このコードを含ませてあるが、
これは、CSRF(クロスサイトリクエストフォージェリ)対策用のトークンを
出力するためのものである。

出力結果は、「hidden」状態の「input」タグに、ランダムな文字列が入ったものである。
トークンとは、一時的なカギのようなもので、このトークンを投稿時に含める事によって、
現在送信中のブラウザが、正規の過程を通ってきたものであるかを照合する。

CSRFとは、ログイン中のブラウザに、攻撃用のURLにアクセスさせ、
本来ログイン中でしか書き換えられない情報を、書き換えてしまうという攻撃方法である。
Laravelでは、すべてのPOSTメソッドでCSRF対策が施されているため、
「<form>」タグ内に、このトークンを含める必要がある。

ちなみに、Ajaxを行いたいがために、
CSRF対策機能を停止させる方法を記した記事も存在するが、
この機能を安易に停止させるのは、やめておいたほうがよい
Ajax処理を行いたいのであれば、Laravelとは独立した方法で行うのが得策だろう。