1

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.

+0

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

+0

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

+0

Wenn Sie die Referenz von B innerhalb von A setzen, kann derselbe Setter auch die Referenz von B für A setzen. – Pradeep

Antwort

0

Wie wäre es mit der Erstellung einer neuen Tabelle für die eigenen Mappings? Dann können Sie diese Komplexität von den Objekten isolieren, die sie ausnutzen.

So könnten Sie ein neues Objekt AtoBMappings haben

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 
} 

Dann stattdessen Ihre Mapper Methode der Hacker, können Sie einfach einen Auftrag in Mya zu MyB Schöpfung machen.

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

Glauben Sie, dass dies funktionieren würde, oder denken Sie, dies ein Hack zu viel ist?

Verwandte Themen