Zend\Form\Fieldset
s und Zend\Form\Collections
s können verschachtelt werden und bieten eine sehr komfortable Möglichkeit, komplexe Objektstrukturen auf sie abzubilden, um ein mehr oder weniger automatisches vollständiges Objekt (bereit zum Speichern) aus der Formulareingabe zu erhalten. Die Form Collections tutorial bietet ein sehr gutes Beispiel.Wie werden unterschiedliche Referenzrichtungen in Datenbank- und ZF2-Anwendungen gehandhabt?
Der Fall, den ich gerade habe, ist ein bisschen komplexer, da er eine Referenzinversion enthält. Das heißt:
Ich habe zwei Entitäten - MyA
und MyB
und während in der Datenbank die Beziehung zwischen ihnen als FOREIGN KEY
von myb.mya_id
zu mya.id
umgesetzt wird, wird die Anwendung unter Verwendung eine invertierte Referenzierung:
MyA has MyB
Or mit einigen Code:
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;
}
Meine Mapper
Objekte erhalten DataObject
s als Argument übergeben: MyAMapper#save(MyA $object)
und 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
...
}
}
Das bedeutet, hat die MyAMapper#save(...)
evrything benötigt, um das MyA
Objekt in die my_a
Tabelle zu speichern. Aber in der MyBMapper
werden die Daten für my_b.my_a_id
fehlen.
Und ich kann auch nicht ein Fieldset MyAFieldset
mit einer verschachtelten Fieldset MyBFieldset
und dann nisten die Fieldset MyBFieldset
in MyAFieldset
erstellen, um MyA#MyB#MyA
zu füllen (um my_b.my_a_id
-MyBMapper#save(...)
die Daten zu übergeben):
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' => []
]);
}
Dies würde eine rekursive Abhängigkeit verursachen und nicht funktionieren.
Wie wird mit einem Fall verfahren, wenn die Referenzrichtung auf der Anwendungsebene von der Richtung in der Datenbank abweicht? Wie erstellt man zwar eine Fieldsets-Struktur, die ein vollständiges ("ready to save") Objekt bereitstellt?
Problemumgehung 1
Wenn das Formular verarbeitet wird, ein weiteres MyA
Objekt erstellt werden kann und zu dem MyB
Objekt bekam von der Form:
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);
}
}
Na ja, vielleicht nicht in der Steuerung, der Mapper könnte ein besserer Ort dafür sein:
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);
...
}
}
Aber trotzdem ist es nur ein Hack.
Behelfslösung 2
Die MyB
Klasse $myAId
eine Eigenschaft bekommt. Aber es ist auch kein sauberer Weg.
Behelfslösung 3
The MyBFieldset
erhält eine MyAFieldsetFake
als Unter Fieldset. Diese Fieldset Klasse ist dann nur eine „flache“ Kopie der MyAFieldset
, dass enthält nur die ID
für das MyA
Datenobjekt:
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' => [...],
]);
}
}
Aber gefälschte Objekte sind auch ein bisschen schmutzig.
Ich bin nicht sicher, ob ich die Komplexität richtig verstanden habe, weil die umgekehrte Umkehrung in der Datenbank meiner Meinung nach die Objektbeziehung nicht beeinflusst. Wenn Sie über Doktrin lesen können, wobei jede Beziehung eine eigene Seite und eine inverse Seite hat, um die tatsächliche Datenbankbeziehung anzuzeigen. Eine saubere Umsetzung ist meiner Meinung nach, wie Sie das Objekt relationship sehen. Da myA myB hat, kann myA ausgewählt werden, um die Objekte myA und myB zu speichern. – Pradeep
Danke für Ihren Kommentar! Eigentlich habe ich keinen wichtigen mentalen Schritt erklärt. Habe gerade die Frage aktualisiert. Verstehst du das Problem jetzt? – automatix
Wenn Sie die Referenz von B innerhalb von A setzen, kann derselbe Setter auch die Referenz von B für A setzen. – Pradeep