1

Zend\Form\Fieldset s и Zend\Form\Collections s могут быть вложенными и обеспечить очень удобный способ сопоставления им сложных структур объектов, чтобы более или менее автоматически получить объект кометы (готовый к сохранению) из ввода формы. Form Collections tutorial - очень хороший пример.Как обрабатывать различные справочные направления в базе данных и приложении ZF2?

Случай, который у меня сейчас есть, немного сложнее, поскольку он содержит обратную ссылку. Это означает, что:

У меня есть два объекта - MyA и MyB и в то время как в базе данных связь между ними осуществляются в FOREIGN KEY от myb.mya_id к mya.id, приложение использует перевернутое реферирование:

MyA has MyB 

Or с некоторым кодом:

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 s передается в качестве аргумента: 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(...) имеет Evrything необходимо сохранить MyA объект в my_a таблице. Но в MyBMapper данные для my_b.my_a_id будут отсутствовать.

И я также не могу создать Fieldset MyAFieldset с вложенной FIELDSET MyBFieldset, а затем гнездо FIELDSET 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' => [] 
    ]); 
} 

Это вызовет рекурсивную зависимость и не сможет работать.

Как обращаться с корпусом, когда ссылочное направление на уровне приложения отличается от его направления в базе данных? Как создать структуру полей, которая обеспечивает полный («готовый к сохранению») объект?


Обход 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

The MyBFieldset получает MyAFieldsetFake как суб-полей. Этот класс FIELDSET тогда просто «мелкая» копия 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' => [...], 
     ]); 
    } 
} 

Но поддельные объекты немного грязные, как хорошо.

+0

Я не уверен, правильно ли я понял сложность, потому что обратная инверсия в базе данных, на мой взгляд, не влияет на объектные отношения. Если вы можете прочитать о доктрине, где каждое отношение имеет свою сторону и обратную сторону, чтобы указать фактическое отношение к базе данных. Чистая реализация, на мой взгляд, заключается в том, как вы видите объект relationsip. Поскольку myA имеет myB, myA можно выбрать для сохранения объектов, как myA, так и myB. – Pradeep

+0

Спасибо за ваш комментарий! На самом деле я не объяснил важный ментальный шаг. Просто обновил вопрос. Поймите проблему сейчас? – automatix

+0

Когда вы устанавливаете ссылку B внутри A, тот же установщик может также установить ссылку B для A. – Pradeep

ответ

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 
} 

Тогда вместо взлома вашего метода Mapper, вы можете просто сделать задание в 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; 
     ... 
    } 
} 

Считаете ли вы, что это сработает или вы считаете, что это слишком тяжело?

Смежные вопросы