【Laravel】 フォームから送信された値の受け取り方
Laravelでは、フォームで送信されたデータをバリデーションしたり、CSRFを使った保護などが可能です。
目次
フォームを作る
フォームは当然ビュー側に作ります。しかし、受け取る側のLaravelのコードはどのようにすればよいのでしょうか。
フォームの送信といったHTTPリクエストの処理は、Request
クラスをコントローラのメソッドの仮引数に用います。
<form action="/hello-world" method="post"> @csrf <label> title: <input type="text" name="title"> </label> <label> content: <textarea name="content" cols="10" rows="5"></textarea> </label> <input type="submit" value="送信"> </form>
Route::post('/hello-world', [App\Http\Controllers\HelloController::class, 'postData']);
public function postData(Request $request) { // 入力欄の name を使って取得 $title = $request->input('title'); $content = $request->input('content'); // 入力がなかったときのデフォルト値は第2引数に入れる $hoge = $request->input('hoge', 'foobarbaz'); return view('thanks', compact('title', 'content')); }
値のバリデーションに関する内容は、下のページに載せています。
上のHTMLを見ると、@csrf
というものが書かれています。これはCSRFを防ぐために用意されているものです。これを使用するだけでCSRF攻撃を防ぐことができ、デフォルトではこれを付与しないと送信できません。
$request->title
のようにしても取得できますが、元から$request
にあるメソッドなどと重複するとうまく行かなくなるため、input
を経由するべきです。
全入力を連想配列で取得したい場合、$request->all()
や$request->input()
で取得できます。
input()
はリクエストボディ+クエリストリング (リクエストボディ優先)、all()
はinput()
+ファイルです。
クエリストリングのみ見たい場合、query
を使用します。
$page = $request->query('page'); // 全てのクエリを連想配列で取得 $query = $request->query();
JSONで受け取る
普通のフォームでは単なる1次元配列ですが、ajaxなどを使用した際にjsonで渡す場合、多次元になることはよくあります。
その場合、->input('hoge.0.fuga')
のようにして取得します。ドットで区切るのはJavascriptなどでよくありますが、配列も[]
ではなく.
区切りです。
content-type
は必ずapplication/json
である必要があります。
{ "hoge": [ { "fuga": 100 }, "", "200" ] }
return json_encode($request->input('hoge.0.fuga')); // 100
パスパラメータを取得
ルート側で/notes/{id}
のように{}
を用いたルートを作成することで、パスパラメータを作成できます。ルートで定義した順番通りに、コントローラ側で定義した仮引数に入ります。
<a href="/notes/2">getNote</a>
Route::get('/notes/{id}', [App\Http\Controllers\HelloController::class, 'getNote']);
// $idは{id}の値を受け取る public function getNote($id) { // このような場合はjsonで返される return Note::find($id); }
ファイルを送信する
ファイルを送信した場合、そのファイルを保存すると思います。
ファイルの情報は、$request->file('name')
で取得できます。
ここで、ファイルを受け取るためには、HTMLのフォーム側でenctype="multipart/form-data"
を指定する必要があります。
<form action="/hello-world" method="post" enctype="multipart/form-data"> @csrf <label> thumbnail: <input type="file" name="thumbnail"> </label> <input type="submit" value="送信"> </form>
取得したファイルを保存するには、store
やstoreAs
を用います。store
の第2引数にはストレージの種類を、storeAs
では第2引数にファイル名を、第3引数にストレージの種類を指定します。
s3
などのストレージ設定は、app/config/filesystems.php
に書かれています。
public function postNote(Request $request) { $thumbnail = $request->file('thumbnail'); // ファイルを保存し、その保存先を取得する $path = $thumbnail->store('images'); // サブディレクトリに保存したい場合 $path = $thumbnail->store('images/fuga/hige'); // ファイル名を指定して保存したい場合 $userName = 'hoge'; $path = $thumbnail->storeAs("images/$userName", 'thumb.jpg'); // ファイル名を指定せずs3などに保存したい場合 $path = $thumbnail->store('images', 's3'); // ファイル名を指定しつつs3などに保存したい場合 $path = $thumbnail->storeAs('images', 'filename.jpg', 's3'); // ... }
保存したファイルは、デフォルトではapp/storage/app/
ディレクトリ内に保存されます。
すでに存在するファイルの場合、そのファイルは上書きされます。
ファイル名を指定しない場合は大文字小文字数字のランダム40文字のため、衝突することはまずありません。
ファイルが正常にアップロードされているかチェック
何らかの理由によりファイルが破損する可能性があります。それのチェックをしたい場合、isValid()
でチェックします。
$thumbnail = $request->file('thumbnail'); if ($thumbnail->isValid()) { // ファイルが正常な場合 }
ファイル情報を取得
ファイルからは様々な情報を取得できます。よく使用するのは、ファイル名を取得するgetClientOriginalName()
、拡張子の取得にextension()
、mime_typeを取得するgetMimeType()
あたりでしょうか。
$thumbnail = $request->file('thumbnail'); // ファイル名 $name = $thumbnail->getClientOriginalName(); // ファイルの拡張子 $ext = $thumbnail->extension(); // mime type $mime = $thumbnail->getMimeType(); // ファイルサイズ (バイト) $size = $thumbnail->getSize(); Log::debug($name); // hogefuga.png Log::debug($ext); // png Log::debug($mime); // image/png Log::debug($size); // 12348
受け取る際の型
input
等を使用して値を取り出した場合、type="number"
等と指定しても型は全てstring
になります。
<label> value: <input type="number" name="v"> </label>
// vは文字列!! $v = $request->input('v');
真偽値
入っている値がon
やoff
、true
、false
の場合、->boolean('name')
を使用すると真偽値として取得できます。
<label> checkbox: <input type="checkbox" name="ch"> </label>
$ch = $request->boolean('ch'); Log::debug(gettype($ch)); // boolean
日付
値の形式が日付の場合、->date('name')
を使用するとCarbon
インスタンスとして取得できます。Carbon
は、PHPの日付操作用のライブラリで、Laravelの場合は最初から組み込まれています。
<label> date: <input type="date" name="date"> </label> <label> datetime: <input type="datetime-local" name="datetime"> </label>
$date = $request->date('date'); $datetime = $request->date('datetime'); Log::debug($date); // 2022-03-02 00:00:00 Log::debug($datetime); // 2022-04-01 20:45:00
date
の第2引数には日付のフォーマットを、第3引数にはタイムゾーンを入れることができます。
入力データをmergeする
Request
に入っている値を取り出すときに、値をmergeすることができます。mergeすることで、後から値があるかどうかを確認して配列に追加するなどの面倒な作業を省略できます。
値を上書きしたい場合はmerge
を、値が入っていなかった場合にデフォルト値として使用したい場合はmergeIfMissing
を使用します。
// Requestで入力されている値 // $title = $request->input('title'); // $content = $request->input('content'); $request->merge([ 'num' => 100, 'content' => 'default value' ]); Log::debug($request->all());
array ( '_token' => 'ztU7bv1uwbTtpoXjfKatsWk93BdPyekpg39fjCOr', 'title' => 'fwfwefwe', 'content' => 'default value', 'num' => 100, )
// Requestで入力されている値 // $title = $request->input('title'); // $content = $request->input('content'); $request->mergeIfMissing([ 'num' => 100, 'content' => 'default value' ]); Log::debug($request->all());
array ( '_token' => 'ztU7bv1uwbTtpoXjfKatsWk93BdPyekpg39fjCOr', 'title' => 'fwfwefwe', 'content' => 'inputvalue abcde', 'num' => 100, )
CSRF対象外にしたい場合
例えば外部サイトからリクエストを受け付けるなどで、特定のURLだけCSRFを使いたくない場合があります。その場合、app/Http/Middleware/VerifyCsrfToken.php
を編集します。
class VerifyCsrfToken extends Middleware { // ドメイン以降のパスを指定する protected $except = [ 'foo/bar', 'hoge/*', ]; }
こうすることで、@csrf
を書く必要がなくなります。
<form action="/hello-world" method="post"> <!-- @csrfが不要 --> <label> title: <input type="text" name="title"> </label> <!-- ... --> <input type="submit" value="送信"> </form>
まとめ
Laravelでは、受け取ったリクエストはまずコントローラの第1引数に入ります。その変数から->input('name')
等をし、値を取得します。
ファイルを保存する際は->file('name')
で情報を取得でき、->store('dir')
のようにして保存できます。
LaravelではデフォルトでCSRFの保護がされていることを知っておきましょう!
値のバリデーションは必須知識なので、下の記事も確認してバリデーションも行えるようになりましょう。
