In this post, we will discuss the most important aspect of Laravel framework Service Container.
Let's understand the definition first...
The Laravel service container is a powerful tool for managing class dependencies and performing dependency injection. Dependency injection is a fancy phrase that essentially means this: class dependencies are “injected” into the class via the constructor or, in some cases, “setter” methods.
In simple terms, the service container is a container that holds all the bindings that need to run Laravel application smoothly.you can bind almost everything you’d like to instantiate programmatically later in your application when needed. That means Service Container holds a single object of all of our various bindings.
So whenever, you need to use any inbuilt or custom service you can type-hint in a class constructor or method it will automatically be injected from service container as its container that holds all class dependencies.
Let's assume, we have one class with the name MathService which used to do some basic operations like addition, multiplication, etc. Below is the code of this class.
App\Services\MathService.php
<?php
namespace App\Services;
class MathService
{
public function doAddition($numbers)
{
return array_sum($numbers);
}
public function doMultiplication($numbers)
{
return array_product($numbers);
}
}
As you can see, there two methods on \App\Services\MathService
class doAddition
which calculates the sum of numbers provided in array using array_sum function and doMultiplication
which multiplies all the numbers provided as an array using array_product function.
To use services provided by \App\Services\MathService
class we first need to bind it with service containers. the binding will be like following, we just need to pass the path of MathService class and it will be bind on the container.
<?php
App::bind('MathService',\App\Services\MathService::class);
By doing this, we are telling laravel to store \App\Services\MathService
class in the container with label MathService and return it back when we need an instance of a class. This is one time binding of class and can get any number of instances from our application.
Now suppose we require service provided by \App\Services\MathService
class in any of our application functionality. To achieve this, we first need to resolve service that we already bound in the container and then call respective methods.
To resolve service from our application can use either of two ways...
<?php
$mathService = app()->make('MathService');
// or
$mathService = resolve('MathService');
The service is resolved now. we can call doAddition
& doMultiplication
methods from our application.
<?php
$sum = $mathService->doAddition([40, 20, 10]);
$product = $mathService->doMultiplication([4, 2, 3]);
print_r($sum);
print_r($product);
// Result: Sum => 70, Product => 24
Automatic Dependency Injection
Important note from the Laravel documentation:
There is no need to bind classes into the container if they do not depend on any interfaces. The container does not need to be instructed on how to build these objects since it can automatically resolve these objects using reflection.
Here, Actually we don't need to bind class on the container. We can just type-hint dependencies directly on the method or constructor of a controller like below.
<?php
function index(MathService $mathService)
{
$sum = $mathService->doAddition([40, 20, 10]);
$product = $mathService->doMultiplication([4, 2, 3]);
}
This is just the basic of service container in which we bind service and use it on our app. But remember, All the binding of services like this should be always on register
method of Service Providers.
Interface binding
On Service Container we can also bind a specific implementation of an interface to it, in this way whenever we resolve this interface we will end up with the concrete class that is bound to it.
For the above example, We can create Interface and two classes with different implementation and bind any of the implementations to the container.
Let's create MathServiceInterface
App\Services\MathServiceInterface.php
<?php
namespace App\Services;
Interface MathServiceInterface
{
public function doAddition(array $numbers);
public function doMultiplication(array $numbers);
}
MathService which implements MathServiceInterface
App\Services\MathService.php
<?php
namespace App\Services;
class MathService implements MathServiceInterface
{
public function doAddition($numbers)
{
return array_sum($numbers);
}
public function doMultiplication($numbers)
{
return array_product($numbers);
}
}
CustomMathService which also implements MathServiceInterface
App\Services\CustomMathService.php
<?php
namespace App\Services;
class CustomMathService implements MathServiceInterface
{
public function doAddition($numbers)
{
$sum = 0;
foreach($numbers as $num)
{
$sum = $sum + $num;
}
return $sum;
}
public function doMultiplication($numbers)
{
$product = 1;
foreach($numbers as $num)
{
$product = $product*$num;
}
return $product;
}
}
As you can see, we have two different classes MathService
& CustomMathService
with a different implementation of doAddition
& doMultiplication
methods.
Now instead of binding these classes on container lets bind MathServiceInterface
with the container.
<?php
App::bind('\App\Services\MathServiceInterface', function () {
return new \App\Services\MathService();
});
Here, when MathServiceInterface
will be resolved, Laravel gives us an instance of MathService
class. And by calling methods using this instance will get results using MathService
class implementation.
Controller:
<?php
public function index() {
$mathService = app()->make('\App\Services\MathServiceInterface');
$sum = $mathService->doAddition([40, 20, 10]);
$product = $mathService->doMultiplication([4, 2, 3]);
/*
This will calculate results
using MathService class implementation
*/
}
Now if we want other implementation called CustomMathService
of MathServicesInterface
then just need to change in binding.
<?php
App::bind('\App\Services\MathServiceInterface', function () {
return new \App\Services\CustomMathService();
});
The code will still work the same as earlier but with a different implementation of CustomMathService
class. this way we can de-couple any of interface implementation easily by changing just binding.
Another way to resolve is by using reflection.
<?php
App::bind(\App\Services\MathServiceInterface::class, \App\Services\CustomMathService::class);
This will bind class and resolve it using reflection. By doing this, don't need to resolve using resolve()
method. We can just type-hint dependency on a constructor.
Controller:
<?php
protected $mathService;
public function __construct(\App\Services\MathServiceInterface $mathService)
{
$this->mathService = $mathService;
}
public function index() {
$sum = $this->mathService->doAddition([40, 20, 10]);
$product = $this->mathService->doMultiplication([4, 2, 3]);
}
Hope this post helps you to get some insights about service container.
For detailed information about Service Containers: https://laravel.com/docs/7.x/container
Thanks for reading.
Top comments (5)
Hi Parixit,
Many thanks for the article, very beautifully explained.
However, I got an error. It reads as
ReflectionException
Class App\Services\MathService does not exist
When I die dump inside the route, I get the binding listed successfully.
MathService class
Controller function
I guess there is something wrong with namespace and class name on
MathService
class. Hope you stored MathService class insideapp/Services
folderTry to update this on
MathService
class :Many thanks.
Once I added the binding it worked.
There must have been some cache refresh issues.
Thanks for the article but WHERE to bind the services? do you just write at any place ?
You can bind the services within
register
method of your service provider.For more information: Service Providers