Requestの各メソッド（query(), get(), all()...）の使い分け

自分のレベルが低いうちは

* `get()`で大体取れる
* プロパティアクセスでも大体取れる
* `get()`と`input()`との違いを聞かれてもわからない
* `only()`や`except()`で欲しいものだけ取れるすごい
* `all()`で全部取れるすごい

ぐらいの認識だったが、ちゃんと使い分けが出来そうだったのでメモ。

マサカリ歓迎。

# 結論

* クエリストリングだけ取得したいなら[`query()`](#query-1)
* リクエストボディ**のみ**を取得したい場合の**関数**は存在しない（[`$request`](#request)プロパティにアクセスすれば可能）
* リクエストボディ＋クエリストリング（クエリストリング優先）は[`get()`](#get)
* リクエストボディ＋クエリストリング（リクエストボディ優先）は[`input()`](#input)
* [`all()`](#all)は[`input()`](#input)＋ファイル
* [`only()`](#only-except)、[`except()`](#only-except)は[`all()`](#all)を利用している
* プロパティアクセスは[`all()`](#all)＋ルートパラメータを利用している

# 前提

`symfony\http-foundation\Request`に3つのプロパティがあり、これらがリクエストのデータを扱っている

```php:symfony\http-foundation\Request.php
    /**
     * Custom parameters.
     *
     * @var \Symfony\Component\HttpFoundation\ParameterBag
     */
    public $attributes;

    /**
     * Request body parameters ($_POST).
     *
     * @var \Symfony\Component\HttpFoundation\ParameterBag
     */
    public $request;

    /**
     * Query string parameters ($_GET).
     *
     * @var \Symfony\Component\HttpFoundation\ParameterBag
     */
    public $query;
```

## $attributes

追加データを格納できるプロパティ。いろんなポイントからアクセスする必要がある情報はここに入れてね！
みたいなことが下記URLに書いてある（と思う）が、どんな場合に有用なのか、思いつかなかった。
自分で何か入れない限り、基本的には空（のはず）。

http://symfony.com/doc/current/components/http_foundation.html#component-foundation-attributes

Thanks to the public attributes property, you can store additional data in the request, which is also an instance of ParameterBag. This is mostly used to attach information that belongs to the Request and that needs to be accessed from many different points in your application.

## $request

`$_POST`
http://php.net/manual/ja/reserved.variables.post.php

## $query

`$_GET`
http://php.net/manual/ja/reserved.variables.get.php

（本当に`$_POST`や`$_GET`と全く同じかどうかは未検証だが、本題とはズレるのでここでは扱わない。）

# 各メソッド

## get()

```php:symfony/http-foundation/Request.php
    /**
     * Gets a "parameter" value from any bag.
     *
     * This method is mainly useful for libraries that want to provide some flexibility. If you don't need the
     * flexibility in controllers, it is better to explicitly get request parameters from the appropriate
     * public property instead (attributes, query, request).
     *
     * Order of precedence: PATH (routing placeholders or custom attributes), GET, BODY
     *
     * @param string $key     The key
     * @param mixed  $default The default value if the parameter key does not exist
     *
     * @return mixed
     */
    public function get($key, $default = null)
    {
        if ($this !== $result = $this->attributes->get($key, $this)) {
            return $result;
        }

        if ($this !== $result = $this->query->get($key, $this)) {
            return $result;
        }

        if ($this !== $result = $this->request->get($key, $this)) {
            return $result;
        }

        return $default;
    }
```

[`$attributes`](#attributes) -> [`$request`](#request) -> [`$query`](#query)
の順番で要素を探しに行く。

メモ：**リクエストボディよりもクエリストリングが優先される。**

## query()

```php:laravel\framework\src\Illuminate\Http\Concerns\InteractsWithInput.php
    /**
     * Retrieve a query string item from the request.
     *
     * @param  string  $key
     * @param  string|array|null  $default
     * @return string|array
     */
    public function query($key = null, $default = null)
    {
        return $this->retrieveItem('query', $key, $default);
    }


    /**
     * Retrieve a parameter item from a given source.
     *
     * @param  string  $source
     * @param  string  $key
     * @param  string|array|null  $default
     * @return string|array
     */
    protected function retrieveItem($source, $key, $default)
    {
        if (is_null($key)) {
            return $this->$source->all();
        }

        return $this->$source->get($key, $default);
    }
```

[`$query`](#query)だけを見に行く

メモ：**クエリストリングのみを扱う。**

## input()

```php:laravel\framework\src\Illuminate\Http\Concerns\InteractsWithInput.php
    /**
     * Retrieve an input item from the request.
     *
     * @param  string  $key
     * @param  string|array|null  $default
     * @return string|array
     */
    public function input($key = null, $default = null)
    {
        return data_get(
            $this->getInputSource()->all() + $this->query->all(), $key, $default
        );
    }
```
```php:laravel\framework\src\Illuminate\Http\Request.php
    /**
     * Get the input source for the request.
     *
     * @return \Symfony\Component\HttpFoundation\ParameterBag
     */
    protected function getInputSource()
    {
        if ($this->isJson()) {
            return $this->json();
        }

        return $this->getRealMethod() == 'GET' ? $this->query : $this->request;
    }
```

（JSONの場合分けに関しては、まだ読んでいないので本記事では扱いません）

1. メソッドが`GET`であれば[`$query`](#query)を、そうでなければ[`$request`](#request)を取得する
1. 上記に[`$query`](#query)を加えたものからデータを取得する（`＋`演算子のため先勝ち）

メモ：**メソッドによって扱うパラメータが変わるが、クエリストリングは必ず扱う**
メモ：**キーが重複した場合はリクエストボディが優先**

## all()

```php:laravel\framework\src\Illuminate\Http\Concerns\InteractsWithInput.php
    /**
     * Get all of the input and files for the request.
     *
     * @param  array|mixed  $keys
     * @return array
     */
    public function all($keys = null)
    {
        $input = array_replace_recursive($this->input(), $this->allFiles());

        if (! $keys) {
            return $input;
        }

        $results = [];

        foreach (is_array($keys) ? $keys : func_get_args() as $key) {
            Arr::set($results, $key, Arr::get($input, $key));
        }

        return $results;
    }
```

上述の[`input()`](#input)にファイルを加えたもの
（ファイルに関しては本記事では深くは扱わない）

メモ：**ファイルを加えた`input()`。**

## only(), except()

```php:laravel\framework\src\Illuminate\Http\Concerns\InteractsWithInput.php
    /**
     * Get a subset containing the provided keys with values from the input data.
     *
     * @param  array|mixed  $keys
     * @return array
     */
    public function only($keys)
    {
        $results = [];

        $input = $this->all();

        $placeholder = new stdClass;

        foreach (is_array($keys) ? $keys : func_get_args() as $key) {
            $value = data_get($input, $key, $placeholder);

            if ($value !== $placeholder) {
                Arr::set($results, $key, $value);
            }
        }

        return $results;
    }
```

```php:laravel\framework\src\Illuminate\Http\Concerns\InteractsWithInput.php
    /**
     * Get all of the input except for a specified array of items.
     *
     * @param  array|mixed  $keys
     * @return array
     */
    public function except($keys)
    {
        $keys = is_array($keys) ? $keys : func_get_args();

        $results = $this->all();

        Arr::forget($results, $keys);

        return $results;
    }
```

どちらも[`all()`](#all)を利用している

## プロパティアクセス

```php:laravel/framework/src/Illuminate/Http/Request.php
    /**
     * Get an input element from the request.
     *
     * @param  string  $key
     * @return mixed
     */
    public function __get($key)
    {
        if (array_key_exists($key, $this->all())) {
            return data_get($this->all(), $key);
        }

        return $this->route($key);
    }
```

[`all()`](#all)になければルートパラメータを探しに行く。

マジックメソッドなので、オブジェクト内にアクセス可能プロパティがあるときはオブジェクトのプロパティが優先される。

メモ：**ルートパラメータも取得できる**

メモ：**オブジェクト内のアクセス可能プロパティ -> [`all()`](#all) -> ルートパラメータの順に優先される**

# おまけ

## リクエストボディのみを取得したい

[`$request`](#request)プロパティにアクセスすることで可能。

```php:例
$request->request->get('hoge');
$request->request->all();
```