6

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

更多细节:

我在/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。

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

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 年 3 月 24 日更新:

尽管我首先通过创建一个初始化程序来遵循建议的解决方案,但我从来没有真正喜欢将它仅用于配置注入。

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

该插件的代码如下所示:

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()。希望这对任何人都有帮助。

4

3 回答 3

6

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

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

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

然后初始化器看起来像:

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

        // Do something for this instance
    }
}
于 2012-10-22T10:05:09.870 回答
1

不确定您如何进行未来更新“UPDATE 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;
    }
}
于 2013-02-28T15:29:26.343 回答
1

甚至更干净:将您的控制器作为可调用对象添加到配置中,然后执行以下操作将您的控制器变成单行:

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';
}
于 2013-02-28T15:32:53.533 回答