Howto Use Constructor Injection In ZF2

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.


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.


You might also be interested in these tutorials too...

Wed, Jan 29, 2014

Easy Setter Injection in Zend Framework 2

Want to learn how to have initialized objects with minimal code and nearly no hands-on configuration in Zend Framework 2? Come learn about setter injection.

Thu, Apr 3, 2014

HowTo Use Child and Segment Routes to Build Simple Routing Tables

Routing is one of the key requirements in modern applications, especially in Zend Framework 2; but they shouldn’t be overly-complicated. Today, we’re going to look at how to build a routing table, simply and easily using child and segment routes.

Thu, Jun 20, 2013

\Zend\Db\Sql - Build SQL Where Clauses Easily and Efficiently

In this tutorial we’re working through the basics of \Zend\Db\Sql\Where, showing how to build SQL Where clauses for database queries. We’ll show how to create different predicates from simple to nested queries using of and, or, like, between and in conditions.


Want more tutorials like this?

If so, enter your email address in the field below and click subscribe.

You can unsubscribe at any time by clicking the link in the footer of the emails you'll receive. Here's my privacy policy, if you'd like to know more. I use Mailchimp to send emails. You can learn more about their privacy practices here.

Join the discussion

comments powered by Disqus