Zend framework2 ZF FactoryInterface-使用选项参数配置加载依赖项

Zend framework2 ZF FactoryInterface-使用选项参数配置加载依赖项,zend-framework2,zend-framework3,zend-servicemanager,zend-hydrator,Zend Framework2,Zend Framework3,Zend Servicemanager,Zend Hydrator,我想知道加载复杂对象的最佳实践。 首先,在讨论这个问题之前,我将概述一些样板。 假设以下情况:使用tablegateway加载简单域模型客户端,每个阶段都使用工厂来注入依赖项: namespace My\Model\Client; class Client implements InputFilterProviderInterface { /**@var integer*/ protected $id; /**@var InputFilter*/ protecte

我想知道加载复杂对象的最佳实践。 首先,在讨论这个问题之前,我将概述一些样板。 假设以下情况:使用tablegateway加载简单域模型客户端,每个阶段都使用工厂来注入依赖项:

namespace My\Model\Client;
class Client implements InputFilterProviderInterface
{
    /**@var integer*/
    protected $id;
    /**@var InputFilter*/
    protected $inputFilter;
    /**@var Preferences */
    protected $preferences;
    /**@var Orders*/
    protected $orders;
    /**@var Contacts*/
    protected $contacts;      
}
此客户端对象的工厂:

namespace My\Model\Client;
class ClientFactory implements FactoryInterface
{
    public function($container, $requestedName, $options)
    {
        $client = new Client();
        $client->setInputFilter($container->get('InputFilterManager')->get('ClientInputFilter'));
        return $client;
    }
}
接下来是mapper工厂,它使用TableGateway:

namespace My\Model\Client\Mapper;
class ClientMapperFactory implements FactoryInterface
{
     public function __invoke($container, $requestedName, $options)
     {
        return new ClientMapper($container->get(ClientTableGateway::class));
     }
}
TableGatewayFactory:

namespace My\Model\Client\TableGateway
class ClientTableGatewayFactory implements FactoryInterface
{
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        $hydrator = new ArraySerialisable();
        $rowObjectPrototype = $container->get(Client::class);
        $resultSet = new HydratingResultSet($hydrator, $rowObjectPrototype);
        $tableGateway = new  TableGateway('clients', $container->get(Adapter::class), null, $resultSet);
        return $tableGateway;
请注意,使用结果集从结果集中返回完全格式的客户端对象。 这一切都很好。 现在,客户端对象有几个相关对象作为属性,因此在使用HyderatingResultSet时,我将添加一个AggregateHydrator来加载它们:

class ClientTableGatewayFactory implements FactoryInterface
{
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        **$hydrator = $container->get('HydratorManager')->get(ClientHydrator::class);**
        $rowObjectPrototype = $container->get(Client::class);
        $resultSet = new HydratingResultSet($hydrator, $rowObjectPrototype);
        $tableGateway = new  TableGateway('clients', $container->get(Adapter::class), null, $resultSet);
        return $tableGateway;
    }
最后,客户或工厂:

class ClientHydratorFactory implements FactoryInterface
{
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        //base ArraySerializable for Client object hydration
        $arrayHydrator = new ArraySerializable();
        $arrayHydrator->addStrategy('dateRegistered', new DateTimeStrategy());

        $aggregateHydrator = new AggregateHydrator();
        $aggregateHydrator->add($arrayHydrator);

        $aggregateHydrator->add($container->get('HydratorManager')->get(ClientsAddressHydrator::class));
        $aggregateHydrator->add($container->get('HydratorManager')->get(ClientsOrdersHydrator::class));
        $aggregateHydrator->add($container->get('HydratorManager')->get(ClientsPreferencesHydrator::class));
        $aggregateHydrator->add($container->get('HydratorManager')->get(ClientsContactsHydrator::class));
        return $aggregateHydrator;
    }
}
class ClientTableGatewayFactory implements FactoryInterface
{
     public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        $hydrator = $container->get('HydratorManager')->get(ClientHydrator::class, '', $options);
        $rowObjectPrototype = $container->get(Client::class);
        $resultSet = new HydratingResultSet($hydrator, $rowObjectPrototype);
        $tableGateway = new  TableGateway('clients', $container->get(Adapter::class), null, $resultSet);
        return $tableGateway;
}
。。。上述水合剂的要点如下:

class ClientsAddressHydrator implements HydratorInterface
{
    /** @var AddressMapper */
    protected $addressMapper;

    public function __construct(AddressMapper $addressMapper){
        $this->addressMapper = $addressMapper;
    }

    public function extract($object){return $object;}

    public function hydrate(array $data, $object)
    {
        if(!$object instanceof Client){
            return;
        }

        if(array_key_exists('id', $data)){
            $address = $this->addressMapper->findClientAddress($data['id']);
            if($address instanceof Address){
                $object->setAddress($address);
            }
        }
        return $object;
    }
}
我们终于开始讨论这个问题了。上面的方法工作得很好,可以非常干净地加载一个客户机对象,并且所有相关对象都已完全成形。但我有一些不需要整个对象图的资源—例如,在查看所有客户机的表时—不需要加载更多的信息

所以我一直在考虑如何使用工厂来选择要包含哪些依赖项

解决方案1 每个用例的工厂。如果只需要客户端数据(没有依赖项),则创建一系列工厂,即ClientFactory、SimpleClientFactory、ComplexClientFactory、ClientWithAppointsFactory等。这些工厂似乎是多余的,不太可重用

解决方案2 使用FactoryInterface中定义的options param将“加载”选项传递给制造商,例如:

  class ViewClientDetailsControllerFactory implements FactoryInterface
    {
         //all Client info needed - full object graph
         public function __invoke($container, $requestedName, $options)
         {
            $controller = new ViewClientDetailsController();
            $loadDependencies = [
                'loadPreferences' => true,
                'loadOrders' => true,
                'loadContacts' => true
             ];
            $clientMapper = $container->get(ClientMapper::class, '', $loadDependencies);
            return $controller;
         }
    }



   class ViewAllClientsControllerFactory implements FactoryInterface
    {
         //Only need Client data - no related objects
         public function __invoke($container, $requestedName, $options)
         {
            $controller = new ViewAllClientsController();
            $loadDependencies = [
                'loadPreferences' => false,
                'loadOrders' => false,
                'loadContacts' => false
             ];
            $clientMapper = $container->get(ClientMapper::class, '', $loadDependencies);
            return $controller;
         }
    }
映射器工厂将选项传递给tablegateway工厂,tablegateway工厂将选项传递给映射器工厂:

class ClientHydratorFactory implements FactoryInterface
{
    public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        //base ArraySerializable for Client object hydration
        $arrayHydrator = new ArraySerializable();
        $arrayHydrator->addStrategy('dateRegistered', new DateTimeStrategy());

        $aggregateHydrator = new AggregateHydrator();
        $aggregateHydrator->add($arrayHydrator);

        $aggregateHydrator->add($container->get('HydratorManager')->get(ClientsAddressHydrator::class));
        $aggregateHydrator->add($container->get('HydratorManager')->get(ClientsOrdersHydrator::class));
        $aggregateHydrator->add($container->get('HydratorManager')->get(ClientsPreferencesHydrator::class));
        $aggregateHydrator->add($container->get('HydratorManager')->get(ClientsContactsHydrator::class));
        return $aggregateHydrator;
    }
}
class ClientTableGatewayFactory implements FactoryInterface
{
     public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
    {
        $hydrator = $container->get('HydratorManager')->get(ClientHydrator::class, '', $options);
        $rowObjectPrototype = $container->get(Client::class);
        $resultSet = new HydratingResultSet($hydrator, $rowObjectPrototype);
        $tableGateway = new  TableGateway('clients', $container->get(Adapter::class), null, $resultSet);
        return $tableGateway;
}
最后,我们可以在这里定义要加载到客户端的信息量:

class ClientHydratorFactory implements FactoryInterface
    {
        public function __invoke(ContainerInterface $container, $requestedName, array $options = null)
        {
            //base ArraySerializable for Client object hydration
            $arrayHydrator = new ArraySerializable();
            $arrayHydrator->addStrategy('dateRegistered', new DateTimeStrategy());

            $aggregateHydrator = new AggregateHydrator();
            $aggregateHydrator->add($arrayHydrator);
            if($options['loadAddress'] === true){
                   $aggregateHydrator->add($container->get('HydratorManager')->get(ClientsAddressHydrator::class));            
            }
            if($options['loadOrders'] === true){
                $aggregateHydrator->add($container->get('HydratorManager')->get(ClientsOrdersHydrator::class));
            }
            if($options['loadPreferences'] === true){
                $aggregateHydrator->add($container->get('HydratorManager')->get(ClientsPreferencesHydrator::class));
            }
            if($options['loadContacts'] === true){
                $aggregateHydrator->add($container->get('HydratorManager')->get(ClientsContactsHydrator::class));
            }
            return $aggregateHydrator;
        }
    }
这似乎是一个干净的解决方案,因为每个请求都可以定义依赖项。但是,我认为这并不是有意使用options param——文档中说明,该参数应该用于将构造函数参数传递给对象,而不是定义工厂应该使用什么逻辑来加载依赖项


任何实现上述目标的建议或替代解决方案都将非常有用。感谢阅读。

创建一个包含所有可能组合的大调色板将不仅仅是一场噩梦,而是一场自杀

使用选项 我也不会建议你这样做。我的意思是,这没那么糟糕,但它有一个主要问题:每次你实例化你的水合器,你应该记住通过这些选项,否则你会得到一个“空水合器”。同样的逻辑适用于所有使用这些水合器的东西

既然你真的想去除你不需要的保湿剂,我建议你避免使用这种方法,因为这样你总是被迫声明你需要哪种保湿剂(老实说,我总是忘记去做…^^^)。 如果你添加了一个新的水合器,你就必须完成你的项目并添加新的选项。真的不值得努力

这就是为什么我向你提出下一个解决方案

去除不必要的水分 在99%的情况下,制图员使用水合器。因此,我认为有一个映射器会更干净,默认情况下,它总是返回相同类型的数据(->单个水合器),但它可以修改以删除某一组水合器

AggregateHydrator
内,所有水合器都转换为侦听器并连接到
EventManager
。在尝试获取所有事件时,我遇到了一些问题,因此我打开了创建聚合水合器的选项,以分离水合器:

类DetachatableAggregateHydrator扩展了AggregateHydrator
{
/**
*所有水合器列表(作为监听器)
*
*@var数组
*/
私有$listeners=[];
/**
*{@inherit}
*/
公共函数添加(HydratorInterface$hydrator,int$priority=self::DEFAULT\u priority):无效
{
$listener=新的HydratorListener($hydrator);
$listener->attach($this->getEventManager(),$priority);
$this->listeners[get_class($or)]=$listener;
}
/**
*拆下一个水合器并分离其侦听器
* 
*@param string$hydratorClass
*/
公共功能分离($hydratorClass)
{
$listener=$this->listeners[$hydratorClass];
$listener->detach($this->getEventManager());
未设置($listener);
取消设置($this->listeners[$hydratorClass]);
}
}
然后,在
表网关工厂中

类ClientTableGatewayFactory实现FactoryInterface
{
公共函数调用(ContainerInterface$container,$requestedName,数组$options=null)
{
$hydrator=$container->get('HydratorManager')->get(ClientHydrator::class);
$rowObjectPrototype=$container->get(客户端::类);
$resultSet=新的HyderatingResultSet($Hyderator,$rowObjectPrototype);
$adapter=$container->get(适配器::类);
$tableGateway=newtablegateway('clients',$adapter,null,$resultSet);
返回$tableGateway;
}
}
ClientHydrator工厂

类ClientHydratorFactory实现FactoryInterface { 公共函数调用(ContainerInterface$container,$requestedName,数组$options=null) { $aggregateHydrator=新的可拆卸aggregateHydrator(); $arrayHydrator=新的ArraySerializable(); $arrayHydrator->addStrategy('dateRegistered',new DateTimeStrategy()); $aggregateHydrator->add($arrayHydrator); $hydratorManager=$container->get('hydratorManager'); $aggregateHydrator->add($hydratorManager->get(ClientsAddressHydrator::class)); $aggregateHydrator->add($HydroMorManager->get(ClientsOrdersHydrator::class)); $aggregateHydrator->add($HydroMorManager->get(客户端)