Zend framework2 使用水合器创建复杂模型:聚合/策略
我以前使用一个服务层构建了复杂的模型(一个包含许多其他类型模型的对象),并将不同的映射器作为构造函数传入 例如 以上是一个简化的例子。在我的领域中,我使用工厂中使用的多个服务来创建复杂的对象图 在阅读了关于聚合水合器的非常有趣的文章之后,我尝试采用这种方法来简化对象创建过程。我喜欢将RowObjectPrototype设置为要返回的对象来创建一个Ulset的想法 我想我需要一些关于如何在现实世界中实现这一点的建议。例如,当使用AggregateHydrator时,我可以根据传递到该聚合器的用户id加载用户约会历史记录Zend framework2 使用水合器创建复杂模型:聚合/策略,zend-framework2,zend-hydrator,Zend Framework2,Zend Hydrator,我以前使用一个服务层构建了复杂的模型(一个包含许多其他类型模型的对象),并将不同的映射器作为构造函数传入 例如 以上是一个简化的例子。在我的领域中,我使用工厂中使用的多个服务来创建复杂的对象图 在阅读了关于聚合水合器的非常有趣的文章之后,我尝试采用这种方法来简化对象创建过程。我喜欢将RowObjectPrototype设置为要返回的对象来创建一个Ulset的想法 我想我需要一些关于如何在现实世界中实现这一点的建议。例如,当使用AggregateHydrator时,我可以根据传递到该聚合器的用户i
class UserModelHydratorFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator) {
$serviceManager = $serviceLocator->getServiceLocator();
/**
* Core hydration
*/
$arrayHydrator = new ArraySerializable();
$arrayHydrator->addStrategy('dateRegistered', new DateTimeStrategy());
$aggregateHydrator = new AggregateHydrator();
$aggregateHydrator->add($arrayHydrator);
$aggregateHydrator->add($serviceLocator->get('Hydrator\Address'));
$aggregateHydrator->add($serviceLocator->get('Hydrator\Appointments'));
return $aggregateHydrator;
}
}
…例如,用户地址如下所示:
class UserAddressHydrator implements HydratorInterface{
protected $locationMapper;
public function __construct(LocationMapper $locationMapper){
$this->locationMapper = $locationMapper;
}
public function hydrate(array $data, $object){
if(!$object instanceof User){
return;
}
if(array_key_exists('userId', $data)){
$object->setAddress($this->locationMapper->findByClientId($data['userId']));
}
return $object;
}
}
这很好用。虽然使用AggregateHydrator方法,但这意味着每一个具有地址作为属性的对象都需要它自己的hydrator。因此,如果我正在构建一个公司模型,那么需要另一个(几乎相同的)hydrator,它也有一个地址,因为上面的hydrator是硬编码的,用于填充用户模型(并且需要包含userId密钥的数据)。这意味着,对于每个关系/交互(has-a),都需要它自己的工具来生成它。这正常吗?所以我需要一个UserAddressHydrator,CompanyAddressHydrator,SupplierAddressHydrator,AppointmentAddressHydrator-所有这些都与上面的代码几乎相同,只是填充了一个不同的对象
如果只有一个addressor,它接受一个addressId并返回地址模型,那么会更简洁。这让我想到了一个更完美的方法或策略,尽管它们只对一个值起作用,因此不能通过传入数组查看pk/fk/Identification键是否存在并基于此加载
我希望能对这种方法进行一些澄清,我觉得我在这一过程中迷失了方向。你完全正确。或策略只对与实体成员对应的单个值起作用。因此,你必须为你的补水器添加一些策略。另一方面,您可以继承
\Zend\hydractor\AbstractHydrator
并覆盖addStrategy()
方法来处理具有多个名称的数组。使用该解决方案,您可以将相同的值设置为添加到addStrategy()
的数组的值
简单水合器策略
这个例子展示了一个简单的水合器策略的使用
class UserHydratorFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $oServiceLocator)
{
$oHydrator = (new ClassMethods(false))
->addStrategy('address', new AddressHydratorStrategy())
->addStrategy('company', new AddressHydratorStrategy());
return $oHydrator;
}
}
这是用户实体的示例或工厂。在这个工厂中,调用一个普通的ClassMethods
hydrator。这种类型的水合器假定实体中的getter和setter方法用于水合实体成员。此外,还为成员地址和公司添加了策略
class AddressHydratorStrategy extends DefaultStrategy
{
public function hydrate($aData)
{
return (new ClassMethods(false))
->hydrate($aData, new AdressEntity())
}
}
该策略只允许添加某种类型的子实体。如果数据中有地址或公司密钥,则地址实体将添加到这些成员中
class UserEntity
{
protected $name;
protected $address;
protected $company;
public function getName() : string
{
return $this->name;
}
public function setName(string $sName) : UserEntity
{
$this-> name = $sName;
return $this;
}
public function getAddress() : AddressEntity
{
return $this->address;
}
public function setAddress(AddressEntity $oAddress) : UserEntity
{
$this->address = $oAddress;
return $this;
}
public function getCompany() : AddressEntity
{
return $this->company;
}
public function setCompany(AddressEntity $oCompany) : UserEntity
{
$this->company = $oCompany;
return $this;
}
}
你注意到字体提示了吗?例如,setAddress
方法将AddressEntity
对象作为参数。这个对象将由我们添加到ClassMethods
or的策略生成
class UserModelHydratorFactory implements FactoryInterface
{
public function createService(ServiceLocatorInterface $serviceLocator) {
$serviceManager = $serviceLocator->getServiceLocator();
/**
* Core hydration
*/
$arrayHydrator = new ArraySerializable();
$arrayHydrator->addStrategy('dateRegistered', new DateTimeStrategy());
$aggregateHydrator = new AggregateHydrator();
$aggregateHydrator->add($arrayHydrator);
$aggregateHydrator->add($serviceLocator->get('Hydrator\Address'));
$aggregateHydrator->add($serviceLocator->get('Hydrator\Appointments'));
return $aggregateHydrator;
}
}
下一步是调用带有一些数据的对象,这些数据将生成一个嵌套复杂的UserEntity
对象
$oUserHydrator = $this->getServiceLocator(UserHydrator::class);
$oUserHydrator->hydrate(
[
'name' => 'Marcel',
'address' =>
[
'street' => 'bla',
'zipcode' => 'blubb',
],
'company' =>
[
'street' => 'yadda',
'zipcode' => 'yadda 2',
],
],
new UserEntity()
);
结果集中的用法
为了让它更清楚,这里有一个小例子,我如何在结果集中直接使用补水剂和策略
class UserTableGateway extends TableGateway
{
public function __construct(Adapter $oAdapter, $oUserHydrator)
{
$oPrototype = new HydratingResultSet(
$oHydrator,
new UserEntity()
);
parent::__construct('user_table', $oAdapter, null, $oPrototype);
}
public function fetchUser()
{
// here a complex join sql query is fired
// the resultset is of the hydrated prototype
}
}
在本例中,TableGateway
类使用原型进行初始化,原型是结果集
。它使用来自UserHydratorFactory
的水合器。当复杂数据直接从数据库或其他源(如返回嵌套数据的webservice)中获取时,这种方法或策略是有意义的
结论
对我个人来说,使用水合器策略比使用聚合水合器更有意义。当然,您不能向策略中添加多个名称。否则,正如我在开始时所说,您可以根据您的需求覆盖继承类中的
addStrategy
方法。在我看来,水化或策略的编码更少。谢谢你的回复。我同意策略方法似乎是一个更好的解决方案。但我有点困惑——如果我没有一个“地址”数据数组,而只有一个要加载的相关id,比如用于加载关联地址的clientId,该怎么办?在属性(例如“addressId”)上设置策略,甚至在clientId(通常为整数)上进行查找,如何返回对象?因此,在您的addressor示例中,我如何定义一个通用接口来允许任何对象从给定的Id/关联加载Address对象呢?我会走另一条路。我将在一个复杂的sql连接查询中获取所有数据,并直接创建UserEntity
。您将直接从数据库中获得一个水合结果集。正如我看到的,您通过用户ID获得用户地址。在这种情况下,我将加入地址表并直接读取所有数据。通过将水合结果集设置为表格网关中的原型,所有工作都会自动完成。我已经用“结果集中的用法”部分编辑了我的答案。也许这会对你有所帮助。感谢你的回答和清晰的示例,但我实际上是从另一个角度来看待这个问题——使用应用程序层来构建对象图,而不是大型、复杂的查询,希望通过一种可重用的方式来附加每种类型的域对象。