DEV Community

Saurabh Mahajan
Saurabh Mahajan

Posted on

Using Service Container to create Objects in Laravel

Recently I tweeted about using Service Container to create an Object instead of using the new Keyword and it led to a lot of questions.

In this article, we will see how to create objects using Service Container and some of its benefits.

Let's say we have a Transistor class as follows:

namespace App\Services;

class Transistor {
    public function __construct()
    {
    }
    .
    .
    .
}
Enter fullscreen mode Exit fullscreen mode

Now, if we want to use this Class in the index method of our Controller, we would do something like below:

public function index()
{
    $transistor = new Transistor();
    .
    .    
}
Enter fullscreen mode Exit fullscreen mode

However, the way Laravel works under the hood, we can simply inject our Transistor Class like below:

public function index(Transistor $transistor)
{
    //$transistor object will be available here.
    .
    .

}
Enter fullscreen mode Exit fullscreen mode

Laravel is automatically able to resolve the Transistor Class and able to inject it into our method (This is also known as Dependency Injection).

In fact, you might have been using this Laravel Feature even without knowing it. Ever wondered how Request Class gets injected into your Controller Methods.

public function store(Request $request)
{
.
.    
}
Enter fullscreen mode Exit fullscreen mode

Now, currently our Transistor Class has, what we call, Zero Configuration so Laravel is able to resolve the Class on its own. Now let us say that our Transistor Class expected a parameter via Constructor as follows:

namespace App\Services;

class Transistor {
    public $key;

    public function __construct($key)
    {
        $this->key = $key;
    }
    .
    .
    .
}
Enter fullscreen mode Exit fullscreen mode

Now since our Transistor Class expects a parameter, Laravel would not be able to resolve the Class on its own. We will need to tell Laravel how to resolve this Class. We can do so in AppServiceProvider as follows:

$this->app->bind(Transistor::class, function () {
    return new Transistor(config('settings.key'));
});
Enter fullscreen mode Exit fullscreen mode

So now every time, we ask for the Transistor Class to be injected, Laravel will look into the Service Provider and inject the Class as we have defined.

Laravel Service Container supports many other type of Binding, which are beyond the scope of this Article. You can check them in the Documentation.

Now, some of the benefits of using the Service Container to resolve your Classes are:

  1. Controller is no longer responsible for creating the Object. We just inject the Object into the Controller and Controller is only responsible for calling the methods related to Transistor Class. This removes a lot of noise and helps to keep our Controller Clean.

  2. If the implementation of our Transistor class changes in the future, let's say our Transistor Class requires another parameter to be passed via constructor, we only need to make changes at one single location. Without Service Container, we will have to make changes at every file where Transistor Class is being used.

  3. Using Service Container makes it very easy to Mock the Transistor Class using Tests.

$mock = $this->mock(Transistor::class, function (MockInterface $mock) {
    $mock->shouldReceive('fetchStations')->once();
});
Enter fullscreen mode Exit fullscreen mode

This is even more useful when our Transistor Class is hitting a Third Party API and we want to avoid hitting the API during our Tests so as to avoid Rate Limit as well as make our Tests faster.

Without using Service Container, it would not be easy to Mock our Transistor Class.

In conclusion, if you are using new to create an Object in Laravel, there is a probably a better way.

Discussion (0)