cult3

Exploring Laravel's IoC container

Mar 24, 2014

Table of contents:

  1. What is an IoC Container?
  2. Laravel’s IoC Container
  3. Using Laravel’s IoC Container
  4. Service Providers
  5. Automatic resolution
  6. Conclusion

Laravel 4 is a modern PHP framework that is built upon the good principles of high quality application design. This makes it easy to extend the core framework, swap out components for alternative implementations and trivially easy to create tests, mock dependencies and start using processes such as continuous deployment.

A big part of the structure that allows Laravel to give you all of these benefits is the powerful IoC container that is at the heart of the framework.

As a developer who builds applications using Laravel, the IoC container is an integral part of your work whether you know it or not.

However I feel that a lot of new developers either don’t understand the magic under the hood or are intimidated by it because they don’t understand what’s really going on.

In this tutorial I’m going to take a deep dive into the heart of the Laravel framework to show you what an IoC really is, how it works and how you can make the most of it in your applications.

What is an IoC Container?

Before we start to look at how the IoC container works, first its important to understand what exactly it is and what is the purpose of it.

IoC stands for Inversion of Control. Inversion of Control is kind of like Dependency Injection. Dependency Injection is where you pass the dependencies of an object through the constructor instead of allowing the object to create them itself.

For example, say we had a UserController:

class UserController
{
    public function index()
    {
        $repository = new UserRepository();
        return $repository->all();
    }
}

The User Controller has a dependency of the User Repository because it needs the repository in order to return the users from the database. However the code above is bad because the User Controller should not be able to instantiate a new UserRepository. Instead it should be passed the instance through the constructor:

public function __construct(UserRepository $repository)
{
    $this->repository = $repository;
}

This allows us to mock this repository so when we run tests we don’t have to actually hit the database or we can switch to a different implementation really easily.

In a typical application, almost every object will likely have dependencies on one or more other objects. If an object requires multiple dependencies and those dependencies require additional dependencies, you end up in a situation where you have a lot of repeated code just to set things up.

An IoC container is basically just a “box” to resolve specific objects out of. So when you want an instance of an object that has dependencies, the IoC container will ensure that the objects are created for you so you don’t have to manually set it up.

An IoC container also allows you to bind an implementation to an interface. This means you can easily switch out an implementation if your application requires it to be different without changing your code outside of the container.

That’s basically all an IoC container is. It’s just away of storing how an object should be resolved and then allowing you to resolve it whenever you need an instance of it.

Laravel’s IoC Container

When a request hits your application in a browser it enters through the index.php in your public directory and then loads the start.php under the bootstrap directory.

The first line of start.php is:

$app = new Illuminate\Foundation\Application();

This class is the IoC container and is used throughout the rest of the booting process whenever a page is loaded in your application.

You can see this file in the Laravel source code here. This Application class also extends a Container class that will be of interest for this tutorial. You can see that class here.

The Application class looks pretty intimidating when you first look at it because there are a lot of methods that seem to refer to a lot of different things. If you have a brief read through those two classes you will likely see a lot of methods that you have already been using up to this point.

Using Laravel’s IoC Container

The IoC container is available to you in your application through the App facade (which ironically resolves itself out of the IoC container).

For example, if you wanted to bind a repository to an interface you would use the bind method on the facade:

App::bind("Cribbb\Repositories\User\UserRepository", function ($app) {
    return new EloquentUserRepository(new User());
});

Now whenever you need an instance of the UserRepository you can get it by using the make method:

$repository = App::make("Cribbb\Repositories\User\UserRepository");

As you can see from the code above, because I’m using the IoC container I don’t have to specify what dependencies are required and I could easily bind a different repository implementation without having to change any of the code that I used to resolve an instance from the container.

If you require that the object you want to bind into the container should be reused throughout the request you can define it using the singleton method:

App::singleton("Cribbb\Scopes\Scope", function () {
    return new UserScope();
});

And if you already have an instance of the object, you can add it using the instance:

$social = new SocialService();

App::instance("social", $social);

Service Providers

If you’ve been following along with this series, many of the methods above will be familiar to you from the Service Providers that I have implemented to bootstrap the requirements of Cribbb.

Those are the exact same methods that I’ve been using in my Service Providers. When you use a Service Provider you are given an instance of the IoC container as the $this->app property of the class.

Using any of the above methods are a perfectly acceptable way of binding your classes into the IoC container. To do that you would need to create a new file for your bindings and ensure that the file is included early on in the application’s request lifecycle.

Service Providers are a ready made solution to bootstrapping your bindings, so for the majority of projects it makes sense to use a Service Provider rather than binding using the App facade.

A typical example of a Service Provider to bind a Repository would be like this:

use Illuminate\Support\ServiceProvider;

class RepositoriesServiceProvider extends ServiceProvider
{
    public function register()
    {
        $this->app->bind(
            "Cribbb\Respositories\User\UserRepository",
            function () {
                return new EloquentUserRepository(new User());
            }
        );
    }
}

As you can see above, I’m calling the bind method on the $this->app property in exactly the same way as above.

When you add your own Service Providers to the Service Providers load list the register method will automatically be run when your application boots up.

Automatic resolution

If you’ve been exploring Laravel yourself, you might have noticed that you often don’t need to set up a Service Provider in order to inject an instance of an object as a dependency.

The most obvious example of this is when you inject an object into a Controller:

public function __construct(UserRepository $repository)
{
    $this->repository = $repository;
}

The code above will automatically work even though I’ve never actually injected the UserRepository. If I tried to do this in vanilla PHP outside of Laravel it wouldn’t work.

This is because the Laravel IoC container is clever enough to know to inject the right instance when you provide the type hint. In the code above I’ve specified that the $repository should be an instance of UserRepository. The IoC container will use that type hint to find the right object and inject it into the controller automatically.

This means you get the flexibility and testability of dependency injection without having to set it all up yourself.

Conclusion

And that is basically all you really need to know about Laravel’s IoC container. The container is basically just a box that stores how an object should be created whenever it is requested. This allows you to specify how an object should be created once, and have that same logic available to you throughout the application.

The IoC container can also bind an implementation to an interface. This means you can easily switch out certain components without having to change your code, or you can structure specific components of your application to ensure that you can easily substitute one for another if you decide to change something in the future. For example, if you wanted to switch from SendGrid to MailGun you could do this very easily without having to find every instance in your application.

The IoC container is at the heart of the Laravel framework, but is certainly not very complicated when you understand how it works.

The theory of Inversion of Control is applicable in many different frameworks and programming languages outside of Laravel and PHP. For example, Pimple is a very popular standalone IoC container that is also written in PHP. You certainly do not have to use a full framework to enjoy the benefits of Inversion of Control and you will likely come across the same principles many times in your career as a developer.

This is a series of posts on building an entire Open Source application called Cribbb. All of the tutorials will be free to web, and all of the code is available on GitHub.

Philip Brown

@philipbrown

© Yellow Flag Ltd 2024.