简体   繁体   中英

SonataAdminBundle embedded forms sonata_admin_type issue

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

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');
    }
}

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()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM