5
votes

I have a Quiz structure with 3 entities:

  • Quiz has Questions (OneToMany)
  • Question has Quiz (ManyToOne)
  • Question has Answers (OneToMany)
  • Answer has Question (ManyToOne)

The code looks like such:

Quiz Entity

class Quiz
{    
    /**
     * @ORM\OneToMany(targetEntity="Question", mappedBy="quiz", cascade={"persist", "remove"})
     */
    private $questions;

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

    public function addQuestion(\Cariboo\QuizBundle\Entity\Question $questions)
    {
        $questions->setQuiz( $this );
        // die(); Not dying here...
        $this->questions[] = $questions;

        return $this;
    }

    public function removeQuestion(\Cariboo\QuizBundle\Entity\Question $questions)
    {
        $this->questions->removeElement($questions);
    }

    public function getQuestions()
    {
        return $this->questions;
    }
}

Question Entity

class Question
{
    /**
     * @ORM\OneToMany(targetEntity="Answer", mappedBy="question", cascade={"persist", "remove"})
     */
    private $answers;

    /**
     * @ORM\ManyToOne(targetEntity="Cariboo\QuizBundle\Entity\Quiz", cascade={"persist"})
     */
    private $quiz;

    public function addAnswer(\Cariboo\QuizBundle\Entity\Answer $answers)
    {
        $answers->setQuestion( $this );
        $this->answers[] = $answers;

        return $this;
    }

    public function removeAnswer(\Cariboo\QuizBundle\Entity\Answer $answers)
    {
        $this->answers->removeElement($answers);
    }

    public function getAnswers()
    {
        return $this->answers;
    }

    public function setQuiz(\Cariboo\QuizBundle\Entity\Quiz $quiz = null)
    {
        $this->quiz = $quiz;

        return $this;
    }

    public function getQuiz()
    {
        return $this->quiz;
    }
}

Answer entity

class Answer
{
    /**
     * @ORM\ManyToOne(targetEntity="Cariboo\QuizBundle\Entity\Question")
     */
    private $question;

    public function setQuestion(\Cariboo\QuizBundle\Entity\Question $question = null)
    {
        $this->question = $question;

        return $this;
    }

    public function getQuestion()
    {
        return $this->question;
    }
}

I cascade persist them.

I configured my setters as explained in Cascaded persist not working (Doctrine ORM + Symfony 2)

The addAnswer gets executed and for answers, the Question is set properly. However the addQuestion does not set the quiz.

image of what gets dumped upon sending the form

I can add a foreach in my controller but I am not satisfied with this, as I feel I am not exploiting symfony's power. ( No duplicate of Entity doesn't associate correctly )

    if ( $form->isValid() ) {
        foreach ( $quiz->getQuestions() as $question ) {
            $question->setQuiz( $quiz );
        }
        $em = $this->getDoctrine()->getManager();
        $em->persist( $quiz );
        $em->flush();

        return $this->redirect( $this->generateUrl( 'quiz_show', array(
            'slug' => $quiz->getSlug()
        ) ) );
    }

I cannot figure out why the addQuestion does not get executed.

Edit

My Quiz form

    $builder
        ->add( 'questions', 'collection', array(
            'type' => new QuestionType(),
            'allow_add' => true,
            'allow_delete' => true,
            'prototype' => true,
            'prototype_name' => '__qst_prot__'
        ) )
    ;

Partial builder of Question

    $builder
        ->add( 'question', 'text' )
        ->add( 'answers', 'collection', array(
            'type' => new AnswerType(),
            'allow_add' => true,
            'allow_delete' => true,
            'prototype' => true,
            'prototype_name' => '__asw_prot__'
        ) );

My Answer form

$builder
        ->add( 'answer', 'text' )
        ->add( 'correct' )
    ;
1
i think that collection type creates problem here. How about setting by_reference to false (in Quiz form)? - Paweł Mikołajczuk
Setting by_reference works wonders! I have no idea why it worked for Answers without it though. - Reynald Lechapt

1 Answers

3
votes

Problem is in collection form type. You need to set by_reference to false if you want to call parent entity setter.

Symfony docs:

Similarly, if you're using the CollectionType field where your underlying collection data is an object (like with Doctrine's ArrayCollection), then by_reference must be set to false if you need the adder and remover (e.g. addAuthor() and removeAuthor()) to be called.

Read more here: http://symfony.com/doc/current/reference/forms/types/collection.html#by-reference