I am really confused on when to use getServiceLocator and when not to. As an example:
+ Module
-+ Helloworld
--+ src
---+ Controller
----+ IndexController.php
----+ IndexControllerFactory.php
---+ Service
----+ LogginService.php
----+ GreetingService.php
----+ GreetingServiceFactory.php
GreetingServiceFactory.php has the content:
<?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;
}
}
And IndexControllerFactory.php has the content:
<?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;
}
}
As you can see, I need $serviceLocator->getServiceLocator() in my ControllerFactory but not in my ServiceFactory. Why? Both use the same interface ServiceLocatorInterface which does not even define the getServiceLocator() method.
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'
),
)
I'd appreciate any clarification :)
Have a nice day!
The method getServiceLocator
is defined on the AbstractPluginManager
, since it implements the ServiceLocatorAwareInterface
. As Maks3w pointed out, it is not part of the ServiceLocatorInterface
, so avoid using it when implementing a service factory.
You can anyway define your factory as closure and still use it:
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;
},
),
);
}
}
While in this example $pm
is indeed a ServiceLocatorInterface
instance, you will still need to get a reference to the "main" service manager to access the 'greetingService'
.
ZF2 uses different service managers or plugin managers for controllers, services, view helpers, controller plugins, etc... That is mainly for type-hinting (look at the interface of the AbstractPluginManager
to understand how type strictness is achieved) and for security.
In this case, the security issue is disallowing access to services that are not controllers, especially with routes with a dynamic controller
parameter. That's why controllers are kept in a separate plugin manager.
Since the controller plugin manager is created from the "main" service manager, it also is initialized thanks to the ServiceLocatorAwareInterface
.
To make this more clear, I've added a graph of the relations (does not include the factory and don't take it as valid UML):
As you can see, I need $serviceLocator->getServiceLocator() in my ControllerFactory but not in my ServiceFactory. Why?
The controller factory is called by a different service manager instance (the "ControllerLoader") to the main one. This is so that the dispatcher can't result in an arbitrary class being instantiated by the main service manager.
As a result, the controller factory's $serviceLocator is not the one you need when you want to retrieve 'greetingService', as 'greetingService' is registered with the main service manager. To get the main server manager from the controller one, you use getServiceLocator() and you now have an instance of the main service manager from which you can get() 'greeting service'
This is known as 'peering'. i.e. the "ControllerLoader" service manager (the one configured via the 'controllers' key in the config or getControllerConfiguration() in a Module class) is set up with the main service manager as a peer.
Denitively you shouln't use getServiceLocator
since that method is not defined in ServiceLocatorInterface
instead use get()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With