zf2 控制器工厂服务定位器
zf2 controller factory serviceLocator
我正在尝试将服务管理器注入控制器。
实际错误:
\vendor\zendframework\zend-servicemanager\src\Exception\ServiceLocatorUsageException.php:34
Service "Project\Service\ProjectServiceInterface" has been requested to plugin manager of type "Zend\Mvc\Controller\ControllerManager", but couldn't be retrieved.
A previous exception of type "Zend\ServiceManager\Exception\ServiceNotFoundException" has been raised in the process.
By the way, a service with the name "Project\Service\ProjectServiceInterface" has been found in the parent service locator "Zend\ServiceManager\ServiceManager": did you forget to use $parentLocator = $serviceLocator->getServiceLocator() in your factory code?
过程进行:
class BaseController extends AbstractActionController implements ServiceLocatorAwareInterface
{
public function __construct(\Zend\ServiceManager\ServiceLocatorInterface $sl)
{
$this->serviceLocator = $sl;
}
}
- 创建控制器并使用构造方法
- 将此
BaseController
扩展为 AdminController
- 设置路由到
AdminController => /admin
使用Module.php
public function getControllerConfig()
使用 closer 作为工厂创建注入 serviceLocator 的控制器对象
'Project\Controller\Project' => function($sm) {
$serviceLocator = $sm->getServiceLocator();
return new \Project\Controller\ProjectController($serviceLocator);
},
- 尝试使用
$this->getServiceLocator()->get('service_name')
- 发现缺少服务的异常......
现在的问题是:
/**
*
* @param ServiceLocatorInterface $sl
*/
public function __construct(\Zend\ServiceManager\ServiceLocatorInterface $sl)
{
$rtn = $sl->has('Project\Service\ProjectServiceInterface');
echo '<br />in Constructor: '.__FILE__;var_dump($rtn);
$this->serviceLocator = $sl;
}
public function getServiceLocator()
{
$rtn = $this->serviceLocator->has('Project\Service\ProjectServiceInterface');
echo '<br />in getServiceLocator: '.__FILE__;var_dump($rtn);
return $this->serviceLocator;
}
在 __constructor() 中找到服务 。在 getServiceLocator() 方法中找不到同名服务 .....
in Constructor: Project\Controller\BaseController.php
bool(true)
in getServiceLocator: Project\Controller\BaseController.php
bool(false)
我错过了什么吗? SharedServiceManager 在这里做些什么吗?
本次练习的全部目的在于这条消息:
Deprecated: ServiceLocatorAwareInterface is deprecated and will be removed in version 3.0, along with the ServiceLocatorAwareInitializer. ...
在Zend Framework 2中有多种服务定位器(docs here),一种通用(主要用于你自己的服务),一种用于控制器,一种用于视图助手,一种用于验证器,...具体的也叫 plugin managers.
您收到的错误消息只是告诉您您使用了错误的服务定位器,是检索控制器的服务定位器,而不是一般的服务定位器。它还建议您如何解决问题:
did you forget to use $parentLocator = $serviceLocator->getServiceLocator() in your factory code
可能发生的事情(不是 100% 确定)是在构造函数中您传递了一个通用服务管理器的实例,并且一切正常。然后,由于控制器实现了 ServiceLocatorAwareInterface
,控制器服务定位器被注入到您的控制器中,覆盖您之前定义的那个。
此外,我认为在版本 3
中删除 ServiceLocatorAwareInterface
的决定之外的想法是您不在控制器中注入服务定位器,而是直接注入控制器依赖项。
您应该尽量避免在控制器中注入服务管理器或服务定位器。将实际依赖项(在您的情况下 'Project\Service\ProjectServiceInterface'
)直接注入 class 的 __construct
方法会好得多。 构造函数注入(依赖通过class构造函数提供)被认为是最佳实践 在 ZF2.
此模式可防止在没有依赖项的情况下实例化控制器(这会引发错误)。
如果您注入 ServiceLocator
或 ServiceManager
,您将从中解析 class 中的实际依赖项,那么不清楚 class 实际需要什么.您最终可能会在 class 实例中缺少一开始就不应该创建的依赖项。您需要在 class 内部进行自定义检查以查看实际依赖项是否可用,如果缺少则抛出错误。您可以使用构造函数依赖模式来防止编写所有这些自定义代码。
另一个问题是很难对 class 进行单元测试,因为您无法 为您的个人依赖项设置模拟 如此轻松。
阅读更多关于如何注入依赖项的信息 in my answer 到类似的问题。
更新
关于您遇到的问题。控制器 classes 实现了一个 ServiceLocatorAwareInterface
并且在构造你的控制器 classes 期间,ControllerManager
在 class 中注入了一个 ServiceLocator
。这种情况发生 here in the injectServiceLocator
method at line 208 in ControllerManager.php
。就像@marcosh 在他的回答中已经提到的那样,这可能是一个与您注入的服务定位器不同的服务定位器。在此 injectServiceLocator
方法中,您还可以找到您在问题中提到的弃用通知。
你的变量在 __construct
方法中可用,因为那时(在构建 class 之后)变量尚未被覆盖。稍后当您尝试在 getServiceLocator
方法中访问它时,它会被覆盖。
如果你真的需要ServiceLocator,你必须用工厂注入它
像这样
控制器:
<?php
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\ServiceManager\ServiceLocatorInterface;
class BaseController extends AbstractActionController
{
protected $serviceLocator = null;
public function __construct(ServiceLocatorInterface $serviceLocator)
{
$this->setServiceLocator($serviceLocator);
}
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
return $this;
}
public function getServiceLocator()
{
return $this->serviceLocator;
}
}
工厂:
<?php
namespace Application\Controller\Factory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Application\Controller\BaseController;
class BaseControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator);
{
$controller = new BaseController($serviceLocator->getServicelocator());
return $controller;
}
}
?>
在module.config.php
<?php
// ...
'controllers' => [
'factories' => [
'Application\Controller\BaseController' => 'Application\Controller\Factory\BaseControllerFactory',
// ...
],
// ...
我正在尝试将服务管理器注入控制器。
实际错误:
\vendor\zendframework\zend-servicemanager\src\Exception\ServiceLocatorUsageException.php:34
Service "Project\Service\ProjectServiceInterface" has been requested to plugin manager of type "Zend\Mvc\Controller\ControllerManager", but couldn't be retrieved. A previous exception of type "Zend\ServiceManager\Exception\ServiceNotFoundException" has been raised in the process. By the way, a service with the name "Project\Service\ProjectServiceInterface" has been found in the parent service locator "Zend\ServiceManager\ServiceManager": did you forget to use $parentLocator = $serviceLocator->getServiceLocator() in your factory code?
过程进行:
class BaseController extends AbstractActionController implements ServiceLocatorAwareInterface
{
public function __construct(\Zend\ServiceManager\ServiceLocatorInterface $sl)
{
$this->serviceLocator = $sl;
}
}
- 创建控制器并使用构造方法
- 将此
BaseController
扩展为AdminController
- 设置路由到
AdminController => /admin
使用
Module.php
public function getControllerConfig()
使用 closer 作为工厂创建注入 serviceLocator 的控制器对象
'Project\Controller\Project' => function($sm) { $serviceLocator = $sm->getServiceLocator(); return new \Project\Controller\ProjectController($serviceLocator); },
- 尝试使用
$this->getServiceLocator()->get('service_name')
- 发现缺少服务的异常......
现在的问题是:
/**
*
* @param ServiceLocatorInterface $sl
*/
public function __construct(\Zend\ServiceManager\ServiceLocatorInterface $sl)
{
$rtn = $sl->has('Project\Service\ProjectServiceInterface');
echo '<br />in Constructor: '.__FILE__;var_dump($rtn);
$this->serviceLocator = $sl;
}
public function getServiceLocator()
{
$rtn = $this->serviceLocator->has('Project\Service\ProjectServiceInterface');
echo '<br />in getServiceLocator: '.__FILE__;var_dump($rtn);
return $this->serviceLocator;
}
在 __constructor() 中找到服务 。在 getServiceLocator() 方法中找不到同名服务 .....
in Constructor: Project\Controller\BaseController.php bool(true) in getServiceLocator: Project\Controller\BaseController.php bool(false)
我错过了什么吗? SharedServiceManager 在这里做些什么吗?
本次练习的全部目的在于这条消息:
Deprecated: ServiceLocatorAwareInterface is deprecated and will be removed in version 3.0, along with the ServiceLocatorAwareInitializer. ...
在Zend Framework 2中有多种服务定位器(docs here),一种通用(主要用于你自己的服务),一种用于控制器,一种用于视图助手,一种用于验证器,...具体的也叫 plugin managers.
您收到的错误消息只是告诉您您使用了错误的服务定位器,是检索控制器的服务定位器,而不是一般的服务定位器。它还建议您如何解决问题:
did you forget to use $parentLocator = $serviceLocator->getServiceLocator() in your factory code
可能发生的事情(不是 100% 确定)是在构造函数中您传递了一个通用服务管理器的实例,并且一切正常。然后,由于控制器实现了 ServiceLocatorAwareInterface
,控制器服务定位器被注入到您的控制器中,覆盖您之前定义的那个。
此外,我认为在版本 3
中删除 ServiceLocatorAwareInterface
的决定之外的想法是您不在控制器中注入服务定位器,而是直接注入控制器依赖项。
您应该尽量避免在控制器中注入服务管理器或服务定位器。将实际依赖项(在您的情况下 'Project\Service\ProjectServiceInterface'
)直接注入 class 的 __construct
方法会好得多。 构造函数注入(依赖通过class构造函数提供)被认为是最佳实践 在 ZF2.
此模式可防止在没有依赖项的情况下实例化控制器(这会引发错误)。
如果您注入 ServiceLocator
或 ServiceManager
,您将从中解析 class 中的实际依赖项,那么不清楚 class 实际需要什么.您最终可能会在 class 实例中缺少一开始就不应该创建的依赖项。您需要在 class 内部进行自定义检查以查看实际依赖项是否可用,如果缺少则抛出错误。您可以使用构造函数依赖模式来防止编写所有这些自定义代码。
另一个问题是很难对 class 进行单元测试,因为您无法 为您的个人依赖项设置模拟 如此轻松。
阅读更多关于如何注入依赖项的信息 in my answer 到类似的问题。
更新
关于您遇到的问题。控制器 classes 实现了一个 ServiceLocatorAwareInterface
并且在构造你的控制器 classes 期间,ControllerManager
在 class 中注入了一个 ServiceLocator
。这种情况发生 here in the injectServiceLocator
method at line 208 in ControllerManager.php
。就像@marcosh 在他的回答中已经提到的那样,这可能是一个与您注入的服务定位器不同的服务定位器。在此 injectServiceLocator
方法中,您还可以找到您在问题中提到的弃用通知。
你的变量在 __construct
方法中可用,因为那时(在构建 class 之后)变量尚未被覆盖。稍后当您尝试在 getServiceLocator
方法中访问它时,它会被覆盖。
如果你真的需要ServiceLocator,你必须用工厂注入它
像这样
控制器:
<?php
namespace Application\Controller;
use Zend\Mvc\Controller\AbstractActionController;
use Zend\ServiceManager\ServiceLocatorInterface;
class BaseController extends AbstractActionController
{
protected $serviceLocator = null;
public function __construct(ServiceLocatorInterface $serviceLocator)
{
$this->setServiceLocator($serviceLocator);
}
public function setServiceLocator(ServiceLocatorInterface $serviceLocator)
{
$this->serviceLocator = $serviceLocator;
return $this;
}
public function getServiceLocator()
{
return $this->serviceLocator;
}
}
工厂:
<?php
namespace Application\Controller\Factory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
use Application\Controller\BaseController;
class BaseControllerFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator);
{
$controller = new BaseController($serviceLocator->getServicelocator());
return $controller;
}
}
?>
在module.config.php
<?php
// ...
'controllers' => [
'factories' => [
'Application\Controller\BaseController' => 'Application\Controller\Factory\BaseControllerFactory',
// ...
],
// ...