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;
}
My
Mapper
对象获取
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的引用。