我有以下课程:
我想基于url:
为工厂加载正确的类(用于DI)现在我正试图避免为我的每项服务创建一个工厂,我想以恐怖的方式做这件事:
<?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');
}
}
如果可能的话,我想避免计算工厂内的路线,因为如果有一天这个工厂将从其他地方调用,它将无法工作。
那你如何基本上做一个工厂“处理创建对象的问题而不用zend 2指定将要创建的对象的确切类?
编辑 - 使用的解决方案
再次编辑,这里是我根据接受的答案选择的最终解决方案:
$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 (任何服务的父类)
<?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;
}
}
理想情况下,抽象构造函数的 $ service 参数应至少由接口输入,我正在处理它。
Zend \ Di帮助我动态定义构造函数api,这就是我想要的。 AbstractFactory更容易阅读,但正如您所指出的那样,每次调用所有抽象工厂的事实
$serviceLocator->get()
被调用它并不是那么好。
答案 0 :(得分:8)
可以从工厂内获取Request
甚至Zend\Mvc\Controller\Plugin\Param
实例;然后,您可以访问所需的路线参数。
// within your factory
$params = $serviceLocator->get('ControllerPluginManager')->get('params');
$serviceName = $params->fromRoute('route_param_name');
然后会导致
$service = $serviceLocator->get('Service\' . $servicename . 'Service');
然而,这不会像你期望的那样起作用;对服务经理get
的调用意味着你需要另一个服务工厂加载'Service\' . $serviceName . 'Service'
- 所以你仍然需要为所述服务创建一个工厂(如果它不是&#39; invoakable&#39; class)。回到你开始的地方!
解决方案?
虽然你明确表示你不想这样做,但我只能假设它是因为你是懒惰的!这个是你应该这样做的方式。为什么?您列出的服务似乎不相关,这意味着它们应该各自具有不同的依赖关系。注入这些特定依赖项的最简单方法是基于每个服务 - 你需要不到5分钟(你在问题中写了一个),你的代码会更简单,你会有更好的时间< em>当您的要求发生变化时
抽象工厂可以被视为“后备” - 如果管理器中不存在该服务,它将把它传递给附加到它的任何抽象工厂,直到其中一个能够返回一个对象。
如果您的服务非常相似(它们具有相同的依赖关系,但配置不同),您可以创建一个抽象工厂,在内部检查所请求服务的名称(例如Service\Client
)并注入该服务所需的依赖项。
use Zend\ServiceManager\ServiceLocatorInterface;
use Zend\ServiceManager\AbstractFactoryInterface;
class FooAbstractFactory implements AbstractFactoryInterface
{
protected $config;
public function getConfig(ServiceLocatorInterface $serviceLocator) {
if (! isset($this->config)) {
$config = $serviceLocator->get('Config');
$this->config = $config['some_config_key'];
}
return $this->config;
}
public function canCreateServiceWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName)
{
$config = $this->getConfig($serviceLocator);
return (isset($config[$requestedName])) ? true : false;
}
public function createServiceWithName(ServiceLocatorInterface $serviceLocator, $name, $requestedName)
{
$config = $this->getConfig($serviceLocator);
$config = $config[$requestedName];
$className = $config['class_name'];
// This could be more complicated
return new $className($config, $this->getSomethingElseExample($serviceLocator));
}
}
需要考虑的事项
get
时创建您的类,这意味着(不必要的)性能损失。如果您的某项服务发生了变化,您需要破解此更改,然后仍然可以创建重新测试每项服务。
Zend\Di
总之,这将允许您通过Reflection在配置中定义服务的所有依赖关系,或者您的服务是否写得很好(参数是使用标准命名约定的类型提示)。
我还没有看到DI需要这种复杂性;虽然检查出来,但在非常 非常大型应用程序中,投入时间可能会有所帮助。
修改强>
另一个选择是创建一个基础工厂类(实现FactoryInterface
)
abstract class DefaultServiceFactory implements FactoryInterface {
public function createService(ServiceLocatorInterface $sl)
{
$className = $this->getClassName();
return new $className(
$this->getApiService($serviceLocator),
$this->getFooService($serviceLocator)
);
}
// Abstract or it could return the 'default' class name
// depending on requirements
abstract protected function getClassName();
protected function getApiService(ServiceLocatorInterface $sl)
{
return $sl->get('Default/Api/Service');
}
protected function getFooService(ServiceLocatorInterface $sl)
{
return $sl->get('Default/Foo/Service');
}
}
不幸的是,你不能再避免写一个具体的课了。但是,由于大多数代码都封装在基类中,它可能会让您的生活变得更容易一些。
class ApiServiceFactory extends DefaultServiceFactory
{
protected function getClassName() {
return '\Custom\Api\Service';
}
protected function getApiService(ServiceLocatorInterface $sl) {
return $sl->get('Another/Api/Service');
}
}
我会勉强鼓励你投资工厂每班选项,尽管使用抽象工厂确实没有问题,从我个人的经验来看,他们在某些时候不会涵盖边缘案例&#39;而且你还是需要去写一个特定的工厂。当正在创建的类具有相同的继承层次结构时,上述方法可能是首选方法。