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

Symfony 6中AJAX PATCH请求下实体验证约束未生效问题排查

Symfony 6 Ajax更新数据时未触发实体验证约束的问题

在Symfony 6项目中,我通过Stimulus.js渲染输入框,用Ajax发起PATCH请求更新数据,期望验证CompteClient实体的signature属性约束(NotBlank、长度5-50),但控制器完全不检查这些规则,不符合要求的数据依然会被持久化到数据库。我已经尝试在FormBuilderType中添加约束,结果还是一样。

相关代码片段

前端JavaScript代码

afficherSpan(id, propriete){
        let aModifier = document.getElementById(propriete+'-'+id)
        let inputToDisplay = document.getElementById('input-'+propriete+'-'+id)
        let contenu = inputToDisplay.value
        this.enregistrer(id, propriete, contenu)
        //Span
        let spanToDisplay = document.createElement('span')
        spanToDisplay.setAttribute('id', 'span-'+propriete+'-'+id)
        spanToDisplay.innerText = contenu
        //Replace
        aModifier.replaceChild(spanToDisplay, inputToDisplay)
    }

enregistrer(id, propriete, contenu){
        fetch("compte/"+id+"/modifier/"+propriete+"/"+contenu,
            {method: 'PATCH'}
        ) 
        .then(res => res.json())
        .then(data => console.log(data))
        .then(function() {
        })
        .catch(err => console.log(err))
    }

表单构建器(SignatureType)

class SignatureType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('signature', TextType::class, [
                'required' => true,
            ]);
    }

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

CompteClient实体的signature属性

#[ORM\Column(type: 'string', length: 50)]
#[Groups(["CompteClient:item:post", "CompteClient:signature:patch"])]
#[Assert\NotBlank(message:'Veuillez renseigner une signature.', groups:["app_compte_client"])]
#[Assert\Length(min:5, max:50, minMessage:'Veuillez renseigner une signature valide (> ou = à 5 car.).', maxMessage:'Veuillez renseigner une signature valide (< ou = à 50 car.).', groups:["app_compte_client"])]
private $signature;

控制器代码

#[Route('/compte/{id}/modifier/{propriete}/{contenu}', name: 'comptes_modifier', methods:['PATCH'])]
public function modifier(CompteClientRepository $compteClientRepository, EntityManagerInterface $entityManager, Request $request, int $id, string $propriete, string $contenu): Response
{
  $compteClient = $compteClientRepository->find($id);
    try {
        if($compteClient){
            if($propriete == 'signature' && !empty($contenu)){
                $form = $this->createForm(SignatureType::class, $compteClient);
                if($request->isMethod("PATCH")){
                    $form->get('signature')->submit($contenu, false);
                    if($form->get('signature')->isSubmitted() && $form->get('signature')->isValid()){
                        $compteClient->setSignature($contenu);
                        $entityManager->persist($compteClient);
                        $entityManager->flush();
                        return new Response(
                            json_encode([   
                            'message'=> $propriete.' du compte de '.$compteClient->getNomCompte().' mis à jour avec succès'
                            ]),202
                        );
                    }else{
                      $errors = $form->getErrors(true, false);
                      return new Response(
                          json_encode([   
                              'message'=> $errors
                          ]),406
                      );
                  };
                } 
            }
        }
    } catch (\Throwable $th) {    
        return new Response(
            json_encode([   
            $th
            ]),500);
    }
}

比如,当我把signature更新为长度仅2的字符串时,数据还是会被写入数据库,请问问题出在哪?


问题原因及修复方案

核心问题

你在调用$form->get('signature')->submit($contenu, false)时,第二个参数false表示不触发表单的默认验证逻辑,同时仅单独校验字段的isValid(),这种方式不会自动触发实体类上的断言约束验证。另外,直接调用字段的isValid()并不完整,应该校验整个表单的有效性。

修复步骤

1. 修改表单提交与校验逻辑

去掉字段单独提交的方式,改用整个表单提交并校验:

// 原代码
$form->get('signature')->submit($contenu, false);
if($form->get('signature')->isSubmitted() && $form->get('signature')->isValid()){

// 修改后
$form->submit(['signature' => $contenu], false); // 允许部分字段提交,适配PATCH请求
if($form->isSubmitted() && $form->isValid()){

解释:

  • 使用整个表单的submit方法传入数组格式的数据,表单会正确绑定数据并触发完整的验证流程
  • 第二个参数false表示允许部分字段提交(适合PATCH请求更新单个字段的场景),同时会正常触发验证逻辑

2. 修复错误信息返回格式

原代码直接返回$form->getErrors(true, false)无法被正确序列化为JSON,需要转换为可读的错误数组:

else {
    $errors = [];
    foreach ($form->getErrors(true) as $error) {
        $errors[] = $error->getMessage();
    }
    return new Response(
        json_encode([   
            'message' => '验证失败',
            'errors' => $errors
        ]),406
    );
}

3. 前端逻辑优化(可选)

当前端收到验证错误时,应回退到输入框状态,让用户重新输入:

enregistrer(id, propriete, contenu){
    fetch("compte/"+id+"/modifier/"+propriete+"/"+contenu, {method: 'PATCH'}) 
    .then(res => {
        if (!res.ok) {
            throw new Error('验证失败');
        }
        return res.json();
    })
    .then(data => {
        console.log(data);
        // 仅成功时替换为span
        let aModifier = document.getElementById(propriete+'-'+id);
        let inputToDisplay = document.getElementById('input-'+propriete+'-'+id);
        let spanToDisplay = document.createElement('span');
        spanToDisplay.setAttribute('id', 'span-'+propriete+'-'+id);
        spanToDisplay.innerText = contenu;
        aModifier.replaceChild(spanToDisplay, inputToDisplay);
    })
    .catch(err => {
        console.error(err);
        alert('签名不符合要求,请输入5-50个字符');
    })
}

4. 验证分组确认(可选)

确认实体约束的分组app_compte_client与表单设置的分组完全一致,这部分你的代码已经匹配,无需修改,但后续变动时要保持同步。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.11 03:01:11