web development articles and portfolio

Using Dependency Injection and IoC in Laravel 4 controllers

Posted on 23-02-2013

I have never been a big framework user when it comes to PHP. While the language is not without its quirks and oddities that a good framework could help smooth over, I just never got around to really investing in a framework, as there is always a significant amount of initial effort needed to understand what the capabilities and limitations are, and whether or not it is truly suitable for a project. With that in mind, I typically just found it easier to write my own stuff.

Recently, however, I felt the need to go framework shopping, as I typically do every so often, just to be sure I'm not missing out on a really great product. After checking out the current status of the usual suspects, I came across a framework called Laravel. While I can't sit here and say with any authority that it is measurably better than any other PHP framework, for whatever reason it drew me in enough to read the documentation. And then it drew me in enough to watch a few screencast how-to's. And before I knew it, I was converting a long running personal project of mine into Laravel, "just to see how it works". I guess the secret to getting past the "which one do I pick?" question for PHP frameworks was to just pick one (and I guess the secret for framework authors to getting developers like me on board is to just be in the right place at the right time, if I'm any indication).

It wasn't quite as random an outcome as that though. There were tangible reasons I liked Laravel, one of which is how it handles object instantiation, or at least how it can. At the heart of Laravel 4, which is the version of Laravel that this article is based on and still in Beta, is an IoC container that makes managing your objects in a Dependency Injection designed app a breeze.

Before I get into some code, let me elaborate on two terms in the previous paragraph which you may not be familiar with. IoC stands for Inversion of Control, and it is effectively just a fancy way of describing a concept in Object Oriented programming where the logic behind how you instantiate a class is abstracted into a class itself, often referred to as a "container". In languages like Java and C#, this means that creating instances of classes is moved from compile time to run time, as instead of using the language itself to directly create an instance of a class e.g. "object = new Class()", you are telling your IoC container to do it, e.g. "object = ioc.get('Class')". Of course, this compile time vs run time effect of IoC isn't all that relevant for PHP, but it is nonetheless just as useful a concept in PHP.

Why is this a benefit? After all, using a language's "new" keyword to create an instance seems technically shorter, and possibly more efficient. The benefit comes when you design your OO code around the principles of Dependency Injection. I won't go into a lengthy dialogue about DI here, so if you're not familiar with the concept of DI you may want to go do some Googling, but in its most simple form, DI means writing classes that depend on instances of other classes in a manner which means they are not tightly coupled to those other classes. In other words, instead of creating instances of other classes inside a class, you feed instances into the class, typically via its constructor, but also possibly through setter methods. This means classes can be unit tested far easier, and it's generally just a better design in the 'separation of concerns' world of OO programming.

The problem with DI is it means actually creating an instance of a class can be tedious. In some cases, it might just be a matter of feeding in a few objects via the constructor, but in a lot of cases it will be far more meaty than that, and even if you only have to feed one object in, doing so multiple times in an application not only adds up effort wise, but it means if you want to change how the instance is created, perhaps adding another dependency, then you have to go back and change every line of code where the instance is established. Yuck. This is where a IoC container comes in - it offers a single point in your app where an implementation of an object is defined. This is much nicer.

So, back to Laravel 4. Not only does "L4" as it is referred to by its faithful offer a IoC container out of the box, but it basically encompasses the entire framework. In fact, if you check out the L4 code on Github, the Application class that the framework operates within extends from the Container class, so the ability to bind a class instance implementation is absolutely core to the Laravel 4 framework. This means even your controller classes that will typically power any application you build on top of L4 can be bound to the IoC container. No more tightly coupled, hard to unit test controllers!

Unfortunately, the Laravel doco seems to heavily promote the use of static calls to access the framework API (they're actually static facades, but they still promote tightly coupled controllers either way), so while the IoC container is documented, its purpose probably won't be very clear to new comers. This is what this article (sorry, it took a while to get to the point) is all about - how to adopt DI and IoC principles in your Laravel 4 controllers. I won't go as far to say "how to code controllers in Laravel 4 the right way", but....oh, stuff it, I will - how to code controllers in Laravel 4 the right way.

First, lets start with a controller, say, app/controllers/UsersController.php:


class UsersController extends BaseController
{
public function getIndex()
{
return Response::make('Hello World!');
}
}

I'm assuming this controller has a route like so in app/routes.php:


Route::controller('users', 'UsersController');

Which means we're mapping the HTTP request methods directly to the controller. In this case, doing a GET on /users will respond with 'Hello World!'. But notice how in our class we use the static facade 'Response' - this may seem innocent enough, after all we need to respond, but this one line makes UsersControler a tightly coupled controller. This controller relies on the static class Response to be available.

There is a better way, the DI way. First, lets change our controller to this:


class UsersController extends BaseController
{
protected $response;
public function __construct(Response $response)
{
$this->response = $response;
}
public function getIndex()
{
return $this->response->make('Hello World!');
}
}

This is a controller that instead relies on Dependency Injection to operate. Rather than reference a hard coded class to perform a response, we're referencing an object that was injected into the controller instance when it is created via its constructor. This is better because all that we require from $response in the constructor is that it is an instance of Response, or that it extends Response. This means you could actually write your own Response class, extend it from Laravel's Response class, and this controller will not care at all. When you factor in type hinting to Interfaces instead of actual classes, then you have a very very flexible controller class (but this is beyond this article's scope).

The crazy thing with Laravel 4 is, this may even work already. Thanks to the PHP Reflection API, Laravel 4 actually has the ability to inspect classes like controllers for dependencies and automatically inject them. Very cool, at least from a web dev geek perspective - personally, I'm a little uneasy about the amount of responsibility this forces on the framework, and I'd prefer to define my own logic for instantiating controller classes in a DI approach. Fortunately, Laravel 4 allows you to do this - this is called doing a "IoC bind".

There is no specific spot to define such "IoC binds" in Laravel 4, but for testing purposes you can just add them to the routes.php file. Alternatively, maybe create a file called app/binds.php and require it from app/start/global.php - this is what I did in a project of mine. In any case, here is an example of an IoC bind to the above UsersController class:


$app->bind('UsersController', function($app) {
return new UsersController(new Response);
});

Pretty simple. We're using the bind() method for the Application instance to define the logic behind how UsersController should be instantiated. We do this using a simple PHP Closure and returning the class instance. Internally, Laravel is now using $app->make('UsersController') to create an instance of UsersController, whenever a route is triggered that needs it.

This is just a very simple example. In reality, a controller is going to have many more dependencies than this due to their purpose in a MVC style application. For example, in Laravel 4 controllers you'll probably want the Request instance, an instance of Validator, and instances of the relevant models at the bare minimum. Since this is a Users controller, we'll possible involve password hashes, so a Hash instance would be handy too. Lets update the example with a more realistic look:


class UsersController extends BaseController
{
protected $response;
protected $request;
protected $validator;
protected $hasher;
protected $user;
public function __construct(
Response $response,
\Illuminate\Http\Request $request,
\Illuminate\Validation\Factory $validator,
\Illuminate\Hashing\BcryptHasher $hasher,
User $user
) {
$this->response = $response;
$this->request = $request;
$this->validator = $validator;
$this->hasher = $hasher;
$this->user = $user;
}
public function getIndex()
{
return $this->response->make('Hello World!');
}
}

The amount of dependencies has now grown (despite the fact the new ones are not being used in this example's getIndex() - just ignore that), and while I said before that Laravel will attempt to inject instances by itself, this particular example resembles a real controller I was recently creating, and I can say that Laravel failed to do so. So creating the IoC bind was necessary, and in this case will look something like this:


$app->bind('UsersController', function($app) {
$controller = new UsersController(
new Response,
$app->make('request'),
$app->make('validator'),
$app->make('hash'),
new User
);
return $controller;
});

Note how that rather than using the "new" keyword for some of these dependencies, I have tasked the IoC container to make them. This is because Laravel 4 has pre-built IoC binds for these classes, as it uses IoC internally (for some reason, Response was not available in this manner). So not only am I using IoC for the controller, but its dependencies may have dependencies of their own, so it makes sense to get an instance of them via the IoC container as well. In fact, it would almost defeat the purpose of using the IoC container for your controllers if you didn't. This is why the Closures for bind()'s second parameter are handed the Application instance - so you can reference the IoC container.

While in most cases you will only need a single instance of a controller class, and the framework would usually be handling that anyway, it still makes sense to utilise IoC for single instance objects due to the benefits of adhering to a Dependency Injection pattern alone. Think about how this approach may also seep into other classes - your models, extra library classes you add, and so on. While not specifically covered in this article, thanks to the scope the IoC container in Laravel 4 has, it can even act as a singleton simulator for your classes, only returning a single instance application wide if that is your desire.

It is my hope that you found this article helpful, and perhaps maybe it has swayed you to consider a DI approach for your time coding in Laravel 4 - I know it is really shaping up to be a framework that changes how I do things in PHP, and for it to allow for everything from static calls to clean DI is excellent. In a sense, the fact the documentation is focused on the static facade method of invoking the framework classes means going down the DI road will involve the occasional snoop around the source code of Laravel to see how to do things (e.g. the 'Input' facade is actually pointing to methods on Request), but one could argue this is a great excuse to become more familiar with the framework you may very well build a career on.

Social

Tags

Comments

comments powered by Disqus
Nathan MVC