Laravel Service Container
Tags: laravel, architecture
Published: 2020-07-16 08:28:13

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.

<code>App\Services\MathService.php</code>
```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 <code>\App\Services\MathService</code> class <code>doAddition</code> which calculates the sum of numbers provided in array using array_sum function and <code>doMultiplication</code> which multiplies all the numbers provided as an array using array_product function. 

To use services provided by <code>\App\Services\MathService</code> 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
<?php

App::bind('MathService',\App\Services\MathService::class);
```
By doing this, we are telling laravel to store <code>\App\Services\MathService</code> 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 <code>\App\Services\MathService</code> 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
<?php

$mathService = app()->make('MathService');

// or

$mathService = resolve('MathService');
```
The service is resolved now. we can call <code>doAddition</code> & <code>doMultiplication</code> methods from our application.
```php
<?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
<?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 <code>register</code> 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**

<code>App\Services\MathServiceInterface.php</code>
```php
<?php

namespace App\Services;
Interface MathServiceInterface
{
    public function doAddition(array $numbers);

    public function doMultiplication(array $numbers);
}
```
**MathService** which implements **MathServiceInterface**

<code>App\Services\MathService.php</code>
```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**

<code>App\Services\CustomMathService.php</code>
```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 <code>MathService</code> & <code>CustomMathService</code> with a different implementation of <code>doAddition</code> & <code>doMultiplication</code> methods.

Now instead of binding these classes on container lets bind <code>MathServiceInterface</code> with the container. 
```php
<?php

App::bind('\App\Services\MathServiceInterface', function () {
    return new \App\Services\MathService();
});
```
Here, when <code>MathServiceInterface</code> will be resolved, Laravel gives us an instance of <code>MathService</code> class. And by calling methods using this instance will get results using  <code>MathService</code> class implementation.  

**Controller:**
```php
<?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 <code>CustomMathService</code> of <code>MathServicesInterface</code> then just need to change in binding.
```php
<?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 <code>CustomMathService</code> 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
<?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 <code>resolve()</code> method. We can just type-hint dependency on a constructor.

**Controller:**
```php
<?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.