8

I need to add questionnaire of multiple choice questions to my registration form. The questions and options are in two entities:

<?php

namespace Me\UserBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * Question
 *
 * @ORM\Table(name="question")
 * @ORM\Entity(repositoryClass="Me\UserBundle\Entity\QuestionRepository")
 */
class Question
{
/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @var string
 *
 * @ORM\Column(name="questionText", type="text")
 */
private $questionText;

/**
 * @var boolean $expanded
 *
 * @ORM\Column(name="expanded", type="boolean")
 */
private $expanded;

/**
 * @var boolean $multiple
 *
 * @ORM\Column(name="multiple", type="boolean")
 */
private $multiple;

/**
 * @var Questionnaire $questionnaire
 *
 * @ORM\ManyToOne(targetEntity="Questionnaire", inversedBy="questions")
 * @ORM\JoinColumn(name="questionnaire", referencedColumnName="id", onDelete="cascade")
 */
private $questionnaire;

/**
 * @var \Doctrine\Common\Collections\ArrayCollection $options
 *
 * @ORM\OneToMany(targetEntity="Option", mappedBy="question", cascade={"all"})
 */
private $options;

public function __construct()
{
    $this->expanded = false;
    $this->multiple = false;

    $this->options = new ArrayCollection();
}


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

/**
 * Set questionText
 *
 * @param string $questionText
 * @return Question
 */
public function setQuestionText($questionText)
{
    $this->questionText = $questionText;

    return $this;
}

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

/**
 * @param mixed $options
 */
public function setOptions($options)
{
    $this->options[] = $options;

    return $this;

}

/**
 * @return mixed
 */
public function getOptions()
{
    return $this->options;
}

function __toString()
{
    return $this->getQuestionText();
}

/**
 * @param boolean $expanded
 */
public function setExpanded($expanded)
{
    $this->expanded = $expanded;
}

/**
 * @return boolean
 */
public function getExpanded()
{
    return $this->expanded;
}

/**
 * @param boolean $multiple
 */
public function setMultiple($multiple)
{
    $this->multiple = $multiple;
}

/**
 * @return boolean
 */
public function getMultiple()
{
    return $this->multiple;
}

/**
 * @param \Me\UserBundle\Entity\Questionnaire $questionnaire
 */
public function setQuestionnaire($questionnaire)
{
    $this->questionnaire = $questionnaire;
}

/**
 * @return \Me\UserBundle\Entity\Questionnaire
 */
public function getQuestionnaire()
{
    return $this->questionnaire;
}


}

and

<?php

namespace Me\UserBundle\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
 * QuestionOption
 *
 * @ORM\Table(name="option")
 * @ORM\Entity(repositoryClass="Me\UserBundle\Entity\OptionRepository")
 */
class Option
{
/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @var integer
 *
 * @ORM\Column(name="questionId", type="integer")
 */
private $questionId;

/**
 * @var string
 *
 * @ORM\Column(name="optionText", type="string", length=255)
 */
private $optionText;

/**
 * @ORM\ManyToOne(targetEntity="Question", inversedBy="options")
 * @ORM\JoinColumn(name="questionId", referencedColumnName="id", onDelete="cascade")
 **/
private $question;

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

/**
 * Set optionText
 *
 * @param string $optionText
 * @return Option
 */
public function setOptionText($optionText)
{
    $this->optionText = $optionText;

    return $this;
}

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

/**
 * @return mixed
 */
public function getQuestion()
{
    return $this->question;
}

/**
 * @param mixed $question
 */
public function setQuestion($question)
{
    $this->question = $question;
}

/**
 * @param int $id
 */
public function setId($id)
{
    $this->id = $id;
}

function __toString()
{
    return $this->getOptionText();
}
}

I also have a questionnaire entity, though I don't think I really need it because users won't be creating questionnaires, only filling the single questionnaire during registration.

My user entity:

<?php

namespace Me\UserBundle\Entity;

use Doctrine\ORM\Mapping as ORM;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * User
 *
 * @ORM\Table(name="user")
 * @ORM\Entity(repositoryClass="Me\UserBundle\Entity\UserRepository")
 */
class User
{
/**
 * @var integer
 *
 * @ORM\Column(name="id", type="integer")
 * @ORM\Id
 * @ORM\GeneratedValue(strategy="AUTO")
 */
private $id;

/**
 * @var string
 *
 * @ORM\Column(name="firstName", type="string", length=50)
 */
private $firstName;

/**
 * @var string
 *
 * @ORM\Column(name="middleInitial", type="string", length=50)
 */
private $middleInitial;

/**
 * @var string
 *
 * @ORM\Column(name="lastName", type="string", length=50)
 */
private $lastName;

/**
 * @var string
 *
 * @ORM\Column(name="homePhoneArea", type="string", length=3)
 */
private $homePhoneArea;

/**
 * @var string
 *
 * @ORM\Column(name="homePhoneNumber", type="string", length=7)
 */
private $homePhoneNumber;

/**
 * @var string
 *
 * @ORM\Column(name="email", type="string", length=50)
 */
private $email;

/**
 * @var \Doctrine\Common\Collections\ArrayCollection
 */
public $questions;

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

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

/**
 * Set firstName
 *
 * @param string $firstName
 * @return User
 */
public function setFirstName($firstName)
{
    $this->firstName = $firstName;

    return $this;
}

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

/**
 * Set middleInitial
 *
 * @param string $middleInitial
 * @return User
 */
public function setMiddleInitial($middleInitial)
{
    $this->middleInitial = $middleInitial;

    return $this;
}

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

/**
 * Set lastName
 *
 * @param string $lastName
 * @return User
 */
public function setLastName($lastName)
{
    $this->lastName = $lastName;

    return $this;
}

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

/**
 * Set homePhoneArea
 *
 * @param string $homePhoneArea
 * @return User
 */
public function setHomePhoneArea($homePhoneArea)
{
    $this->homePhoneArea = $homePhoneArea;

    return $this;
}

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

/**
 * Set homePhoneNumber
 *
 * @param string $homePhoneNumber
 * @return User
 */
public function setHomePhoneNumber($homePhoneNumber)
{
    $this->homePhoneNumber = $homePhoneNumber;

    return $this;
}

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


/**
 * Set email
 *
 * @param string $email
 * @return User
 */
public function setEmail($email)
{
    $this->email = $email;

    return $this;
}

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



/**
 * @return \Doctrine\Common\Collections\ArrayCollection
 */
public function getQuestions()
{
    return $this->questions;
}


}

My user controller:

public function newAction()
{
    $user = new User();

    $em = $this->getDoctrine()->getManager();

    $questions = $em->getRepository('MeUserBundle:Question')->findAll();



    if (!$questions) {
        throw $this->createNotFoundException('Unable to find Questions.');
    }

    $builder = $this->createFormBuilder();

    $optionEntities = array();
    foreach ($questions as $question)
    {
        $options = array();
        foreach ($question->getOptions() as $option)
        {
            $options[$option->getId()] = $option->getOptionText();
            $optionEntities[$option->getId()] = $option;
        }
        $builder->add('question_'. $question->getId(), 'choice', array(
            'label' => $question->getQuestionText(),
            'expanded' => $question->getExpanded(),
            'multiple' => $question->getMultiple(),
            'choices' => $options
        ));
    }

    $user->getQuestions()->add($questions);

    $form = $this->createForm(new MyFormType(), array('User' => $user));

    return $this->render('MeUserBundle:User:new.html.twig', array(
        'entity' => $user,
        'form'   => $form->createView(),
    ));
}

The form type as it stands today:

<?php

namespace Me\UserBundle\Form;

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

class MyFormType extends AbstractType
{
protected $questions;

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

public function buildForm(FormBuilderInterface $builder, array $options)
{

    $builder
        ->add('questions', 'collection', array(
            'type' => new QuestionType()
        ))
        ->add('firstName')
        ->add('middleInitial')
        ->add('lastName')
        ->add('homePhoneArea')
        ->add('homePhoneNumber')
        ->add('email')
    ;
}

public function setDefaultOptions(OptionsResolverInterface $resolver)
{
    $resolver->setDefaults(array(

    ));
}

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

This setup doesn't work to get the questions and their associated options, and display them in the same user creation form. I've seen instructions and docs for combining forms, but not creating forms with this kind of configuration. Any guidance would be appreciated.

Fo.
  • 3,752
  • 7
  • 28
  • 44
  • just to clarify, does User and Question are connected with any kind of relation? and, can you post your MyFormType form? – João Alves Jun 05 '13 at 22:59
  • Where you want to store answers? – Alexey B. Jun 08 '13 at 04:42
  • Show us your User Entity and your MyFormType Form – João Alves Jun 08 '13 at 11:18
  • I have been so focused on trying to display this form I haven't thought about processing/storing responses yet. – Fo. Jun 08 '13 at 15:47
  • entity and form type added as requested – Fo. Jun 08 '13 at 15:56
  • You are mistaken with your Option class, you don't need your "$questionId" field as you already have a ManyToOne "$question" field mapped, just add a @JoinColumn annotation (see http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html#one-to-many-bidirectional) – Nanocom Aug 30 '13 at 08:41

3 Answers3

1

I'm not sure if I've understood your question correctly but maybe this could help.

From what I see you're building the form(for questions) but you're not using it anywhere! The easiest way is to pass the questions(in your case $user->getQuestions() ) to the MyFormType and the add all the questions inside of the type.

So it would look like something like this

$this->createForm(new MyFormType($user->getQuestions()), array('User' => $user));

And inside your type

protected $questions;

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

protected $questions;

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

public function buildForm(FormBuilderInterface $builder, array $options) 
{
    foreach ($this->questions as $question) 
    {
        $options = array();
        foreach ($question->getOptions() as $option)
        {
            $options[$option->getId()] = $option->getOptionText();
            $optionEntities[$option->getId()] = $option;
        }
        $builder->add('question_'. $question->getId(), 'choice', array(
            'label' => $question->getQuestionText(),
            'expanded' => $question->getExpanded(),
            'multiple' => $question->getMultiple(),
            'choices' => $options
        ));
    }
 }

Edit 1

Why don't you try the following?

Add the method setQuestionnaire in User and create a type called QuestionnaireType which is responsible to create the whole questionnaire In the UserType(sorry for the wrong names) add the QuestionnaireType as an embedded form Once the user submits the data and you call the bind symfony will pass the whole questionnaire object to the created method so you can iterate over it and save the user's aswers!

PHP lover
  • 238
  • 2
  • 11
  • What to do about this error? Neither property "question_13" nor method "getQuestion13()" nor method "isQuestion13()" exists in class "Me\UserBundle\Entity\User" It's trying to loop through the questions and can't find them in the User entity, which is correct since they are in the Question entity – Fo. Jun 06 '13 at 20:02
  • Sorry I misunderstood the problem. Read my answer and see if it helps :) – PHP lover Jun 11 '13 at 19:18
  • I've posted a bounty for this, I'm happy to award it to you if you post working code I can learn from. – Fo. Jun 11 '13 at 19:31
0

Your users entity needs a relation to his $answers you should store in an $answers-field in your user entity, (look up "embedding collections")

Then in your controller that digests the form your store the values by $user->setAnswers(value)) and then you'll find the answers values in the users entity's $answers field ($user->getAnswers()).

ANd dont forget to add your getters and setters.

$php app/console doctrine:generate:entities BundleName:Entityname

Phil
  • 200
  • 10
0

At first you could add a setQuestions method in user, this method will receive an ArrayCollection.

Then have you got a look at http://symfony.com/doc/current/reference/forms/types/collection.html ?

And maybe you could ask yourself what does this form should display/do Does it need to embbed a collection or a list of QuestionFormType could be ok?

Each time i have to do embedded form type, i draw sketches and activity diagram for being sure to not dive into a to complex form structure

jeremy
  • 309
  • 2
  • 9