我想知道加载复杂对象的最佳实践。首先,在解决问题之前,我将概述一些样板文件。假设如下:一个简单的域模型客户端使用 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;
}
此 Client 对象的工厂:
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;
}
}
接下来是使用 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;
请注意使用 HydratingResultset 从 ResultSet 返回完全形成的 Client 对象。这一切都很好。现在客户端对象有几个相关的对象作为属性,所以在使用 HydratingResultSet 时,我将添加一个 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 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、ClientWithAppointmentsFactory 等。似乎是多余的,并且不是很可重用。
解决方案 2 使用 FactoryInterface 中定义的选项参数将“加载”选项传递给水合器工厂,例如:
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 工厂,后者将它们传递给 hydrator 工厂:
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;
}
}
这似乎是一个干净的解决方案,因为可以根据请求定义依赖关系。但是我不认为这是按预期使用选项参数 - 文档指出该参数应该用于将构造函数参数传递给对象,而不是定义工厂应该使用什么逻辑来加载依赖项。
任何建议或实现上述目标的替代解决方案都会很棒。谢谢阅读。