ZF2,我在开始时不知道的依赖

时间:2016-12-06 10:52:21

标签: zend-framework2

在我的控制器中,通过服务,我从DB获取小部件名称列表(例如图表,日历等)。每个小部件都实现WidgetInterface,并且可能需要其他服务作为其自己的依赖项。每个用户的窗口小部件列表可能不同,因此我不知道控制器中需要哪些窗口小部件/依赖项。通常,我使用工厂通过DI放置依赖关系,但在这种情况下,我不知道控制器初始化时的依赖关系。 我想避免直接在控制器中使用服务定位器。我该如何处理这个问题?我应该获得控制器工厂中小部件名称的列表吗?并根据小部件列表获取所有依赖项并将它们放到控制器?

谢谢,汤姆

解决方案

我以一种建议Kwido和Sven Buis的方式解决了我的问题,这意味着,我建立了自己的插件管理器。

优点:我不需要在控制器中直接使用服务定位器,我有清晰,可扩展的方式来获取不同类型的小部件。

谢谢。

3 个答案:

答案 0 :(得分:1)

这确实是一个有趣的问题。您可以考虑为小部件使用插件,可以即时加载。

Depency注入是一种很好的做法,但有时候,动态内容无法实现。

另一种方法是创建自己的小部件管理器。然后,此管理器可以加载您需要的特定小部件。可以将小部件管理器注入控制器。

编辑: 正如你在上面看到的,来自@kwido的相同想法。

答案 1 :(得分:1)

为您的小部件创建自己的管理器,就像某种ServiceManager一样。

class WidgetManager extends AbstractPluginManager

看看:Samsonik tutorial - pluginManager。因此,这样您就可以注入WidgetManager并仅从该管理器中检索小部件作为您的函数:validatePlugin,检查所获取的实例是否正在使用WidgetInterface。请记住,您仍然可以调用父ServiceManager。

或者保持简单,并为您的控制器构建一个插件,将您的小部件名称映射到服务。然后,此插件可以使用serviceLocator / Manager来检索您的窗口小部件,无论它们是由工厂还是由invokableFactories创建的。所以你不要直接注入所有的小部件,而只是在请求时才获取它们。有点简单:

protected $map = [
    // Widget name within the plugin => Name or class to call from the serviceManager
    'Charts' => Widget\Charts::class,
];

public function load($name)
{
    if (array_key_exists($name, $this->map)) {
        return $this->getServiceManager()->get($this->map[$name]);
    }

    return null;
}

注入所有小部件可能对您的性能有害,因此您可能会考虑其他因素,例如当您的小部件列表增长时,处理您的请求的时间就会增加。

希望这对你有所帮助,并将你推向某个方向。

答案 2 :(得分:1)

我会使用单独的服务并将其注入控制器。

interface UserWidgetServiceInterface
{
    public function __construct(array $widgets);
    public function getWidget($name);
}

控制器工厂

class MyControllerFactory
{
    public function __invoke(ControllerManager $controllerManager, $name, $requestedName)
    {
        $serviceLocator = $controllerManager->getServiceLocator();

        $userWidgetService = $serviceLocator->get('UserWidgetService');

        return new MyController($userWidgetService);
    }
}

然后加载小部件的逻辑将移动到UserWidgetServiceFactory

public function UserWidgetServiceFactory
{
    public function __invoke(ServiceManager $serviceLocator, $name, $requestedName)
    {
        $userId = 123; // Load from somewhere e.g session, auth service.

        $widgetNames = $this->getWidgetNames($serviceLocator, $userId); 
        $widgets = $this->loadWidgets($serviceManager, $widgetNames);

        return new UserWidgetService($widgets);
    }

    public function getWidgetNames(ServiceManager $sm, $userId)
    {
       return ['foo','bar'];
    }

    public function loadWidgets(serviceManager $sm, array $widgets)
    {
        $w = [];
        foreach($widgets as $widgetName) {
            $w[$widgetName] = $sm->get($widgetName);
        }
        return $w;
    }   
}

loadWidgets()的调用会急切加载所有小部件;如果您希望对此进行优化,可以将您的小部件注册为LazyServices