Howto Use Constructor Injection In ZF2

Creating a ZF2Skeleton Application and running it with PHP's built-in webserver

Synopsis

Is it right to use setter injection? Or is it evil, to be avoided at all costs, for the explicitness of constructor injection? In today’s post, we explore that and how to implement constructor injection in ZF2 controller classes.

Recently on Master Zend Framework, I wrote about using Setter Injection in Zend Framework 2, to supply dependencies to Controller classes.

The post came about after having a really great experience doing so on a client’s project. However, some of the ZF2 core contributors, Gary Hockin and Ocramius disagreed with this approach.

They raised the valid concerns that it’s all fine, until something goes wrong. Also in Stephan Hochdörfer’s talk at PHPUK in February this year, he also wasn’t too positive, giving only tacit approval for doing so.

So after having a good discussion with Gary about it at PHPUK14, he suggested that the right approach to use is Constructor Injection. Since then, I’ve been experimenting with it on some projects and really like what I see.

NB: Whilst I’m still ok with using setter injection, I understand the reasons why it should be either avoided or used with care. But I’ll go in to that in another post.

In today’s tutorial, I’m going to take you through how to use constructor injection, when initialising your controllers in Zend Framework 2, to set a ServiceManager service as an explicit dependency.

It’s really quite simple to do, consisting only of 3 steps:

  1. Implement a class constructor in your controller class
  2. Instantiate your controllers with a class implementing FactoryInterface
  3. Use the factories element in module.config.php instead of invokables

Let’s step through how this works now.

1. Implement a Class Constructor

use Zend\Cache\Storage\Adapter\AbstractAdapter;

class IndexController
{
    protected $_cache;
    public function __construct(AbstractAdapter $cache = null)
    {
        if (!is_null($cache)) {
            $this->_cache = $cache;
        }
    }
}

Here, in our IndexController, we’ve added a protected member variable, $_cache, and implemented the __construct method, specifying an optional variable, which is an instance of the Zend\Cache\Storage\Adapter\AbstractAdapter. If the object isn’t null, then I instantiate the $_cache variable with it. If you have other dependencies, then just expand on this example as needs be.

2. Initialise your controllers via FactoryInterface

Next we need to create a new class, which implements FactoryInterface. To keep things organised, under the src/Controller directory in your module, create a new directory called Factory. In there, create a new class, called IndexControllerFactory.php.

In there, you’ll need code in this Gist. I’ll assume this is your first time using this approach in action, so we’ll step through the code together.

namespace YourModule\Controller\Factory;
use Zend\ServiceManager\FactoryInterface,
    Zend\ServiceManager\ServiceLocatorInterface,  
    Zend\ServiceManager\Exception\ServiceNotCreatedException,
    YourModule\Controller\IndexController;

Firstly, we declared the namespace properly, so we can access our classes. We then added four use statements. The first is required as our class will implement FactoryInterface.

The second is required because FactoryInterface has one method, which must be implemented, createService, and it takes one argument, which implements the ServiceLocatorInterface.

The third is required, because we need to catch the case, where for some reason, the service we’re attempting to retrieve from the service locator isn’t able to be created. The fourth, well that goes without saying.

class IndexControllerFactory implements FactoryInterface
{
  public function createService(ServiceLocatorInterface $serviceLocator)
  {

Here, we define the class implementing the FactoryInterface, and begin to define the createService method. The service manager automatically passes in the $serviceLocator object for us, because of how we define it in module.config.php shortly.

$sm = $serviceLocator->getServiceLocator();

try {
  $cache = $sm->get('Application\Cache');
} catch (ServiceNotCreatedException $e) {
  $cache = null;
} catch (ExtensionNotLoadedException $e) {
  $cache = null;
}

Next, we get access to the serviceLocator object and with that, attempt to retrieve the Application\Cache service, which returns an initialised object implementing Zend\Cache\Storage\Adapter\AbstractAdapter.

I’ve wrapped it in a try/catch block as one of two exceptions, ServiceNotCreatedException or ExtensionNotLoadedException could be thrown.

NB: If you’re trying this code out in an existing application, interchange the service name specified with a service you’ve already defined.

  $controller = new IndexController($cache);
    return $controller;
  }
}

Finally, we now instantiate a new IndexController object, passing in the retrieved <code=”minimalist”>$cache object and return the initialised controller. So far, so good.

3. Use factories Instead of invokables

If you’ve followed the standard approach in the ZF2SkeletonApp, you’ve likely used the <code=”minimalist”>invokables element of the controllers array in <code=”minimalist”>modules.config.php to initialise your Controller classes.

return array(
  'controllers' => array(
    'invokables' => array(
        'YourModule\Controller\Index' => 'YourModule\Controller\IndexController'
    )
  )
);

To use the FactoryInterface class, we’ll need to use the <code=”minimalist”>factories element instead. Assuming that you had a configuration as above, change it to look like the example below.

return array(
  'controllers' => array(
    'factories' => array(
        'YourModule\Controller\Index'  => 'YourModule\Controller\Factory\IndexControllerFactory',
    )
),

When done this way, ZF2 will automatically inject the ServiceLocator into IndexControllerFactory when it’s instantiated.

That’s It

As you can see, you’re really not doing that much different than before. However, this approach has a number of benefits.

Firstly, it makes testing your controllers much simpler. Secondly, it keeps your controllers and their actions a lot smaller and more maintainable.

If you were using the getServiceLocator() method in your controller actions, you’d have to mock different services, depending on the action you were dispatching to. This could make your tests unwieldily, if nothing else.

Now it’s simpler. You have all your dependencies injected at one spot, so you set them all up in setUp, then run your tests.

What’s more, and I’m sure GeeH, Ocramius, TomPHP, and Stephan Hochdörfer will like this – there’s no magic. The dependencies are declared explicitly. It’s absolutely clear as to what’s going on, and what dependencies the class requires.

Plus, migrating to this approach is really simple. There’s a new interface to learn and implement, but really, you’re trading a bit of learning time for a large amount of time saved later. So that’s a definite net positive!

Wrapping Up

Whilst I still believe it’s ok to use setter injection, I now see that constructor injection is the better of the two.

I hope, through this post, you can see some of the pros and cons of both approaches, as well as how to implement either style. This way, depending on your needs, you can implement the approach that best suits your needs.

Where do you stand on this? Which approach is right from your perspective and when would you use either one? Share your thoughts in the comments.

Intermediate ServiceManager Tutorial
  • http://phpdev.ro/ Andrei Gabreanu

    I’ve been down that road. Starting from injecting the service locator, to using dedicated factory classes and injecting in the constructor. Then going back to closures factories while still keeping constructor injection and at the end just reverting back to implementing the service locator aware interface and having the SM available all the time (in our services also). I agree there is a lot of magic happening, but I don’t consider it bad magic anymore. Although this would be the perfect way to write code (especially in ZF2), the downside lays in the time you waste just writing your dependencies & factories. In a big project you waste SO much time with these (I even considered building a tool to do it for you from the cmd. line, but it’s more complicated than you’d think) – that ultimately, I think the magic part is rather OK and actually helps you speed up your coding. And if you think about it, you can always create a tool that converts all your $this->getServiceLocator()->get(‘someFactory’) to a $this->someFactory, generates the protected vars, creates your factories etc and you never actually push your “magic” code.

    You can still test all of your stuff, but you’re not seeing the actual dependencies (which yes I agree kind of makes the whole testing part awkward). But then again, if you spend 2x the same time for doing the (almost) same thing, and you don’t have any major downsides, why do it?

    • http://www.matthewsetter.com/ Matthew Setter

      Hi Andrei,

      I see what you’re saying, and that was the argument which I put to ocramius and Gary Hockin regarding use of Setter Injection. To me it was clear how I was approaching it, especially as the way I was organising it was very explicit. From memory, they were ok with it, so long as the configuration remained so explicit. So to me, there wasn’t an issue in this case.

      However, I can see that if you’re not careful, it could descend into madness and be crazy hard to maintain. But on the flip side, I see what you’re saying, that there’s a tradeoff to make between organisation and time spent. In part that’s a never ending debate. How far’s too far in one direction, versus the advantages gained. Thanks for writing such detailed feedback.

      • Ian

        The advantage of using concrete factory classes over closures for DI is that your config can be cached.

        • http://www.matthewsetter.com/ Matthew Setter

          definitely. a big plus!