What is service container in Laravel?

In this article we will learn what is service container in Laravel, how it works, and what are its benefits.

Service Container in Laravel

Overview

The service container is a core component in Laravel. The service container allows us to manage class dependencies and perform dependency injection in Laravel. By default, Laravel allows us to use a completely zero configuration (configuration-free) dependency injection system. Now let’s learn what dependency injection means.

Dependency Injection

Dependency Injection means that the class dependencies are injected into the class via the constructor or, in some cases, “setter” methods.

At some point in PHP class we have to include dependency whether it is controller class or service class or any other class.

To understand the dependency injection with an example, let’s assume that we have a class named Shapes and a controller named PortraitController and the Shapes class will be used in the PortraitController.

The code of the PortraitController.php file for this example would be as follows.

<?php

namespace App\Http\Controllers;

class Shapes{
    public function getShapes(){
        return ['circle', 'triangle', 'rectangle'];
    }
}

class PortraitController extends Controller
{
   public function index(){
        $shapes = new Shapes();
        $allowed_shapes = $shapes->getShapes();    
        return $allowed_shapes;
   }
}

Now if we have the following route in our routes/web.php file

<?php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\PortraitController;

Route::get('/portrait', [PortraitController::class, 'index']);

Then if we browse the URL http://<SITEURL>/portrait we will get the following output in the browser.

[
    "circle",
    "triangle",
    "rectangle"
]

In the above example we see that inside the index method, we created a new instance of the Shapes class.

Now the question is what to do if we want to use the Shape class in another part in the application. Or what if the Shapes class has an additional dependency that we need to pass. In this way it will be difficult to manage the application.

Now in the above example we will use dependency injection. The code will be as follows.

<?php

namespace App\Http\Controllers;

class Shapes{
    public function getShapes(){
        return ['circle', 'triangle', 'rectangle'];
    }
}

class PortraitController extends Controller
{
   public function index(Shapes $shapes){        
        $allowed_shapes = $shapes->getShapes();    
        return $allowed_shapes;
   }
}

Now if we browse the URL http://<SITEURL>/portrait we will get the same output in the browser.

[
    "circle",
    "triangle",
    "rectangle"
]

In a controller, we might also need a dependency throughout all of the controller methods. The code will be as follows.

<?php

namespace App\Http\Controllers;

class Shapes{
    public function getShapes(){
        return ['circle', 'triangle', 'rectangle'];
    }
}

class PortraitController extends Controller
{
    public function __construct(Shapes $shapes){
        $this->shapes = $shapes;
    }
   
    public function index(){        
        $allowed_shapes = $this->shapes->getShapes();    
        return $allowed_shapes;
   }
}

In the above example we do not create any instance of Shapes class instead here we type hint Shapes class that we want to inject in the PortraitController. We can type hint the class that we want to inject in our controller, middleware, jobs, event listeners, etc.

In this way, by simply type-hinting the Shapes class in the index method of the PortraitController, Laravel automatically resolves the Shapes class and its dependencies, tries to create a new instance of the Shapes class and pass it to the controller method.

We often type-hint dependencies on routes, controllers, event listeners, and elsewhere without manually interacting with the container. For example, we often type-hint the Illuminate\Http\Request object on our route definition to easily access the current request.

<?php
use Illuminate\Support\Facades\Route;
use Illuminate\Http\Request;
 
Route::get('/home', function (Request $request) {
    // ...
});

In this example, we do not interact with the container to write this code. Laravel automatically manages the injection of these dependencies without our manual interaction.

Now we will learn how to manually interact (binding) with the container.

Binding

The container is accessible through the $this->app property in our service provider. We can also use the app() helper function to access the container.

We can register a binding using the bind, bindif, singleton, singletonif, scoped, instance methods.

Here within bind method we can pass our class or interface name, which we want to register with closure that returns an instance of this class.

The bind method

The bind method is used to bind a class or interface into the container. In this case, a new instance will be created for every request.

Example of the bind method

The following example is an example of the bind method.

<?php
use Illuminate\Support\Facades\Route;

Route::get('/container', function () {
	app()->bind('Color', function(){
		return 'Green';
	});
});

In the above example we have registered value. If we register the class then the code will be as follows:

<?php
use Illuminate\Support\Facades\Route;

Route::get('/containers', function () {

    class Color{
        
    }

    app()->bind('Color', function(){
	    return new Color;
    });    
    
});
The bindIf method

The bindIf method is used to bind a class or interface into the container only if a binding has not already been registered for the given type.

Example of the bindIf method

The following example is an example of the bindif method.

<?php
use Illuminate\Support\Facades\Route;

Route::get('/containers', function () {

    class Color{
        
    }

    app()->bindIf('Color', function(){
	    return new Color;
    });    
    
});
The singleton method

The singleton method is used to bind a class or interface into the container that should only be resolved one time i.e the same instance will be returned for every request.

Example of the singleton method

The following example is an example of the singleton method.

<?php
use Illuminate\Support\Facades\Route;

Route::get('/containers', function () {

    class Color{
        
    }

    app()->singleton('Color', function(){
	    return new Color;
    });    
    
});
The singletonIf method

The singletonIf method is used to bind a class or interface into the container that should only be resolved one time only if a binding has not already been registered for the given type.

Example of the singletonIf method

The following example is an example of the singletonIf method.

<?php
use Illuminate\Support\Facades\Route;

Route::get('/containers', function () {

    class Color{
        
    }

    app()->singletonIf('Color', function(){
	    return new Color;
    });    
    
});
The scoped method

The scoped method is used to bind a class or interface into the container that should only be resolved one time within a given Laravel request / job lifecycle. This method is similar to the singleton method but for the scoped method, when the Laravel application starts a new life cycle, like when a Laravel queue worker processes a new job, instances registered using this will be returned.

Example of the scoped method

The following example is an example of the scoped method.

<?php
use Illuminate\Support\Facades\Route;

Route::get('/containers', function () {

    class Color{
        
    }

    app()->scoped('Color', function(){
	    return new Color;
    });    
    
});
The instance method

The instance method is used to bind an existing object instance into the container.

Example of the instance method

The following example is an example of the instance method.

<?php
use Illuminate\Support\Facades\Route;

Route::get('/containers', function () {

    class Color{
        
    }

    app()->instance('Color', function(){
	    return new Color;
    });    
    
});

I have discussed bind and singleton methods with examples in https://webtechbased.com/what-is-service-provider-in-laravel/.

Resolving

We can use the make, makeWith, and bound methods or resolve helper function to resolve a class instance from the container.

The make Method

The make Method is used to resolve a class instance from the container. The make method accepts the name of the class or interface we want to resolve.

Example of the make method

The following example is an example of the make method.

<?php
use Illuminate\Support\Facades\Route;

Route::get('/containers', function () {
	app()->bind('Color', function(){
		return 'Green';
	});
    	$resolve = app()->make('Color');
    	dd($resolve);
});

Now if we browse http://<SITEURL>/containers, we will get the output: Green.

In the above example we have registered value. If we register the class then the code will be as follows:

<?php
use Illuminate\Support\Facades\Route;

Route::get('/containers', function () {
    class Color{
        public $color;
        public function __construct($color='blue'){
            $this->color = $color;
        }
    }
    app()->bind('Color', function(){
	return new Color('green');
    });
    $resolve = app()->make('Color');
    var_dump($resolve);
});

Now if we browse http://<SITEURL>/containers, we will get the output: object(Color)#391 (1) { [“color”]=> string(5) “green” }.

Let’s take a look at what happens if we don’t use the bind method in the above code.

<?php
use Illuminate\Support\Facades\Route;

Route::get('/containers', function () {
    class Color{
        public $color;
        public function __construct($color='blue'){
            $this->color = $color;
        }
    }
    
    $resolve = app()->make('Color');
    var_dump($resolve);
});

If we browse http://<SITEURL>/containers, we will get the output: object(Color)#395 (1) { [“color”]=> string(4) “blue” }.

Here, container is taking care of the class dependency even if we do not use app()->bind().

Instead of the make method in the above example, we can also use the resolve helper function. The resolve function resolves a given class or interface name to an instance using the service container

<?php
use Illuminate\Support\Facades\Route;

Route::get('/containers', function () {
    class Color{
        public $color;
        public function __construct($color='blue'){
            $this->color = $color;
        }
    }    
    
    $resolve = resolve('Color');
    var_dump($resolve);
});

Now if we browse http://<SITEURL>/containers, we will get the output: object(Color)#395 (1) { [“color”]=> string(4) “blue” }.

The makeWith Method

If some of our class’s dependencies are not resolvable via the container, we can inject them by passing them as an associative array in this method. Here we have passed an associative array – [‘shape’ => ‘rectangle’].

Example of the makeWith method

The following example is an example of the makeWith method.

<?php
use Illuminate\Support\Facades\Route;

Route::get('/containers', function () {

    class Color{
        public $color;
        public $shape;
        
        public function __construct($color='blue'){
            $this->color = $color;
        }

        public function getShape($shape='circle'){
            $this->shape = $shape;
        }        
    }

    app()->bind('Color', function($app, $param){
        $obj = new Color('green');        
        $obj->getShape($param['shape']);    
        return $obj;
    });

    $resolve = app()->makeWith('Color', ['shape' => 'rectangle']);
    var_dump($resolve);

});

If we browse http://<SITEURL>/containers, we will get the output: object(Color)#391 (2) { [“color”]=> string(5) “green” [“shape”]=> string(9) “rectangle” }.

The bound method

The bound method is used to determine if a class or interface has been explicitly bound in the container.

Example of the bound method

The following example is an example of the bound method.

<?php
use Illuminate\Support\Facades\Route;

Route::get('/containers', function () {
    class Color{
        public $color;
        public function __construct($color='blue'){
            $this->color = $color;
        }
    }
    app()->bind('Color', function(){
	return new Color('green');
    });
    $resolve = app()->bound('Color');
    var_dump($resolve);
});

Now if we browse http://<SITEURL>/containers, we will get the output: bool(true).

In this post I discussed service container of Laravel. The URL of official documentation of service container in Laravel is: https://laravel.com/docs/10.x/container.

1 thought on “What is service container in Laravel?”

Comments are closed.