【问题标题】:zf2 controller factory serviceLocatorzf2控制器工厂服务定位器
【发布时间】:2016-04-06 05:44:27
【问题描述】:

我正在尝试将服务管理器注入控制器。

实际错误:

\vendor\zendframework\zend-servicemanager\src\Exception\ServiceLocatorUsageException.php:34

已向“Zend\Mvc\Controller\ControllerManager”类型的插件管理器请求服务“Project\Service\ProjectServiceInterface”,但无法检索。 在此过程中引发了先前类型为“Zend\ServiceManager\Exception\ServiceNotFoundException”的异常。 顺便说一句,在父服务定位器“Zend\ServiceManager\ServiceManager”中找到了一个名为“Project\Service\ProjectServiceInterface”的服务:您是否忘记在您的工厂中使用 $parentLocator = $serviceLocator->getServiceLocator()代码?

流程如下:

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的控制器对象

    '项目\控制器\项目' => 函数($sm){ $serviceLocator = $sm->getServiceLocator(); 返回新的 \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() 方法中,同名服务未找到....

在构造函数中:Project\Controller\BaseController.php 布尔(真) 在 getServiceLocator 中:Project\Controller\BaseController.php 布尔(假)

我错过了什么吗? SharedServiceManager 在这里做了什么吗?

这个练习的全部目的是因为这条信息:

已弃用:ServiceLocatorAwareInterface 已弃用,将在 3.0 版中与 ServiceLocatorAwareInitializer 一起删除。 ...

【问题讨论】:

  • 能否从您的controllerservice_manager 配置中添加控制器工厂以及各自的区域?
  • 感谢您的快速回复。 service_manager 配置在上面的第 5 点中说明,工厂是在 Module.php => getControllerConfig() 方法中生成的。控制器调用 $this->getServiceLocator()->get('service_name') 并抛出异常。

标签: php zend-framework2 service-locator


【解决方案1】:

如果你真的需要 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',
            // ...
        ],
// ...

【讨论】:

  • 谢谢。这大致就是我所拥有的(可能从 OP 中不清楚)。原理是一样的,尽管我不是从工厂创建它,而是在 Module.php 中使用闭包 事情是这样的:当通过构造函数调用控制器工厂时 ->get('service_name') 是成立。当通过 getServiceLocator() 请求相同的服务时,找不到它......这就是混乱所在。该服务在实例化时存在,但是当从控制器调用它时 serviceLocator 似乎失去了所有服务知识......
  • 重新访问您的示例和代码下方的建议是正确的,因为您正在从 BaseController 中删除 serviceAwareLocator 接口。因此,您的答案也是正确的,并且可以解决问题。不过,其他人的建议更符合我对未来的需求以及如何为 ZF3 做准备。感谢您的帮助
【解决方案2】:

在 Zend Framework 2 中有多个服务定位器(docs here),一个通用(主要用于您自己的服务),一个用于控制器,一个用于视图助手,一个用于验证器,...具体的还有称为插件管理器

您收到的错误消息只是告诉您您使用了错误的服务定位器,即检索控制器的服务定位器而不是通用服务定位器。它还建议您如何解决问题:

您是否忘记在工厂代码中使用 $parentLocator = $serviceLocator->getServiceLocator()

可能发生的事情(不是 100% 肯定)是在构造函数中传递了一个通用服务管理器的实例,并且一切正常。然后,由于控制器实现了ServiceLocatorAwareInterface,控制器服务定位器被注入到您的控制器中,覆盖您之前定义的那个。

此外,我认为在版本 3 中删除 ServiceLocatorAwareInterface 的决定之外的想法是,您不会在控制器中注入服务定位器,而是直接注入控制器依赖项。

【讨论】:

  • 感谢您的反馈。 Factory 方法在工厂关闭(Module::getContollerConfig())中使用 $serviceLocator->getServiceLocator() (请参阅上面的第 5 点)我相信不同的服务定位器存在一些分层,这会导致问题,但可能是错误的。关于依赖关系:您是否建议创建持有变量(例如受保护的 $projectService )并使用这些...?我需要一些服务,因此单个 serviceLocator 背后的想法......我想这与瘦控制器背道而驰......
  • 您在工厂中使用getServiceLocator,这就是您可以在构造函数中检索依赖项的原因。当您的服务定位器被ServiceLocatorAwareInterface 覆盖时,您将返回到控制器服务定位器。
  • 我肯定会建议在控制器工厂中检索控制器的所有依赖项,然后将它们传递给构造函数,将它们保存在 protected(甚至更好,private)类属性中
  • 谢谢马科什。问题是我的 BaseController 中的 SetServiceLocator()。当控制器创建(我相信)我解决它的方式是删除分配 $this->serviceLocator = $serviceLocator; 时,这被 ServiceLocatorAwareInterface 覆盖在 setServiceLocator() 方法中。感谢您和威尔茨的帮助。整体“解决方案”或答案是正在传递不同的 serviceLocator。注意:可能最好使用工厂方法并直接在实例化时分配服务(根据 Wilts 和您的建议)
【解决方案3】:

您应该尽量避免在控制器中注入服务管理器或服务定位器。将实际依赖项(在您的情况下为 'Project\Service\ProjectServiceInterface')直接注入您的类的 __construct 方法会更好。 构造函数注入(依赖项通过类构造函数提供)在 ZF2 中被认为是最佳实践

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

如果您注入一个ServiceLocatorServiceManager,您将从中解决类中的实际依赖关系,那么不清楚该类实际需要什么。您最终可能会得到一个缺少依赖项的类实例,而这些依赖项本来就不应该被创建。您需要在类内部进行自定义检查以查看实际依赖项是否可用,如果缺少则抛出错误。您可以使用构造函数依赖模式来防止编写所有这些自定义代码。

另一个问题是对你的类进行单元测试更加困难,因为你不能为你的个人依赖项设置模拟

详细了解如何将您的依赖项 in my answer 注入到类似问题中。

更新

关于您遇到的问题。控制器类实现ServiceLocatorAwareInterface,并且在构造控制器类期间ControllerManager 在类中注入ServiceLocator。这发生在here in the injectServiceLocator method at line 208 in ControllerManager.php。就像@marcosh 在他的回答中已经提到的那样,这可能是您注入的不同服务定位器。在这个injectServiceLocator 方法中,您还可以找到您在问题中提到的弃用通知。

Yours 在__construct 方法中可用,因为那时(就在构造类之后)该变量还没有被覆盖。稍后当您尝试在 getServiceLocator 方法中访问它时,它会被覆盖。

【讨论】:

  • 谢谢威尔特。说得通。使用您和 Marcosh 的信息,我相信问题已解决。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-05-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-19
  • 1970-01-01
相关资源
最近更新 更多