ZF2, когда использовать getServiceLocator(), а когда нет

Я действительно запутался, когда использовать 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;
    }
}

Как видите, мне нужна $serviceLocator->getServiceLocator() в моей ControllerFactory, но не в моей 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'
    ),
)

Буду рад любым разъяснениям :)

Хорошего дня!


person machete    schedule 16.02.2013    source источник


Ответы (4)


Метод 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

person Ocramius    schedule 16.02.2013

Как видите, мне нужна $serviceLocator->getServiceLocator() в моей ControllerFactory, но не в моей ServiceFactory. Почему?

Фабрика контроллеров вызывается экземпляром диспетчера служб ("ControllerLoader"), отличным от основного. Это делается для того, чтобы диспетчер не мог привести к созданию экземпляра произвольного класса главным диспетчером служб.

В результате $serviceLocator фабрики контроллеров не тот, который вам нужен, когда вы хотите получить 'greetingService', так как 'greetingService' зарегистрирован в главном диспетчере служб. Чтобы получить диспетчер основного сервера от контроллера, вы используете getServiceLocator(), и теперь у вас есть экземпляр диспетчера основного сервиса, из которого вы можете получить () «службу приветствия»

Это известно как «пиринг». т. е. диспетчер служб «ControllerLoader» (тот, который настроен с помощью ключа «контроллеры» в конфигурации или getControllerConfiguration() в классе модуля) настраивается с главным диспетчером служб в качестве однорангового узла.

person Rob Allen    schedule 17.02.2013

Определенно вы не должны использовать getServiceLocator, поскольку этот метод не определен в ServiceLocatorInterface, вместо этого используйте get()

person Maks3w    schedule 16.02.2013

Я предлагаю это как альтернативу, использующую базовую настройку 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, а затем 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);
} 
person jumpinJack    schedule 09.08.2015