9

我真的很困惑何时使用 getServiceLocator 以及何时不使用。举个例子:

+ Module
-+ Helloworld
--+ src
---+ Controller
----+ IndexController.php
----+ IndexControllerFactory.php

---+ Service
----+ LogginService.php
----+ GreetingService.php
----+ GreetingServiceFactory.php

GreetingServiceFactory.php 内容如下:

<?php
namespace Helloworld\Service;

use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;


class GreetingServiceFactory implements FactoryInterface
{

    public function createService (ServiceLocatorInterface $serviceLocator)
    {
        $greetingService = new GreetingService();

        $greetingService->setEventManager($serviceLocator->get('eventManager'));

        $loggingService = $serviceLocator->get('loggingService');

        $greetingService->getEventManager()->attach('getGreeting', array(
            $loggingService,
            'onGetGreeting'
        ));

        return $greetingService;
    }
}

IndexControllerFactory.php 的内容如下:

<?php
namespace Helloworld\Controller;

use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class IndexControllerFactory implements FactoryInterface
{

    public function createService (ServiceLocatorInterface $serviceLocator)
    {
        $ctr = new IndexController();

        $ctr->setGreetingService($serviceLocator->getServiceLocator()
            ->get('greetingService'));
        return $ctr;
    }
}

如您所见,我在 ControllerFactory 中需要 $serviceLocator->getServiceLocator() 而在 ServiceFactory 中不需要。为什么?两者都使用相同的接口 ServiceLocatorInterface,它甚至没有定义 getServiceLocator() 方法。

模块.config.php:

'controllers' => array(
    'factories' => array(
        'Helloworld\Controller\Index' => 'Helloworld\Controller\IndexControllerFactory'
    )
)
,
'service_manager' => array(
    'invokables' => array(
        'loggingService' => 'Helloworld\Service\LoggingService'
    ),
    'factories' => array(
        'greetingService'=> 'Helloworld\Service\GreetingServiceFactory'
    ),
)

我会很感激任何澄清:)

祝你今天过得愉快!

4

4 回答 4

20

该方法getServiceLocator是在 上定义的AbstractPluginManager,因为它实现了ServiceLocatorAwareInterface. 正如 Maks3w 指出的那样,它不是 的一部分ServiceLocatorInterface,因此在实现服务工厂时避免使用它。

无论如何,您都可以将您的工厂定义为闭包并仍然使用它:

class MyModule
{
    public function getControllerConfig()
    {
        return array(
            'factories' => array(
                'IndexController' => function (
                    \Zend\ServiceManager\AbstractPluginManager $pm
                ) {
                    $ctr = new IndexController();

                    $ctr->setGreetingService(
                        $pm
                            ->getServiceLocator()
                            ->get('greetingService')
                    );

                    return $ctr;
                },
            ),
        );
    }
}

虽然在此示例$pm中确实是一个ServiceLocatorInterface实例,但您仍需要获取对“主”服务管理器的引用才能访问'greetingService'.

ZF2 对控制器、服务、视图助手、控制器插件等使用不同的服务管理器或插件管理器......主要用于类型提示(查看接口AbstractPluginManager以了解如何实现类型严格性)和安全性。

在这种情况下,安全问题是不允许访问非控制器的服务,尤其是带有动态controller参数的路由。这就是为什么控制器被保存在一个单独的插件管理器中的原因。

由于控制器插件管理器是从“主”服务管理器创建的,因此它也通过ServiceLocatorAwareInterface.

为了更清楚地说明这一点,我添加了关系图(不包括工厂并且不将其视为有效的 UML):

伪UML

于 2013-02-16T21:11:09.243 回答
5

如您所见,我在 ControllerFactory 中需要 $serviceLocator->getServiceLocator() 而在 ServiceFactory 中不需要。为什么?

控制器工厂由与主服务管理器实例(“ControllerLoader”)不同的服务管理器实例调用。这样调度程序就不会导致主服务管理器实例化任意类。

因此,当您想要检索“greetingService”时,控制器工厂的 $serviceLocator 不是您需要的,因为“greetingService”已在主服务管理器中注册。要从控制器中获取主服务器管理器,请使用 getServiceLocator() 并且您现在有一个主服务管理器的实例,您可以从中获取()“问候服务”

这被称为“对等互连”。即,“ControllerLoader”服务管理器(通过配置中的“控制器”键或模块类中的 getControllerConfiguration() 配置的服务管理器)与主服务管理器一起设置为对等点。

于 2013-02-17T11:28:16.490 回答
3

拒绝你不应该使用getServiceLocator,因为该方法没有定义,ServiceLocatorInterface而是使用get()

于 2013-02-16T15:45:04.963 回答
0

我提供这个作为使用基本 module.config.php 设置的替代方案

现在我正在做类似的事情,但使用类似的东西。

class BaseServices extends AbstractActionController
implements ServiceLocatorAwareInterface{
    ...
  public function setServiceLocator(ServiceLocatorInterface $serviceLocator){
        if($serviceLocator instanceof ControllerManager){
          $this->service_locator = $serviceLocator->getServiceLocator();

          $this->entities_service = $this->service_locator
            ->get('entities_service');

          $this->session = new Session(array(
            'entities_service'=>$this->entities_service,
            'service_locator'=>$this->service_locator,
          ));
          return $this;
        }
      }
  }
...
}

现在让我难过的事情是不得不意识到我只需要使用在扩展此类的任何控制器的实例化过程中使用的第一个服务定位器......

在实例化时:这个类首先得到一个 ControllerManager,然后是 setServiceLocator 方法的 ServiceManager。

我只想使用 ControllerManger 及其方法让 ServiceManager 实例化我的工厂;

我的 module.config.php 的部分内容

module.config.php
{
...
'service_manager' =>
  'abstract_factories' => array(
    'Zend\Log\LoggerAbstractServiceFactory',
   ),
  'factories' => array(
       'entities_service' => 'Service\Providers\Entities',
  ),
   'invokables' => array(
     'post'      => 'Service\Controllers\Runtime\Post',
    ),
  ),
}

现在我可以使用类似下面的东西来过滤正确的 ServiceLocator ......但我喜欢使用尽可能少的样板......

interface AbstractFactoryInterface
{
  public function canCreateServiceWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName);

  public function createServiceWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName);
} 
于 2015-08-09T20:30:01.750 回答