What is the IoC Container in Laravel?

Click to share! ⬇️

Laravel IoC Container

So you’re learning Laravel you say? Well what is this IoC Container you speak of?! For a lot of us PHP programmers, we may not be familiar with this concept, but thanks to Laravel, proper software engineering is being brought into the PHP world. By getting a handle on what and how the IoC Container works in Laravel, we’ll have a better understanding of how Laravel provides the magic that it does when coding. Let’s take a closer look!


Inversion of Control

First up, just a little bit about Inversion of Control, in the generic sense.

Wikepedia provides us this explanation:

In software engineering, inversion of control (IoC) is a programming technique, expressed here in terms of object-oriented programming, in which object coupling is bound at run time by an assembler object and is typically not known at compile time using static analysis.

In traditional programming, the flow of the business logic is determined by objects that are statically assigned to one another. With inversion of control, the flow depends on the object graph that is instantiated by the assembler. Such a dynamic flow is made possible by object interactions being defined through abstractions. The binding process is achieved through dependency injection, although some argue that the use of a service locator also provides inversion of control.

What chu talkin bout Willis? 🙂 Well, that is a lot of verbiage to basically say, with good software engineering, we are going to make your life easier. How so you say? Let’s examine.

A container is just that, something that can hold things. In Laravel, the $app object is your container. So it stands to reason that this $app you speak of contains things, and if fact, it does. If you were to run this code


Route::get('/', function()
{
	$app = app();
	print_r($app);

});

You would see an ungodly amount of information dumped to the browser. The main thing to notice however is that it is an Object of IlluminateFoundationApplication. The illuminate namespace contains all components of the Laravel framework. It’s an apropos name in as much as Laravel is bringing software design features from other enterprise languages into the world of PHP. Now, the Application class extends the Container class. It is from the Container class that wizardry emanates. Think of learning Laravel, not so much as an exercise in coding chops, but rather an expansion of your mind. It provides a new and different way of looking at problems and how to solve them with ingenious organization. In fact, Laravel challenges the very definition of a framework. At least that is how I think of it.

The Container class implements the ArrayAccess interface. What this means is that you have flexibility when accessing your data. You can use what feels comfortable to you. For example you could use both of the following syntax versions with the same result:


$foo->bar = "baz";
$foo['bar'] = "baz";

Nifty!

The container is comprised of many Layers, or Components, such as Routing, Validation, Authentication, Cookie, Database, Encryption, Session, Filesystem, and many many more. The container neatly packages all of these components together to work in concert to provide you with PHP super powers!

Dependency Injection

Dependency Injection is great but it can become a pain to implement if you have to continuously instantiate and pass in dependencies but with the IoC container, we don’t have to worry about it.

So how does this all work in code? Well let’s whip up a few classes and see it in action. We’ll add a Car, Tire, and Engine class. We know the App class extends the Container, so we have access to it’s methods. Let’s go ahead and bind our Car class to the Container.


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

Route::get('/', function()
{
	dd(App::make('Car'));  //  resolve it
});

//  object(Car)[146]
Excellent – When we run `make` by way of `App`, we can see our `Car` object comes into being.

Let’s consider that a Car depends on the Tire and Engine classes. How can we do this?


class Car {
	protected $tire;
	protected $engine;
	public function __construct(Tire $tire, Engine $engine) {
		$this->tire = $tire;
		$this->engine = $engine;	
	}
}

class Tire {}

class Engine {}

App::bind('Car', function()
{
	return new Car(new Tire, new Engine);
});


Route::get('/', function()
{
	dd(App::make('Car'));  //  resolve it
});
// object(Car)[145]
//  protected 'tire' => 
//    object(Tire)[143]
//  protected 'engine' => 
//    object(Engine)[150]
So here we bind the `Car` to the `App` Container and since we specified it’s dependencies by passing in a `Tire` and `Engine` to it, we’ll get a shiny new object with all needed dependencies having been passed in.

Ok let’s change it up a little:


class Car {
	protected $tire;
	protected $engine;
	public function __construct(Tire $tire, Engine $engine) {
		$this->tire = $tire;
		$this->engine = $engine;	
	}
}

class Tire {}

class Engine {}

Route::get('/', function()
{
	dd(App::make('Car'));  //  resolve it
});

//  Notice our binding is gone.  What happens now?

// object(Car)[152]
//  protected 'tire' => 
//    object(Tire)[153]
//  protected 'engine' => 
//    object(Engine)[154]
Notice our binding is gone. Not only is the binding gone, but the dependencies that we had manually created and passed in are gone. What happens now? Yes my man, you still get the object you need, Dependencies included! What the?!

In all of it’s goodness, Laravel will take the following actions for you:

  • Did the user specifically create a binding for Car? (use it if so)
  • What if the programmer didn’t specify one? Then reflect into Car to determine any dependencies.
  • Resolve any dependencies needed by Car (Tire and Engine).
  • Create the new instance for you, including any dependencies, Free of charge!

Wow. I wish I would have thought of that 🙂 If I had a glyphicon for a jaw drop, I would add it. In light of this we’ll go for a big thumbs up Thanks to Taylor Otwell and his team of superstars such as Dayle Rees, Shawn McCool, Jeffrey Way, Jason Lewis, Ben Corlett, Franz Liedke, Dries Vints, Mior Muhammad Zaki, and Phil Sturgeon for making all this cool stuff!

Dependencies of Dependencies

So let’s take this concept just a little further. We know our Car has dependencies of a Tire and an Engine. What happens if the Tire or Engine have their own dependencies? Let’s say our Tire has a Bridgestone dependency, and the Engine has a Turbo dependency. We still just want to make a Car, will our IoC take care of all of this for us?

Let’s see!


class Car {
	protected $tire;
	protected $engine;
	public function __construct(Tire $tire, Engine $engine) {
		$this->tire = $tire;
		$this->engine = $engine;	
	}
}

class Tire {
	protected $bridgestone;
	public function __construct(Bridgestone $bridgestone) {
		$this->bridgestone = $bridgestone;	
	}
}

class Engine {
	protected $turbo;
	public function __construct(Turbo $turbo) {
		$this->turbo = $turbo;	
	}
}

class Bridgestone {
	public $tread;
	public function __construct(){
		$this->tread = 'Performance';
	}
}

class Turbo {
	public $stage;
	public function __construct(){
		$this->stage = 2;
	}
}

Route::get('/', function()
{
	dd(App::make('Car'));  //  resolve it
});
// object(Car)[154]
//  protected 'tire' => 
//    object(Tire)[155]
//      protected 'bridgestone' => 
//        object(Bridgestone)[158]
//          public 'tread' => string 'Performance' (length=11)
//  protected 'engine' => 
//    object(Engine)[157]
//      protected 'turbo' => 
//        object(Turbo)[160]
//          public 'stage' => int 2

Holy Macaroni! Double thumbs up! The Container was able to work some real magic in resolving whatever the Car needed.

In a nutshell, this is what the IoC container is for – it makes the task of setting up dependency injection much easier. Understanding the IoC Container is going to be very helpful once we start using the Laravel Repository Pattern to facilitate more readable and maintainable code.

Click to share! ⬇️