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;
    }
}
  1. 创建控制器并使用构造方法
  2. 将此 BaseController 扩展为 AdminController
  3. 设置路由到AdminController => /admin
  4. 使用Module.php

    public function getControllerConfig()

  5. 使用 closer 作为工厂创建注入 serviceLocator 的控制器对象

    'Project\Controller\Project' => function($sm) { $serviceLocator = $sm->getServiceLocator(); return new \Project\Controller\ProjectController($serviceLocator); },

  6. 尝试使用$this->getServiceLocator()->get('service_name')
  7. 发现缺少服务的异常......

现在的问题是:

/**
 * 
 * @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.

此模式可防止在没有依赖项的情况下实例化控制器(这会引发错误)。

如果您注入 ServiceLocatorServiceManager,您将从中解析 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',
            // ...
        ],
// ...