Dependency Injection with Laravel’s IoC

Share this article

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.

Key Takeaways

  • Dependency Injection (DI) is a design pattern that allows for the decoupling of hard-coded dependencies, making code more flexible, reusable, and easier to test. In Laravel’s IoC (Inversion of Control), this is achieved by passing the needed dependencies into the object externally rather than having the objects create a dependency or ask a factory object to make one for them.
  • Laravel’s IoC container is a powerful tool for managing class dependencies. It controls how different objects are resolved and created. When a class has dependencies, the container automatically injects them when the class is instantiated through a process called “auto-wiring”.
  • A service can be bound to Laravel’s IoC container using the bind method. This method accepts two arguments: the class or interface name that will be used when resolving the service, and a closure that returns an instance of the class. The closure will receive the container instance, allowing you to resolve any other dependencies needed to instantiate the class.
  • Laravel’s IoC container also allows for interface binding. This allows you to bind an interface to a given implementation. Then, whenever the interface is requested, the container will inject the bound implementation. This further enhances the flexibility and reusability of the code.

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!

Frequently Asked Questions on Dependency Injection in Laravel’s IOC

What is the main purpose of Dependency Injection in Laravel’s IOC?

Dependency Injection in Laravel’s IOC (Inversion of Control) is a design pattern that allows for the decoupling of hard-coded dependencies. This means that instead of having your objects creating a dependency or asking a factory object to make one for them, you pass the needed dependencies into the object externally. This makes your code more flexible, reusable, and easier to test since you can control the dependencies from outside the class.

How does Laravel’s IOC container work?

Laravel’s IOC container is a powerful tool for managing class dependencies. It controls how different objects are resolved and created. When a class has dependencies, the container automatically injects them when the class is instantiated. This is done through a process called “auto-wiring,” where the container inspects the class to determine the dependencies automatically.

How can I bind a service to the Laravel’s IOC container?

To bind a service to Laravel’s IOC container, you can use the bind method. This method accepts two arguments: the class or interface name that will be used when resolving the service, and a closure that returns an instance of the class. The closure will receive the container instance, allowing you to resolve any other dependencies needed to instantiate the class.

What is the difference between bind and singleton in Laravel’s IOC container?

The difference between bind and singleton in Laravel’s IOC container lies in how instances are managed. When you bind a service, a new instance of the service will be created each time you resolve it. On the other hand, when you use singleton, the same instance will be returned every time the service is resolved.

How can I resolve a service from Laravel’s IOC container?

To resolve a service from Laravel’s IOC container, you can use the make method. This method accepts the name of the service to resolve as its argument. If the service has been bound to the container, it will return an instance of the service, with all its dependencies automatically injected.

How does Dependency Injection improve testing in Laravel?

Dependency Injection improves testing in Laravel by making your code more flexible and decoupled. This means you can easily swap out dependencies with mock objects during testing. This makes it easier to isolate the code under test and to control the test environment.

Can I use interface binding in Laravel’s IOC container?

Yes, you can use interface binding in Laravel’s IOC container. This allows you to bind an interface to a given implementation. Then, whenever the interface is requested, the container will inject the bound implementation.

How does Laravel’s IOC container handle automatic resolution?

Laravel’s IOC container handles automatic resolution by using reflection to inspect the dependencies of a class. When you attempt to resolve a class, the container will automatically build and inject all the dependencies the class needs.

What is a service provider in Laravel’s IOC container?

A service provider in Laravel’s IOC container is a way to group related IoC registrations in a single location. They are the central place to configure your application. Every Laravel application includes a number of service providers out of the box for core services.

How can I register a service provider in Laravel’s IOC container?

To register a service provider in Laravel’s IOC container, you can add it to the providers array in the config/app.php configuration file. Once the service provider is registered, it will be bootstrapped by Laravel when the application is bootstrapped.

Younes RafieYounes Rafie
View Author

Younes is a freelance web developer, technical writer and a blogger from Morocco. He's worked with JAVA, J2EE, JavaScript, etc., but his language of choice is PHP. You can learn more about him on his website.

dependency injectiondiFrameworksinversion of controlioclaraveloopphpPHP
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week