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' => [...],
]);
}
}
Но поддельные объекты немного грязные, как хорошо.
Я не уверен, правильно ли я понял сложность, потому что обратная инверсия в базе данных, на мой взгляд, не влияет на объектные отношения. Если вы можете прочитать о доктрине, где каждое отношение имеет свою сторону и обратную сторону, чтобы указать фактическое отношение к базе данных. Чистая реализация, на мой взгляд, заключается в том, как вы видите объект relationsip. Поскольку myA имеет myB, myA можно выбрать для сохранения объектов, как myA, так и myB. – Pradeep
Спасибо за ваш комментарий! На самом деле я не объяснил важный ментальный шаг. Просто обновил вопрос. Поймите проблему сейчас? – automatix
Когда вы устанавливаете ссылку B внутри A, тот же установщик может также установить ссылку B для A. – Pradeep