1

I'm trying to inject the service manager into a controller.

Actual Error:

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

Service "Project\Service\ProjectServiceInterface" has been requested to plugin manager of type "Zend\Mvc\Controller\ControllerManager", but couldn't be retrieved. A previous exception of type "Zend\ServiceManager\Exception\ServiceNotFoundException" has been raised in the process. By the way, a service with the name "Project\Service\ProjectServiceInterface" has been found in the parent service locator "Zend\ServiceManager\ServiceManager": did you forget to use $parentLocator = $serviceLocator->getServiceLocator() in your factory code?

The process goes:

class BaseController extends AbstractActionController implements ServiceLocatorAwareInterface { public function __construct(\Zend\ServiceManager\ServiceLocatorInterface $sl) { $this->serviceLocator = $sl; } } 
  1. Create controller and use constructor method
  2. Extend this BaseController to AdminController
  3. Setup Routes to AdminController => /admin
  4. use Module.php

    public function getControllerConfig()

  5. Use closer as factory to create controller object injecting the serviceLocator

    'Project\Controller\Project' => function($sm) { $serviceLocator = $sm->getServiceLocator(); return new \Project\Controller\ProjectController($serviceLocator); },

  6. try to use $this->getServiceLocator()->get('service_name')
  7. Exception found for missing service.....

Now the problem is this:

/** * * @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; } 

Within the __constructor() the service IS FOUND. Within the getServiceLocator() method the service with the same name IS NOT FOUND....

in Constructor: Project\Controller\BaseController.php bool(true) in getServiceLocator: Project\Controller\BaseController.php bool(false)

Am I missing something? Is the SharedServiceManager doing something here?

The entire purpose of this exercise was due to this message:

Deprecated: ServiceLocatorAwareInterface is deprecated and will be removed in version 3.0, along with the ServiceLocatorAwareInitializer. ...

2
  • Could you please add the controller factory as well as the respective areas from your controller and service_manager configuration? Commented Apr 6, 2016 at 7:00
  • Thanks for the quick reply. The service_manager config is stated in point 5 above and the factory is generated within the Module.php => getControllerConfig() method. The controller calles $this->getServiceLocator()->get('service_name') and the exception is thrown. Commented Apr 6, 2016 at 7:31

3 Answers 3

2

If you really need the ServiceLocator, you have to inject it with a factory
Something like this

Controller:

<?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; } } 

Factory:

<?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; } } ?> 

in module.config.php

<?php // ... 'controllers' => [ 'factories' => [ 'Application\Controller\BaseController' => 'Application\Controller\Factory\BaseControllerFactory', // ... ], // ... 
Sign up to request clarification or add additional context in comments.

2 Comments

Thanks. That is roughly what I have (maybe not clear from OP). The principle is the same although I'm not creating it from a factory, I'm using a closure in Module.php The thing is this: when the controller factory is called via the constructor the ->get('service_name') is found. When the same service is requested via the getServiceLocator() it is not found... that is where the confusion lies. The service is there on instantiation BUT when calling it from the controller the serviceLocator seems to lose all knowledge of services...
Revisiting your examples and the suggestions below you code is correct in that you're removing the serviceAwareLocator interface from the BaseController. Therefore, your answer is also correct and would solve the problem. The recommendations of the others though are more inline what what I need for the future and how to prepare for ZF3. Thanks for your help
1

In Zend Framework 2 there are multiple service locators (docs here), one general (mainly used for your own services), one for controllers, one for view helpers, one for validators, ... The specific ones are also called plugin managers.

The error message you are receiving is just telling you that you are using the wrong service locator, the ones that retrieves controllers and not the general one. It is also suggesting you how to solve your problem:

did you forget to use $parentLocator = $serviceLocator->getServiceLocator() in your factory code

What is probably happening (not 100% sure about this) is that in the constructor you are passing in an instance of the general service manager, and everything works fine with it. Then, since the controller implements the ServiceLocatorAwareInterface, the controller service locator is injected into your controller, overriding the one that you defided before.

Moreover, I think that the idea beyound the decision of removing ServiceLocatorAwareInterface in version 3 is that you don't inject the service locator inside your controller, but instead you inject directly the controller dependencies.

4 Comments

Thank for the feedback. The Factory method uses the $serviceLocator->getServiceLocator() within the factory closure( Module:: getContollerConfig() ) (please see point 5 above) I believe there is some layering with the different service locators which is causing the problem but could be wrong. Regarding the dependencies: would you recommend creating holding variables (e.g. protected $projectService ) and use these...? There are a few services I'll need hence the thought behind the single serviceLocator..... I guess this goes against a thin controller...
you are using getServiceLocator in the factory and that's why you can retrieve the dependency in the conctructor. When your service locator is overridden by the ServiceLocatorAwareInterface, you are back to the controller service locator.
I would surely recommend to retrieve all the dependencies of your controller in the controller factory and then pass them through the constructor, holding them in protected (or even better, private) class properties
Thanks Marcosh. The problem was the SetServiceLocator() in my BaseController. This was being overwritten by the ServiceLocatorAwareInterface when the controller created (i believe) the way I solved it was to remove the allocation $this->serviceLocator = $serviceLocator; in the setServiceLocator() method. Thanks for your and Wilts help. The overall 'solution' or answer was that a different serviceLocator was being passed. NOTE: Probably best to use the factory method and assign the service(s) directly on instantiation (as per Wilts and your suggestions)
1

You should try to prevent injecting the service manager or service locator in the controller. It would be much better to inject the actual dependencies (in your case 'Project\Service\ProjectServiceInterface') directly into the __construct method of your class. Constructor injection (the dependencies are provided through a class constructor) is considered best practice in ZF2.

This pattern prevents the controller from ever being instantiated without your dependencies (it will throw an error).

If you inject a ServiceLocator or ServiceManager from which you will resolve the actual dependencies in the class, then it is not clear what the class actually needs. You can end up in a class instance with missing dependencies that should never have been created in the first place. You need to do custom checking inside the class to see if the actual dependency is available and throw an error if it is missing. You can prevent writing all this custom code by using the constructor dependency pattern.

Another issue is that it is harder to unit-test your class since you cannot set mocks for your individual dependencies so easily.

Read more on how to inject your dependencies in my answer to a similar question.

UPDATE

About the issue you encountered. Controller classes implement a ServiceLocatorAwareInterface and during construction of your controller classes the ControllerManager injects a ServiceLocator inside the class. This happens here in the injectServiceLocator method at line 208 in ControllerManager.php. Like @marcosh already mentioned in his answer, this might be a different service locator then you injected. In this injectServiceLocator method you also find the deprecation notice you mentioned in your question.

Yours is available in the __construct method because at that time (just after constructing the class) the variable is not yet overwritten. Later when you try to access it in your getServiceLocator method it is overwritten.

1 Comment

Thanks Wilt. Make sense. Using your and Marcosh info I believe the problem is resolved.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.