Dependency Injection with Laravel’s IoC

Younes Rafie
Share

As developers, we are always trying to find new ways to write well designed and clean code by adopting new styles, using design patterns, and trying new robust frameworks. In this article we will explore the dependency injection design pattern through Laravel’s IoC component and see how it can improve our design.

Dependency Injection

Dependency injection is a term coined By Martin Fowler, and it’s the act of injecting components into your application. Like Ward Cunningham said:

Dependency Injection is a key element of agile architecture.

let’s see an example:

class UserProvider{
    protected $connection;
    
    public function __construct(){
        $this->connection = new Connection;
    }
    
    public function retrieveByCredentials( array $credentials ){
        $user = $this->connection
                        ->where( 'email', $credentials['email'])
                        ->where( 'password', $credentials['password'])
                        ->first();
                        
        return $user;
    }
}

If you want to test or maintain this class, you would have to access a real database and do some queries. To avoid having to do that and to decouple the class from the rest, you have one of three options to inject the Connection class without actually using it directly.

When injecting components into your class you can use one of the three options:

Constructor Injection

class UserProvider{
    protected $connection;
    
    public function __construct( Connection $con ){
        $this->connection = $con;
    }
    ...

Setter Injection

Similarly we can inject our dependency using a setter method:

class UserProvider{
    protected $connection;
    public function __construct(){
        ...
    }
    
    public function setConnection( Connection $con ){
        $this->connection = $con;
    }
    ...

Interface Injection

interface ConnectionInjector{
    public function injectConnection( Connection $con );
}

class UserProvider implements ConnectionInjector{
    protected $connection;
    
    public function __construct(){
        ...
    }
    
    public function injectConnection( Connection $con ){
        $this->connection = $con;
    }
}

When a class implements our interface, we define the injectConnection method to resolve the dependency.

Advantages

Now, when testing our class we can mock the dependency class and pass it as a parameter. Each class must be focused on a particular task and should not be concerned with resolving their dependencies. That way, you will have a better focused and maintainable application.

If you’d like to know more about DI, Alejandro Gervassio covered it extensively and professionally in this series, so be sure to give these articles a read. Now, what about IoC? Ioc (Inversion of control) is not necessary to use dependency injection, but it can help you to manage your dependencies effectively.

Inversion Of Control

Ioc is a simple component that makes resolving dependencies more convenient. You describe your objects to the container, and every time you resolve a class, the dependencies are injected automatically.

Laravel Ioc

Laravel Ioc is somehow special with its way of resolving dependencies, when you ask for an object:

We will use a simple example that we will improve during this article.
The SimpleAuth class has a dependency of FileSessionStorage, so our code might look like this:

class FileSessionStorage{
  public function __construct(){
    session_start();
  }
  
  public function get( $key ){
    return $_SESSION[$key];
  }

  public function set( $key, $value ){
    $_SESSION[$key] = $value;
  }
}

class SimpleAuth{
  protected $session;

  public function __construct(){
    $this->session = new FileSessionStorage;
  }
}

//creating a SimpleAuth
$auth = new SimpleAuth();

This is the classic way of doing it, let’s start by using the constructor injection.

class SimpleAuth{
  protected $session;

  public function __construct( FileSessionStorage $session ){
    $this->session = $session;
  }
}

Now we create our object:

$auth = new SimpleAuth( new FileSessionStorage() );

Now I want to use Laravel Ioc to manage all of that.

Because the Application class extends the Container class, you can always access the container via the App facade.

App::bind( 'FileSessionStorage', function(){
    return new FileSessionStorage;
});

The first parameter for the bind method is a unique id to bind to the container, the second is a callback function to be executed each time we resolve the FileSessionStorage class, we can also pass a string representing class name as we will see next.

Note: if you inspect Laravel packages you will that sometimes bindings are grouped like ( view, view.finder..).

Let’s say that maybe we want to switch our session storage to MySql, our class should be similar to:

class MysqlSessionStorage{

  public function __construct(){
    //...
  }

  public function get($key){
    // do something
  }

  public function set( $key, $value ){
    // do something
  }
}

Now that we have changed the dependency, we need to change the SimpleAuth constructor and bind a new object to the container!

High level modules should not depend upon low level modules. Both
should depend upon abstractions.
Abstractions should not depend upon details. Details should depend
upon abstractions.

Robert C. Martin

Our SimpleAuth class should not be concerned about how our storage is done, instead it should focus more on just consuming the service.

So we can abstract our storage implementation:

interface SessionStorage{
  public function get( $key );
  public function set( $key, $value );
}

This way we can just implement and ask for an instance of the SessionStorage interface:

class FileSessionStorage implements SessionStorage{

  public function __construct(){
    //...
  }

  public function get( $key ){
    //...
  }

  public function set( $key, $value ){
    //...
  }
}

class MysqlSessionStorage implements SessionStorage{

  public function __construct(){
    //...
  }

  public function get( $key ){
    //...
  }

  public function set( $key, $value ){
    //...
  }
}

class SimpleAuth{

  protected $session;

  public function __construct( SessionStorage $session ){
    $this->session = $session;
  }

}

If we try to resolve the SimpleAuth class through the container using App::make('SimpleAuth'), the container will throw a BindingResolutionException, after trying to resolve the class from the bindings, falling back to the reflection method and resolving all the dependencies.

Uncaught exception 'Illuminate\Container\BindingResolutionException' with message 'Target [SessionStorage] is not instantiable.'

The container is trying to instantiate the interface. We can fix that by creating a specific binding for our interface.

App:bind( 'SessionStorage', 'MysqlSessionStorage' );

Now every time we try to resolve the interface through the container, we will get a MysqlSessionStorage instance. If we want to switch our storage service we can just update the bindings.

Note: if you want to see if a class is bound to the container you can use App::bound('ClassName') or use the App::bindIf('ClassName') to register a binding if it hasn’t already been registered.

Laravel Ioc also offers App::singleton('ClassName', 'resolver') for shared bindings.
You can also use App::instance('ClassName', 'instance') to create a shared instance.
If the container can’t resolve the dependency it will throw a ReflectionException, but we can use the App::resolvingAny(Closure) to resolve any given type or as a form of fallback.

Note: if you register a resolver for a given type, the resolvingAny method will be also called, but the value from the bind method is returned.

Final Tips

  • Where to store bindings:
    If you have just a small application you can use your global/start.php, but if your project is getting larger you must use a service provider.
  • Testing:
    When you’re just testing you need to consider using php artisan tinker, it’s very powerful, and can increase your Laravel testing workflow.
  • Reflection API:
    The PHP Reflection API is very powerful and if you want to understand the Laravel Ioc you need to get familiar with the Reflection API, be sure to check this tutorial for more information.

Conclusion

As always, the best way to learn about something is inspecting the source code. Laravel Ioc is just one file and shouldn’t take you long to go through all the functions. Would you like to know more about Laravel IoC or IoC in general? Let us know!