📈

【Laravel】Requestの値取得方法まとめ・解説

2023/05/06に公開

💡Requestとは?

LaravelのIlluminate\Http\Requestクラスは、アプリケーションが処理している現在のHTTPリクエストを操作し、リクエストとともに送信される入力、クッキー、およびファイルを取得するオブジェクト指向の手段を提供しています。

(と、ドキュメントは言っています。)

強力で便利なクラスですが、注意点もあるため、Laravelを学び始めた方、使ってるけどよく知らない方は、ぜひ一読ください。(熱いフィールドバックも受付中)

内容が大体良さそう!と思っていただけたら、いいねしていただけると嬉しいです。

✅ Request::get()

コードを辿ると、Symfony\Component\HttpFoundation\SymfonyRequestクラスに定義されていることがわかります。
コードは下記のようになっています。

    public function get(string $key, mixed $default = null): mixed
    {
        if ($this !== $result = $this->attributes->get($key, $this)) {
            return $result;
        }

        if ($this->query->has($key)) {
            return $this->query->all()[$key];
        }

        if ($this->request->has($key)) {
            return $this->request->all()[$key];
        }

        return $default;
    }

$this->queryには、GET、$this->requestにはPOSTの値が入っているため、GET -> POST の順で、見つかったものから返すというような処理になります。

✅ Request::input()

Illuminate\Http\Requestクラス内でuseされているInteractsWithInputトレイトに定義されているメソッドです。トレイトなので、実質Requestクラス直属のメソッドになります。

    public function input($key = null, $default = null)
    {
        return data_get(
            $this->getInputSource()->all() + $this->query->all(), $key, $default
        );
    }

コードを見ての通りですが、$this->getInputSource()、つまりフォーム等から送信されたリクエストの内容と、GETの内容を + 演算子で結合したものから、キーに該当する値を取得するようになっています。

また、配列を.区切りで指定できます。
これは、data_get()というヘルパー関数により実現されています。

✅ Request::all()

こちらも、Illuminate\Http\Concerns\InteractsWithInputに定義されているメソッドなので、実質Requestクラス直属のメソッドになります。

ファイルを含めた全てのリクエスト内容を取得します。
$input = array_replace_recursive($this->input(), $this->allFiles());の部分では、 $this->input()と同じキーがファイルに存在すると、ファイルで上書きされます。優先順位がファイルの方が上のようです。

    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;
    }

✅ Request::only()

こちらも、Illuminate\Http\Concerns\InteractsWithInputに定義されているメソッドなので、実質Requestクラス直属のメソッドになります。

only()は、all()で取得した値を元に、指定されたキーの値を取得します。

    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;
    }

このメソッドも、data_get()を使って、.区切りの配列の指定ができます。

✅ Request::query() / Request::post()

こちらも、Illuminate\Http\Concerns\InteractsWithInputに定義されているメソッドなので、実質Requestクラス直属のメソッドになります。

同じような仕組みですので、まとめて説明します。
コードはこのようになっています。

query()

    public function query($key = null, $default = null)
    {
        return $this->retrieveItem('query', $key, $default);
    }

post()

    public function post($key = null, $default = null)
    {
        return $this->retrieveItem('request', $key, $default);
    }

どうやら、retrieveItem()というメソッドがキモになっていそうです。
中身はこのようなコードです。

    protected function retrieveItem($source, $key, $default)
    {
        if (is_null($key)) {
            return $this->$source->all();
        }

        if ($this->$source instanceof InputBag) {
            return $this->$source->all()[$key] ?? $default;
        }

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

中身はシンプルで、$sourceを元に、プロパティを使い分けて、それに応じた内容を取得してきているようです。query()であれば$this->querypost()であれば、$this->requestからそれぞれall()を呼ぶような形になっています。

これらのプロパティは、Symfony\Component\HttpFoundation\Requestクラスに定義されています。
プロパティの定義を見ると、このようになっています。

    /**
     * Request body parameters ($_POST).
     *
     * @var InputBag
     */
    public $request;

    /**
     * Query string parameters ($_GET).
     *
     * @var InputBag
     */
    public $query;

つまり、それぞれInputBagのインスタンス(ParameterBagを継承)であり、post()を実行すると$request$_POSTの内容、query()$query$_GETの内容を取得するような処理になっています。

✅ Request::__get()

Illuminate\Http\Requestクラスに定義してあるメソッドです。

コードはシンプルで、all()から取得、なければroute()から取得しています。

    public function __get($key)
    {
        return Arr::get($this->all(), $key, fn () => $this->route($key));
    }

⚠ 例

ユーザ情報を編集するようなルーティングを用意します。

web.php
Route::get('users/{user}/edit', 'edit')->name('edit');

URLは、https://example.com/users/1/editのようになります。

2. ルーティングモデルバインディングしたUserモデルインスタンスと、Requestを受け取るようにします。

UserController.php
class UserController extends Controller
{
    public function edit(User $user, Request $request)
    {
        //
    }
}
UserController.php
    public function edit(User $user, Request $request)
    {
        $request->__get('user');
    }

この場合、$userと同じ、Userモデルのインスタンスが取得できます。

次に、URLにuserというクエリパラメータを付与してみます。https://example.com/users/1/edit?user=hogeのようになります。

UserController.php
    public function edit(User $user, Request $request)
    {
        $request->__get('user');
    }

同じように実行すると、hogeになります。

✅ Request::route()

Illuminate\Http\Requestクラスのメソッドです。
Request::__get()で少し触れましたが、このメソッドは、URLのパスパラメータから取得するメソッドです。
中身はこのようになっており、$this->getRouteResolver()により、Illuminate\Routing\Routeクラスに解決されます。

Request.php
    public function route($param = null, $default = null)
    {
        $route = call_user_func($this->getRouteResolver());

        if (is_null($route) || is_null($param)) {
            return $route;
        }

        return $route->parameter($param, $default);
    }

$route->parameter()を見てみると、このようになっています。

Route.php
    public function parameter($name, $default = null)
    {
        return Arr::get($this->parameters(), $name, $default);
    }

さらに$this->parameters()を辿るとこのようなコードになっています。

Route.php
    public function parameters()
    {
        if (isset($this->parameters)) {
            return $this->parameters;
        }

        throw new LogicException('Route is not bound.');
    }

$this->parametersがセットされる箇所を見てみると、bind()というメソッドでRouteParameterBinderクラスのparameters()が呼ばれていることがわかります。

Route.php
    public function bind(Request $request)
    {
        $this->compileRoute();

        $this->parameters = (new RouteParameterBinder($this))
                        ->parameters($request);

        $this->originalParameters = $this->parameters;

        return $this;
    }

この中は、このような、パスパラメータをバインドしてるっぽいコードになっています。

RouteParameterBinder.php
    public function parameters($request)
    {
        $parameters = $this->bindPathParameters($request);

        ...
    }

基本的には、ルートモデルバインディングしたモデルインスタンスをリクエストから取得する場合は、Request::route()を使うと確実と言えます。

まとめ

これらを見てみた限り、リクエスト内容を取得するときは、Request::input()が良い感じに取ってきてくれるため、こちらを使用すると良いと思います。
また、先述したように、ルートモデルバインディングしたインスタンスを取得したい場合は、Request::route()が良いと思います。

Discussion