Symfony Form — вопросы (викторина) с выбором

Я пытаюсь создать форму Symfony для викторины, в которой есть вопросы и варианты выбора для каждого вопроса (см. код сущностей). У меня есть сущность RequestQuestion, которая содержит description, isActive, choices. Выбор — это еще одна сущность, которая содержит name, correct и relation вопрос. И, наконец, Request содержит варианты выбора ManyToMany (что означает, что пользователь отметил этот выбор).

Но теперь у меня проблема, что мне нужно как-то в форме сгруппировать варианты по вопросам (используя EntityType с множественным и расширенным значением true). И нет - group_by EntityType не работает для множественного = расширенного = истинного. Это работает только для поля выбора.

Позже я добавил к Request отношение к Question. Это решило половину проблемы — теперь я мог в FormType добавить к вопросам CollectionType (это еще один FormType RequestQuestionType). Обратите внимание, что это создает избыточность в БД, что не очень хорошо. (На самом деле нет. У меня не было бы информации, какие вопросы использовались для этого запроса, так как вопросы могут меняться во времени, устанавливая isActive или добавляя новые вопросы). Но проблема теперь в RequestQuestionType Я не могу добавить ответ, так как вопрос не имеет этого отношения (есть только Request или QuestionChoice).

Вопрос в том, как я могу получить ответы в этой форме? Я не могу использовать родитель (RequestFormType), так как я не мог сгруппировать варианты по вопросам, а в вопросах (RequestQuestionType) я не могу добавить отношение. Ниже я отправляю текущее состояние кода.

Запрос

    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type = "integer")
     */
    private $id;

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

    /**
     * @ORM\ManyToOne(targetEntity=User::class, inversedBy = "requests")
     * @ORM\JoinColumn(nullable=false)
     */
    private $User;

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

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

    /**
     * @ORM\ManyToOne(targetEntity=User::class)
     */
    private $resolvedBy;

    /**
     * @ORM\Column(type = "string", length=32)
     */
    private $state;

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

    /**
     * @ORM\ManyToMany(targetEntity=RequestQuestion::class, inversedBy = "requests")
     */
    private $questions;

    /**
     * @ORM\ManyToMany(targetEntity=RequestQuestionChoice::class, inversedBy = "scholarRequestsAnswers")
     */
    private $answers;

ЗапросВопрос

    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type = "integer")
     */
    private $id;

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

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

    /**
     * @ORM\OneToMany(targetEntity=RequestQuestionChoice::class, mappedBy = "Question", orphanRemoval=true)
     */
    private $choices;

    /**
     * @ORM\ManyToMany(targetEntity=Request::class, mappedBy = "questions")
     */
    private $requests;

ЗапросВопросВыбор

    /**
     * @ORM\Id
     * @ORM\GeneratedValue
     * @ORM\Column(type = "integer")
     */
    private $id;

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

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

    /**
     * @ORM\ManyToOne(targetEntity=RequestQuestion::class, inversedBy = "choices")
     * @ORM\JoinColumn(nullable=false)
     */
    private $Question;

Тип ЗапросаФормы

class RequestFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('address', TextType::class, [
                'constraints' => [
                    new NotBlank([
                        'message' => "Zadejte adresu"
                    ])
                ]
            ])
            ->add('questions', CollectionType::class, [
                'entry_type' => RequestQuestionType::class,
                'entry_options' => [
                    'questions' => $builder->getData()->getQuestions()
                ]
            ])
            ->add('tos', CheckboxType::class, [
                'mapped' => false,
                'value' => false,
                'constraints' => [
                    new IsTrue([
                        'message' => "Musíte souhlasit s našimi podmínkami použití"
                    ])
                ]
            ])
            ->add('Submit', SubmitType::class)
        ;
    }

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

ЗапросВопросТип

class RequestQuestionType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $index = str_replace(["[", "]"], "", $builder->getPropertyPath());

        $builder
            ->add('???', EntityType::class, [
                'class' => RequestQuestionChoice::class,
                'choice_label' => 'name',
                'choices' => $options["questions"][$index]->getChoices(),
                'expanded' => true,
                'multiple' => true
            ]);
    }

    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults([
            'questions' => []
        ]);
    }
}

Примечание: по какой-то причине вопросы от RequestFormType не передаются как данные (data = null), поэтому я передаю их как entry_options. Но в RequestQuestionType он вызывает его столько раз, сколько вопросов, так что это немного странно, но мне удалось обойти это с помощью entry_otpions и использовать индекс из propertyPath.

Примечание: запрос предварительно построен с фиктивными данными - вопросами и передан в эту форму.

Примечание. Я также пытался раньше разложить отношение manyToMany в Request - RequestChoice как RequestAnwer с bool, поскольку пользователь отметил или нет выбор, и предварительно сгенерировать все ответы на questionChoices. Но проблема с группировкой вариантов по вопросам тоже была, поэтому мне тоже не удалось заставить ее работать.

Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Symfony Station Communiqué - 7 июля 2023 г
Symfony Station Communiqué - 7 июля 2023 г
Это коммюнике первоначально появилось на Symfony Station .
Symfony Station Communiqué - 17 февраля 2023 г
Symfony Station Communiqué - 17 февраля 2023 г
Это коммюнике первоначально появилось на Symfony Station , вашем источнике передовых новостей Symfony, PHP и кибербезопасности.
Управление ответами api для исключений на Symfony с помощью KernelEvents
Управление ответами api для исключений на Symfony с помощью KernelEvents
Много раз при создании api нам нужно возвращать клиентам разные ответы в зависимости от возникшего исключения.
0
0
42
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Решено.

Я добавил RequestQuestionAnswers, который имеет OneToMany к RequestQuestionRequestQuestionAnswers есть один вопрос) и отвечает как ManyToMany к RequestQuestionChoice. Таким образом, эта новая сущность привязывается 1:1 к вопросу, и для каждого вопроса я могу генерировать EntityType для каждого вопроса отдельно и генерировать варианты вопросов.

Если у кого-то будет похожая проблема, я вставляю сюда окончательные коды. Также этот вопрос был очень полезен: Создать форму викторины symfony

ПРИМЕЧАНИЕ: к сожалению, я не могу использовать это для множественного false, так как RequestQuestion.requestAnswers является коллекцией, поэтому выдает ошибку: Entity of type "Doctrine\Common\Collections\ArrayCollection" passed to the choice field must be managed. Maybe you forget to persist it in the entity manager?

Тип ЗапросаФормы

class RequestFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('address', TextType::class, [
                'constraints' => [
                    new NotBlank([
                        'message' => "Zadejte adresu"
                    ])
                ],
            ])
            ->add('requestAnswers', CollectionType::class, [
                'entry_type' => RequestQuestionType::class,
                'entry_options' => [
                    'request' => $builder->getData(),
                    'label_attr' => [
                        'class' => 'd-none'
                    ]
                ],
                'label' => 'Dotazník'
            ])
            ->add('tos', CheckboxType::class, [
                'mapped' => false,
                'value' => false,
                'constraints' => [
                    new IsTrue([
                        'message' => "Musíte souhlasit s našimi podmínkami použití"
                    ])
                ],
                'label' => 'Souhlasím s podmínkami použití'
            ])
            ->add('Submit', SubmitType::class, [
                'label' => 'Odeslat'
            ])
        ;
    }

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

ЗапросВопросТип

class RequestQuestionType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $index = str_replace(["[", "]"], "", $builder->getPropertyPath());
        /** @var RequestAnswers $answers */
        $answers = $options["request"]->getRequestAnswers()[$index];

        $builder
            ->add('selectedChoices', EntityType::class, [
                'class' => RequestQuestionChoice::class,
                'choices' => $answers->getQuestion()->getChoices(),
                'choice_label' => 'name',
                'label' => $answers->getQuestion()->getDescription(),
                'expanded' => true,
                'multiple' => true
            ]);
    }

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

Другие вопросы по теме