zf2将参数传递给服务工厂

时间:2014-09-17 06:27:09

标签: dependency-injection zend-framework2 factory-pattern

我有以下课程:

  • CommonService
  • ClientService
  • InvoiceService

我想基于url:

为工厂加载正确的类(用于DI)
  • CommonService:localhost / service / common
  • ClientService:localhost / service / 客户端
  • InvoiceService:localhost / service / invoice

现在我正试图避免为我的每项服务创建一个工厂,我想以恐怖的方式做这件事:

<?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()

被调用它并不是那么好。

1 个答案:

答案 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)。回到你开始的地方!

解决方案?

  1. 为每项服务创建工厂
  2. 虽然你明确表示你不想这样做,但我只能假设它是因为你是懒惰的!这个你应该这样做的方式。为什么?您列出的服务似乎不相关,这意味着它们应该各自具有不同的依赖关系。注入这些特定依赖项的最简单方法是基于每个服务 - 你需要不到5分钟(你在问题中写了一个),你的代码会更简单,你会有更好的时间< em>当您的要求发生变化时

    1. 创建abstract factory
    2.   

      抽象工厂可以被视为“后备” - 如果管理器中不存在该服务,它将把它传递给附加到它的任何抽象工厂,直到其中一个能够返回一个对象。

      如果您的服务非常相似(它们具有相同的依赖关系,但配置不同),您可以创建一个抽象工厂,在内部检查所请求服务的名称(例如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));
          }
      } 
      

      需要考虑的事项

      • 仅当您的服务具有相同的构造函数签名时,此方法才有效。
      • 服务经理将检查&#39;如果抽象工厂负责在每次调用get时创建您的类,这意味着(不必要的)性能损失。
      • 如果您的某项服务发生了变化,您需要破解此更改,然后仍然可以创建重新测试每项服务。

        1. 使Zend\Di
        2. 变得复杂

      总之,这将允许您通过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;而且你还是需要去写一个特定的工厂。当正在创建的类具有相同的继承层次结构时,上述方法可能是首选方法。