💪

リクエストパラメータの「ベストな受け取り方」と「危ない受け取り方」(Laravel)

2021/03/08に公開3

今回は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点です。

  1. 大量にリクエストパラメータがある場合にも、どのパラメータを受け取っているかが分かりやすい
  2. 一行で書けるため超コンパクト
  3. そのまま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

yuyu

自分の知識の整理と普段書いているコードを見直すきっかけになりました。非常に勉強になりました。

1点初心者ながら質問させてください。
多くの場合、ベストなリクエストの受け取り方は「only」を使うのが良いとされていますが、逆にonlyを使わない方がいいケースというのは出てくるのでしょうか?(そのようなケースはなさそうですかね?)

おしうみなおきおしうみなおき

逆にonlyを使わない方がいいケースというのは出てくるのでしょうか?

onlyを使わない方がいいケースは今の所出会ったことはありません。
inputで1つずつ受け取ってもonlyを使ってもどっちでも良い(好みの問題)っていう場合は多々あります。

例えば、新規ユーザー登録を例にすると、Aがinputを使用した場合、Bがonlyを使用した場合です。
受け取ったパラメータに何らかの加工を施す必要がある場合は、どっちでも良いと思います。

※下記の例は趣旨とは外れるためあえてバリデーションをしていません。

Aの場合

public function create(Request $request)
{
    $email = $request->input('email');
    $password = $request->input('password');
    
    User::create([
        'email' => $email,
        'password' => bcrypt($password) // ハッシュ化する
    ]);
}

Bの場合

public function create(Request $request)
{
    $attributes = $request->only(['email', 'password']);
    
    User::create([
        'email' => $attributes['email'],
        'password' => bcrypt($attributes['password']) // ハッシュ化する
    ]);
}
yuyu

なるほど、ありがとうございます!
みた感じどちらでも良さそうなイメージですね、、