Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Single Factory for multiple Controllers?

My question in short: Can I use a single Factory for multiple Controllers?

More details:

I have some global non-module specific settings in /config/autoload/global.php which look like this:

return array(
    'settings' => array(
        'setting_a' => 'foo',
        'setting_b' => 'bar'
    ),

    // More ZF default configuration ...
);

Now I want to make these settings accessable in every controller without having to call $this->getServiceLocator()->get('config') all the time.

So my idea was to introduce a class attribute $settings in my AbstractController which get’s injected with the configuration array. I tried to fetch the config directly in the constructor of AbstractController. However getServiceLocator() doesn’t seem ready at the time and returns NULL.

I could build Controller Factories for every controller and inject the settings like this:

class ControllerFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $serviceLocator) {
        $config = $serviceLocator->get('config');
        return new \MyModule\Controller\MyController($config['settings']);
    }
}

But it would be the same over and over again. So my question is: Can I use a single Factory for multiple controllers?

In my module.config.php I could specify a single factory class for multiple controllers:

return array(
    'controllers' => array(
        'factories' => array(
            'MyModule\Controller\MyControllerA' => 'MyModule\Factory\ControllerFactory',
            'MyModule\Controller\MyControllerB' => 'MyModule\Factory\ControllerFactory',
            'MyModule\Controller\MyControllerC' => 'MyModule\Factory\ControllerFactory',
        )
    ),
);

But in the factory I need to return the actual Controller object by hand (see example above) which of course only works with one Factory per Controller.

Hope, I made my problem clear.

UPDATE 2013-03-24:

Although I first followed the suggested solution by creating an initializer, I never really liked using it just for the injection of the configuration.

So I kept digging around and ended up creating a Controller plugin to receive the settings.

The code for the plugin looks like this:

use Zend\Mvc\Controller\Plugin\AbstractPlugin;

class Settings extends AbstractPlugin
{
    protected $settings;

    public function __invoke()
    {
        $config = $this->getController()->getServiceLocator()->get('Config');

        if (isset($config['settings']) && is_array($config['settings'])) {
            $this->settings = $config['settings'];
        }

        return $this->settings;
    }
}

After adding the plugin in module.config.php with

'controller_plugins' => array(
    'invokables' => array(
        'settings' => 'My\Controller\Plugin\Settings',
    )
),

I can easily access my settings within a controller by simply calling $this->settings(). Hope this helps anyone.

like image 987
Rob Avatar asked Oct 21 '12 18:10

Rob


2 Answers

You could try attaching an initializer, then as the controller is created the instance is checked against known initializers. If the instance matches a given interface or abstract class, then you can apply some common logic.

I've not tested this approach with controllers but given that the ControllerLoader is a type of ServiceManager/ServiceLocator it should in theory work.

'controllers' => array (
    'initializers' => array(
        'MyApplication\Controller\Initializer'
    )
),

Then the Initalizer would look something like:

class Initializer implements InitializerInterface
{
    public function initialize($instance, ServiceLocatorInterface $serviceLocator)
    {
        if (!$instance instanceof MyControllerInterface)
        {
            return;
        }

        // Do something for this instance
    }
}
like image 192
DrBeza Avatar answered Nov 19 '22 15:11

DrBeza


or even cleaner: add your controllers as invokables to the config, then do something like this which turns your controllers into one-liners:

abstract class AbstractControllerFactory implements FactoryInterface
{
    protected $controllerKey;

    public function createService(ServiceLocatorInterface $serviceLocator) {
        $config     = $serviceLocator->get('config');
        $controller = $serviceLocator->get($this->controllerKey);
        $controller->setConfig($config);
        return $controller;
    }
}

class ControllerFactory extends AbstractControllerFactory implements FactoryInterface
{
    protected $controllerKey = 'mymodule_controller_mycontroller';
}
like image 1
Jon Gilbert Avatar answered Nov 19 '22 14:11

Jon Gilbert