• Jeder User im Forum verpflichtet sich zur Akzeptanz und zur Einhaltung dieser Regeln:
    1. Umgangston
      Ein angemessener höflicher Umgangston, ohne Beleidigungen, Beschimpfungen und aggressive Postings ist für jedes Mitglied Pflicht.
    2. Beiträge
      Jedes Mitglied sollte sich bemühen nur sinnvolle Beiträge zum Thema zu posten. Dabei ist unbedingt vorher zu prüfen, ob das Thema vorher schon einmal diskutiert wurde und daher fortgesetzt werden kann
      • Suchfunktion benutzen!
      • offizielle Doku lesen!
    3. Haftung
      Jeder Beitragsersteller übernimmt die alleinige Verantwortung seiner Inhalte.
    4. Werbung
      Wir erlauben keine Beiträge, Signaturen, Private Nachrichten oder eMails an Benutzer, die Werbung enthalten. Ausgenommen
      sind Stellengesuche /-angebote, welche ausschließlich im Forum "Stellengesuche" veröffentlicht werden dürfen.
    5. Verstöße
      Regelwidrige Beiträge sollten dem Team gemeldet werden. Nach deren Überprüfung werden wir schnellstmöglich
      entsprechend handeln.
    6. Authorität
      Den Anweisungen der Team-Mitglieder (Administratoren und Moderatoren) sind in diesem Forum Folge zu leisten.
      Bei Fragen oder Beschwerden bitte an diese wenden.
    Wir möchten Euch darauf aufmerksam machen, dass es bei Verstößen gegen einen oder mehreren der oben genannten
    Punkte dem Team frei steht entsprechend zu handeln. Dies kann z.B. das Löschen eines Beitrags, das Ausschliessen bzw.
    Sperren von Mitgliedern oder aber lediglich eine Verwarnung sein.

    In diesem Zusammenhang sollte erwähnt werden, dass das Forum automatisch die IP-Adresse jedes Beitrag-Erstellers
    speichert. Bei schweren Vergehen, behalten wir es uns vor, die IP-Adresse zur Strafverfolgung weiterzugeben.
  • Willkommen im Zend Framework Forum

    ZF1 Zend Framework 1 + ZF2 Zend Framework 2

    Das Zend Framework Forum ist seit 2006 die erste Anlaufstelle für Zend Framework Entwickler in Deutschland. Mit über 70.000 Beiträgen und einer steigenden Nutzerzahl bietet das Forum hilfreiche Themen und ZF-Tutorials für professionelle Entwickler, fortgeschrittene Programmierer sowie Zend Framework Einsteiger.
    Wenn dies Dein erster Besuch in der Zend Framework Community ist, lies bitte zuerst die Hilfe - FAQ durch. Du musst Dich registrieren, bevor Du Beiträge verfassen kannst. Klicke oben auf 'Registrieren', um die Registrierung zu starten. Du kannst auch jetzt schon Beiträge lesen. Hier im Forum findest Du die Zend Framework Hilfe, die Du suchst!

    Grüße an alle Zend Framework Entwickler. Das Team vom Zend Framework Forum!

    Drupal Agentur

Daten aus Doctrine Entity in Zend Form zu Verfügung stellen

Powers

New member
Hallo!

Ich bin fleißig am rumbasteln mit Doctrine2 und habe Zend-Form direkt mit den Doctrine Entitäten verbunden. Ich arbeite also mit Entitäten, die in Fieldsets verwendet werden und die wiederrum in Formularen eingebunden werden (mit Hilfe des Hydrators).

So kann ich Informationen direkt aus dem Formular speichern, ohne sie nochmal bearbeiten zu müssen. Auch Formulare, die weitere Entitäten enthalten sind kein Problem.

ABER: Wie integriere ich in diese feste Struktur ein Formularfeld, dass mit bereits vorhandenen Daten einer Entität gefüttert werden soll (und deren Zusammenhänge das einfach in einer Referenztabelle gespeichert werden)?

PHP:
class EntityFieldset extends Fieldset 
{
    public function __construct() {

        parent::__construct('book');
        $this->setHydrator(new \Zend\Stdlib\Hydrator\Reflection());
        $this->setObject(new \Article\Entity\Entity1());

        $this->add(array(
               'name' => 'title',
               'attributes' => array(
                      'type' => 'text',
                      'id' => 'title'
               ),
               'options' => array(
                      'id' => 'title',
                      'label' => 'Titelitel:',
              )
        ));

        $this->add(array(
               'type' => 'Zend\Form\Element\Select',
               'name' => 'gender',
               'options' => array(
                      'label' => 'gender',
                      'value_options' => array(
                             '1' => 'Geschlecht',  
                             '2' => 'Female',
                             '3' => 'Male'
                     ),
               ),
               'attributes' => array(
                      'value' => '1' //set selected to '1' 
               )
        ));

    }
}
Wie interegriere ich bei dem zweiten $this->add eine weitere Entität, die einfach ihre Daten zur Verfügung stellt (anstelle von "Geschlecht, Female, Male") so, dass es anschließend auch als Objekt abgeleitet und die Abhängigkeiten automatisch mit in die Datenbank eingetragen werden?

Lg,

Powers :)
 
Zuletzt bearbeitet:

Kaiuwe

Super-Moderator
Wie interegriere ich bei dem zweiten $this->add eine weitere Entität, die einfach ihre Daten zur Verfügung stellt (anstelle von "Geschlecht, Female, Male") so, dass es anschließend auch als Objekt abgeleitet und die Abhängigkeiten automatisch mit in die Datenbank eingetragen werden?
Wenn ich dich richtig verstanden habe, dann sollte genau folgender Abschnitt in der Doku zum Ziel führen: „Zend\Form - Form Collections
 

Kaiuwe

Super-Moderator
Schau dir mal DoctrineModule\Form\Element\ObjectSelect an. Ein Beispiel dazu findest du auf https://github.com/doctrine/DoctrineModule/blob/master/docs/form-element.md

Als Hydrator nutze ich DoctrineModule\Stdlib\Hydrator\DoctrineObject. Gerade einfache Sachen wie select-Elemente funktionieren da bei mir hervorragend :)
Die Frage ist, ob er nun die Auswahlliste mit Werten dynamisch befüllen möchte oder ob er die Werte aus der Auswahlliste auf ein weiteres Objekt „mappen“ möchte. Zumindest fand ich den letzten Satz dazu etwas unklar:
Powers schrieb:
Wie interegriere ich bei dem zweiten $this->add eine weitere Entität, die einfach ihre Daten zur Verfügung stellt (anstelle von "Geschlecht, Female, Male") so, dass es anschließend auch als Objekt abgeleitet und die Abhängigkeiten automatisch mit in die Datenbank eingetragen werden?
 

Powers

New member
Hallo Kaiuwe, hallo ev3nger!

Ich komme morgen erst wieder zum austesten, deswegen kam noch keine Antwort.

Anwendung einfach beschrieben: Ich habe ein Formular, welches ich bin dem Doctrine Objekt "Buch" verbinde. Dieses Buch kann aber einen oder mehrere Autoren haben - und das ist die Frage der ich gerade nachgehe. Diese Autoren sind im vornherein angelegt und sollen beim Erstellen eines Buches dann zur Auswahl im Formular erscheinen (und anschließend natürlich mit abgespeichert werden).

Ich hab derlei Kram auch schon im ZF1 mit Doctrine1 schon gemacht, jedoch noch auf ziemlich "handwerkliche" Art und Weise. Und wie ich das jetzt im ZF2 mit Doctrine2 und Form / Fieldsets / Hydrator so richtig schön zusammen einbinde - da bin ich noch im Lernmodus, auch wenns doch schon ganz gut klappt mittlerweile :)

Das sieht schonmal sehr vielversprechend aus, danke für die Tips. Ich bin morgen wieder an der Sache dran.
 

Powers

New member
Soo, das sieht mittlerweile nun schon ganz gut aus. Ich kann ein Select Form erstellen, welches mit Daten aus der Datenbank (bzw. eines Doctrine-Objektes) gefüllt wird.

Es war zuerst etwas kompliziert, weil ich immer wieder auf den Fehler "No object manager was set" gestossen bin. Im Netz bin ich dann auf Anleitungen getroffen, die in der Module.php die Formulare initialisieren und bereitstellen wollen. Ist aber alles quark, falls man es nicht gezielt so machen will.

Zum Erfolg hat dieses Tutorial geführt. Es ist sehr kurz, simpel und so aufgebaut, dass es auf Anhieb funktioniert (zumindest bei mir):
ZendFormElementSelect and Database Values - Sam minds

Ich bin av3nger's Empfehlung gefolgt und habe im Abschnitt "Choose you method" die ObjectManager-Methode gewählt. Der Rest hat sehr gut geklappt und ich habe jetzt auch gesehen, wo zuvor meine Fehler waren.
Wie es aussieht, lässt sich das Tutorial-Beispiel auch wunderbar übertragen.

Danke :)
 

Powers

New member
Also eine Besonderheit ist jetzt noch offen: Wie lagere ich das ganze in ein Fieldset aus?

Wenn ich ein Formular direkt aufrufe, dann klappt alles:
PHP:
public function testformAction()
{
        $entityManager = $this->getServiceLocator()->get('Doctrine\ORM\EntityManager');
        $form = new \Application\Form\MyForm($entityManager);
        return new ViewModel(array(
            'form' => $form
        ));
}
PHP:
namespace Application\Form;

use Zend\Form\Form;
use DoctrineModule\Persistence\ObjectManagerAwareInterface;
use Doctrine\Common\Persistence\ObjectManager;

class MyForm extends Form implements ObjectManagerAwareInterface
{
    protected $objectManager;

    public function __construct(ObjectManager $objectManager)
    {
        $this->setObjectManager($objectManager);

        parent::__construct('db-adapter-form');

        $this->add(array(
            'type'    => 'DoctrineModule\Form\Element\ObjectSelect',
            'name'    => 'name',
            'options' => array(
                'label'          => 'Dynamic ObjectManager Select',
                'object_manager' => $this->getObjectManager(),
                'target_class'   => 'Application\Entity\Author',
                'property'       => 'firstname',
                'empty_option'   => '--- please choose ---'
            ),
        ));
    }

    public function setObjectManager(ObjectManager $objectManager)
    {
        $this->objectManager = $objectManager;

        return $this;
    }

    public function getObjectManager()
    {
        return $this->objectManager;
    }
}
Aber in ein Fieldset ausgelagert, scheitert es leider am ObjectManager, der nicht mehr "mit sich reden lässt":
PHP:
namespace Application\Fieldset;
use Application\Entity\Book;
use Zend\Form\Element;
use Zend\Form\Fieldset;
use DoctrineModule\Persistence\ObjectManagerAwareInterface;
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;


class BookFieldset extends Fieldset implements ObjectManagerAwareInterface
{
    protected $objectManager;

    public function setObjectManager(ObjectManager $objectManager)
    {
        $this->objectManager = $objectManager;
        return $this;
    }

    public function getObjectManager()
    {
        return $this->objectManager;
    }

    public function __construct(ObjectManager $objectManager)
    {
        $om = $this->setObjectManager($objectManager);

        parent::__construct('book');
        $this->setHydrator(new \Zend\Stdlib\Hydrator\Reflection());
        $this->setObject(new \Application\Entity\Book());

        $this->add(array(
            'name' => 'title',
            'attributes' => array(
                'type' => 'text',
                'id' => 'title'
            ),
            'options' => array(
                'id' => 'title',
                'label' => 'Buchtitel:',
            )
        ));

        $this->add(array(
            'type'    => 'DoctrineModule\Form\Element\ObjectSelect',
            'name'    => 'name',
            'options' => array(
                'label'          => 'Dynamic ObjectManager Select',
                'object_manager' => $this->getObjectManager(),
                'target_class'   => 'Application\Entity\Author',
                'property'       => 'firstname',
                'empty_option'   => '--- please choose ---'
            ),
        ));
    }
}
Da erscheint dann folgender Fehler: "Catchable fatal error: Argument 1 passed to Application\Fieldset\BookFieldset::__construct() must implement interface Doctrine\Common\Persistence\ObjectManager, none given, called in C:\xampp\htdocs\xxxxxxxx\vendor\zendframework\zendframework\library\Zend\ServiceManager\AbstractPluginManager.php on line 170 and defined in C:\xampp\htdocs\xxxxxxxx\module\Application\src\Application\Fieldset\BookFieldset.php on line 28"

Line 28 entspricht der Zeile: "public function __construct(ObjectManager $objectManager)"
 
Zuletzt bearbeitet:

av3nger

New member
Mir fehlt jetzt irgendwie die Stelle, an welcher du BookFieldset aufrufst. Aber das Problem ist klar: Es müsste ein ObjectManager als Parameter übergeben werden, jedoch macht er das nicht. Vermutlich hast du jetzt ganz normal dein Fieldset bei "type" eingetragen, oder? Anstelle des Arrays könntest du dann einfach eine Variable, welcher du new BookFieldset($om) zugeordnet hast, hinzufügen. Das wäre zumindest die erste Idee, die ich hätte. Ob das jetzt die gängige/beste Vorgehensweise ist, weiß ich nicht.


Was mir auch noch auffällt:

PHP:
$om = $this->setObjectManager($objectManager);
mach dort bitte das $om davor, ich habe sonst Angst, dass du die Variable zukünftig als objectManager nutzen möchtest ;) Der Rückgabewert vom setter ist nämlich nicht der objectManager, sondern $this - also das BookFieldset.
 

Powers

New member
Ja stimmt, dass $om war auch nur eine Sache / Versuch von einer Quelle, die ich mal ausprobiert habe :)

Also ich habe halt schon ne kleine Verkettung.
Im ersten Versuch habe ich den ObjectSelect direkt im Formular selbst aufgerufen. Anstatt des direkten Aufrufs wird dort nun ein Fieldset aufgerufen:
PHP:
//Formular
$this->add(array(
       'type' => 'Application\Fieldset\ArticleFieldset',
        'options' => array(
            'use_as_base_fieldset' => true
        )
));
In diesem Fieldset wird wiederrum das nächste Fieldset aufgerufen:

PHP:
//ArticleFieldset
$this->add(array(
      'type' => 'Application\Fieldset\BookFieldset'
));
Und dort im BookFieldset befindet sich dann das oben aufgeführte Fieldset. Ich habe mir auch schon gedacht, dass ich den Objektmanager da durchschleusen muss.
Soll das dann etwa so aussehen?

PHP:
//Formular
$this->add(array(
        'type' => 'Article\Fieldset\ArticleFieldset',
        'options' => array(
            'use_as_base_fieldset' => true,
                'objekt_manager' => $this->getObjectManager(),
        )
));
Mache mich morgen wieder dran :)
 
Zuletzt bearbeitet:

av3nger

New member
Nein, das wird vermutlich nicht klappen, weil ZF2 die Eigenschaft wohl nicht kennen wird. Ich würde mal folgendes ausprobieren:

PHP:
$bookFieldset = new Article\Fieldset\BookFieldset($this->getObjectManager());
$bookFieldset->setOptions(array('use_as_base_fieldet' => true));
$this->add($bookFieldset);
Ist jetzt nur hier frei eingetippt und ungetestet. Gleiches Spiel dann vermutlich auch mit dem ArticleFieldset.
 

Powers

New member
Ja super, mit dieser Schreibweise klappt das hervorragend. Ich kann den Objektmanager vom Grundformular bis ins letzte Fieldset durchreichen.
Ich fand die "$this->add" Array-Schreibweise ziemlich praktisch. Aber ich mag es Einheitlich und steige dann wohl sogar auf die nun vorgeschlagene Schreibweise um.

Vielen Dank jedenfalls nochmal :)

Einzige Sache für Nachahmer, die sich über einen Fehler beim implementieren wundern - noch ein Backslash kommt an den Anfang des Fieldsets-Aufrufs:
PHP:
$bookFieldset = new \Article\Fieldset\BookFieldset($this->getObjectManager());
$bookFieldset->setOptions(array('use_as_base_fieldet' => true));
$this->add($bookFieldset);
 

Powers

New member
Puh, ich muss zugeben das ich beim speichern der Objekte wiederrum grandios gescheitert bin heute :-/ Ich habe da eine riesige Verständnislücke, wie mir scheint.

Da die Book-Entity der Owner der Author-Entity ist, habe ich nun auf ein extra-Fieldset verzichtet und den "ObjectSelect" ebenefalls in das Book-Fieldset gepackt (und nicht in ein weiteres, seperates Author-Fieldset).

Dort bekomme ich nun den Fehler: "Found entity of type on association Article\Entity\Bkbook#authors, but expecting Article\Entity\Bkauthor"

Ich vermute jedoch, dass der Fehler ggf nichtmal in die Nähe von dem kommt, wo ich eigentlich hin möchte (und zwar, dass zu dem neu angelegten Buch die Authoren schlicht in der Referenztabelle als Relation angelegt werden).

PHP:
<?php
namespace Article\Fieldset;

use Article\Entity\Bkbook;

use Zend\Form\Element;
use Zend\Form\Fieldset;
use DoctrineModule\Persistence\ObjectManagerAwareInterface;
use Doctrine\Common\Persistence\ObjectManager;
use DoctrineModule\Stdlib\Hydrator\DoctrineObject as DoctrineHydrator;


class BookFieldset extends Fieldset implements ObjectManagerAwareInterface
{
	protected $objectManager;

	public function setObjectManager(ObjectManager $objectManager)
	{
		$this->objectManager = $objectManager;
		return $this;
	}

	public function getObjectManager()
	{
		return $this->objectManager;
	}

	public function __construct(ObjectManager $objectManager)
	{
		$this->setObjectManager($objectManager);

		parent::__construct('book');
		$this->setHydrator(new \Zend\Stdlib\Hydrator\Reflection());
		$this->setObject(new \Article\Entity\Bkbook());
	//	$this->setHydrator(new DoctrineHydrator($objectManager, '\Article\Entity\Bkbook'));

		$this->add(array(
			'name' => 'title',
			'attributes' => array(
				'type' => 'text',
				'id' => 'title'
			),
			'options' => array(
				'id' => 'title',
				'label' => 'Buchtitel:',
			)
		));

		$this->add(array(
			'type'    => 'DoctrineModule\Form\Element\ObjectSelect',
			'name'    => 'authors',
			'attributes' => array(
				'multiple' => 'multiple',
				'required' => true
			),
			'options' => array(
				'label'          => 'Buchautor/en',
				'object_manager' => $this->getObjectManager(),
				'target_class'   => 'Article\Entity\Bkauthor',
				'value'			 => 'id',
				'property'       => 'lastname',
				'empty_option'   => '--- please choose ---',

			),
		));
/*
		$bookauthorSelect = new \Article\Fieldset\BookauthorSelect($this->getObjectManager());
		$bookauthorSelect->setOptions(array('use_as_base_fieldset' => true));
		$this->add($bookauthorSelect);
*/
	}


}
Verbunden sind die beiden Entitäten so (ManyToMany bidirektional):
PHP:
// Book Entität
/**
	* @ORM\ManyToMany(targetEntity="Bkauthor", inversedBy="authors")
	* @ORM\JoinTable(name="bk_authors_books",
	* 		joinColumns={@ORM\JoinColumn(name="book_id", referencedColumnName="id")},
	* 		inverseJoinColumns={@ORM\JoinColumn(name="author_id", referencedColumnName="id")}
	* )
	*/
	protected $authors;
PHP:
// Author Entität
/**
	* @ORM\ManyToMany(targetEntity="Bkbook", mappedBy="book")
	* @ORM\JoinTable(name="bk_authors_books",
	* 		joinColumns={@ORM\JoinColumn(name="author_id", referencedColumnName="id")},
	* 		inverseJoinColumns={@ORM\JoinColumn(name="book_id", referencedColumnName="id")}
	* )
	*/
	protected $book;
Also es scheint klar, dass mit dem Reflection-Hydrator in der Richtung nicht viel laufen kann. Schließlich bin ich bei dem ObjectSelect ja davon abhängig, dass Objekte für das Formular "lesbar" gemacht werden und anschließend als Objekte wieder rausgehen, richtig? Av3nger hatte ja anfangs direkt vom DoctrineObject Hydrator geschrieben, der da standardmäßig benutzt wird.

Das Book-Fieldset bringe ich mit dem DoctrineObject-Hydrator aber wohl durcheinander. Und auf ein eigenes Fieldset habe ich nun erstmal verzichtet, weil es wie gesagt nicht meiner Doctrine-Struktur entspricht und es ohnehin dazu geführt hat, dass Doctrine schlicht neue Autoren-Objekte erstellt hat beim versenden.

Ja sorry für diese riesige Verständnislücke, ich bin da echt dran gegangen, konnte die Sachen so aber erstmal nicht zusammenführen und meine beiden ZendFramework Bücher (Eggert / Romer), als auch das Doctrine Buch (Romer) haben dazu leider auch nichts auf Lager. Der einzige Ansatz den ich grad sehe, wäre ein eigener Hydrator, wie in Eggert ZF2 in Listing 12.23 beschrieben. Aber ich glaube das wäre garnicht das, was ein muss.

Ja blöd das dieser Schritt für mich noch so zäh ist. Aber es ist der letzte bedeutende Schritt in meinem Projekt, deswegen bin ich da wirklich dabei und versuche es.
 

av3nger

New member
Meine Vermutung wäre jetzt erst mal eine andere, kann jedoch sein, dass ich damit jetzt aber völlig daneben liege. Ich habe nämlich die Annotationen im Verdacht. Bei inversedBy müsste eher book anstelle von authors und bei mappedBy dann authors statt book drin stehen, also jeweils die Variable der anderen Klasse.

Das ist erst mal nur das erste, was mir auffiel. Hinsichtlich ZF2 mit Doctrine2 bin ich selbst noch irgendwo am Anfang des Lernprozesses. Irgendwie fehlt es da an brauchbarer Dokumentation, da im ZF2-Manual darauf natürlich nicht eingegangen wird, weil es kein Bestandteil vom Framework ist, und im Doctrine2-Manual wiederum nicht, weil's dort eher Framework-unabhänig beschrieben ist. Das einzige, was vorzufinden ist, ist ja eher der docs-Ordner vom DoctrineModule - und halt einige Blog-Einträge. Vielleicht sollte ich selbst mal einige Artikel mit den bisherigen Erkenntnissen schreiben.
 

Powers

New member
Ja, dass ist wirklich so, dass weder die Zend-Literatur, noch die Doctrine-Literatur diesen Part abdeckt. Da wird die Zuständigkeit quasi ins Nirgendwo abgeschoben (was wiederrum verständlich ist, wenn auf jeder Seite jeweils die notwendigsten Dinge abgehandelt werden sollen).

Nun denn, das heißt wohl einen Kaffee machen und alles nochmal vom Grund aus aufrollen. Da wird irgendwo ein ganz grober Fehler gemacht.

LG
:)
 
Zuletzt bearbeitet:

hoagster

New member
edit: Habe av3ngers Antwort überlesen, sorry :eek:

Hi Powers,

Dem Verdacht, dass schlicht beim Mapping was nicht stimmt, habe ich auch ständig vor Augen.
das stimmt schon mal ;)
inversedBy und mappedBy sind falsch rum gesetzt

PHP:
// Book Entität
/**
 * @ORM\ManyToMany(targetEntity="Bkauthor", inversedBy="books")
 * @ORM\JoinTable(name="bk_authors_books")
 */
protected $authors;

// Author Entität
/**
 * @ORM\ManyToMany(targetEntity="Bkbook", mappedBy="authors")
 * @ORM\JoinTable(name="bk_authors_books")
 */
protected $books;
Siehe dazu auch 5. Association Mapping - Doctrine 2 ORM 2.0.0 documentation

Ich habe in den Annotations Teile entfernt, der Doctrine 2 standard ist.
Das es eine many to many (bidirektionale) Assoziation ist, sollte der Klarheit halber "book" "books" heißen.
 
Zuletzt bearbeitet:

Powers

New member
Heureka, es klappt ;)
Kaffee und viel Geduld sind doch immer eine gute Lösung :p

@Hoagster
Ja, mit dem plural bei "books" hast Du völlig recht. Die Singularform ist auch hier mal wieder durch ausprobieren reingerutscht und ist natürlich unsinn. Und das Mapping werd ich insgesamt nochmal Prüfen. Beim auslesen mag Doctrine einem sowas verzeihen, aber nicht bei einlesen :)

Ich versuche mal meine Lösung für Nachahmer darzustellen. Der Erfolg kam mit der Umsetzung von diesem Beispiel:
Zend Framework - ZF2 Using the same Fieldset multiple names with different names

Und zwar rufe ich mein Fieldset nun folgend auf:
PHP:
// Form oder Fieldset, dass ein weiteres Fieldset aufruft
$bookauthorSelect = new BookauthorSelect($objectManager);
   $this->add(array(
      'type' => 'Zend\Form\Element\Collection',
      'name' => 'authors',
      'options' => array(
 //        'label' => 'Leagues',
           'should_create_template' => true,
           'allow_add' => true,
           'target_element' => $bookauthorSelect,
    ),
));
Und das Fieldset bleibt entsprechend so. Nur eines war ganz wichtig: bei 'name' wird nun 'id' zugewiesen (!).
PHP:
//Fieldset welches den ObjectSelect beinhaltet
public function __construct(ObjectManager $objectManager)
{
    $this->setObjectManager($objectManager);

    parent::__construct('authors');
    $this->setHydrator(new DoctrineHydrator($objectManager, 'Article\Entity\Bkauthor'));
    $this->setObject(new \Article\Entity\Bkauthor());

    $this->add(array(
        'type'    => 'DoctrineModule\Form\Element\ObjectSelect',
        'name'    => 'id',
  //    'attributes' => array(
  //        'multiple' => 'multiple',
  //    ),
        'options' => array(
            'label'          => 'Buchautor/en',
            'object_manager' => $this->getObjectManager(),
            'target_class'   => 'Article\Entity\Bkauthor',
            'value'            => 'id',
            'property'       => 'lastname',
            'empty_option'   => '--- bitte wählen ---',
        ),
    ));
}
Und auch wenn ich damit jetzt schonmal leben kann, ist da dennoch ein verbleibender Stolperstein, wenn man mehrere Autoren aufeinmal zu einem Buch abspeichern möchte (also das multiple => multiple wieder mit reinnehme). Der Fehler lautet dann:
"Notice: Array to string conversion in C:\xxx\xxx\xxx\vendor\doctrine\orm\lib\Doctrine\ORM\UnitOfWork.php on line 2791"

Und dieser Fehler wird klar, wenn man sich das übergebene Array anschaut, wo anstelle der Autor-ID nun ein neues Array erscheint (welches den Fehler erzeugt, da ein String erwartet wird):
PHP:
//var_dump auf "book->authors"
array   
   0 =>      
    object(Article\Entity\Bkauthor)[488]       
    protected 'id' =>          
        array           
           0 => string '1' (length=1)           
           1 => string '2' (length=1)       
      protected 'firstname' => string 'Brian' (length=5)       
      protected 'lastname' => string 'Lumley' (length=6)       
      protected 'date_of_birth' => string '0000-00-00' (length=10)       
      protected 'date_of_death' => string '0000-00-00' (length=10)       
      protected 'gender' => string '' (length=0)       
      protected 'information_text' => string '' (length=0)       
......
Zum Vergleich die wohlgeordnete Ausgabe, wenn nur ein Autor mit hinein gegeben wird (da klappts dann auch mit dem speichern, als einheitliches Objekt):
PHP:
//var_dump auf "book->authors"
array   
    0 =>      
    object(Article\Entity\Bkauthor)[488]       
    protected 'id' => string '1' (length=1)       
    protected 'firstname' => string 'Brian' (length=5)       
    protected 'lastname' => string 'Lumley' (length=6)       
    protected 'date_of_birth' => string '0000-00-00' (length=10)       
    protected 'date_of_death' => string '0000-00-00' (length=10)       
    protected 'gender' => string '' (length=0)       
    protected 'information_text' => string '' (length=0)
.....
Hat da vielleicht noch jemand einen Vorschlag? Ansonsten bin ich schonmal froh für den Moment :p
 
Zuletzt bearbeitet:

Powers

New member
Das klingt logisch.

Aber wenn ich das mache, dann werden die ausgewählten Objekte vom Formular leider nicht weitergereicht. Stattdessen wird beim abschicken des Formulares einfach ein leeres Objekt mit den Properties der authors-Entity erstellt.

Umstellen des Formulares:
PHP:
public function __construct(ObjectManager $objectManager)
    {
   $this->setObjectManager($objectManager);

        parent::__construct('authors');
        $this->setHydrator(new DoctrineHydrator($objectManager, 'Article\Entity\Bkauthor'));
        $this->setObject(new \Article\Entity\Bkauthor());

        $this->add(array(
               'type'    => 'DoctrineModule\Form\Element\ObjectSelect',
               'required' => FALSE,
               'name'    => 'authors',
               'attributes' => array(
                      'multiple' => 'multiple',
               ),
               'options' => array(
                      'label'          => 'Buchautor/en',
                      'object_manager' => $this->getObjectManager(),
                      'target_class'   => 'Article\Entity\Bkauthor',
                      'value'             => 'id',
                      'property'       => 'lastname',
                      'empty_option'   => '--- bitte wählen ---',
              ),
        ));
    }
führt zu:

PHP:
array  
   0 =>     
      object(Article\Entity\Bkauthor)[426]      
         protected 'id' => null      
         protected 'firstname' => null      
         protected 'lastname' => null       
         protected 'gender' => null
Wenn bei 'name' die 'id' eingetragen wird, könnten einzelne Objekte vom Formular übergeben werden.
Aber bei einer multiplen Auswahl wird nach wie vor nur ein Objekt der Klasse Author erstellt, nur dass der ID-Eintrag des Author-Objektes dann alle ausgewählten ID's als array beinhaltet, was zu einer "Array to string conversion"-Fehlermeldung führt (und ja eeh so keinen Sinn macht).
Was muss man umstellen, damit wirklich mehrere Authoren-Objekte weitergegeben werden, und nicht nur eines?
 
Zuletzt bearbeitet:

Powers

New member
Mal eine Verständnisfrage zum speichern von mehreren Objekten einer Entität:

Wenn ein Autor zu einem Buch gespeichert werden soll, wird dafür einfach die Autor-Entität herangezogen und die Relation Buch - Autor wird gespeichert.
Aber wenn ich mehrere Autoren zu einem Buch speichern möchte (multiple bei ObjektSelect), dann führt das zu fehlern.

Ändert sich diese Vorgehensweise, wenn mehrere Autoren zu einem Buch gespeichert werden sollen?
Muss anstelle der Autoren Entität ggf die Buch-Entität aufgerufen werden, die ja die Property "authors" enthält?
Ähnlich wie in dieser Quelle hier: Doctrine is an object relational mapper (ORM) for PHP that sits on top of a powerful database abstraction layer (DBAL). Doctrine User mailing list. For questions regarding installation and usage of doctrine.

Dort werden dann (äquivalent gesagt) in der Buch-Entität die property in Mehrzahl von "autoren" aufgerufen. Mithilfe von weitere get, add und remove-Funktionen ein Austausch gewährleistet.

Leider auch bei dieser Umsetzung die Fehlermeldung: "Fatal error: Call to a member function toArray() on a non-object in C:\xxx\xxx\xxx\vendor\doctrine\doctrine-module\src\DoctrineModule\Stdlib\Hydrator\Strategy\AllowRemoveByValue.php on line 57"

Ist das der richtige Ansatz? Aber wenn ja, warum?
 

Powers

New member
Eine Sache, die ich schon länger nachreichen wollte für Leute, die auf ähnliche Probleme stoßen, wenn sie eine ManyToMany-Relation mit dem Hydrator verarbeiten wollen:
Ich bin zu der Erkenntnis gekommen, dass dies nicht (einfach so) möglich ist. Aus diesem Grund bin ich bei der Verarbeitung in Doctrine-Klassen auch immer wieder auf Fehler gestoßen, wenn ein Array, anstelle eines einzigen Strings übergeben wurde.

Gehen wir von diesem Beitrag aus, der die Arbeitsweise des Hydrators darstellt:
https://github.com/josielrocha/ZF2---DoctrineModule---ManyToOne-/blob/master/vendor/doctrine/doctrine-module/docs/hydrator.md

Es werden "OneToOne", "OneToMany" und "ManyToOne" erklärt. Aber -kein- "ManyToMany".
Warum das so ist, wird klar, wenn man den unmittelbaren Verwendungszweck des Hydrators überdenkt:

In meinem Fall möchte ich zu einem Buch mehrere Autoren speichern. Da ein Autor auch mehrere Bücher haben kann, habe ich mich bei der Modellierung für eine MayToMany-Relation entschieden, wobei Bücher und Autoren durch eine NM-Tabelle verbunden werden.

Wollen wir das jedoch auf den vorliegenden Fall anwenden, bekommen wir ein Problem. Denn beim erstellen eines Buches, können wir zwar mehrere Autoren auswählen (das ist erlaubt). Aber reduziert auf diese Formular-Verarbeitung haben wir eine OneToMany-Relation. Ein Buch -> mehrere Autoren.
Da wird klar, warum der Hydrator keine ManyToMany-Verarbeitung anbietet. Reduziert auf den Formularzweck macht es schlicht keinen Sinn.

Bei der Verarbeitung stoßen die aufbereiteten Informationen aus dem Hydrator (in Form von OneToMany) intern dann auf meine zuvor gebaute "ManyToMany"-Architektur. Und an dieser Stelle knallt es.

Das zeigt sich auch dadurch, dass, wenn man hingehend alles korrekt konfiguriert, der Fehler kommt, dass die Autoren keine "buch_id" besitzen. An der Stelle wird es dann wohl interessant, weil man nun eine OneToMany-Relation in Doctrine mit einer NM-Tabelle verbinden müsste, damit keine sinnlosen Redundanzen auftreten (Wir wollen ja nicht 10x einen Autor in der Autoren-Tabelle anlegen, nur weil er 10 Bücher geschrieben hat).

Über Antworten zur Sache und Lösungen würd ich mich freuen. Es liegt zwar kein akutes Problem vor, ist aber interessant :)
 
Zuletzt bearbeitet:
Oben