Zend framework2 如何在数据库和ZF2应用程序中处理不同的参考方向?
Zend framework2 如何在数据库和ZF2应用程序中处理不同的参考方向?,zend-framework2,nested,zend-form,zend-form-element,formcollection,Zend Framework2,Nested,Zend Form,Zend Form Element,Formcollection,Zend\Form\Fieldsets和Zend\Form\Collectionss可以嵌套,并提供一种非常舒适的方式将复杂的对象结构映射到它们,以便或多或少自动从表单输入中获取完整的对象(准备保存)。这是一个很好的例子 我现在的例子有点复杂,因为它包含一个参考反转。这意味着: 我有两个实体--MyA和MyB,在数据库中,它们之间的关系作为外键从MyB.MyA\u id到MyA.id,应用程序使用反向引用: MyA has MyB 或者使用一些代码: namespace My\DataObje
Zend\Form\Fieldset
s和Zend\Form\Collections
s可以嵌套,并提供一种非常舒适的方式将复杂的对象结构映射到它们,以便或多或少自动从表单输入中获取完整的对象(准备保存)。这是一个很好的例子
我现在的例子有点复杂,因为它包含一个参考反转。这意味着:
我有两个实体--MyA
和MyB
,在数据库中,它们之间的关系作为外键
从MyB.MyA\u id
到MyA.id
,应用程序使用反向引用:
MyA has MyB
或者使用一些代码:
namespace My\DataObject;
class MyA {
/**
* @var integer
*/
private $id;
/*
* @var text
*/
private $foo;
/**
* @var MyB
*/
private $myB;
}
namespace My\DataObject;
class MyB {
/**
* @var integer
*/
private $id;
/*
* @var text
*/
private $bar;
/*
Actually it's even bidirectional, but it's not crucial for this issue.
For this problem it's not important,
wheter the class MyB has a property of type MyA.
We get the issue already,
when we define a property of type MyB in the class MyA.
Since the direction of the reference MyA.myB->MyB differes
from the direction of the reference my_b.my_a.id->my_a.id.
*/
/**
* @var MyA
*/
// private $myA;
}
MyMapper
对象获取DataObject
作为参数传递:MyAMapper\35;保存(MyA$object)
和MyBMapper\35;保存(MyB$object)
这意味着,MyAMapper#save(…)
具有将MyA
对象保存到myu a
表所需的所有内容。但是在MyBMapper
中,my_b.my_a_id
的数据将丢失
我也不能用嵌套的字段集MyBFieldset
创建一个字段集MyBFieldset
,然后将字段集MyBFieldset
嵌套到MyAFieldset
中,以填充MyA#MyB#MyA
(以便将myu b.myu a(id
的数据传递到MyBMapper#保存(
):
这将导致递归依赖,无法工作
当应用程序级别上的参考方向与数据库中的参考方向不同时,如何处理案例?如何通过fieldsets结构创建一个完整的(“准备保存”)对象?
解决方案1 处理表单时,可以创建另一个
MyA
对象,并将其添加到从表单获取的MyB
对象中:
class MyConrtoller {
...
public function myAction() {
$this->myForm->bind($this->myA);
$request = $this->getRequest();
$this->myForm->setData($request->getPost());
// here the hack #start#
$this->myB->setMyA($this->myA);
// here the hack #stop#
$this->myAService->saveMyA($this->myA);
}
}
嗯,也许不在控制器中,映射器可能是更好的地方:
class MyAMapper
{
...
public function save(MyA $myA)
{
$data = [];
$data['foo'] = [$myA->getFoo()];
// common saving stuff #start#
$action = new Insert('my_a');
$action->values($data);
$sql = new Sql($this->dbAdapter);
$statement = $sql->prepareStatementForSqlObject($action);
$result = $statement->execute();
$newId = $result->getGeneratedValue()
// common saving stuff #stop#
...
// hack #start#
if(! $myA->getB->getA()) {
$myA->getB->setA(new MyA());
$myA->getB->getA()->setId($newId);
}
// hack #stop#
// and only after all that we can save the MyB
$myB = $this->myBMapper->save($myB);
$myA->setMyB($myB);
...
}
}
但不管怎么说,这只是一个黑客
解决方案2
MyB
类获取一个属性$myAId
。但这也不是一种干净的方式
解决方案3
MyBFieldset
获取一个MyAFieldsetFake
作为子字段集。然后,该字段集类只是MyAFieldset
的一个“浅”副本,它只包含MyA
数据对象的ID
:
class MyAFieldset {
...
public function init()
{
$this->add([
'type' => 'text',
'name' => 'id',
'options' => [...],
]);
$this->add([
'type' => 'text',
'name' => 'foo',
'options' => [...],
]);
}
}
class MyAFieldset {
...
public function init()
{
$this->add([
'type' => 'text',
'name' => 'id',
'options' => [...],
]);
$this->add([
'type' => 'text',
'name' => 'bar',
'options' => [...],
]);
$this->add([
'type' => 'text',
'name' => 'foo',
'type' => 'My\Form\Fieldset\MyAFakeFieldset',
'options' => [...],
]);
}
}
class MyAFieldset {
...
public function init()
{
$this->add([
'type' => 'text',
'name' => 'id',
'options' => [...],
]);
}
}
但是伪对象也有点脏。创建一个新表来自己处理映射怎么样。然后,您可以将这种复杂性与利用它们的对象隔离开来 所以,你可以有一个新的对象AtoBMappings
namespace My\DataObject;
class MyA {
/**
* @var integer
*/
private $id;
/*
* @var text
*/
private $foo;
/**
* @var MyAtoB
*/
private $myAtoB;
}
namespace My\DataObject;
class MyB {
/**
* @var integer
*/
private $id;
/**
* @var AtoBMapperID
*/
private $myAtoB;
}
class MyAtoBMapper {
/**
* @var myB
*/
private $myB
/**
* @var myA
**
private $myA
}
然后,您可以简单地在MyA中为MyB创建赋值,而不必修改映射器方法
class MyAMapper
{
...
public function save(MyA $myA)
{
$myAtoB = new MyAtoBMapper();
//.... instert new myAtoB into DB
$data = [];
$data['foo'] = [$myA->getFoo()];
$data['myAtoB'] = $myAtoB->getId();
// common saving stuff #start#
$action = new Insert('my_a');
$action->values($data);
$sql = new Sql($this->dbAdapter);
$statement = $sql->prepareStatementForSqlObject($action);
$result = $statement->execute();
$newId = $result->getGeneratedValue();
$myA->setMyAtoB($newAtoB);
$myAtoBMapper->myA = $newId;
// common saving stuff #stop#
// and only after all that we can save the MyB
$myB = $this->myBMapper->save($myB);
$myB->setMyAtoB($newAtoB);
$myAtoBMapper->myB = $myB;
...
}
}
您认为这样做行得通吗,还是觉得这太麻烦了?创建一个新表来自行处理映射怎么样。然后,您可以将这种复杂性与利用它们的对象隔离开来 所以,你可以有一个新的对象AtoBMappings
namespace My\DataObject;
class MyA {
/**
* @var integer
*/
private $id;
/*
* @var text
*/
private $foo;
/**
* @var MyAtoB
*/
private $myAtoB;
}
namespace My\DataObject;
class MyB {
/**
* @var integer
*/
private $id;
/**
* @var AtoBMapperID
*/
private $myAtoB;
}
class MyAtoBMapper {
/**
* @var myB
*/
private $myB
/**
* @var myA
**
private $myA
}
然后,您可以简单地在MyA中为MyB创建赋值,而不必修改映射器方法
class MyAMapper
{
...
public function save(MyA $myA)
{
$myAtoB = new MyAtoBMapper();
//.... instert new myAtoB into DB
$data = [];
$data['foo'] = [$myA->getFoo()];
$data['myAtoB'] = $myAtoB->getId();
// common saving stuff #start#
$action = new Insert('my_a');
$action->values($data);
$sql = new Sql($this->dbAdapter);
$statement = $sql->prepareStatementForSqlObject($action);
$result = $statement->execute();
$newId = $result->getGeneratedValue();
$myA->setMyAtoB($newAtoB);
$myAtoBMapper->myA = $newId;
// common saving stuff #stop#
// and only after all that we can save the MyB
$myB = $this->myBMapper->save($myB);
$myB->setMyAtoB($newAtoB);
$myAtoBMapper->myB = $myB;
...
}
}
你认为这行得通吗,还是你认为这是一种过分的攻击?我不确定我是否正确理解了复杂性,因为在我看来,数据库中的反向反转不会影响对象关系。如果您可以阅读有关条令的内容,其中每个关系都有一个拥有方和一个反向,以指示实际的数据库关系。在我看来,一个干净的实现就是如何看待对象关系SIP。因为myA有myB,所以可以选择myA来保存对象,包括myA和myB。谢谢您的评论!实际上我没有解释一个重要的心理步骤。刚刚更新了问题。现在你明白这个问题了吗?当你在A中设置B的引用时,同一个setter也可以为A设置B的引用。我不确定我是否正确理解了复杂性,因为我认为数据库中的反向反转不会影响对象关系。如果您可以阅读有关条令的内容,其中每个关系都有一个拥有方和一个反向,以指示实际的数据库关系。在我看来,一个干净的实现就是如何看待对象关系SIP。因为myA有myB,所以可以选择myA来保存对象,包括myA和myB。谢谢您的评论!实际上我没有解释一个重要的心理步骤。刚刚更新了问题。你现在明白这个问题了吗?当你在A中设置B的引用时,同一个设置者也可以为A设置B的引用。