SonataAdminBundle embedded forms sonata_admin_type issue

10,451

Solution 1

Ok, I solved it replacing this:

->add('questions', 'sonata_type_admin', array(), array('required' => false, 'edit' => 'inline'));

with this:

->add('questions','collection', array( 'type' =>  new QuizQuestionType(),
                                          'allow_add' => true,
                                          'prototype' => true,
                                          'by_reference' => true,
                                          ));

but for me it looks rather like a workaround than solution :-|

Anyway, I checked bundles versions, symfony branch and found nothing incoherent, everything is on 2.0 branch.

PS - QuizQuestionType class, if anyone's interested... nothing fancy here, just regular form class:

<?php 

namespace Some\SiteBundle\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilder;

class QuizQuestionType extends AbstractType
  {
      public function buildForm(FormBuilder $builder, array $options)
      {
    $builder
         ->add('title', NULL, array('label' => 'pytanie:'))
         ->add('body', NULL, array('label' => 'treść:', 'attr' => array(
          'class' => 'tinymce', 'data-theme' => 'simple')
         ))
         ->add('answer1', NULL, array('label' => 'odp. 1:'))
         ->add('answer2', NULL, array('label' => 'odp. 2:'))
         ->add('answer3', NULL, array('label' => 'odp. 3:'))
         ->add('answer4', NULL, array('label' => 'odp. 4:'))
         ->add('correctAnswer', NULL, array('label' => 'prawidłowa odp.:'))
    ;
    }

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

    public function getDefaultOptions(array $options){
        return array('data_class' => 'Some\SiteBundle\Entity\QuizQuestion');
    }
}

Solution 2

It looks like error explains why you're getting exception of not existing methods.

You are trying to pass collection instead of an object. Probably, you call addQuestion() or setQuestion() method to the collection instead of an object from it.

This usually happens with relation-populated objects.

In the place where you use that methods, try to call $object->first()->addQuestion() instead of $object->addQuestion()

Share:
10,451
heliogabal
Author by

heliogabal

Updated on June 09, 2022

Comments

  • heliogabal
    heliogabal almost 2 years

    I've got two entities, Quiz and QuizQuestion, with manyToMany one-sided relation. I'd like to embed questions form in quiz form. I'm on 2.0 branches. I am able to get sonata_type_model to work, getting list of id's in a dropdown, and working "add" button. However, I'm getting an error when trying to use sonata_type_admin:

    Neither property "title" nor method "getTitle()" nor method "isTitle()" exists in class "Doctrine\ORM\PersistentCollection"
    500 Internal Server Error - InvalidPropertyException
    

    Here's my Quiz entity:

        <?php 
    
    namespace Some\SiteBundle\Entity;
    
    use Doctrine\ORM\Mapping as ORM;
    use Doctrine\Common\Collections\ArrayCollection;
    
    
    /**
     * Some\SiteBundle\Entity\Quiz
     * @ORM\Table(name="quiz")
     * @ORM\Entity(repositoryClass="Some\SiteBundle\Entity\QuizRepository")
    
     */
    class Quiz
    {
        /**
         * @var integer $id
         *
         * @ORM\Column(name="id", type="integer")
         * @ORM\Id
         * @ORM\GeneratedValue(strategy="AUTO")
         */
        private $id;
    
    
    
        /**
         * @ORM\Column(type="datetime", name="created_at")
         * 
         * @var DateTime $createdAt
         */
        protected $createdAt;
    
    
        /**
         * @var string $title
         *
         * @ORM\Column(name="title", type="string", length=255)
         */
        private $title;
    
    
        /**
         * @var string $body
         *
         * @ORM\Column(name="body", type="text")
         */
        private $body;
    
    
      /**
        * @var QuizQuestion questions
         * @ORM\ManyToMany(targetEntity="QuizQuestion", cascade={"persist", "remove"} )
         **/
        protected $questions;
    
    
        public function __construct() {
            $this->questions = new ArrayCollection();
            $this->createdAt = new \DateTime();
        }
    
    
        /**
         * Get id
         *
         * @return integer 
         */
        public function getId()
        {
            return $this->id;
        }
    
        /**
         * Set title
         *
         * @param string $title
         */
        public function setTitle($title)
        {
            $this->title = $title;
        }
    
        /**
         * Get title
         *
         * @return string 
         */
        public function getTitle()
        {
            return $this->title;
        }
    
    
        /**
         * Get quiz body.
         * 
         * @return string body
         */
        public function getBody()
        {
            return $this->body;
        }
    
        /**
         * Sets body
         * 
         * @param string $value body
         */
        public function setBody($body)
        {
            $this->body = $body;
        }
    
    
    
        /**
         * Gets an object representing the date and time the quiz was created.
         * 
         * @return DateTime A DateTime object
         */
        public function getCreatedAt()
        {
            return $this->createdAt;
        }   
    
    
    
        /**
         * Add questions
         *
         * @param Some\SiteBundle\Entity\QuizQuestion $questions
         */
        public function addQuestion(\Some\SiteBundle\Entity\QuizQuestion $question)
        {
            $this->questions[] = $question;
        }
    
        /**
         * set question
         *
         * @param Some\SiteBundle\Entity\QuizQuestion $questions
         */
        public function setQuestion(\Some\SiteBundle\Entity\QuizQuestion $question)
        {
            foreach ($this->questions as $doc) {
                $this->questions->removeElement($doc);
            }
            $this->questions[] = $question;
        }
    
        /**
         * Get questions
         *
         * @return Doctrine\Common\Collections\Collection 
         */
        public function getQuestions()
        {
            return $this->questions;
        }
    
    
        /**
         * @ORM\PrePersist
         */
        public function beforePersist()
        {
            //$this->setCreatedAt(new \DateTime());        
            //$this->setModifiedAt(new \DateTime());
        }
    
    
          public function __toString()
      {
        return 'Quiz';
      }
    }
    

    And QuizQuestion entity:

    <?php 
    
    namespace Some\SiteBundle\Entity;
    
    use Doctrine\ORM\Mapping as ORM;
    use Doctrine\Common\Collections\ArrayCollection;
    
    
    /**
     * Some\SiteBundle\Entity\QuizQuestion
     * @ORM\Table(name="quiz_question")
     * @ORM\Entity
     */
    class QuizQuestion
    {
        /**
         * @var integer $id
         *
         * @ORM\Column(name="id", type="integer")
         * @ORM\Id
         * @ORM\GeneratedValue(strategy="AUTO")
         */
        private $id;
    
    
    
        /**
         * @ORM\Column(type="datetime", name="created_at")
         * 
         * @var DateTime $createdAt
         */
        protected $createdAt;
    
    
        /**
         * @var string $title
         *
         * @ORM\Column(name="title", type="string", length=255)
         */
        private $title;
    
    
        /**
         * @var string $body
         *
         * @ORM\Column(name="body", type="text")
         */
        private $body;
    
         /**
         * @var string $answer1
         *
         * @ORM\Column(name="answer1", type="text")
         */
        private $answer1;
    
         /**
         * @var string $answer2
         *
         * @ORM\Column(name="answer2", type="text")
         */
        private $answer2;
    
          /**
         * @var string $answer3
         *
         * @ORM\Column(name="answer3", type="text")
         */
        private $answer3;
    
         /**
         * @var string $answer4
         *
         * @ORM\Column(name="answer4", type="text")
         */
        private $answer4;
    
        /**
         * @var string $correctAnswer
         *
         * @ORM\Column(name="correct_answer", type="integer", length="1")
         */
        private $correctAnswer;
    
    
    
    
        public function __construct() {
            $this->createdAt = new \DateTime();
        }
    
    
        /**
         * Get id
         *
         * @return integer 
         */
        public function getId()
        {
            return $this->id;
        }
    
        /**
         * Set title
         *
         * @param string $title
         */
        public function setTitle($title)
        {
            $this->title = $title;
        }
    
        /**
         * Get title
         *
         * @return string 
         */
        public function getTitle()
        {
            return $this->title;
        }
    
    
        /**
         * Get question body.
         * 
         * @return string body
         */
        public function getBody()
        {
            return $this->body;
        }
    
        /**
         * Sets body
         * 
         * @param string $value body
         */
        public function setBody($body)
        {
            $this->body = $body;
        }
    
    
        /**
         * Get question answer1.
         * 
         * @return string answer1
         */
        public function getAnswer1()
        {
            return $this->answer1;
        }
    
        /**
         * Sets answer1
         * 
         * @param string $value answer1
         */
        public function setAnswer1($answer1)
        {
            $this->answer1 = $answer1;
        }
    
            /**
         * Get question answer2.
         * 
         * @return string answer2
         */
        public function getAnswer2()
        {
            return $this->answer2;
        }
    
        /**
         * Sets answer2
         * 
         * @param string $value answer2
         */
        public function setAnswer2($answer2)
        {
            $this->answer2 = $answer2;
        }
    
            /**
         * Get question answer3.
         * 
         * @return string answer3
         */
        public function getAnswer3()
        {
            return $this->answer3;
        }
    
        /**
         * Sets answer3
         * 
         * @param string $value answer3
         */
        public function setAnswer3($answer3)
        {
            $this->answer3 = $answer3;
        }
    
            /**
         * Get question answer4.
         * 
         * @return string answer4
         */
        public function getAnswer4()
        {
            return $this->answer4;
        }
    
        /**
         * Sets answer4
         * 
         * @param string $value answer4
         */
        public function setAnswer4($answer4)
        {
            $this->answer4 = $answer4;
        }
    
         /**
         * Get question correctAnswer.
         * 
         * @return string correctAnswer
         */
        public function getCorrectAnswer()
        {
            return $this->correctAnswer;
        }
    
        /**
         * Sets answer1
         * 
         * @param string $value correctAnswer
         */
        public function setCorrectAnswer($correctAnswer)
        {
            $this->correctAnswer = $correctAnswer;
        }
    
        /**
         * Gets an object representing the date and time the question was created.
         * 
         * @return DateTime A DateTime object
         */
        public function getCreatedAt()
        {
            return $this->createdAt;
        }   
    
    
    
    
      public function __toString()
      {
        return $this->title;
      }
    }
    

    And relevant admin classes. QuizAdmin first:

        <?php 
    
    namespace Some\SiteBundle;
    
    use Some\SiteBundle\Form\QuizQuestionType;
    use Sonata\AdminBundle\Admin\Admin;
    use Sonata\AdminBundle\Datagrid\ListMapper;
    use Sonata\AdminBundle\Datagrid\DatagridMapper;
    use Sonata\AdminBundle\Validator\ErrorElement;
    use Sonata\AdminBundle\Form\FormMapper;
    use Sonata\AdminBundle\Show\ShowMapper;
    use Symfony\Component\HttpFoundation\File\File;
    use Symfony\Component\HttpFoundation\Request;
    
    class QuizAdmin extends Admin
    {
    
    
        protected function configureFormFields(FormMapper $formMapper)
        {
            $formMapper
                  ->add('title', NULL, array('label' => 'tytuł:'))
                  ->add('body', NULL, array('label' => 'opis:', 'required' => false, 'attr' => array(
                      'class' => 'tinymce', 'data-theme' => 'simple')
                  ))
                  ->add('questions', 'sonata_type_admin', array(), array('required' => false, 'edit' => 'inline'));
                  //->add('categories', NULL, array('label' => 'kategorie:'))
            ;
        }
    
        protected function configureDatagridFilters(DatagridMapper $datagridMapper)
        {
            $datagridMapper
                ->add('title')
                ->add('body')
            ;
        }
    
        protected function configureListFields(ListMapper $listMapper)
        {
            $listMapper
                ->addIdentifier('id')
                ->add('title')
                ->add('_action', 'actions', array(
                    'actions' => array(
                        'view' => array(),
                        'edit' => array(),
                    )
                ))
                //->add('body')
            ;
    
        }
    
        public function validate(ErrorElement $errorElement, $object)
        {
            $errorElement
                ->with('title')
                    ->assertMinLength(array('limit' => 2))
                ->end()
            ;
        }
    
    
    
    }
    

    And QuizQuestionAdmin:

        <?php 
    
    namespace Some\SiteBundle;
    
    use Some\SiteBundle\Form\QuizQuestionType;
    use Sonata\AdminBundle\Admin\Admin;
    use Sonata\AdminBundle\Datagrid\ListMapper;
    use Sonata\AdminBundle\Datagrid\DatagridMapper;
    use Sonata\AdminBundle\Validator\ErrorElement;
    use Sonata\AdminBundle\Form\FormMapper;
    use Sonata\AdminBundle\Show\ShowMapper;
    use Symfony\Component\HttpFoundation\File\File;
    use Symfony\Component\HttpFoundation\Request;
    
    class QuizAdmin extends Admin
    {
    
    
        protected function configureFormFields(FormMapper $formMapper)
        {
            $formMapper
                  ->add('title', NULL, array('label' => 'tytuł:'))
                  ->add('body', NULL, array('label' => 'opis:', 'required' => false, 'attr' => array(
                      'class' => 'tinymce', 'data-theme' => 'simple')
                  ))
                  ->add('questions', 'sonata_type_admin', array(), array('required' => false, 'edit' => 'inline'));
                  //->add('categories', NULL, array('label' => 'kategorie:'))
            ;
        }
    
        protected function configureDatagridFilters(DatagridMapper $datagridMapper)
        {
            $datagridMapper
                ->add('title')
                ->add('body')
            ;
        }
    
        protected function configureListFields(ListMapper $listMapper)
        {
            $listMapper
                ->addIdentifier('id')
                ->add('title')
                ->add('_action', 'actions', array(
                    'actions' => array(
                        'view' => array(),
                        'edit' => array(),
                    )
                ))
                //->add('body')
            ;
    
        }
    
        public function validate(ErrorElement $errorElement, $object)
        {
            $errorElement
                ->with('title')
                    ->assertMinLength(array('limit' => 2))
                ->end()
            ;
        }
    
    
    
    }
    

    I tried to register quizQuestion as a service, but then I get Expected argument of type "Festus\SiteBundle\Entity\QuizQuestion", "Doctrine\ORM\PersistentCollection" given 500 Internal Server Error - UnexpectedTypeException

    Couldn't find any solution after few hours of looking things up...

  • heliogabal
    heliogabal almost 12 years
    Yes, error seems to be self explanatory (to some extent), but it's rather form mapping related, so I'm not sure what object should I call exactly? Chain of form mapping functions seems to get collection instead od entity or other object and raises exception. Collection of forms is where the embedded form sholud be, I guess. If I comment out ->add('title', NULL, array('label' => 'tytuł:')) then I get same exception, but with body property.
  • Vitalii Zurian
    Vitalii Zurian almost 12 years
    Could you try to pass additional parameter into third argument to add() method - 'by_reference' => false? symfony.com/doc/current/reference/forms/types/…
  • heliogabal
    heliogabal almost 12 years
    Like this: ->add('questions', 'sonata_type_admin', array(), array('required' => false, 'edit' => 'inline', 'by_reference' => false));? Nope. Doesn't work either. Same error.
  • Vitalii Zurian
    Vitalii Zurian almost 12 years
    Sorry, I confused you with by_reference. It should be called during creation of form builder, not during add method. One more tip - may be you use wrong form_widget in twig to bind form data? So it expects object, but got collection
  • heliogabal
    heliogabal almost 12 years
    I didn't tinker with twig templates (and sonataAdminBundle comes with whole bunch of them) so I guess this is not the case. At least shouldn't be. Maybe I should double check all the bundles with regard to their git branch once again...
  • Matheno
    Matheno over 9 years
    I would give you a million upvotes if I could! You totally saved my day.