Dependency injection zf2将参数传递给服务工厂
我有以下课程:Dependency injection zf2将参数传递给服务工厂,dependency-injection,zend-framework2,factory-pattern,Dependency Injection,Zend Framework2,Factory Pattern,我有以下课程: 公共服务 客户服务 发票服务 我想根据url使用工厂(对于DI)加载正确的类: CommonService:localhost/service/common ClientService:localhost/service/客户端 发票服务:本地主机/服务/发票 目前,我正在努力避免为我的每项服务创建工厂,我希望以友好的方式做到这一点: 可以从工厂内获取请求甚至Zend\Mvc\Controller\Plugin\Param实例;然后,您可以从那里访问所需的路由参数 //
- 公共服务
- 客户服务
- 发票服务
- CommonService:localhost/service/common
- ClientService:localhost/service/客户端
- 发票服务:本地主机/服务/发票
可以从工厂内获取请求
甚至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\'$服务名。”服务“
——因此您仍然需要为所述服务创建工厂(如果它不是“可开票”类)。回到你开始的地方
解决方案
为每项服务创建工厂
虽然你明确地说你不想这样做,但我只能假设这是因为你太懒了!你应该这样做。为什么?您列出的服务似乎不相关,这意味着它们应该具有不同的依赖关系。注入这些特定依赖项的最简单方法是基于每个服务-这将花费您不到5分钟的时间(您在问题中编写了一个),您的代码将更简单,并且当您的需求发生变化时,您将有更好的时间
创建一个
抽象工厂可以被视为“回退”——如果服务在管理器中不存在,它会将其传递给任何附加到它的抽象工厂,直到其中一个能够返回对象
如果您的服务非常相似(它们具有相同的依赖项,但配置不同),您可以创建一个抽象工厂,在内部检查请求的服务的名称(例如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
时创建您的类,这意味着(不必要的)性能损失
- 如果您的某个服务发生了更改,您需要在该更改中进行黑客攻击,然后重新测试仍然可以创建的每个服务
- 变得复杂化
总之,这将允许您在配置中定义对服务的所有依赖关系,或者如果您的服务编写良好(参数使用标准命名约定暗示类型),则可以通过反射来定义
我还没有看到DI需要这种复杂性;尽管可以查看,但在一个非常大的应用程序中,时间投资可能是有益的
编辑
另一个选项是创建一个基本工厂类(实现FactoryInterface
)
不幸的是,您将无法避免再次编写一个具体的类。然而,由于大部分代码都封装在基类中,这可能会让您的生活变得更轻松
class ApiServiceFactory extends DefaultServiceFactory
{
protected function getClassName() {
return '\Custom\Api\Service';
}
protected function getApiService(ServiceLocatorInterface $sl) {
return $sl->get('Another/Api/Service');
}
}
我极力鼓励你投资每类工厂选项,尽管使用抽象工厂确实没有问题,但从我个人的经验来看,在某些情况下,抽象工厂不会涵盖“边缘案例”,你无论如何都需要编写一个特定的工厂。当创建的类具有相同的继承层次结构时,上述方法可能是首选方法。嘿,非常感谢您的回答。我的服务类都是相同的,具有相同的依赖项和相同的父类。唯一不同的是他们所指的“真正的api”类。你认为抽象工厂是实现这一目标的一个坏方法吗?我希望每次服务都避免使用工厂,因为所有工厂都是一样的。也许我应该查一下Zend\Di。你认为(我会用一个示例服务类来更新我的想法)(顺便说一句,我会接受你的回答,但我想在做这件事之前听听你对我的观点)抽象工厂当然是一种有效的方式;记住我注意到的缺点。另一种选择,我倾向于使用“抽象基工厂”(不是AbstractFactoryInterface
,而是AbstractClass BaseFactory实现FactoryInterface
),然后您可以仅为不同的服务(同样是每个服务一个类)扩展它。我将用另一个例子更新我的答案。谢谢你的例子,但我不是一个很喜欢它的人,它再次迫使我为每个类创建一个工厂(不是真正的工厂),我宁愿尝试Zend\Di,它对我来说太完美了,我会接受你的答案,因为你给我指明了一个上帝的方向(这就是我所问的:))并用我使用的解决方案更新我的问题同样,ZF本身似乎不是Zend\Di
的超级粉丝。
class ApiServiceFactory extends DefaultServiceFactory
{
protected function getClassName() {
return '\Custom\Api\Service';
}
protected function getApiService(ServiceLocatorInterface $sl) {
return $sl->get('Another/Api/Service');
}
}