如何处理数据库和ZF2应用程序中的不同参考方向?

时间:2016-03-21 09:20:38

标签: zend-framework2 nested zend-form zend-form-element formcollection

Zend\Form\FieldsetZend\Form\Collections可以嵌套,并提供一种非常舒适的方式来将复杂的对象结构映射到它们,以便从表单输入中获取一个comlete对象(准备保存)或多或少地自动化。 Form Collections tutorial提供了一个很好的例子。

我目前的情况有点复杂,因为它包含参考反转。这意味着:

我有两个实体 - MyAMyB,而在数据库中,它们之间的关系从FOREIGN KEYmyb.mya_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;
}

我的Mapper个对象获得DataObject作为参数传递:MyAMapper#save(MyA $object)MyBMapper#save(MyB $object)

namespace My\Mapper;
use ...
class MyAMapper
{
    ...
    public fuction save(MyA $object)
    {
        // save the plain MyA propertis a new entry in the my_a table
        ...
        $myMapperB->save($myA->getMyB());
    }
}

namespace My\Mapper;
use ...
class MyBMapper
{
    ...
    public fuction save(MyB $object)
    {
        // save the plain MyB propertis a new entry in the my_b table
        ...
    }
}

这意味着,MyAMapper#save(...)需要将MyA对象保存到my_a表。但在MyBMapper中,my_b.my_a_id的数据将会丢失。

我也无法使用嵌套字段集MyAFieldset创建字段集MyBFieldset,然后将字段集MyBFieldset嵌套到MyAFieldset中以填充MyA#MyB#MyA(为了将my_b.my_a_id的数据传递给MyBMapper#save(...)):

class MyAFieldset {
    $this->add([
        'name' => 'my_b',
        'type' => 'My\Form\Fieldset\MyBFieldset',
        'options' => []
    ]);
}

class MyBFieldset {
    $this->add([
        'name' => 'my_a',
        'type' => 'My\Form\Fieldset\MyAFieldset',
        'options' => []
    ]);
}

这会导致递归依赖,无法正常工作。

当应用程序级别的参考方向与数据库中的方向不同时,如何处理案例?如何创建一个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的副本,其中仅包含ID数据对象的MyA

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' => [...],
        ]);
    }
}

但假物品也有点脏。

1 个答案:

答案 0 :(得分:0)

如何创建一个新表来自己处理映射。然后,您可以将这种复杂性与利用它们的对象隔离开来。

所以,你可以有一个新对象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创建中进行分配,而不是破解Mapper方法。

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;
        ...
    }
}

你认为这会起作用,还是你觉得这太过分了?