Question

Most of the controllers in my application need to be able to access the 'account' of the currently logged in user, so I'm trying to inject that into each controller class. The way to do this seems to be creation of an abstract factory for the controller classes which can supply all the dependencies. So I've created the factory class with a method for doing this:

public function createServiceWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName)
{
    $controllerClassName = $requestedName.'Controller';

    $controller = new $controllerClassName();

    $account = $serviceLocator->get('Account');
    $controller->setAccount($account);

    return $controller;
}

however the $serviceLocator->get('Account'); line gives me the error:

Zend\Mvc\Controller\ControllerManager::get was unable to fetch or create an instance for Account

Calling $this->getServiceLocator()->get('Account') from within a controller action works just fine, so why doesn't this call work from within the controller factory?

Alternatively is there a better way to achieve this?

Was it helpful?

Solution

Look at the error

Zend\Mvc\Controller\ControllerManager::get was unable to fetch or create an instance for Account

The ControllerManager doesn't have a service named Account, it only has controllers. You need to get the main service locator from the controller manager

$account = $serviceLocator->getServiceLocator()->get('Account');

Alternatively is there a better way to achieve this?

Personally, I find the better approach is to use a controller plugin as a proxy to wrap the service

First create the plugin with a constructor that accepts your service instance as its parameter

<?php
namespace Application\Controller\Plugin;

use Zend\Mvc\Contoller\Plugin\AbstractPlugin;

class Account extends AbstractPlugin
{
    protected $account;

    public function __construct($account)
    {
         $this->account = $account;
    }

    // .. write plugin methods to proxy to your service methods 

    public function getId()
    {
        return $this->account->getId();
    }
}

Then make it available by registering it with the framework in your Module.php file using the getControllerPluginConfig() method and defining a closure as a factory to compose your plugin, and inject your service to its constructor

<?php
namespace Application;
class Module
{
    public function getControllerPluginConfig()
    {
        return array(
            'factories' => array(
                 'account' => function($sm) {
                      $account = $sm->getServiceLocator()->get('Account')
                      // create a new instance of your plugin, injecting the service it uses
                      $plugin = new \Application\Controller\Plugin\Account($account);
                      return $plugin;
                 },
             ),
        );
    }
}

Finally, in your controller (any controller), you can call your plugin methods to access your service

 public function actionIndex()
 {
     $accountId = $this->account()->getId();
 }
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top