Symfony 4 формы стиля викторины, встроенные типы коллекций - PullRequest
0 голосов
/ 24 февраля 2020

Я пытаюсь отобразить форму стиля викторины, где конечный пользователь получает вопросы, которые могут иметь один или несколько правильных ответов.

Сущности:

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

И формы:

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,
        ]);
    }
}

Кажется, я не могу получить examQuestionAnswers поле в коллекции отображается правильно. Я получаю набор несвязанных полей (входных данных), если у вопроса есть только один ответ, и если у вопроса есть несколько ответов, флажки не отображаются. Любая помощь очень ценится!

1 Ответ

0 голосов
/ 25 февраля 2020

ChoiceType ожидает атрибут choices "

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,
                    ]);
            }

. Это приведет к тому, что в качестве выбора для вашего ChoiceType будет добавлено значение $ examQuestionAnswers. Если вы получаете исключение, измените тип на EntityType (он наследует от ChoiceType), поэтому варианты выбора все равно должны работать одинаково. Возможно, вам также понадобится реализовать метод __toString () в вашей сущности ExamQuestionAnswer или определить атрибут choice_label, что должно решить вашу проблему в случае, если он не добавит комментарий о том, что не работает, и я буду реализовать это сам

...