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.
What is the value of this?
When using a implicit binding, it allows you to simplify your controller. If you are looking to find a single record from table, you will not have to even write a single line for this – Laravel will take care of this, and will check if the record really exist. But the true power of this can be used with explicit route binding.. Why? Because it will have direct impact into your efficiency. Many times before you will do any business logic on object you would like to check it for some conditions. We are not talking here about form fields validation.Let’s say that we have an object, which can be edited only if some special conditions (e.g. The user has access to it, the record has some specified status etc). In this case you will probably want to use the middleware to check them. In that case – you will have to get the object values from database in few places – in middleware, and later in controller (or somewhere below it). To avoid this database request duplication you can use explicit route binding. This way the object will be initialized once and you will have access to THE SAME object in your middleware and later in your controller.
How does it work (the route binding)?
Well – there is a small difference between pure Service Container useage and Route binding. Since the object binding in IoC (with e.g. Bind method) has an impact to whole application dependencies – the route binding has an impact only to the middleware and controllers, and that is all. This way you can be sure that the specified object will be used only in those two places!! It is possible, because the Router class is „above” the Container class.
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.
We have missed in all above one thing – the object instance can be passed to the controller method also by using the app()->instance method. We will not show how to do this, because the explicit route binding is the proper way of doing this – anyway, we need to notice that there is such possibility. So summary – the parameters in controller methods can have its values in few ways:
- just by passing the parameter value from request
- be initialized as an „empty” object by IoC
- be initialized with app()->instance method – which correspond with the IoC
- be initialized as a filled object by implicit route binding
- be initialized as a filled object or something else by explicit route binding.
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.