Accessing ServiceManager Services in Controller Plugins

Do you need access to a service from the ServiceManager in a custom Controller Plugin you’re creating? Today’s tutorial shows you just how to do it, in a testable and documentable way.


I’ve seen some questions on Google+ and StackOverflow of late, regarding how to get access to the Zend Framework 2 database adapter, along with other ServiceManager-defined services, in a custom controller plugin.

This type of setup can come in handy for a number of situations. You may want to access services such as caching, logging or databases and want to provide a simple interface for doing so.

People seem really interested in how to do it, but how to get access to services from the ServiceManager doesn’t seem to be as clear as it could be. Gladly, there’s not much involved in actually doing it.

I’ll assume that you’re creating the plugin for an existing module. If you’re writing a stand-alone controller plugin the steps are a little different and aren’t covered in this tutorial.

To complete today’s tutorial, you only need to do 3 things:

  • Create 2 classes
  • Add a function in Module.php

The Plugin Factory

I’m a big believer in using factories (and abstract factories) in Zend Framework 2, mainly because they make constructor injection almost a breeze. So the first thing we need to do is to create a new class, called MyPluginFactory under src/Application/Mvc/Controller/Plugin/Factory.

I’m using this directory structure as it follows along with the existing Zend Framework 2 structure, which keeps your class structure well organised. Now let’s work through the class’ code.

namespace Application\Mvc\Controller\Plugin\Factory;

use Application\Mvc\Controller\Plugin\MyPlugin;
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\FactoryInterface;

First we need to add in the namespace and required use statements. We’ve started with the statement for the plugin class we’ll create, followed by the two core classes we’ll need, so that the class can be managed by the ServiceManager.

class MyPluginFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $pluginManager)
    {
        $serviceManager = $pluginManager->getServiceLocator();

        return new MyPlugin($serviceManager->get('Zend\Db\Adapter\Adapter'));
    }
}

We then implement the FactoryInterface, which requires that we implement the createService() function. If you want some background on this approach, check out this article on constructor injection in Zend Framework 2.

In the method, we’re able to get access to the ServiceLocator by calling getServiceLocator() on the $pluginManager argument, initialising a new object, $serviceManager.

Then we’re able to access the database adapter service by calling the get() method on $serviceManager, specifying the key of ‘Zend\Db\Adapter\Adapter’, and pass the result to the constructor of our plugin class.

Now our plugin will have the database adapter as a fixed dependency.

The Plugin Class

Now that we have the factory class setup to manage instantiation of our plugin, let’s dive in and look at the plugin class itself.

namespace Application\Mvc\Controller\Plugin;

use Zend\Mvc\Controller\Plugin\AbstractPlugin;

As always, we start of by declaring the namespace and the use statement. In this case, we only need to specify Zend\Mvc\Controller\Plugin\AbstractPlugin.

class MyPlugin extends AbstractPlugin
{
    protected $dbAdapter;

    public function __construct(\Zend\Db\Adapter\Adapter $dbAdapter)
    {
        $this->dbAdapter = $dbAdapter;
    }
}

Now let’s look at the core of the plugin. First we have a single class member variable, $dbAdapter. We then define the class constructor, specifying one, required, argument, also called $dbAdapter, which is of type \Zend\Db\Adapter\Adapter. This will be used to initialise $dbAdapter.

Quick Note: I’m not making assumptions about what you may want to do with the database adapter; just provided enough code to show you how to get access to it. If you’d like to know more, then check out Zend\Db\Adapter\Adapter in the Zend Framework 2 manual.

The Module.php Configuration

With those two classes defined, we only need to do one more thing, which is to register the plugin in the plugin configuration. So in the module’s Module.php file, if you don’t already have it defined, define a new function, getControllerPluginConfig as follows.

public function getControllerPluginConfig()
{
    return array(
        'factories' => array(
            'MyPlugin' => 'VideoHoster\Controller\Plugin\Factory\MyPluginFactory'
        ),
    );
}

This defines a new plugin, which we’ve named the same as the core class, MyPlugin. The elements value is the namespace of the plugin factory. When the plugin is referenced, which we’ll see in a moment, the ServiceManager will use the factory to instantiate and return the plugin with the database adapter injected.

Using It In A Controller Action

To use it, in any controller action, you can call it as follows: $this->myPlugin();. Now there’s not much you can do with it, based on the code here. So, naturally, add the relevant functions and methods you need.

That’s It!

And that’s how to create a custom controller plugin which accesses a service defined in the ServiceManager. I’d love to hear your thoughts on it in the comments.

ServiceManager
  • http://ocramius.github.io/ Marco Pivetta

    One tip that I always suggest to people that are lucky enough to use PHP 5.5 is to use the ::class magic constant in order to avoid having a lot of string => string mappings.

    Using the ::class magic constant allows easy refactoring of service definitions together with your classes, which is a huge plus.

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

      Thanks for sharing. I’d not thought of that. Do you have a Gist available?

      • http://ocramius.github.io/ Marco Pivetta

        It’s so simple that it doesn’t need one:

        InterfaceName::class => SomeFactory::class

        This obviously with the required imports :)

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

          Sweet, I wasn’t aware of it. Thanks for sharing mate. I’ll be trying that out.