I have the following classes:
- CommonService
- ClientService
- InvoiceService
And I would like to load the correct class with a factory (for DI) based on the url:
- CommonService: localhost/service/common
- ClientService: localhost/service/client
- InvoiceService: localhost/service/invoice
For now I'm trying to avoid creating a factory for each one of my services, and I'd like to do this dinamically:
<?php namespace App\Service\Factory;
use Zend\ServiceManager\FactoryInterface;
use Zend\ServiceManager\ServiceLocatorInterface;
class ServiceFactory implements FactoryInterface
{
/**
* Create service
* @param ServiceLocatorInterface $serviceLocator
* @return \App\Service\AbstractService
*/
public function createService(ServiceLocatorInterface $serviceLocator)
{
$servicename = ''; // how can I get something like this, based on the route ?
$service = $serviceLocator->get('Service\' . $servicename . 'Service');
}
}
I'd like to avoid, if possible, to compute the route inside the factory, because if one day this factory will be called from elsewhere, it won't work.
So how do you basically do a factory "deal with the problem of creating objects without specifying the exact class of object that will be created" with zend 2 ?
EDIT - USED SOLUTION
Edited again, here the final solution I preferred based on the accepted answer:
$apiName = str_replace(' ', '', ucwords(str_replace('_', ' ', $this->params('api'))));
$serviceName = str_replace(' ', '', ucwords(str_replace('_', ' ', $this->params('service'))));
$di = new Di();
$di->instanceManager()->setParameters(sprintf('App\Service\%s\%sService', $apiName, $serviceName), [
'service' => sprintf('%s\Service\%sService', $apiName, $serviceName),
]);
$service = $di->get(sprintf('App\Service\%s\%sService', $apiName, $serviceName));
AbstractService (parent class of any service)
<?php namespace App\Service;
use Zend\Log\Logger;
abstract class AbstractService
{
/**
* @var object|mixed
*/
protected $api;
/**
* @var \Zend\Log\Logger
*/
protected $logger;
/**
* Constructor
* @param mixed $service Api service class
* @param Logger $logger Logger instance
*/
public function __construct($service, Logger $logger)
{
$this->api = $service;
$this->logger = $logger;
}
}
Ideally, the $service parameter for the abstract constructor should be typed at least by interface, I'm working on it.
Zend\Di helps me defining the constructor api dynamically and that's all I wanted. AbstractFactory was easier to read, but as you pointed out, the fact that all abstract factories are invoked each time a
$serviceLocator->get()
is invoked it's not that great.