Laravel Service Container (IoC) and Route Binding – what is the rellation and how does it work.
One of the best Laravel features is the Service Container implementation (called also „IoC”). In this article we will not cover explanation about basic use of Service Container – if you are not familiar with it, we recommend to watch this video: https://laracasts.com/series/laravel-5-fundamentals/episodes/26 This article will try to explain what is the relation between Laravel Route Binding and the IoC. As the IoC take care about the „empty” objects initialization for our dependency injection places, there is another great feature which cooperate with it, which is route binding. The difference is that route binding is prepared to be used strictly with the Routes + Middleware + Controllers. What it will give us? The great explanation you can find directly in the documentation: https://laravel.com/docs/5.4/routing#route-model-binding which says that summary it will give you the exact instance of required object.
Let’s go with this step by step.
Consider this route:
The first step, we consider, is that nothing else is done – so as the result of this, the object instance of Post class will be initialized by the IoC. So the $post will be an „empty” initialization of object. Empty means here that the object will not contains any values (except this which are initialized in its own constructor method).
So when you will try to dump the $id you will receive the value from url. When you will dump the $user you will see that it’s an „empty” object of User class.
The id is just passed by Router, but for the $user instance – the IoC controller is called which is responsible to create it. This is the standard behaviour which all Laravel programmers should be familiar with. Let’s consider another step – so the route will be changed to this:
And our controller method will have only $user parameter (so that the route param name is equal to the controller method param name).
What we are doing now is an implicit route binding. The result of this will be that the Laravel Router will try to find the user in database and initialize the User class object. So now when you will debug it, you will have concrete filled object. Nice – isn’t it? You don’t have to write a query to find the user row. Let’s stop for a while here and try to make rough explanation of this process. The first thing is that the Router is checking the index method parameters. If he will find that parameter name is equal to the parameter name in the route AND the parameter is indicated that it should be an instance of some class (User) he will use this class and invoke the findOrFail method.
This diagram should explain it a little bit:
The above diagram is only a simplified representation of the whole process, which is a little bit more complicated – but it is enough to start understanding the whole algorithm. In the next step even those simplified diagrams will start to be more complex – but still they will be only simplified representation of this process. Important note is that implicit bindings only works for class which extends Eloquent Model class. So let’s see, how our process is handling the example, when we will bind the object with explicit binding. In the implicit binding, the parameter name in controller method has to be the same as the parameter name in the route definition. This is not required for explicit route binding.
To present that, we will use those code – as a route:
and for the controller:
So as we can see above – the parameter names are different. In this case, the implicit route binding will not work, so as a result of this we will get an „empty” instance of User class object in the $user param. This is because the Router will use IoC container->make method to initialize this. But since we want to have here the filled object, we need to use explicit binding and in RouteServiceProvider we have to add this code:
This way we can tell to Laravel that again he should find a User row in the database with id = concreate_user. Later, Router will check that the controller method need an instance of User class in its parameters and he will use this instance (the user row which he just found in the database). But the important note here is that we can do this in other way and instead of using Route::model we can bind here whatever we want – e.g. this can be a contract:
Thanks to this we can have full control over the process and decide exactly what we want to bind (of course the type of binded element has to be the same type as we declared in the controller as a method parameter). Also the important thing here is that we can also use this object in the middleware.
How those point can be reached? Quite simple ? There are two things which we need to know.
Both Router and IoC container have the array which contains the binded objects. In Router (IlluminateRoutingRouter.php) this array is called $binders.
Router contain also a $container which is an instance of IoC container (IlluminateContainerContainer.php). IoC container array is named $instances. In both of this arrays (bindings and instances) the keys are the class names and the values are the initialized object instances, which later will be used to pass as a parameters.
The process goes like this, that first the Router is checking its own „bindings” table if there is a binded object instance for a class. If he found something – he will use this value. If not – then he is calling a container (IoC) make method.
The IoC make method first is checking the $instances array if there is an instance of the required class. If he will find it – he will return its value, if not – he will create a new „empty” object of required class.