Ankündigung

Einklappen
Keine Ankündigung bisher.

[Erledigt] Symfony2 FormBuilder/FormType mit Relation

Einklappen

Neue Werbung 2019

Einklappen
X
  • Filter
  • Zeit
  • Anzeigen
Alles löschen
neue Beiträge

  • [Erledigt] Symfony2 FormBuilder/FormType mit Relation

    Servus,

    ich bin aktuell beim FormBuilder von Symfony2 und möchte für folgendes Beispiel ein Formular implementieren, welches die Daten in die DB schreibt. Allerdings kriege ich beim Versuch das Formular anzuzeigen schon einen Fehler (siehe unten):

    Author <-> Book

    Relation: 1 zu n

    Hierbei soll ein Formular erzeugt werden, in welchem ich einen Autor anlegen und diesem direkt ein Buch zuweisen kann:

    Author:
    Firstname: _______
    Lastname: _______

    Book:
    Title: ____

    [save]


    Woran ich hierbei hänge ist, die Relation 1 zu n in dem FormType darzustellen, sodass bei korrekter Eingabe der Felder automatisch die 1 zu n Beziehung (author_id in Book) in meiner mysql Tabelle abgebildet wird.

    Die Tabellen stehen alle in der Datenbank korrekt (Entities sollten also korrekt sein), es geht mir um meinen AuthorType und BookType. Wie stelle ich dort die 1 zu n Relation richtig dar?

    Hier ist mein Code:

    Author-Entity:
    PHP-Code:
    namespace Acme\UserBundle\Entity;

    use 
    Doctrine\Common\Collections\ArrayCollection;
    use 
    Doctrine\ORM\Mapping as ORM;
    use 
    Symfony\Component\Validator\Constraints as Assert;

    /**
     * @ORM\Entity
     * @ORM\Table(name="author")
     */
    class Author {

        
    /**
         * @ORM\Column(type="integer")
         * @ORM\Id
         * @ORM\GeneratedValue(strategy="AUTO")
         */
        
    protected $id;

        
    /**
         * @ORM\Column(type="string", length=100)
         *
         * @Assert\NotBlank()
         * @Assert\Length(min = "2")
         * @Assert\Type(type="string", message="The value {{ value }} is not a valid {{ type }}.")
         */
        
    protected $firstname;

        
    /**
         * @ORM\Column(type="string", length=100)
         *
         * @Assert\NotBlank()
         * @Assert\Length(min = "2")
         * @Assert\Type(type="string", message="The value {{ value }} is not a valid {{ type }}.")
         */
        
    protected $lastname;

        
    /**
         * @ORM\OneToMany(targetEntity="Book", mappedBy="author")
         */
        
    protected $books;

        public function 
    __construct()
        {
            
    $this->books = new ArrayCollection();
        }

        
    /**
         * Get id
         *
         * @return integer 
         */
        
    public function getId()
        {
            return 
    $this->id;
        }

        
    /**
         * Set firstname
         *
         * @param string $firstname
         * @return Author
         */
        
    public function setFirstname($firstname)
        {
            
    $this->firstname $firstname;

            return 
    $this;
        }

        
    /**
         * Get firstname
         *
         * @return string 
         */
        
    public function getFirstname()
        {
            return 
    $this->firstname;
        }

        
    /**
         * Set lastname
         *
         * @param string $lastname
         * @return Author
         */
        
    public function setLastname($lastname)
        {
            
    $this->lastname $lastname;

            return 
    $this;
        }

        
    /**
         * Get lastname
         *
         * @return string 
         */
        
    public function getLastname()
        {
            return 
    $this->lastname;
        }

        
    /**
         * Add books
         *
         * @param \Acme\UserBundle\Entity\Book $books
         * @return Author
         */
        
    public function addBook(\Acme\UserBundle\Entity\Book $books)
        {
            
    $this->books[] = $books;

            return 
    $this;
        }

        
    /**
         * Remove books
         *
         * @param \Acme\UserBundle\Entity\Book $books
         */
        
    public function removeBook(\Acme\UserBundle\Entity\Book $books)
        {
            
    $this->books->removeElement($books);
        }

        
    /**
         * Get books
         *
         * @return \Doctrine\Common\Collections\Collection 
         */
        
    public function getBooks()
        {
            return 
    $this->books;
        }


    Book-Entity:
    PHP-Code:
    namespace Acme\UserBundle\Entity;

    use 
    Doctrine\ORM\Mapping as ORM;
    use 
    Symfony\Component\Validator\Constraints as Assert;

    /**
     * @ORM\Entity
     * @ORM\Table(name="book")
     */
    class Book {

        
    /**
         * @ORM\Column(type="integer")
         * @ORM\Id
         * @ORM\GeneratedValue(strategy="AUTO")
         */
        
    protected $id;

        
    /**
         * @ORM\Column(type="string", length=100)
         *
         * @Assert\NotBlank()
         * @Assert\Length(min = "2")
         * @Assert\Type(type="string", message="The value {{ value }} is not a valid {{ type }}.")
         */
        
    protected $title;

        
    /**
         * @ORM\ManyToOne(targetEntity="Author", inversedBy="books")
         * @ORM\JoinColumn(name="author_id", referencedColumnName="id")
         *
         * @Assert\NotBlank()
         * @Assert\Length(min = "2")
         * @Assert\Type(type="string", message="The value {{ value }} is not a valid {{ type }}.")
         */
        
    protected $author;

        
    /**
         * Get id
         *
         * @return integer 
         */
        
    public function getId()
        {
            return 
    $this->id;
        }

        
    /**
         * Set title
         *
         * @param string $title
         * @return Book
         */
        
    public function setTitle($title)
        {
            
    $this->title $title;

            return 
    $this;
        }

        
    /**
         * Get title
         *
         * @return string 
         */
        
    public function getTitle()
        {
            return 
    $this->title;
        }

        
    /**
         * Set author
         *
         * @param \Acme\UserBundle\Entity\Author $author
         * @return Book
         */
        
    public function setAuthor(\Acme\UserBundle\Entity\Author $author null)
        {
            
    $this->author $author;

            return 
    $this;
        }

        
    /**
         * Get author
         *
         * @return \Acme\UserBundle\Entity\Author 
         */
        
    public function getAuthor()
        {
            return 
    $this->author;
        }


    AuthorType-Class:
    PHP-Code:
    namespace Acme\UserBundle\Form\Type;

    use 
    Symfony\Component\Form\AbstractType;
    use 
    Symfony\Component\Form\FormBuilderInterface;
    use 
    Symfony\Component\OptionsResolver\OptionsResolverInterface;

    class 
    AuthorType extends AbstractType
    {
        public function 
    buildForm(FormBuilderInterface $builder, array $options)
        {
            
    $builder
                
    ->add('firstname','text')
                ->
    add('lastname''text')
                ->
    add('books', new BookType());
            ;
        }

        public function 
    setDefaultOptions(OptionsResolverInterface $resolver)
        {
            
    $resolver->setDefaults(array(
                
    'data_class' => 'Acme\UserBundle\Entity\Author',
                
    'cascade_validation' => true,
            ));
        }

        public function 
    getName()
        {
            return 
    'author';
        }


    BookType-Class:
    PHP-Code:
    namespace Acme\UserBundle\Form\Type;

    use 
    Symfony\Component\Form\AbstractType;
    use 
    Symfony\Component\Form\FormBuilderInterface;
    use 
    Symfony\Component\OptionsResolver\OptionsResolverInterface;

    class 
    BookType extends AbstractType
    {
        public function 
    buildForm(FormBuilderInterface $builder, array $options)
        {
            
    $builder
                
    ->add('title','text')
            ;
        }

        public function 
    setDefaultOptions(OptionsResolverInterface $resolver)
        {
            
    $resolver->setDefaults(array(
                
    'data_class' => 'Acme\UserBundle\Entity\Book',
            ));
        }

        public function 
    getName()
        {
            return 
    'book';
        }


    Und schließlich meine Action im Controller:
    PHP-Code:
    public function createAuthorAndBookAction(){

            
    $author = new Author();
            
    $form $this->createForm(new AuthorType(), $author);

            return 
    $this->render('UserBundle:Main:author.html.twig', array('form' => $form->createView()));
        } 
    Im Template render ich das ganze mit :
    PHP-Code:
    {{ form(form) }} // normal ausführlicher 

    Als Fehler kriege ich:

    "The form's view data is expected to be an instance of class Acme\UserBundle\Entity\Book, but is an instance of class Doctrine\Common\Collections\ArrayCollection. You can avoid this error by setting the "data_class" option to null or by adding a view transformer that transforms an instance of class Doctrine\Common\Collections\ArrayCollection to an instance of Acme\UserBundle\Entity\Book."


    _________________________________

    Ich hoffe jmd. kann mir hier weiter helfen.
    Habe nach dem Fehler schon gesucht und etwas herumprobiert, es will aber nicht funktionieren.

    Regards
    No Sacrifice , no Glory--


  • #2
    Sieht für mich danach aus, dass du ihm sagst: "BookType ist vom Typ EntityBook" aber in deiner Entity ist "$books" nicht vom Typ EntityBook, sondern eine ArrayCollection -> weil es OneToMany ist.

    Im Grunde erwartet er, dass dort EIN Buch drinsteht, aber du hast tatsächlich VIELE Bücher drinstehen.

    Mal mit http://symfony.com/doc/current/refer...ollection.html rumprobiert?
    Lerne Grundlagen | Schreibe gute Beispiele | PDO > mysqli > mysql | Versuch nicht, das Rad neu zu erfinden | Warum $foo[bar] böse ist | SQL Injections | Hashes sind keine Verschlüsselungen! | Dein E-Mail Regex ist falsch

    Kommentar


    • #3
      Hi Apo,
      das klingt nachvollziehbar. Ich muss ihm ja aber ne Collection geben aufgrund der OneToMany, wie du schon sagst.

      Rumprobiert hatte ich nicht mit dem Collection Type, sondern Entity Type (Da die ja in Beziehung miteinander stehen).
      Leider ohne Erfolg, ich denke ich habe da irgendwie nicht die richtige Syntax o.ä. genutzt.. Das ist ja eigentlich kein seltener Anwendungsfall mein Beispiel, deswegen wundert es mich dass ich dazu nichts gescheites gefunden habe.


      Edit: Ich lese grade unter deinem Link "you can embed entire forms, which is useful when creating forms that expose one-to-many relationships (e.g. a product from where you can manage many related product photos) ".

      Das klingt ja genau nach dem was ich suche. Werde ich mir angucken, wenns nicht funkt hört ihr hier von mir nochmal .
      Hatte das mit dem Entity Type in irgend nem anderen Forum gelesen gehabt, vlt. hab ich mich da auch im Anwendungsfall verguckt..

      Regards.
      No Sacrifice , no Glory--

      Kommentar


      • #4
        Also mit dem Collection Type geht das glaube ich nicht. Das würde nämlich, so wie ich das verstanden habe, alle Bücher einem Autor zuordnen, die bereits existieren.

        Mein Ziel ist es ja, ein Formular zu erzeugen wo ich einen Autor mit einem neuen Buch anlegen kann.
        Hast du da noch ne weitere Idee Apo ? Vllt. doch der Entity Type?

        Habe folgende Änderungen nun vorgenommen:

        AuthorType:
        PHP-Code:
        use Symfony\Component\Form\AbstractType;
        use 
        Symfony\Component\Form\FormBuilderInterface;
        use 
        Symfony\Component\OptionsResolver\OptionsResolverInterface;

        class 
        AuthorType extends AbstractType
        {
            public function 
        buildForm(FormBuilderInterface $builder, array $options)
            {
                
        $builder
                    
        ->add('firstname','text')
                    ->
        add('lastname''text')
                    ->
        add('books''collection', array('type' => new BookType()));
            }

            public function 
        setDefaultOptions(OptionsResolverInterface $resolver)
            {
                
        $resolver->setDefaults(array(
                    
        'data_class' => 'Acme\UserBundle\Entity\Author',
                    
        'cascade_validation' => true,
                ));
            }

            public function 
        getName()
            {
                return 
        'author';
            }

        BookType ist gleich geblieben.

        Zur Action im Controller:
        PHP-Code:
        public function createAuthorAndBookAction(){

                
        $author = new Author();
                
        $form $this->createForm(new AuthorType(), $author);

                return 
        $this->render('UserBundle:Main:author.html.twig', array('form' => $form->createView()));
            } 

        Formular das nun angezeigt wird sieht so aus:


        Firstname: [empty Text Field]
        Lastname: [empty Text Field]

        Books


        Erst wenn ich im Controller folgendes mache, steht unter Books ein Textfeld mit einem Wert:
        PHP-Code:
        $author = new Author();
                
        $book = new Book();
                
        $book->setTitle("test");
                
        $author->addBook($book);
                
        $form $this->createForm(new AuthorType(), $author); 
        Nun steht das so da:

        Firstname: [empty Text Field]
        Lastname: [empty Text Field]

        Books
        0
        Title: [Text Field with value = test]



        Jede Hilfe ist herzlichst Willkommen.

        Regards.
        No Sacrifice , no Glory--

        Kommentar


        • #5
          So, also das mit den Collections hat nun doch geklappt. Das Feld "Title" wird mir als ein leeres Textfeld wie gewünscht angezeigt unter den Author-Daten, es hat folgendes im Controller gefehlt:
          ($author->addBook(new Book())

          PHP-Code:
          public function createAuthorAndBookAction(Request $request){

                  
          $author = new Author();
                  
          $author->addBook(new Book());

                  
          $form $this->createForm(new AuthorType(), $author);

                  
          $form->handleRequest($request);

                  if(
          $form->isValid()){

                      
          $firstname $form["firstname"]->getData();
                      
          $lastname $form["lastname"]->getData();
                      
          $title $form["title"]->getData();

                      
          $author->setFirstname($firstname);
                      
          $author->setLastname($lastname);


                      
          $em $this->getDoctrine()->getManager();
                      
          $em->persist($author);
                      
          $em->flush();
                  }

                  return 
          $this->render('UserBundle:Main:author.html.twig', array('form' => $form->createView()));
              } 
          Nun habe ich aber noch eine Frage:
          Wie kriege ich den $title nun in das Buch des Authors aus dem Formular rein ?

          In dem Cookbook Eintrag von Symfony machen die das nur mit Dummy Daten, ohne die aus nem Formular auszulesen direkt am Anfang:
          PHP-Code:
           $tag1 = new Tag();
                  
          $tag1->name 'tag1';
                  
          $task->getTags()->add($tag1);
                  
          $tag2 = new Tag();
                  
          $tag2->name 'tag2';
                  
          $task->getTags()->add($tag2);
                  
          // end dummy code

                  
          $form $this->createForm(new TaskType(), $task);

                  
          $form->handleRequest($request);

                  if (
          $form->isValid()) {
                      
          // ... maybe do some form processing, like saving the Task and Tag objects
                  

          Das bringt mir ja aber nichts, wenn ich die Daten erst nach "isValid()" in die Objekte füllen kann ?

          Regards.
          No Sacrifice , no Glory--

          Kommentar


          • #6
            Hat sich erledigt, danke : http://www.youtube.com/watch?v=Wb2f8oZlHtU

            Da wird alles erklärt

            ~closed.
            No Sacrifice , no Glory--

            Kommentar

            Lädt...
            X