Easy Setter Injection in Zend Framework 2

Zend Framework 2 Forms

Recently I’ve been learning loads, thanks @ocramius, about dependency injection, and how it’s implemented in Zend Framework 2. Despite being conversant with constructor injection, I’ve not been as familiar with setter injection as I’d like to be.

I’ve been learning how to do it, through ServiceManager-aware interfaces, and will share how it’s done, in today’s post.

For configuring objects, reused throughout the application, I’ve found it to be nothing short of amazing. With next to no code, one Module configuration setting, along with the magic of OOP, classes are suitably initialized throughout the application, without any hands-on configuration on my part.

Whilst Zend Framework 2 is great without this. When you start using setter injection, it becomes so much more. In today’s post, I’ll take you through an example which uses setter injection to ensure that the AuthService, or authenticated user object is always available to a class and any of its descendents.

NOTE: There are likely better ways than how I’ve implimented it. But that’s for another day. It makes for a simple example.

How It’s Implimented

There are 3 things which you need to do to have a dependency automatically injected, by Zend Framework, into your class.

  1. Define an interface. This keeps things neat and tidy and really just ensures that your class has a function which can be called by the service manager, to inject the desired dependency
  2. Implement that interface in your class
  3. Define an initializers element in the getServiceConfig method of your module’s Module.php class

Let’s do all three, where I want to have a simple object injected into my class.

The Interface

namespace MaltBlue\Table;

use Zend\Authentication\AuthenticationService;

interface AuthAwareInterface
{
    /**
     * @param $authUser
     * @return mixed
     */
    public function setAuthUser(AuthenticationService $authUser);

    /**
     * @return mixed
     */
    public function getAuthUser();
}

Here I’ve created a simple interface with two methods, setAuthUser which accepts a AuthenticationService object, and getAuthUser. They set and return the auth user object in the implementing class, callable by the ServiceManager.

Implimenting the Interface

use MaltBlue\Table\AuthAwareInterface;

class CacheableTable implements AuthAwareInterface
{
    protected $authUser;

    public function setAuthUser(AuthenticationService $authUser)
    {
        $this->authUser = $authUser;
    }

    public function getAuthUser()
    {
        return $this->authUser;
    }
}

This class, is intended as a base implementation of the TableGateway pattern, which impliments the new Interface.

Initializers Configuration

MaltBlue\Table\AuthAwareInterface

public function getServiceConfig()
{
    return array(
        // ... existing code ...
        'initializers' => array(
            'AuthAwareInterface' => function($model, $serviceLocator) {
                if ($model instanceof AuthAwareInterface) {
                    $authObj = $serviceLocator->get('MaltBlue\AuthService');
                    $model->setAuthUser($authObj);
                }
            }
        ),
    )
}

Finally, we have the module configuration in Module.php. Here, in an initializers element, I’ve specified the interface name and defined what will happen, when a class implimenting it is encountered by the ServiceManager, via a closure.

Simply, if the class implements AuthAwareInterface, then MaltBlue\AuthService is retrieved and set in the object via calling setAuthUser. What I like about it is that there’s not a lot of work, for a lot of gain, and it’s very clear, very structured in the implimentation.

Now, just retrieve your instantiated objects, via the ServiceManager, and they’ll have the dependencies injected for you.

A Word of Warning

I need to point out that you should use setter injection with some care, always being very methodical about it. If the configuration were disparate, or not managed through a framework, it would get very difficult to keep track of what was being injected, when, and where, leading to a very hard to maintain application.

The way that Zend Framework 2′s designed, having a specific ‘initializers’ section of the getServiceConfig return array, does a lot of this for you. If you manage setter injection in a single location, as Zend Framework 2 can, to me that’s fine. Because you only have one place to look to know what’s being injected. But please bear this in mind when using it.

Why the Warning?

The reason for this warning was because of a Twitter conversation I had with Ocramius, which started as follows:

@maltblue yes, magic works until it works fine – that’s common. The problem comes up when it doesn’t work ;-) #php #di

I’ll write more about the downside of setter injection soon. But for the time being, please handle with care. A big thanks to Ocramius for being a great mentor on this topic.

Over To You

So, what do you think? Have you experience with it already? What do you think? Are you keen to use them, despite the objections? Share your feedback in the comments.

Want to Know More?

If you’d like to know more about setter injection vs. constructor injection, here are some excellent links:

Intermediate Patterns Tutorial Zend Framework