ZF2何时使用getServiceLocator()而何时不使用

时间:2013-02-16 15:36:12

标签: php frameworks zend-framework2 service-locator

我真的很困惑何时使用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-&gt; getServiceLocator(),而不是在我的ServiceFactory中。为什么?两者都使用相同的接口ServiceLocatorInterface,它甚至没有定义getServiceLocator()方法。

module.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 个答案:

答案 0 :(得分:20)

方法getServiceLocatorAbstractPluginManager上定义,因为它实现了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):

Pseudo-UML

答案 1 :(得分:5)

  

正如您所看到的,我需要在我的ControllerFactory中使用$ serviceLocator-&gt; getServiceLocator(),而不是在我的ServiceFactory中。为什么?

控制器工厂由另一个服务管理器实例(“ControllerLoader”)调用。这使得调度程序不能导致主服务管理器实例化任意类。

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

这被称为'对等'。即,将“ControllerLoader”服务管理器(通过配置中的“控制器”键配置的服务管理器或Module类中的getControllerConfiguration()配置)与主服务管理器作为对等体进行设置。

答案 2 :(得分:3)

由于getServiceLocator未定义该方法,而是使用ServiceLocatorInterface

,否则您不能使用get()

答案 3 :(得分:0)

我提供此功能作为使用base module.config.php setup

的替代方案

现在我做了类似的事,但是使用了类似的东西。

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,然后是ServiceManager用于setServiceLocator方法。

我只想使用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);
}