ZF2,我一开始不知道的依赖项

ZF2, dependencies which I don't know at start

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

谢谢,汤姆

解决方案

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

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

谢谢。

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

依赖注入是一种很好的做法,但有时动态内容无法实施。

另一种方法是制作您自己的小部件管理器。然后该管理器可以加载您需要的特定小部件。 widget-manager可以注入到controller中。

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

为您的小部件创建您自己的管理器,例如某种 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;
}

注入所有 Widget 可能对您的性能不利,因此您可以考虑其他事情,因为当您的 widget 列表增加时,处理您的请求的时间也会增加。

希望这对您有所帮助并推动您朝着某个方向前进。

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

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