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.
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.
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.
@maltblue yes, magic works until it works fine - that’s common. The problem comes up when it doesn’t work 😉 #php #di
— Marco Pivetta (@Ocramius) January 27, 2014
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:
module.config.php
instead of invokablesLet’s step through how this works now.
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.
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.
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.
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!
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.
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.
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.
For the longest time, I’ve been using closures in my Zend Framework 2 Modules Module
class. I know they’re not always the best approach, but they’re not necessarily wrong either.
But after reviewing Gary Hockin’s recent talk at PHP Conference UK, I was reminded that outside of APC and OPCache, closures aren’t cacheable.
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.
Please consider buying me a coffee. It really helps me to keep producing new tutorials.
Join the discussion
comments powered by Disqus