Configuring the ServiceManager with Abstract Factories

In this tutorial, we see how to use abstract factories to configure the ServiceManager in Zend Framework 2. It’s one of the simplest approaches available.


One of the best features about Zend Framework 2 is undoubtedly the ServiceManager; because it makes it almost painless to configure and retrieve services in a consistent and predictable manner, anywhere in your application, at any time.

But the catch is, there’s quite a bit to learn if you want to use it properly. As well as that, there’s quite a number of ways to use it. For example, you can instantiate classes using the invokables, factories and abstract factories elements as well as being able to use closures.

Now each of these approaches have their place, as well as the pros and cons. For example, you can use closures when you creating a prototype and invokables for classes which require no constructor arguments.

You could then use factories for classes which have dependencies, managed via constructor injection. But it’s not all sweetness and light. Let’s have a quick look at the negative points.

You can use closures, but your configuration won’t be cacheable, resulting in a bottleneck in your application.

And if you create an invokable or factory configuration for each class, you may end up creating quite a lot of classes (and an accompanying maintenance headache).

Despite these points, throughout the manual you’ll see a lot of reference to these three approaches.

So I don’t blame you if you thought that these were the only options. But there’s a better option, one which I only started to appreciate recently.

So I’ve made it the subject of today’s post - abstract factories. Abstract Factories in Zend Framework 2 allow you to implement the pattern of the same name. If you’re not familiar with it,

The abstract factory pattern provides a way to encapsulate a group of individual factories that have a common theme without specifying their concrete classes. This pattern separates the details of implementation of a set of objects from their general usage and relies on object composition, as object creation is implemented in methods exposed in the factory interface.

What Are Abstract Factories?

In Zend Framework 2 specifically, abstract factories are a fallback instantiation method for the ServiceManager, allowing it to instantiate and return services if all of the other methods fail.

When a service is requested and the ServiceManager can’t find it, because you’ve not setup an explicit configuration for it, it will iterate through the configured abstract factories and see if one of them can provide a matching service.

Similar to the factories classes, abstract factories classes implements two interfaces:

  • Zend\ServiceManager\AbstractFactoryInterface
  • Zend\ServiceManager\ServiceLocatorInterface

As a result of these interfaces, they need to implement two methods:

  • canCreateServiceWithName
  • createServiceWithName

canCreateServiceWithName returns a boolean value, confirming whether the class can return the service requested or not. If true, then createServiceWithName is called which carries out the instantiation of the service, returning the object requested.

Both of these methods take three parameters, a ServiceLocatorInterface object and two strings, commonly set as $name and $requestedName.

The ServiceLocatorInterface object, provides access to the ServiceLocator. So if you need other services, then you’re able to access them through it.

The $requestedName parameter is the service name being requested which, for today’s post, will be YourModule\Table\YourFormName.

A Working Example

Right, now that we’ve talked about what they are and why they’re good to use, let’s work through an example. Firstly we’ll create the class, then we’ll cover the Module.php configuration required.

namespace Application\ServiceManager\AbstractFactory;

use Zend\ServiceManager\AbstractFactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

The first thing we need to do is to bring in the required use statements, specifically AbstractFactoryInterface and ServiceLocatorInterface. The other two are there as I’ve based this example on an existing codebase.

class TableAbstractFactory implements AbstractFactoryInterface
{

Next, we need to ensure that the class always implements AbstractFactoryInterface.

public function canCreateServiceWithName(
        ServiceLocatorInterface $serviceLocator, $name, $requestedName
    ) {
        return (fnmatch('*Table', $requestedName)) ? true : false;
    }

Now we create the canCreateServiceWithName function. First, we’re going to inspect the $requestedName argument. For this example, I’m attempting to retrieve a service called “YourModule\Table\MyUserTable”.

As this is a TableAbstractGateway file, responsible for instantiating table classes, we first check if the service name ends in Table. If it does, we then check if the class exists. If it does, we return true, otherwise false. For the rest of the example, we’ll assume that it does.

public function createServiceWithName(
        ServiceLocatorInterface $serviceLocator, $name, $requestedName
    ) {
        if (class_exists($requestedName)) {
            $tableGateway = $requestedName . "Gateway";
            return new $requestedName($tableGateway);
        }

        return false;
    }
}

Next we implement the createServiceWithName method. This one handles instantiation and return of the service object. As the canCreateService method checks if we can do it, I’ve not checked the file name pattern again.

Instead I’ve checked if the class exists and if so, instantiated and returned it. The net result is that one class can now handle instantiation of nearly any object of a specific type; just like the abstract factory pattern is meant to do.

How Do You Configure Them

Now that we’ve setup the class to instantiate all of our table objects, we need to configure the ServiceManager in Module.php to make use of it. Honestly, I would have thought it would be more complicated than it is; it only requires one line. Have a look at the example below:

public function getServiceConfig()
{
    return array(
        'abstract_factories' => array(
            'Application\ServiceManager\AbstractFactory\TableAbstractFactory',
        ),
    ),
}

In the associative array returned from the getServiceConfig method, we just add the namespaced path to the new class we’ve just created. I’ve been rather explicit about mine for the sakes of clarity. That’s it.

Save Module.php and now if you when your tables are attempted to be retrieved from the ServiceManager, if they’re not explicitly defined, the new abstract factory class will attempt to instantiate them for you.

This will result in a more cacheable configuration, less development and maintenance effort.

Wrapping Up

So, have you tried out abstract factories yet? Are you tempted to do so now? Would you do this differently? Let me know in the comments.


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

Tue, Apr 15, 2014

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.

Tue, Apr 8, 2014

Building and Executing SQL Queries In Zend

Whilst there are many ways for building and executing SQL queries in Zend Framework 2, the two that I usually use, and which are also used in the ZF2 manual, are closures and the selectWith function. I previously wrote a three part series, showing how to get started using the \Zend\Db\Sql classes with Zend Framework 2, but I didn’t cover how to actually run them. So in today’s tutorial, let’s do that.


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