Laravel Dependency Management: From Zero to IoC

By Patrick Noonan | March 2014

Note: This is part 2 in a series on Laravel Application Development at ArcStone. Read part 1 here.

In part 1 of this series, I explained why ArcStone has shifted from using Symfony to Laravel for building data-driven applications. In this and future parts, I'll be going over a few techniques we've picked up from using Laravel.

Today's post is about how Laravel's Inversion of Control container makes it easier for developers to write SOLID applications. It's also an attempt to put the IoC container into context for developers who might not be familiar with the problem it's meant to solve. So I'll start with a brief intro to SOLID, a quick walkthrough on Dependency Injection (specifically "Constructor Injection"), and wrap up by introducing Pimple and finally Laravel's IoC container.

Disclaimer: While part 1 was written with the "non-developer" public in mind, what follows here is probably not going to make sense unless you are familiar with OOP.

Striving For SOLID Architecture

SOLID refers to a set of coding principles that allow an application to remain stable and testable even amidst "the constant rain of changing requirements[*]" from the client. SOLID stands for:

  • Single Responsibility Principle (SRP): Each entity or class has one specific job and only one reason to change.
  • Open-Closed Principle (OCP): Each entity is "open for extension" but "closed for modification". Try to use base classes and interfaces that *never* change. Extend them with concrete classes tailored to specific use-cases.
  • Liskov Substitution Principle (LSP): Any two classes or entities "of the same sub-type" should be interchangeable with one another. To take it further, any inheriting class needs to fulfill all the implicit and explicit expectations set by the base class.
  • Interface Segregation Principle (ISP): Don't make a class implement an interface if it doesn't use all the methods of that interface. E.g., if an interface has methods that not all of its clients use, split it into two and just let classes that need both implement both.
  • Dependency Inversion Principle (DIP): Don't specify concrete class implementations when defining class dependencies - specify interfaces.

Laravel is designed to help you build SOLID applications.

Laravel is designed to help you build SOLID applications. That said, take the above with a grain of salt, preferably one large enough to inscribe "do as I say, not as I do" on the surface. SOLID architecture is an ideal which is rarely embodied to perfection. Not only that, sometimes it's a good idea to break the rules. However, if you strive after the SOLID principles, your code will be better and your clients will be happier!

Dependency Management

Effectively managing class dependencies is crucial for writing SOLID code. A "dependency" is any external object your class requires in order to do its job.

So let's skip ahead to the "D" in SOLID: the Dependency Inversion Principle (DIP), which Robert "Uncle Bob" Martin refers to as the "primary mechanism" of fulfilling the goals of SOLID architecture. Uncle Bob summed up the DIP in two succinct sentences:

"Depend upon Abstractions. Do not depend upon concretions." [*]
-- Robert "Uncle Bob" Martin

Let's use a simple example to illustrate what the heck that means.

How to Change a Tire With the DIP

Say we have a class "Bike" which depends on another class "Tire". Our Bike class could use the Tire class like this:

<code>
    Class Bike {

        protected $tire;

        public function __construct()
        {
            $this-&gt;tire = new Tire();
        }
    }

</code>

There is a problem here - because we're using the "new" keyword, we can not easily run unit tests on the Bike class. We want our application to be fully testable (that topic can't fit into this post but trust me it's a very good thing), so we'll use a form of dependency injection called "constructor injection." That means we'll "inject" the Tire dependency through the Bike constructor:

<code>
    Class Bike {

        protected $tire;

        public function __construct(Tire $tire)
        {
            $this-&gt;tire = $tire;
        }
    }

</code>

Now, when we go to initialize the Bike class in our application, it will look something like this:

<code>
    $tire = new Tire();
    $bike = new Bike($tire);

</code>

But wait a second! Not all tires are created equal. When winter sweeps in, you can't navigate that ice on slick tires. And no way do you want studs slowing you down in the summer. The bike needs a specific type of tire - SummerTire or WinterTire.

Unfortunately, the way the tire dependency is set up now, that means we'd have to change our code in two places every time we wanted to change the tire dependency - once when we initialize the Tire itself, and again in the Bike class's type-hinted constructor.

Furthermore, if your SummerTire and WinterTire don't both implement the same interface, you'll likely need to change code in the Bike class everywhere the Tire is used. Yuck. Let's proceed under the assumption that both SummerTire and WinterTire extend an AbstractTire class, which in turn implements TireInterface. That way we'll know our Tire classes are DRY and that they adhere to the LSP - we can easily swap one in for the other.

Now all we have to do is change the Bike constructor's type-hint so that it calls for an instance of TireInterface instead of a specific type of tire:

<code>
    Class Bike {

        protected $tire;

        public function __construct(TireInterface $tire)
        {
            $this-&gt;tire = $tire;
        }
    }

</code>

This is exactly what was meant above by "Depend upon abstractions." Rather than calling for a specific, concrete class, the Bike class can now work just fine with any class that implements TireInterface. Does your Bike class suddenly need to use a FatTire? Not a problem!

Inversion of Control / Dependency Injection Containers

Ok, so now when we go to instantiate the Bike, our code will look something more like this:

<code>
    $tire = new SummerTire();
    $bike = new Bike($tire);

</code>

Pretend that our application is meant to represent Bob and Sue going on a bike ride. Bob and Sue each need their own bike, and the bikes both need their own tires. If you're not sure why that is, just imagine Bob riding over a tack and it causing both his and Sue's tire to go flat. In PHP 5, objects are passed by reference, not by value. So here's roughly how we'd need to set up those dependencies:

<code>
    $bob_tire = new SummerTire();
    $bob_bike = new Bike($bob_tire);
    $bob = new Person($bob_bike);

    $sue_tire = new SummerTire();
    $sue_bike = new Bike($sue_tire);
    $sue = new Person($sue_bike);

</code>

It's already starting to become a bit of a pain, isn't it? That's a lot of bikes and tires to keep track of. And it's not even DRY - when winter comes we'll need to change SummerTire to WinterTire in multiple places. It would be a lot easier to write an anonymous function that can deliver a fully-formed bike for Bob, Sue, and any of their friends:

<code>
    $bike_factory = function() {
        $tire = new SummerTire();
        return new Bike($tire);
    }

    $bob = new Person($bike_factory);
    $sue = new Person($bike_factory);
    $sam = new Person($bike_factory);
    ...
</code>

See how much easier it is now to create new people? What's more, when winter comes we only need to change SummerTire in one place to give the right tire to all the people in the application!

As the application gets more complex, you might find yourself writing many of those factory methods and wishing you had one tidy place to keep track of them all. At that point, you'd be looking for something called a "dependency injection (DI) container". A DI container is so-named because it contains the dependencies you wish to inject.

You could use a simple DI container like Pimple. Pimple is tiny, beautiful in its simplicity and *very* fun to use. At ArcStone we've actually started using it to manage dependencies for more complicated functionality in WordPress. It works like this:

<code>
    require_once 'path/to/Pimple.php';
    $container = new Pimple();

    $container['tire'] = $container-&gt;factory(function($c) {
        return new SummerTire();
    });

    $container['bike'] = $container-&gt;factory(function($c) {
        return new Bike($c['tire'])
    });

    $bob = new Person($container['bike']);

</code>

The Laravel IoC Container

In a Laravel application, you don't need to use Pimple, because at its core the Laravel framework is itself a dependency injection container. (Take a look at the Application class that gets initialized in start/bootstrap.php.) Only it's quite a bit more powerful than Pimple.

Laravel's Inversion of Control (IoC) container uses Reflection to automatically resolve any dependencies your class might have - and all the dependencies' dependencies. Furthermore it lets you "bind" an interface to a specific concrete implementation. That way, when your class asks for an interface in its constructor, Laravel knows what to give it. It's this easy:

<code>
    App::bind('TireInterface', 'SummerTire');

</code>

So you might be wondering, where then do you put all that "$bob = new Person($container['bike'])" code?

In some cases, the answer is "nowhere", such as when passing dependencies to the constructor of a Controller. Laravel's IoC Container will automatically resolve all of a Controller's dependencies for you (assuming your interfaces are all properly bound to concrete classes). Also, the IoC Container can resolve any concrete dependencies (i.e., any dependency that is a concrete class, not an interface or abstract class) without any extra configuration.

In other cases, the answer could be right inside the call to App::bind(). Instead of passing a class name as the second parameter, you can pass a closure. Let's pretend we've created a BikeInterface, and we want the App to instantiate it using the Tire we've bound to TireInterface above:

<code>
    App::bind('BikeInterface', function($app) {
        $tire = $app-&gt;make('TireInterface');
        return new FixedGear($tire);
    });

</code>

This way, any class that asks for a BikeInterface will receive a FixedGear!

The final place to register bindings is inside of a Service Provider. But I'm saving that for the next post.

In Part 3 of this series, I'll go a little more in-depth into creating services and using service providers.

Follow Patrick on Twitter @devopat

Topics: Design and Technology