全书目录

第三十七篇:Symfony 完全指南 —— 组件化政务城区宇宙

第十章:Form + Validator,是 Symfony 最像“政务大厅”的地方

1 分钟 323 字 第 707 / 962 个阅读单元

如果你做的是后台、管理台、审批流、录入型业务,Symfony 的 Form 系统会非常对味。

因为它的心智本来就像:

text
市民填写申报表
-> 受理台回填与渲染
-> 提交后自动映射回对象
-> 审核规则处验证
-> 不通过则带错误回显
-> 通过则进入下一流程

先定义表单类:

php
<?php

namespace App\Form;

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

final class BusinessPermitType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('applicantName', TextType::class, [
                'label' => '申请人姓名',
            ])
            ->add('businessName', TextType::class, [
                'label' => '商户名称',
            ])
            ->add('description', TextareaType::class, [
                'label' => '经营说明',
                'required' => false,
            ]);
    }

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

再让单个 action 既负责显示,也负责处理提交:

php
<?php

#[Route('/permits/new', name: 'permit_new', methods: ['GET', 'POST'])]
public function new(
    Request $request,
    PermitService $permitService
): Response {
    $permit = new BusinessPermit();
    $form = $this->createForm(BusinessPermitType::class, $permit);

    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {
        $permitService->submit($permit);

        return $this->redirectToRoute('permit_show', [
            'id' => $permit->getId(),
        ]);
    }

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

模板里甚至可以很直接:

twig
{# templates/permit/new.html.twig #}

<h1>新建营业许可申请</h1>

{{ form_start(form) }}
    {{ form_row(form.applicantName) }}
    {{ form_row(form.businessName) }}
    {{ form_row(form.description) }}

    <button type="submit">提交申请</button>
{{ form_end(form) }}

这里最值得建立的两个高级心智是:

#1. 表单类是“申报表定义”,不是业务逻辑中心

它负责:

  • 字段结构
  • 字段展示层选项
  • 映射方式

它不该承担整个业务流程。

#2. 约束最好放在底层对象上,而不是只绑在某个表单字段上

因为如果你把校验只写在 Form 里,这些规则就很难复用到:

  • API 输入
  • 后台导入
  • 命令行任务
  • 其他表单
  • 领域服务

所以 Symfony 官方最佳实践才会强调:

text
校验约束尽量定义在底层对象上
表单尽量做成类
渲染与处理尽量用同一个 action

这就是它的政务大厅感。
同一份规则,多个办事入口都能共享。