Skip to content
Advertisement

Extended Request missing data when reaching controller with type-hint

A lot of pieces to this so here’s the meat. Code very slightly tweaked for brevity.

Extended class:

JavaScript

Middleware:

JavaScript

Routes:

JavaScript

Controller:

JavaScript

The /books1/5?foo=bar and frontend1() path works. $request is populated as expected.

Populated Correctly

The /books2/5?foo=bar and frontend2() path is broken. $request has vast amounts of missing data, like it was instantiated with nothing.

Populated Incorrectly

Evidently if I type-hint my subclass instead of the more generic parent, it’s causing some kind of broken instantiation. From an OO perspective I think this should be perfectly fine and I do specifically need my subclass being provided so prefer that type-hint. Is something deep within Laravel tripping this up? Is this some obscure PHP behavior I haven’t seen before?

Advertisement

Answer

This is kind of tricky.

First of all, you need to be familiar with the service container and dependency injection. Here is the full doc: https://laravel.com/docs/8.x/container


When you type hint a class inside a controller method, Laravel will try to understand what it should do with it.

If nothing is registered inside the service container, it will try to make a new instance of it.

IlluminateHttpRequest is bound as a singleton (https://laravel.com/docs/8.x/container#binding-a-singleton).

While a simple bind will return a new instance at each call, a singleton will always return the exact same instance.

Here is a quick demo:

AppModelsUser::class is a class that is not explicitly bound.

When you try to resolve it using the service container, it will not find it and will try to make a new instance:

JavaScript

But since IlluminateHttpRequest::class is bound by Laravel, it follows a different path:

JavaScript

Now, what’s happening?

Under the hood, when a new request is made to your app and before hitting the controller method, Laravel will do a lot of things to prepare the IlluminateHttpRequest instance.

For instance, it will setup the route resolver inside IlluminateRoutingRouter:

JavaScript

Each time Laravel internally call a method like this:

JavaScript

$request is always the same instance, because it is bound as a singleton.


We are now in your controller.

JavaScript

If you are still here, how to solve this problem?

The easiest way is to use a FormRequest, initially designed to handle form validation, but if you return an empty rules array, you should be able to do everything you did with your custom AppHttpRequest instance:

JavaScript

Try again, everything should work fine, since this is a feature specially designed to replace the initial IlluminateHttpRequest object.

The full doc is here: https://laravel.com/docs/8.x/validation#creating-form-requests

User contributions licensed under: CC BY-SA
2 People found this is helpful
Advertisement