简体   繁体   English

Symfony 4 测验样式形式,嵌入式集合类型

[英]Symfony 4 quiz style form, embedded collection types

I'm trying to display a quiz style form where end-user gets presented with questions that can have one or multiple correct answers.我正在尝试显示一种测验样式的表单,其中向最终用户呈现可以有一个或多个正确答案的问题。

Entities:实体:

Exam Question

<?php

namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Timestampable\Traits\TimestampableEntity;
use Ramsey\Uuid\UuidInterface;

/**
 * @ORM\Entity(repositoryClass="App\Repository\ExamQuestionRepository")
 */
class ExamQuestion
{
    use TimestampableEntity;

    /**
     * @var UuidInterface
     *
     * @ORM\Id
     * @ORM\Column(type="uuid", unique=true)
     * @ORM\GeneratedValue(strategy="CUSTOM")
     * @ORM\CustomIdGenerator(class="Ramsey\Uuid\Doctrine\UuidGenerator")
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Module", inversedBy="questions")
     * @ORM\JoinColumn(nullable=false)
     */
    private $module;

    /**
     * @ORM\Column(type="text")
     */
    private $text;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\ExamQuestionAnswer", mappedBy="question", orphanRemoval=true, cascade={"persist"})
     */
    private $examQuestionAnswers;

    /**
     * @ORM\Column(type="boolean")
     */
    private $hasMultipleAnswers;

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

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

    public function getId(): ?UuidInterface
    {
        return $this->id;
    }

    public function getModule(): ?Module
    {
        return $this->module;
    }

    public function setModule(?Module $module): self
    {
        $this->module = $module;

        return $this;
    }

    public function getText(): ?string
    {
        return $this->text;
    }

    public function setText(string $text): self
    {
        $this->text = $text;

        return $this;
    }

    /**
     * @return Collection|ExamQuestionAnswer[]
     */
    public function getExamQuestionAnswers(): Collection
    {
        return $this->examQuestionAnswers;
    }

    public function addExamQuestionAnswer(ExamQuestionAnswer $examQuestionAnswer): self
    {
        if (!$this->examQuestionAnswers->contains($examQuestionAnswer)) {
            $this->examQuestionAnswers[] = $examQuestionAnswer;
            $examQuestionAnswer->setQuestion($this);
        }

        return $this;
    }

    public function removeExamQuestionAnswer(ExamQuestionAnswer $examQuestionAnswer): self
    {
        if ($this->examQuestionAnswers->contains($examQuestionAnswer)) {
            $this->examQuestionAnswers->removeElement($examQuestionAnswer);
            // set the owning side to null (unless already changed)
            if ($examQuestionAnswer->getQuestion() === $this) {
                $examQuestionAnswer->setQuestion(null);
            }
        }

        return $this;
    }

    public function getHasMultipleAnswers(): ?bool
    {
        return $this->hasMultipleAnswers;
    }

    public function setHasMultipleAnswers(bool $hasMultipleAnswers): self
    {
        $this->hasMultipleAnswers = $hasMultipleAnswers;

        return $this;
    }
}
Exam Question Answer

<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;
use Gedmo\Timestampable\Traits\TimestampableEntity;
use Ramsey\Uuid\UuidInterface;

/**
 * @ORM\Entity(repositoryClass="App\Repository\ExamQuestionRepository")
 */
class ExamQuestionAnswer
{
    use TimestampableEntity;

    /**
     * @var UuidInterface
     *
     * @ORM\Id
     * @ORM\Column(type="uuid", unique=true)
     * @ORM\GeneratedValue(strategy="CUSTOM")
     * @ORM\CustomIdGenerator(class="Ramsey\Uuid\Doctrine\UuidGenerator")
     */
    private $id;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\ExamQuestion", inversedBy="examQuestionAnswers")
     * @ORM\JoinColumn(nullable=false)
     */
    private $question;

    /**
     * @ORM\Column(type="boolean")
     */
    private $isCorrect;

    /**
     * @ORM\Column(type="text")
     */
    private $text;

    /**
     * @ORM\Column(type="boolean", nullable=true)
     */
    private $selected;

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

    public function getId(): ?UuidInterface
    {
        return $this->id;
    }

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

    public function setQuestion(ExamQuestion $question): self
    {
        $this->question = $question;

        return $this;
    }

    public function getIsCorrect(): ?bool
    {
        return $this->isCorrect;
    }

    public function setIsCorrect(bool $isCorrect): self
    {
        $this->isCorrect = $isCorrect;

        return $this;
    }

    public function getText(): ?string
    {
        return $this->text;
    }

    public function setText(string $text): self
    {
        $this->text = $text;

        return $this;
    }

    public function getSelected(): ?bool
    {
        return $this->selected;
    }

    public function setSelected(?bool $selected): self
    {
        $this->selected = $selected;

        return $this;
    }
}
Exam Take

<?php

namespace App\Entity;

use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Gedmo\Timestampable\Traits\TimestampableEntity;
use Ramsey\Uuid\UuidInterface;

/**
 * @ORM\Entity(repositoryClass="App\Repository\ExamTakeRepository")
 */
class ExamTake
{
    use TimestampableEntity;

    /**
     * @var UuidInterface
     *
     * @ORM\Id
     * @ORM\Column(type="uuid", unique=true)
     * @ORM\GeneratedValue(strategy="CUSTOM")
     * @ORM\CustomIdGenerator(class="Ramsey\Uuid\Doctrine\UuidGenerator")
     */
    private $id;

    /**
     * @ORM\ManyToMany(targetEntity="App\Entity\ExamQuestion")
     */
    private $questions;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Module", inversedBy="examTakes")
     * @ORM\JoinColumn(nullable=false)
     */
    private $module;

    /**
     * @ORM\ManyToOne(targetEntity="App\Entity\Student", inversedBy="examTakes")
     * @ORM\JoinColumn(nullable=false)
     */
    private $student;

    /**
     * @ORM\Column(type="boolean", nullable=true)
     */
    private $passed;

    /**
     * @ORM\OneToMany(targetEntity="App\Entity\ExamQuestionStudentAnswer", mappedBy="examTake", orphanRemoval=true)
     */
    private $examQuestionStudentAnswers;

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

    public function getId(): ?UuidInterface
    {
        return $this->id;
    }

    /**
     * @return Collection|ExamQuestion[]
     */
    public function getQuestions(): Collection
    {
        return $this->questions;
    }

    public function addQuestion(ExamQuestion $question): self
    {
        if (!$this->questions->contains($question)) {
            $this->questions[] = $question;
        }

        return $this;
    }

    public function removeQuestion(ExamQuestion $question): self
    {
        if ($this->questions->contains($question)) {
            $this->questions->removeElement($question);
        }

        return $this;
    }

    public function getModule(): ?Module
    {
        return $this->module;
    }

    public function setModule(?Module $module): self
    {
        $this->module = $module;

        return $this;
    }

    public function getStudent(): ?Student
    {
        return $this->student;
    }

    public function setStudent(?Student $student): self
    {
        $this->student = $student;

        return $this;
    }

    public function getPassed(): ?bool
    {
        return $this->passed;
    }

    public function setPassed(?bool $passed): self
    {
        $this->passed = $passed;

        return $this;
    }

    /**
     * @return Collection|ExamQuestionStudentAnswer[]
     */
    public function getExamQuestionStudentAnswers(): Collection
    {
        return $this->examQuestionStudentAnswers;
    }

    public function addExamQuestionStudentAnswer(ExamQuestionStudentAnswer $examQuestionStudentAnswer): self
    {
        if (!$this->examQuestionStudentAnswers->contains($examQuestionStudentAnswer)) {
            $this->examQuestionStudentAnswers[] = $examQuestionStudentAnswer;
            $examQuestionStudentAnswer->setExamTake($this);
        }

        return $this;
    }

    public function removeExamQuestionStudentAnswer(ExamQuestionStudentAnswer $examQuestionStudentAnswer): self
    {
        if ($this->examQuestionStudentAnswers->contains($examQuestionStudentAnswer)) {
            $this->examQuestionStudentAnswers->removeElement($examQuestionStudentAnswer);
            // set the owning side to null (unless already changed)
            if ($examQuestionStudentAnswer->getExamTake() === $this) {
                $examQuestionStudentAnswer->setExamTake(null);
            }
        }

        return $this;
    }
}

And the forms:和表格:

class ExamTakeType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('questions', CollectionType::class, [
                'entry_type' => ExamQuestionType::class,
                'allow_add' => false,
            ])
        ;
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => ExamTake::class,
        ]);
    }
}
class ExamQuestionType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('text', TextType::class, [
                'attr' => ['readonly' => true],
            ])
            ->add('examQuestionAnswers', CollectionType::class, [
                'entry_type' => ExamQuestionAnswerType::class,
                'allow_add' => false,
            ])
        ;

    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => ExamQuestion::class,
        ]);
    }
}
class ExamQuestionAnswerType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('text');

        $builder->addEventListener(FormEvents::POST_SET_DATA, static function (FormEvent $event) {

            $form = $event->getForm();

            /** @var ExamQuestion $question */
            $question = $event->getData()->getQuestion();

            if ($question->getHasMultipleAnswers()) {
                $form
                    ->add('select', ChoiceType::class, [
                        'expanded' => true,
                        'multiple' => true,
                        'mapped' => false,
                    ]);
            } else {
                $form
                    ->add('select', ChoiceType::class, [
                        'expanded' => true,
                        'multiple' => false,
                        'mapped' => false,
                    ]);
            }
        });
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'data_class' => ExamQuestionAnswer::class,
        ]);
    }
}

It seems i can't get the examQuestionAnswers field in collection displayed correctly.看来我无法正确显示集合中的examQuestionAnswers字段。 What i get is a collection of unrelated fields(inputs) if question has only one answer, and if question has multiple answers checkboxes are not shown.如果问题只有一个答案,并且如果问题有多个答案复选框,则我得到的是一组不相关的字段(输入)。 Any help is much appreciated!任何帮助深表感谢!

The ChoiceType expects the choices attribute" ChoiceType 需要选择属性”

if ($question->getHasMultipleAnswers()) {
                $form
                    ->add('select', ChoiceType::class, [
                        'choices' => $question->getExamQuestionAnswers(),
                        'expanded' => true,
                        'multiple' => true,
                        'mapped' => false,
                    ]);
            } else {
                $form
                    ->add('select', ChoiceType::class, [
                        'choices' => $question->getExamQuestionAnswers(),
                        'expanded' => true,
                        'multiple' => false,
                        'mapped' => false,
                    ]);
            }

This will result in putting $examQuestionAnswers as choices to your ChoiceType.这将导致将 $examQuestionAnswers 作为您的 ChoiceType 的选项。 In case you get an exception change the type to EntityType(it inherits from ChoiceType) so choices should still work the same.如果您遇到异常,请将类型更改为 EntityType(它继承自 ChoiceType),因此选择仍应保持不变。 You might also needed to implement either a __toString() method in your ExamQuestionAnswer entity or define choice_label attribute this should solve your problem in case it doesn't add a comment about what does not work and I'll implement it myself您可能还需要在您的 ExamQuestionAnswer 实体中实现 __toString() 方法或定义 choice_label 属性,这应该可以解决您的问题,以防它没有添加关于什么不起作用的评论,我将自己实现

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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