Zend framework2 ZF FactoryInterface-使用选项参数配置加载依赖项
我想知道加载复杂对象的最佳实践。 首先,在讨论这个问题之前,我将概述一些样板。 假设以下情况:使用tablegateway加载简单域模型客户端,每个阶段都使用工厂来注入依赖项: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
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(客户端)