Vaidikalaya

Service Container

In this chapter, we will discuss the most important topic of Laravel which is the service container. Service Container is probably a very confusing topic for beginners in Laravel.

So first, We understand the definition:

A service container is a powerful tool for managing class dependencies and performing dependency injection. Its sole responsibility is to manage the dependencies in your Laravel project.

So let's discuss it with practical:


First, create a Laravel app for practice:
laravel new laravel-service-container

Go to the app directory
cd laravel-service-container

Create service (App\Services\CalculationServices)

Create a Services folder in the App directory and then create one Service (CalculationServices) in this Services directory.

This CalculationServices class provides addition and multiplication services for given data.

<?php
namespace App\Services;

class CalculationServices
{
    public function addition($numbers)
    {
        return array_sum($numbers);
    }

    public function multiplication($numbers)
    {
        return array_product($numbers);
    }
}



Create a controller and set routes.

php artisan make:controller ServicesController
<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\ServicesController;

Route::get('/', function () {
    return view('welcome');
});

Route::get('/test-service',[ServicesController::class,'index']);


Now go to ServicesController and test the service container concept

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Services\CalculationServices;

class ServicesController extends Controller
{
    public function index(){
        $obj=new CalculationServices();
        $sum=$obj->addition([1,2,3,4,5]);
        $mul=$obj->multiplication([1,2,3,4,5]);
        dd($sum,$mul);
    }
}

In the above index method first, you create an instance(object) of CalculationServices Class and then call the addition and multiplication methods of CalculationServices Class and it will return the sum and multiplication of given data. So this is the normal method to use Services.

But in Laravel you can use this like:

<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Services\CalculationServices;

class ServicesController extends Controller
{
    public function index(CalculationServices $obj){
        $sum=$obj->addition([1,2,3,4,5]);
        $mul=$obj->multiplication([1,2,3,4,5]);
        dd($sum,$mul);
    }
}

In the above index method, we pass the CalculationServices Class as a type-hint, and It automatically returns us the instance of the CalculationServices Class with the help of this instance we can call the addition and multiplication method and get the same result. So the process of returning an instance of a class is done by the service container. So the service container is the internal mechanism that allows us to do certain class injections.

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 the service container as it's a container that holds all class dependencies.


There are two ways (automatic and manually) to resolve the dependency in Laravel:

Automatic:

When we pass the Services class name in the constructor or method (type-hinting process) then It will automatically resolve the dependency and is called automatic dependency injection.

public function index(CalculationServices $obj){
        $sum=$obj->addition([1,2,3,4,5]);
        $mul=$obj->multiplication([1,2,3,4,5]);
        dd($sum,$mul);
 }


Manually:

To resolve service manually in the application can use either of two ways:

Using resolve() method:

public function index(){
        $obj = resolve(CalculationServices::class);
        $sum=$obj->addition([1,2,3,4,5]);
        $mul=$obj->multiplication([1,2,3,4,5]);
        dd($sum,$mul);
 }

Using app()->make() method:
public function index(){
        $obj = app()->make(CalculationServices::class);
        $sum=$obj->addition([1,2,3,4,5]);
        $mul=$obj->multiplication([1,2,3,4,5]);
        dd($sum,$mul);
}

Now I think you understand the concept of service container and in the next chapter, we will discuss Service providers.


Problem with Auto Resolving

Now we create another service invoice service and in this service, we pass one random invoice number with the help of the constructor.

namespace App\Services;

class InvoiceServices
{
    public $invoiceNumber;
    public function __construct($invoiceNumber){
        $this->invoiceNumber=$invoiceNumber;
    }

    public function invoice()
    {
        return [
            'Invoice Number'=>$this->invoiceNumber,
            'Pay Amount'=>11679
        ];
    }
}

And call this service from a controller.

namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Services\InvoiceServices;

class ServicesController extends Controller
{
    public function index(InvoiceServices $invoice){
        dd($invoice->invoice());
    }
}

Now it gives the error 


This error occurs because we define a constructor in the InvoiceServices class and this constructor needs an argument. The Laravel container has no clue what to pass in here, and this is why auto-resolving is not working anymore. This is when we need to tell Laravel explicitly how to create our export instance and its dependencies. And the best place to write this code is inside a service provider.

Note: Service providers are the central place to configure your application. We use them to register services or components of Laravel or ourselves.

So Now, We register this service within the register() method of the app\Providers\AppServiceProvider.php file. There are many ways to register services in service providers but here we use the bind method for registering. In the ServiceProvider chapter, you will learn a different method of registering.

<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Services\InvoiceServices;

class AppServiceProvider extends ServiceProvider
{

    public function register(): void
    {
        $this->app->bind(InvoiceServices::class, function() {
$randomNumber=rand();
          return new InvoiceServices($randomNumber);
        });
    }

    public function boot(): void
    {
        //
    }
}



Now run your application and It will work perfectly.