You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Symfony非实体映射表单POST_SUBMIT事件失效问题排查

问题:Symfony非实体表单POST_SUBMIT事件未触发,无法更新城市选择框

我在非实体映射的Symfony表单中尝试使用POST_SUBMIT事件,目标是修改邮编输入框时,通过API更新城市选择框的内容。但问题是POST_SUBMIT事件完全没触发,哪怕在事件回调里加dump($postalCode); die;也没有任何输出。

以下是我原有的实现代码:

控制器代码

$form = $this->createForm(EligibilityType::class);

if($form->isSubmitted() && $form->isValid()){
}

return $this->render('eligibility/eligibility.html.twig', [
    'form' => $form->createView(),
]);

表单代码(含事件处理)

->add('postalCode', IntegerType::class, [
        'label' => 'Code postal',
        'attr' => [
            'placeholder' => 'Ex : 75002',
            'class' => 'form-control eligibility_postalcode'
        ]
    ])
    ->add('city', ChoiceType::class, [
        'label' => 'Ville',
        'placeholder' => 'Veuillez saisir un code postal',
        'attr' => [
            'id' => 'eligibility_city'
        ]
    ])

$formModifier = function (FormInterface $form, $postalCode = null) {
    $towns[] = null === $postalCode ? [] : $this->eligibility->getAlphaLinkTown('fr', $postalCode);
    $form->add('city', ChoiceType::class, [
        'placeholder' => 'Sélectionner votre ville...',
        'choices' => $towns,
        'choice_label' => function($choice) {
            return $choice;
        }
    ]);
};

$builder->addEventListener(
    FormEvents::PRE_SET_DATA,
    function (FormEvent $event) use ($formModifier) {
        $data = $event->getData();
        $formModifier($event->getForm(), $data);
    }
);

$builder->get('postalCode')->addEventListener(
    FormEvents::POST_SUBMIT,
    function (FormEvent $event) use ($formModifier) {
        $postalCode = $event->getData();
        $formModifier($event->getForm()->getParent(), $postalCode);
    }
);

AJAX代码

$(document).ready(function() {
let $postalCode = $('#eligibility_postalCode');
$postalCode.on('change', function() {
  let $form = $(this).closest('form');
  let data = {};
  data[$postalCode.attr('name')] = $postalCode.val();
  console.log(data);
  $.ajax({
    url : '/eligibility',
    type: $form.attr('method'),
    data : data,
    success: function(html) {
      console.log(html)
      $('#eligibility_city').replaceWith(
          $(html.responseText).find('#eligibility_city')
      );
    }
  });
});
});

解决方案

1. 修复控制器的表单处理逻辑

控制器必须调用handleRequest才能触发表单事件,同时要区分AJAX请求返回部分表单:

public function eligibility(Request $request)
{
    $form = $this->createForm(EligibilityType::class);
    // 关键:调用handleRequest触发表单事件
    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {
        // 处理正常表单提交逻辑
    }

    // AJAX请求只返回城市选择框的HTML
    if ($request->isXmlHttpRequest()) {
        return $this->render('eligibility/_city_field.html.twig', [
            'form' => $form->createView(),
        ]);
    }

    return $this->render('eligibility/eligibility.html.twig', [
        'form' => $form->createView(),
    ]);
}

2. 修正表单事件与formModifier逻辑

  • PRE_SET_DATA事件需提取postalCode字段值,而非传入整个表单数据
  • 移除towns多余的数组包裹,直接赋值API返回结果
public function buildForm(FormBuilderInterface $builder, array $options)
{
    $builder
        ->add('postalCode', IntegerType::class, [
            'label' => 'Code postal',
            'attr' => [
                'placeholder' => 'Ex : 75002',
                'class' => 'form-control eligibility_postalcode'
            ]
        ])
        ->add('city', ChoiceType::class, [
            'label' => 'Ville',
            'placeholder' => 'Veuillez saisir un code postal',
            'attr' => [
                'id' => 'eligibility_city'
            ]
        ]);

    $formModifier = function (FormInterface $form, $postalCode = null) {
        // 直接赋值API返回的城镇列表,去掉多余数组包裹
        $towns = null === $postalCode ? [] : $this->eligibility->getAlphaLinkTown('fr', $postalCode);
        
        $form->add('city', ChoiceType::class, [
            'label' => 'Ville',
            'placeholder' => 'Sélectionner votre ville...',
            'choices' => $towns,
            'choice_label' => function($choice) {
                return $choice;
            },
            'attr' => [
                'id' => 'eligibility_city'
            ]
        ]);
    };

    $builder->addEventListener(
        FormEvents::PRE_SET_DATA,
        function (FormEvent $event) use ($formModifier) {
            $data = $event->getData();
            // 提取postalCode字段值
            $postalCode = $data['postalCode'] ?? null;
            $formModifier($event->getForm(), $postalCode);
        }
    );

    $builder->get('postalCode')->addEventListener(
        FormEvents::POST_SUBMIT,
        function (FormEvent $event) use ($formModifier) {
            $postalCode = $event->getData();
            // 这里可以加dump测试,现在应该能触发了
            // dump($postalCode); die;
            $formModifier($event->getForm()->getParent(), $postalCode);
        }
    );
}

3. 创建城市字段子模板

新建templates/eligibility/_city_field.html.twig,用于AJAX返回单独的城市选择框:

{{ form_row(form.city) }}

4. 修正AJAX逻辑

  • 直接使用返回的HTML替换,不要错误访问html.responseText
  • 添加错误处理方便调试
$(document).ready(function() {
    let $postalCode = $('#eligibility_postalCode');
    $postalCode.on('change', function() {
        let $form = $(this).closest('form');
        let data = {};
        data[$postalCode.attr('name')] = $postalCode.val();
        
        $.ajax({
            url: '/eligibility',
            type: $form.attr('method'),
            data: data,
            success: function(html) {
                // 替换整个城市字段的form-group容器
                $('#eligibility_city').closest('.form-group').replaceWith(html);
            },
            error: function(xhr, status, error) {
                console.error('AJAX错误:', error);
                console.log('响应内容:', xhr.responseText);
            }
        });
    });
});

调试建议

  1. 查看浏览器控制台的网络请求,确认AJAX请求状态码为200
  2. 检查表单的method属性,默认是POST,确保路由配置支持POST方法
  3. 在POST_SUBMIT事件中加dump($postalCode); die;,查看AJAX返回内容是否包含该输出,确认事件是否触发

内容的提问来源于stack exchange,提问作者Valoo

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 16:45:20