リクエストパラメータの「ベストな受け取り方」と「危ない受け取り方」(Laravel)
今回はLaravelのControllerでのリクエストの受け取り方について説明したいと思います。
どのプロジェクトでも必ずと言っていいほど、リクエストを受け取る部分は出てくるかと思います。
僕自身、とりあえずリクエストを受け取れればいいやと思っていて、ずーっとなあなあにやっていましたが、いろいろな人のリクエストの受け取り方を目の前にして、
こんなコンパクトになるのかー! と驚愕しました。
この記事では、リクエストの受け取り方に関して、ベストな方法・やってはいけない方法の2点をお伝えします。
まずはControllerでのリクエストの受け取り方のパターンを知る
こちらは公式ドキュメントを見れば一目瞭然なのですが、Controllerでリクエストを受け取る方法をまとめてみました。
例として、Controllerに渡しているパラメータは下記のように name
, age
, job
とします
[
'name' => '太郎',
'age' => 20,
'job' => 'student',
]
では、これからリクエストの受け取り方を確認してみます。
リクエストを1つずつ受け取る
inputを使用すると、リクエストパラメータを1つずつ取得できます。
public function getRequestDemo(Request $request)
{
$name = $request->input('name'); // 太郎
$age = $request->input('age'); //20
$job = $request->input('job'); // student
}
下記のように動的プロパティを使用すると、先にリクエストパラメータの中で名前が一致するものを探し、なければルーティングのパラメータを探しに行きます。
public function getRequestDemo(Request $request)
{
$name = $request->name; // 太郎
$age = $request->age; //20
$job = $request->job; // student
}
上記2つはリクエストパラメータ全体からパラメータを取得できますが、クエリ文字列からのみ取得したい場合はこのようにquery
を使います
下記のように動的プロパティを使用すると、先にリクエストパラメータの中で名前が一致するものを探し、なければルーティングのパラメータを探しに行きます。
クエリ文字列にない場合は値は取得できません。
```php
public function getRequestDemo(Request $request)
{
$name = $request->query('name');
$age = $request->query('age');
$job = $request->query('job');
}
リクエストをまとめて受け取る
個人的にはずっと↑で説明したように、1つ1つパラメータを取っていたのですが、多くの場合はパラメータをまとめて受け取ってしまったほうが、可読性を落とさずにコンパクトにすることができます
受け取りたいパラメータを指定して、連想配列で受け取る場合はonly
を使用します。
public function getRequestDemo(Request $request)
{
$attributes = $request->only(['name', 'age', 'job']);
// $userDataの中身は、['name' => '太郎', 'age' => 20, 'job' => 'student'];
}
受け取りたくないパラメータを指定して、連想配列で受け取る場合はexcept
を使用します。
public function getRequestDemo(Request $request)
{
$attributes = $request->except(['name']);
// $attributesの中身はname以外のパラメータが入る。['age' => 20, 'job' => 'student'];
}
全てまとめてリクエストパラメータを受け取っちゃう場合は all
を使用します。
public function getRequestDemo(Request $request)
{
$attributes = $request->all();
// $attributesの中身は全パラメータが入る。['name' => '太郎', 'age' => 20, 'job' => 'student'];
}
多くの場合、ベストなリクエストの受け取り方は「only」を使う方法
多くの場合はonly
を使うと可読性が担保されたままコードをコンパクトにできます。
onlyを使うべき理由はこの2点です。
- 大量にリクエストパラメータがある場合にも、どのパラメータを受け取っているかが分かりやすい
- 一行で書けるため超コンパクト
- そのままcreateやupdateに渡せる
個人的には3番のインパクトがめちゃくちゃ大きいなと思いました。
例えば、先程のように name
, age
, job
のパラメータを受け取ってstudents
テーブルに入れたい場合はこのようになります。
pubic function store(Request $request)
{
$attributes = $request->only(['name', 'age', 'job']);
User::create($attributes);
}
2行でできた!!すげ!!
※プロダクトで使用する場合はちゃんとバリデーションしてください(笑)
pubic function store(Request $request)
{
$params = $request->only(['name', $age, $job]);
$this->userService->create($params['name'], $params['age'], $params['job']);
}
危ないリクエストパラメータの受け取り方法
リクエストパラメータを受け取って、そのままDBに保存したいケースはよくあります。
その場合に絶対にやってはいけない、リクエストパラメータの受け取り方があるのです。
それが all
を使用する場合です。
理由は唯一つ、 allを使用すると、受け取りたくない値まで受け取っちゃう可能性があるからです。
フロント側でせっせと必要なテキストボックスを用意していても、
<input type="text" name="name">
<input type="number" name="age">
<input type="text" name="job">
Chrome DeveloperツールなどでHTMLは自由に書き換えられちゃうんです!!!!!!!
つまり、name, age, jobしかパラメータは飛んでこないだろうと思っていても、
HTMLが書き換えられて 'profile' => 'HogeHoge'
が飛んでくる可能性だってあるんです。
それなのに、all
で取得していると、
予期せぬ値までDBに突っ込んじゃう可能性が大いにあります!
pubic function store(Request $request)
{
$attributes = $request->all(); // ['name' => '太郎', 'age' => 20, 'job' => 'student', 'profile' => 'HogeHoge'];
User::create($attributes);
}
予期しない値をDBに保存しないための方法
1.onlyを使用する
しかし、onlyを使用していると、 'profile' => 'HogeHoge'
が飛んできたって大丈夫。なぜなら取得するパラメータを指定しているからですね。
pubic function store(Request $request)
{
$attributes = $request->only(['name', 'age', 'job']); // ['name' => '太郎', 'age' => 20, 'job' => 'student'];
User::create($attributes);
}
2.fillableやguardedを正しく指定する
Modelのfillableに指定していない値もしくはguardedに指定している値はDBには保存しません。
DBに保存したくない項目はfillableやguardedで制御しましょう。
ただ絶対に保存したくない値ではないけど、特定のAPIでは指定の項目のみ保存したいという場合が多いと思います。なので、基本的にはonlyを使用するのが良いと思います。
終わりに
なんとなく使っているリクエストパラメータの取得ですが、個人的にベストな方法とやってはいけない方法が見つかりました。
もっとこうした方が良い!などのアイデアがありましたら、ぜひコメントください!
Discussion
自分の知識の整理と普段書いているコードを見直すきっかけになりました。非常に勉強になりました。
1点初心者ながら質問させてください。
多くの場合、ベストなリクエストの受け取り方は「only」を使うのが良いとされていますが、逆にonlyを使わない方がいいケースというのは出てくるのでしょうか?(そのようなケースはなさそうですかね?)
onlyを使わない方がいいケースは今の所出会ったことはありません。
inputで1つずつ受け取ってもonlyを使ってもどっちでも良い(好みの問題)っていう場合は多々あります。
例えば、新規ユーザー登録を例にすると、Aがinputを使用した場合、Bがonlyを使用した場合です。
受け取ったパラメータに何らかの加工を施す必要がある場合は、どっちでも良いと思います。
※下記の例は趣旨とは外れるためあえてバリデーションをしていません。
Aの場合
Bの場合
なるほど、ありがとうございます!
みた感じどちらでも良さそうなイメージですね、、