【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']);
controller側
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"
    ]
}
controller
return json_encode($request->input('hoge.0.fuga')); // 100

パスパラメータを取得

ルート側で/notes/{id}のように{}を用いたルートを作成することで、パスパラメータを作成できます。ルートで定義した順番通りに、コントローラ側で定義した仮引数に入ります。

view
<a href="/notes/2">getNote</a>
route
Route::get('/notes/{id}', [App\Http\Controllers\HelloController::class, 'getNote']);
controller
// $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>

取得したファイルを保存するには、storestoreAsを用います。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');

真偽値

入っている値がonofftruefalseの場合、->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を使用します。

mergeの場合
// Requestで入力されている値
// $title = $request->input('title');
// $content = $request->input('content');

$request->merge([
    'num' => 100,
    'content' => 'default value'
]);

Log::debug($request->all());
Logの例
array (
  '_token' => 'ztU7bv1uwbTtpoXjfKatsWk93BdPyekpg39fjCOr',
  'title' => 'fwfwefwe',
  'content' => 'default value',
  'num' => 100,
)
mergeIfMissingの場合
// Requestで入力されている値
// $title = $request->input('title');
// $content = $request->input('content');

$request->mergeIfMissing([
    'num' => 100,
    'content' => 'default value'
]);

Log::debug($request->all());
Logの例
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の保護がされていることを知っておきましょう!

値のバリデーションは必須知識なので、下の記事も確認してバリデーションも行えるようになりましょう。

laravel request thumb

役に立ったらシェアしよう!