在我的控制器中,通过服务,我从DB获取小部件名称列表(例如图表,日历等)。每个小部件都实现WidgetInterface,并且可能需要其他服务作为其自己的依赖项。每个用户的窗口小部件列表可能不同,因此我不知道控制器中需要哪些窗口小部件/依赖项。通常,我使用工厂通过DI放置依赖关系,但在这种情况下,我不知道控制器初始化时的依赖关系。 我想避免直接在控制器中使用服务定位器。我该如何处理这个问题?我应该获得控制器工厂中小部件名称的列表吗?并根据小部件列表获取所有依赖项并将它们放到控制器?
谢谢,汤姆
解决方案
我以一种建议Kwido和Sven Buis的方式解决了我的问题,这意味着,我建立了自己的插件管理器。
优点:我不需要在控制器中直接使用服务定位器,我有清晰,可扩展的方式来获取不同类型的小部件。
谢谢。
答案 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