Chathura86
Chathura86

Reputation: 15

Zend 2 Best Practice - Service Locator out side of Controller

Im new to Zend 2 and have this situation. Would like to know whats the best way to handle the situation.

Inside the Application module i have these classes

namespace Application\Foo;

class Base {
    protected $this->services; //service locator
    public function myFunc() {
        //needs service locator here
        $this->services;
    }
}

namespace Application\Foo;

class A extends Base {
    //can have some method that uses $this->services;
}

namespace Application\Foo;

class B extends Base {

}

As you can see i need the service locator inside my Base class. and then i want to use the A,B... extended classes in my controllers.

Thanks in advice


Update

According to jmleroux's suggestion i updated the code to look like this

namespace Application\Foo;

use Zend\ServiceManager\ServiceLocatorAwareInterface;
use Zend\ServiceManager\ServiceLocatorInterface;

class Base implements ServiceLocatorAwareInterface {
    protected $this->services; //service locator

    public function setServiceLocator(ServiceLocatorInterface $serviceLocator) {
        $this->services = $serviceLocator;
    }

    public function getServiceLocator() {
        return $this->services;
    }


    public function myFunc() {
        //needs service locator here
        $this->services;
    }
}

module.conf.php

service_manager => array(
    'invokables' => array(
        'my_ser' => 'Application\Foo\Base'
    )
)

in my controller

$this->serviceLocator->get('my_ser'); 

returns the base class with service locator loaded

now my question is how to get my extended classes(A, B...) with the service locator loaded. since

$x = new \Application\Foo\A(); 

does not have the service locator loaded. do i have to add all my extended classes to module config as invokables? or is there a better alternative.

Thanks again.


Update 2

Thanks all (@jmleroux specially) for the support. Found these solutions and im adding them here thinking it will help some one else

NOTE: Just a general idea. namespaces and class names are not accurate

Solution 1 (recommended) - with ServiceLocatorAwareInterface / ServiceLocatorAwareTrait for php 5.4+

each class implements ServiceLocatorAwareInterface and add it to the service manager

namespace Application\Foor;

class A implements ServiceLocatorAwareInterface {
protected $services;

public function setServiceLocator(ServiceLocatorInterface $serviceLocator) {
    $this->services = $serviceLocator;
}

public function getServiceLocator() {
        return $this->services;
    }
}

class B implements ServiceLocatorAwareInterface {
    use ServiceLocatorAwareTrait;
}

module.conf.php (or inside the Module getServiceConfig() function )

'service_manager' => array(
    'invokables' => array(
        'my_ser_a' => 'Application\Foo\A',
        'my_ser_b' => 'Application\Foo\B',
        ......
    )
)

call the service by name inside controllers


Solution 2 - with AbstractFactoryInterface

create a factory class

class MyFac implements AbstractFactoryInterface {
    public function canCreateServiceWithName(ServiceLocatorInterface $locator, $name, $requestedName) {
        if (class_exists($requestedName)){ \\eg. check Application\Foo\A exists
            return true;
        }

        return false;
    }

    public function createServiceWithName(ServiceLocatorInterface $locator, $name, $requestedName) {
        $class = new $requestedName;   \\eg. creates an instance of Application\Foo\A
        $class->setServiceLocator($locator); \\make sure your class has this method 
        return new $class;
    }
}

module.conf.php (or inside the Module getServiceConfig() function )

'service_manager' => array(
    'abstract_factories' => array(
        'Application\Foo\MyFac'
    )
)

in controllers call

$this->getServiceLocator()->get('Application\Foo\A');

Thanks.

Upvotes: 0

Views: 1640

Answers (1)

jmleroux
jmleroux

Reputation: 947

In order to access the service locator, your base class need to be declared as an invokable service implementing ServiceLocatorAwareInterface.php

If you are on PHP 5.4+, you can use ServiceLocatorAwareTrait.php

Upvotes: 2

Related Questions