【问题标题】:Multiple forms of the same entity同一实体的多种形式
【发布时间】:2021-02-18 14:12:39
【问题描述】:

我正在努力验证包含同一实体的三种不同形式的页面形式。

我的 formType 上有这个,“usuario”字段是我的 html 上的一个选择,其中仅返回我使用“dpto”选项过滤的内容:

<?php

namespace App\Form;

use App\Entity\Responsavel;
use App\Entity\Usuario;
use Doctrine\ORM\EntityRepository;
use Symfony\Bridge\Doctrine\Form\Type\EntityType;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\HiddenType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class ResponsavelType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
        $builder
            ->add('competenciaInicial')
            ->add('cliente')
            ->add('usuario', EntityType::class, [
                'required'=> false,
                'placeholder'=> '',
                'class'=> Usuario::class,
                'query_builder' => function(EntityRepository $er) use ($options){
                return $er->createQueryBuilder('u')
                ->where('u.ativo = 1 AND u.acesso < 9 AND u.departamento = :dpto')
                ->setParameter('dpto',$options['dpto'])
                ->orderBy('u.nome', 'ASC');}
            ])
            ->add('ativo')
        ;
    }

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

这是我的控制器(与加载页面时相同),我在其中创建了 form1、form2、form3 并传递了不同的“dpto”作为创建视图的选项,然后我做了一个简单的测试到提交表单时,它会转储它来自哪个表单。

/**
 * @Route("/responsavel", name="responsavel_index")
 */
public function index(Request $request)
{

    $responsavel = new Responsavel();
    $contabil = $request->request->get('contabil');
    
    $form1 =  $this->createForm(ResponsavelType::class, $responsavel, ['dpto'=>'C']);
    $form1->remove('ativo');
    $form1->remove('cliente');
    $form1->remove('competenciaInicial');
    $form1->handleRequest($request);
    
    $form2 =  $this->createForm(ResponsavelType::class, $responsavel, ['dpto'=>'D']);
    $form2->remove('ativo');
    $form2->remove('cliente');
    $form2->remove('competenciaInicial');
    $form2->handleRequest($request);

    $form3 =  $this->createForm(ResponsavelType::class, $responsavel, ['dpto'=>'F']);
    $form3->remove('ativo');
    $form3->remove('cliente');
    $form3->remove('competenciaInicial');
    $form3->handleRequest($request);
    
    if ($form1->isSubmitted()) {
        dump('form1');
    }

    if($form2->isSubmitted()){
        dump('form2');
    }
    
    if($form3->isSubmitted()){
        dump('form3');
    }

    return $this->render('responsavel/index.html.twig', [
        'entity' => [],
        'form1' => $form1->createView(),
        'form2' => $form2->createView(),
        'form3' => $form3->createView()
    ]);
}

这里是表单的渲染和表单提交的js:


<div class="modal-body px-4">
  <div id="clienteId" data-cliente="" class="d-none">
  </div>
  {% if app.user.acesso in [1,2,3,4,7]%}
    {% if app.user.acesso <= 3 or app.user.departamento == 'C' %}
      {{ form_start(form1, {'name' : 'responsavelContabil'})}}
        <label class="text-monospace"> Contabilidade </label>
        <div class="row">
          <div class="mb-3 col-10">
              {{form_widget(form1.usuario, {'id' : 'usuarioContabil'})}}
          </div>
          <div class="col-2 mb-3">
              <button id="okContabil" type="submit" class="btn btn-success"><i class="mdi mdi-check"></i></button>
          </div>
        </div>
      {{ form_end(form1)}}
    {% endif %}
    {% if app.user.acesso <= 3 or app.user.departamento == 'D' %}
      {{ form_start(form2, {'name' : 'responsavelDp'})}}
        <label class="text-monospace"> Dpto. Pessoal </label>
        <div class="row">
          <div class="mb-3 col-10">
              {{form_widget(form2.usuario, {'id' : 'usuarioDp'})}}
          </div>
          <div class="col-2 mb-3">
              <button id="okDp" class="btn btn-success"><i class="mdi mdi-check"></i></button>
          </div>
        </div>
      {{ form_end(form2)}}
    {% endif %}
    {% if app.user.acesso <= 3 or app.user.departamento == 'F' %}
      {{ form_start(form3, {'name' : 'responsavelFiscal'})}}
        <label class="text-monospace"> Fiscal </label>
        <div class="row">
          <div class="mb-3 col-10">
              {{form_widget(form3.usuario, {'id' : 'usuarioFiscal'})}}
          </div>
          <div class="col-2 mb-3">
              <button id="okFiscal" class="btn btn-success"><i class="mdi mdi-check"></i></button>
          </div>
        </div>
      {{ form_end(form3)}}
    {% endif %}
  {% endif %}
</div>
...

$('form').submit(function(e){
  e.preventDefault();
  console.log($(this));
  var dpto = $(this[0]).attr('id').replace('usuario', '').charAt(0);

  var responsavel = {dpto:dpto, cliente: $('#clienteId').data('cliente'), responsavel: {usuario: $(this[0]).val()}}
  $.ajax({
    url: "{{ path('responsavel_index')}}",
    type: 'post',
    dataType: 'json',
    data: responsavel,
    success: function(){
      console.log('foi');
    }
  });

显然,问题在于,当我提交表单时,它不会将表单与其他表单区分开来,然后迭代所有条件,也只会检查请求中传递的“usuario”ID 是否与在 form1 上创建的表单的选择列表进行比较然后在表单验证器上返回传递的“usuario”id 的“字段无效”错误。

关于我做错了什么以及如何修复它或如何调试它的任何想法?

感谢 ind Advance!

【问题讨论】:

    标签: php forms symfony symfony4 symfony-forms


    【解决方案1】:

    我认为您可以尝试通过它们的名称、通过请求的键来区分表单。

    public function __construct() {
        $this->prefix = self::$count++;
    }
    
    private $prefix;
    private static $count = 0;
    
    public function getBlockPrefix() {
        return $this->prefix . '_form';
    }
    

    这是您的表单类型的代码。这样,渲染的输入将为每个表单具有不同的名称,例如:

        <input type="text" id="form_name" name="0_form[name]" class="form-control">
    

    每个表单都有不同的后缀,它们提交的值不会干扰其他表单。请确保在稍后呈现和处理提交的表单时以相同的顺序创建表单,因为后缀取决于顺序(“count++”部分)。试试看,如果可行,请告诉我。

    更新:

    这在 symfony 3+ 中不起作用。实例只创建一次,但每次在Controller中启动表单时都会调用yout Type中的方法buildForm保证。所以:

    private $formPrefix;
    
    public function __construct()
    {
        $this->formPrefix = 0;
    }
    public function getBlockPrefix()
    {
        return $this->formPrefix . '-' . parent::getBlockPrefix();
    }
    
    public function buildForm(FormBuilderInterface $builder, array $options) {
        ++$this->formPrefix;
        ...
    }
    

    块引用

    The result of creating 2 forms

    【讨论】:

    • 另外,getBlockPrefix 的 '_form' 部分和 'foo' 一样,可以随意替换。 $count 变量也可以用作 ofc 的后缀。
    • 感谢您的帮助!我的控制器会是什么样子?我已将 _construct 和 getBlocPrefix 粘贴到我的 formType 上,它更改了渲染表单元素的名称,但在所有三个上,前缀都是“0_form”
    • 哦,我明白为什么这在 Symfony 3+ 中不起作用,实例只创建一次。我会更新我的答案。试过这个方法,效果很好
    • 没用,还是重复0作为前缀
    • 对不起,我没有在 buildForm @LucasMarques 中提到 ++。buildForm 函数已添加到答案中
    猜你喜欢
    • 1970-01-01
    • 2014-09-24
    • 1970-01-01
    • 2018-03-13
    • 1970-01-01
    • 2020-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多