Symfony 嵌入表单集合不持久

发布于 2025-01-14 12:34:31 字数 7638 浏览 1 评论 0原文

我正在尝试构建一个可以包含多个问题(年龄、性别等)的表单,为此我需要动态添加字段,我遵循 这个 指南,添加字段的按钮效果很好。

添加字段

但是,当我提交表单时,它只包含表单的值减去通过JS添加字段 var_dump(),其中应包含问题[ 0], [1]...

var_dump() 的输出:

array(1) { ["recruitment_form"]=> array(4) { ["name"]=> string(6) "ezrzer" ["description"]=> string(15) "
zzrrez

" ["submit"]=> string(0) "" ["_token"]=> string(131) "d3e9191f742255bb23bf5d69b628d.u4Ko0rH_5plSnfypsRTxNVL3" 
} }

Controller:

 #[Route('/admin/server/form/create', name: 'create_form')]
    public function admin_create_form(Request $request, ManagerRegistry $doctrine): Response
    {
        $form = $this->createForm(RecrutementFormType::class);
        
        $form->handleRequest($request);
        if($form->isSubmitted() AND $form->isValid()) {
            $recrutementForm = $form->getData();
            foreach($form->get('questions') as $q) {
                $question = new FormInput($q);
                $doctrine->getManager()->persist($question);
                $recrutementForm->addQuestion($question);
            }
            $doctrine->getManager()->persist($recrutementForm);
            $doctrine->getManager()->flush();
            return $this->redirectToRoute('view_server', ['id' => $recrutementForm->getId()]);
        }
        
        return $this->render('recrutement_form/admin/form.html.twig', [
            'form' => $form->createView()
        ]);
    }

RecrutementFormType:

<?php

namespace App\Form;

use App\Entity\RecrutementForm;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\ButtonType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;

class RecrutementFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('name', TextType::class, [
                'label' => 'Nom du formulaire',
                'attr' => [
                    'class' => 'form-control'
                ]
            ])
            ->add('description', TextareaType::class, [
                'label' => 'Description',
                'required' => false,
                'attr' => [
                    'class' => 'ckeditor form-control'
                ]
            ])
            ->add('questions', CollectionType::class, [
                'label' => 'Questions',
                'entry_type' => QuestionFormType::class,
                'allow_add' => true,
                'by_reference' => false
            ])
            ->add('submit', SubmitType::class, [
                'label' => 'Valider',
                'attr' => [
                    'class' => 'btn btn-success'
                ]
            ])
        ;
    }

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

QuestionFormType:

<?php
namespace App\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use App\Entity\FormInput;

class QuestionFormType extends AbstractType
{
    
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
        ->add('name', TextType::class, [
            'label' => 'Nom interne de la question', 
            'attr' => [
                'class' => 'form-control'
            ]
        ])
        ->add('title', TextType::class, [
            'label' => 'Titre de la question',
            'attr' => [
                'class' => 'form-control'
            ]
        ])
        ->add('description', TextareaType::class, [
            'label' => 'Description',
            'attr' => [
                'class' => 'ckeditor form-control'
            ]
        ])
        ->add('type', InputType::class, [
            'label' => 'Type de champ',
        ])
        ;
    }
    
    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => FormInput::class,
        ]);
    }
}

Twig Template:

{% extends 'form.html.twig' %}
{% block formblock %}
<div class="container">
    {{ form_start(form) }}
    
        {% if form_errors(form) is not empty %}
            <div class="alert alert-danger">
                {{ form_errors(form) }}
            </div>
        {% endif %}
        
        
        <div class="row">
            <div class="col lg-12">
                {{ form_row(form.name) }}
            </div>
        </div>
        
        <div class="row>
            <div class="col-lg-12">
                {{ form_row(form.description) }}
            </div>
        </div>
        
        <div class="row>
            <div class="col-lg-6">
                <ul class="questions" data-index="{{ form.questions|length > 0 ? form.questions|last.vars.name + 1 : 0 }}" data-prototype="{{ form_widget(form.questions.vars.prototype)|e('html_attr') }}">
                    {% for q in form.questions %}
                        {{ form_row(q) }}
                    {% endfor %}
                </ul>
            </div>
            <div class="col-lg-6">
                <button type="button" class="add_item_link" data-collection-holder-class="questions">Add a tag</button>
            </div>
        </div>
        
         <div class="row>
            <div class="col-lg-12">
                {{ form_row(form.submit) }}
            </div>
        </div>
        
    {{ form_end(form) }}
</div>
<script>  
const addFormToCollection = (e) => {
  const collectionHolder = document.querySelector('.' + e.currentTarget.dataset.collectionHolderClass);

  const item = document.createElement('li');

  item.innerHTML = collectionHolder
    .dataset
    .prototype
    .replace(
      /__name__/g,
      collectionHolder.dataset.index
    );

  collectionHolder.appendChild(item);

  collectionHolder.dataset.index++;
};
document
  .querySelectorAll('.add_item_link')
  .forEach(btn => {
      btn.addEventListener("click", addFormToCollection)
  });
</script>

{% endblock %}

我正在使用 Symfony 6 和 PHP 8

Final HTML 代码,当模板呈现: https://pastebin.com/LWUU1pKv (包括 CKEditor 代码,抱歉)

我猜事实是“questions” ul 元素位于表单之外并不是很好,但无法弄清楚如何移动它,因为它应该已经在表单内部

I'm trying to build a form that can contain multiple questions (Age, sex, ...), and for that I need to add fields dynamically, I followed this guide, and the button to add fields works great.

Added fields

But then, when I submit my form, it only contains the values of the form minus the added fields via JS
var_dump(), where it should contain question[0], [1]...

Output of var_dump():

array(1) { ["recruitment_form"]=> array(4) { ["name"]=> string(6) "ezrzer" ["description"]=> string(15) "
zzrrez

" ["submit"]=> string(0) "" ["_token"]=> string(131) "d3e9191f742255bb23bf5d69b628d.u4Ko0rH_5plSnfypsRTxNVL3" 
} }

Controller:

 #[Route('/admin/server/form/create', name: 'create_form')]
    public function admin_create_form(Request $request, ManagerRegistry $doctrine): Response
    {
        $form = $this->createForm(RecrutementFormType::class);
        
        $form->handleRequest($request);
        if($form->isSubmitted() AND $form->isValid()) {
            $recrutementForm = $form->getData();
            foreach($form->get('questions') as $q) {
                $question = new FormInput($q);
                $doctrine->getManager()->persist($question);
                $recrutementForm->addQuestion($question);
            }
            $doctrine->getManager()->persist($recrutementForm);
            $doctrine->getManager()->flush();
            return $this->redirectToRoute('view_server', ['id' => $recrutementForm->getId()]);
        }
        
        return $this->render('recrutement_form/admin/form.html.twig', [
            'form' => $form->createView()
        ]);
    }

RecrutementFormType:

<?php

namespace App\Form;

use App\Entity\RecrutementForm;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Form\Extension\Core\Type\ButtonType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Doctrine\Common\Collections\ArrayCollection;
use Symfony\Component\Form\Extension\Core\Type\CollectionType;

class RecrutementFormType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('name', TextType::class, [
                'label' => 'Nom du formulaire',
                'attr' => [
                    'class' => 'form-control'
                ]
            ])
            ->add('description', TextareaType::class, [
                'label' => 'Description',
                'required' => false,
                'attr' => [
                    'class' => 'ckeditor form-control'
                ]
            ])
            ->add('questions', CollectionType::class, [
                'label' => 'Questions',
                'entry_type' => QuestionFormType::class,
                'allow_add' => true,
                'by_reference' => false
            ])
            ->add('submit', SubmitType::class, [
                'label' => 'Valider',
                'attr' => [
                    'class' => 'btn btn-success'
                ]
            ])
        ;
    }

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

QuestionFormType:

<?php
namespace App\Form;

use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\TextareaType;
use Symfony\Component\OptionsResolver\OptionsResolver;
use App\Entity\FormInput;

class QuestionFormType extends AbstractType
{
    
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
        ->add('name', TextType::class, [
            'label' => 'Nom interne de la question', 
            'attr' => [
                'class' => 'form-control'
            ]
        ])
        ->add('title', TextType::class, [
            'label' => 'Titre de la question',
            'attr' => [
                'class' => 'form-control'
            ]
        ])
        ->add('description', TextareaType::class, [
            'label' => 'Description',
            'attr' => [
                'class' => 'ckeditor form-control'
            ]
        ])
        ->add('type', InputType::class, [
            'label' => 'Type de champ',
        ])
        ;
    }
    
    public function configureOptions(OptionsResolver $resolver): void
    {
        $resolver->setDefaults([
            'data_class' => FormInput::class,
        ]);
    }
}

Twig Template:

{% extends 'form.html.twig' %}
{% block formblock %}
<div class="container">
    {{ form_start(form) }}
    
        {% if form_errors(form) is not empty %}
            <div class="alert alert-danger">
                {{ form_errors(form) }}
            </div>
        {% endif %}
        
        
        <div class="row">
            <div class="col lg-12">
                {{ form_row(form.name) }}
            </div>
        </div>
        
        <div class="row>
            <div class="col-lg-12">
                {{ form_row(form.description) }}
            </div>
        </div>
        
        <div class="row>
            <div class="col-lg-6">
                <ul class="questions" data-index="{{ form.questions|length > 0 ? form.questions|last.vars.name + 1 : 0 }}" data-prototype="{{ form_widget(form.questions.vars.prototype)|e('html_attr') }}">
                    {% for q in form.questions %}
                        {{ form_row(q) }}
                    {% endfor %}
                </ul>
            </div>
            <div class="col-lg-6">
                <button type="button" class="add_item_link" data-collection-holder-class="questions">Add a tag</button>
            </div>
        </div>
        
         <div class="row>
            <div class="col-lg-12">
                {{ form_row(form.submit) }}
            </div>
        </div>
        
    {{ form_end(form) }}
</div>
<script>  
const addFormToCollection = (e) => {
  const collectionHolder = document.querySelector('.' + e.currentTarget.dataset.collectionHolderClass);

  const item = document.createElement('li');

  item.innerHTML = collectionHolder
    .dataset
    .prototype
    .replace(
      /__name__/g,
      collectionHolder.dataset.index
    );

  collectionHolder.appendChild(item);

  collectionHolder.dataset.index++;
};
document
  .querySelectorAll('.add_item_link')
  .forEach(btn => {
      btn.addEventListener("click", addFormToCollection)
  });
</script>

{% endblock %}

I'm using Symfony 6 and PHP 8

Final HTML code when the template is rendered: https://pastebin.com/LWUU1pKv (CKEditor code included, sry)

I guess the fact that the "questions" ul element is outside the form isn't great but can't figure out how to move it as it should already be inside

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文