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实例;然后,您可以从那里访问所需的路由参数 //

我有以下课程:

  • 公共服务
  • 客户服务
  • 发票服务
我想根据url使用工厂(对于DI)加载正确的类:

  • 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');
        }
    }