单工厂适用于多个控制器?

时间:2012-10-21 18:21:25

标签: zend-framework2

我的问题简而言之:我可以为多个控制器使用单个工厂吗?

更多详情:

我在 /config/autoload/global.php 中有一些全局非模块特定设置,如下所示:

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

    // More ZF default configuration ...
);

现在我希望在每个控制器中都可以访问这些设置,而无需一直致电$this->getServiceLocator()->get('config')

所以我的想法是在我的$settings中引入一个类属性AbstractController,它将注入配置数组。我试图直接在AbstractController的构造函数中获取配置。但是getServiceLocator()当时似乎没有准备好并返回NULL。

我可以为每个控制器构建Controller Factories并注入如下设置:

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

但它会一遍又一遍地相同。所以我的问题是:我可以为多个控制器使用一个工厂吗?

在我的 module.config.php 中,我可以为多个控制器指定一个工厂类:

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

但是在工厂中我需要手工返回实际的Controller对象(参见上面的例子),当然这只适用于每个Controller一个工厂。

希望,我明白了问题。

更新2013-03-24:

虽然我首先通过创建初始化程序来遵循建议的解决方案,但我从未真正喜欢将其用于注入配置。

所以我一直在挖掘并最终创建一个Controller插件来接收设置。

插件的代码如下所示:

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;
    }
}

使用

module.config.php 中添加插件后
'controller_plugins' => array(
    'invokables' => array(
        'settings' => 'My\Controller\Plugin\Settings',
    )
),

只需拨打$this->settings(),即可轻松访问控制器中的设置。希望这对任何人都有帮助。

3 个答案:

答案 0 :(得分:6)

您可以尝试附加初始化程序,然后在创建控制器时,根据已知的初始化程序检查实例。如果实例匹配给定的接口或抽象类,那么您可以应用一些通用逻辑。

我没有用控制器测试这种方法,但鉴于ControllerLoader是一种ServiceManager / ServiceLocator,理论上应该可以工作。

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

然后Initalizer看起来像:

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

        // Do something for this instance
    }
}

答案 1 :(得分:1)

不确定您今后如何更新“更新2013-03-24”;)

为什么不像你建议的那样使用控制器,而是将你想要的代码放在一个抽象工厂中,然后从中继承,例如:

abstract class AbstractControllerFactory implements FactoryInterface
{
    protected function initialise(ServiceLocatorInterface $serviceLocator, $controller) {
        $config = $serviceLocator->get('config');
        $controller->setConfig($config);
    }
}

class ControllerFactory extends AbstractControllerFactory implements FactoryInterface
{
    public function createService(ServiceLocatorInterface $serviceLocator) {
        $controller = new \MyModule\Controller\MyController();
        $this->initialise($serviceLocator, $controller);
        return $controller;
    }
}

答案 2 :(得分:1)

甚至更干净:将您的控制器作为invokables添加到配置中,然后执行类似这样的操作,将您的控制器转换为单行:

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';
}