I have an abstract service class SAbstract
which is inherited by ConcreteServiceA
and ConcreteServiceB
. Now I am instantiating ConcreteServiceA
in the factory class of my controller and inject the service in my controller.
In a specific action in my controller I want to exchange ConcreteServiceA
with ConcreteServiceB
to change behavior. Because they have same interface (abstract class SAbstract
) I could inject it in my controller as well (the services are a Strategy-Pattern).
But I don't want to instantiate ConcreteServiceB
directly in my controller to keep my code clean (for easy refactoring and exchanging behavior).
A possible solution is to create a second factory for my controller which injects ConcreteServiceB
instead of ConcreteServiceA
but then I have duplicated lots of code which is not good...
Another solution would be to inject both services in my controller (but this "smells" like bad code).
Is a delegator factory the right way to do this? Then I have to implement setters in my controller...
Is there a better way?
I tried to schematically visualize my class relationships.
AbstractService <|--<inherit>- ConcreteServiceA
AbstractService <|--<inherit>- ConcreteServiceB
Controller -<use>--> AbstractService
Controller:ActionA -<use>--> ConcreteServiceA:exportAction()
Controller:ActionB -<use>--> ConcreteServiceB:exportAction()
In a specific action in my controller I want to exchange ConcreteServiceA with ConcreteServiceB to change behavior. Because they have same interface.
You can configure the route to use a different controller service name for each action; then configure a controller factory to inject the required service using configuration.
The route config could look like this.
'router' => [
'routes' => [
'foo' => [
'type' => 'literal',
'options' => [
'route' => '/foo',
'defaults' => [
'controller' => 'MyControllerWithFooService',
'action' => 'actionThatNeedsFooService',
],
],
],
'bar' => [
'type' => 'literal',
'options' => [
'route' => '/bar',
'defaults' => [
'controller' => 'MyControllerWithBarService',
'action' => 'actionThatNeedsBarService',
],
],
],
],
]
Then add the config for the services and controllers.
'app_config' => [
'MyControllerWithFooService' => [
'service_name' => 'FooService',
],
'MyControllerWithFooService' => [
'service_name' => 'BarService',
],
],
'service_manager' => [
'factories' => [
'FooService' => 'FooServiceFactory'
'BarService' => 'BarServiceFactory'
],
],
'controllers' => [
'factories' => [
'MyControllerWithFooService' => 'MyControllerServiceFactory'
'MyControllerWithBarService' => 'MyControllerServiceFactory'
],
]
The MyControllerServiceFactory
could be very simple.
class MyControllerServiceFactory
{
public function __invoke($controllerManager, $name, $requestedName)
{
$sm = $controllerManager->getServiceLocator();
$config = $sm->get('config');
if (empty($config['app_config'][$requestedName])) {
throw new ServiceNotCreatedException('No config set!');
}
$serviceName = $config['app_config'][$requestedName]['service_name'];
$service = $sm->get($serviceName);
return new MyController($service);
}
}