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
在我的控制器中,通过服务,我从数据库中获取了一个小部件名称列表(例如图表、日历等)。每个小部件都实现 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