Backend Development

Introducing Laravel Service Container

Introducing Laravel Service Container

Laravel service container is the most important component in laravel framework which main functionality is management of laravel classes and dependencies.

 

 

Let’s assume that you want to typehint a class in Controller constructor, how laravel resolves this class in runtime. The answer laravel handles this through the service container behind the scenes, so what is the service container?

The service container is a holder which holds all system classes and whenever any service in the application requests a class it will ask the service container and in turn the service container asks the the service provider to autoload the requested class and deliver it to the service.

But there are situations that you need to tell laravel how to handle this through a concept of binding so we need to understand the different ways of binding classes.

 

Automatic Injection

When injecting classes into controller, middlewares, event listeners constructor there is no need to tell laravel explicitly about binding because laravel handles this automatically for example consider this example:

<?php

namespace App\Http\Controllers;


use App\Library\Repository\PostRepository;
use Illuminate\Http\Request;

class PostController extends Controller
{
    protected $repo;


    public function __construct(PostRepository $repo)
    {
        $this->repo = $repo;
    }

    public function index()
    {
        $this->repo->getAll();
    }

    public function store(Request $request)
    {
        $this->repo->store($request);
    }

    public function show($id)
    {
        $this->repo->show($id);
    }
}

App/Library/Repository/PostRepository.php

<?php

namespace App\Library\Repository;


class PostRepository
{
    public function getAll()
    {
        //
    }

    public function store($request)
    {
        //
    }

    public function show($id)
    {
        //
    }
}

 

Binding Interfaces To Implementations

In case when you have an interface and you need to inject it to controller constructor, you need to tell laravel about the implementation to use when you run this controller for example let’s extend the above example to use an interface:

App/Library/Repository/PostRepositoryInterface.php

<?php

namespace App\Library\Repository;


interface PostRepositoryInterface
{
    public function getAll();

    public function store($request);

    public function show($id);
}

App/Library/Repository/PostRepository.php

<?php

namespace App\Library\Repository;


class PostRepository implements PostRepositoryInterface
{
...
...

PostController

<?php

namespace App\Http\Controllers;

use App\Library\Repository\PostRepositoryInterface;
use Illuminate\Http\Request;

class PostController extends Controller
{
    protected $repo;


    public function __construct(PostRepositoryInterface $repo)
    {
        $this->repo = $repo;
    }

....
....

}

Here in the post controller i typehint the PostRepositoryInterface instead of the PostRepository, now to bind this interface to the class open AppServiceProvider and add this line in the register() method:

app/Providers/AppServiceProvider.php

<?php

namespace App\Providers;

use App\Library\Repository\PostRepository;
use App\Library\Repository\PostRepositoryInterface;
use Illuminate\Support\ServiceProvider;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }

    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind(PostRepositoryInterface::class, PostRepository::class);
    }
}

The first parameter to bind is the abstract class or interface and the second parameter is the real implementation.

 

Binding Using Closures

The bind() method can take a closure instead as the second argument and it must return the instance of the implementation you want, so let’s rewrite the previous example using closure:

app/Providers/AppServiceProvider.php

class AppServiceProvider extends ServiceProvider
{

....
....
    public function register()
    {
        $this->app->bind(PostRepositoryInterface::class, function() {
            return new PostRepository();
        });
    }
...
}

The closure approach can be helpful when dealing with classes that has parametrized constructors because it gives the flexibility by passing the arguments while binding as shown:

$this->app->bind(PostRepository::class, function() {
       return new PostRepository(20);
});



Binding Singleton

If you want to bind a class but return a singleton, which is single instance to be used across all requests the singleton() method can be used instead of the bind() method.

$this->app->singleton(PostRepository::class, function() {
       return new PostRepository(20);
});

 

 

Binding Based On Context

There are some cases when you have multiple implementations which share the same interface and you need to bind each implementation depending on context.

for example imagine we have two types of user repositories (MemberRepository and SellerRepository) both types implement UserRepositoryInterface and we have two controllers (MemberController and SellerController), and we need to inject the UserRepositoryInterface into each controller, now we need to tell laravel to bind each repository to each controller respectively.

$this->app->when(MemberController::class)
          ->needs(UserRepositoryInterface::class)
          ->give(function () {
              return new MemberRepository();
          });

$this->app->when(SellerController::class)
          ->needs(UserRepositoryInterface::class)
          ->give(function () {
              return new SellerRepository();
          });

 

Resolving with Make()

The make() method used to resolve a class instance out of the container

$video = $this->app->make(Video::class);

We can also the resolve() helper function which is equivalent to $this->app->make().

$video = resolve(Video::class);

If the class constructor contains parameters that need to be passed we can the makeWith() method which takes an array of parameters as a second argument.

$video = $this->app->makeWith(Video::class, ['url' => '']);



Creating Custom Providers

You may need to create a custom service provider use the artisan command below:

php artisan make:provider AdminProvider

The new provider will be created in app/Providers directory. Next you need to register this provider in order for the system detect it, to do this open config/app.php and add your new provider in the providers array as shown:

config/app.php

'providers' => [

        /*
         * Laravel Framework Service Providers...
         */
        Illuminate\Auth\AuthServiceProvider::class,
        Illuminate\Broadcasting\BroadcastServiceProvider::class,
        Illuminate\Bus\BusServiceProvider::class,
        
        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
        App\Providers\AdminProvider::class,

],

 

 

 

5 3 votes
Article Rating

What's your reaction?

Excited
2
Happy
5
Not Sure
2
Confused
1

You may also like

Subscribe
Notify of
guest

0 Comments
Oldest
Newest Most Voted
Inline Feedbacks
View all comments