如果你做的是后台、管理台、审批流、录入型业务,Symfony 的 Form 系统会非常对味。
因为它的心智本来就像:
市民填写申报表
-> 受理台回填与渲染
-> 提交后自动映射回对象
-> 审核规则处验证
-> 不通过则带错误回显
-> 通过则进入下一流程先定义表单类:
<?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
#[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,
]);
}模板里甚至可以很直接:
{# 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 官方最佳实践才会强调:
校验约束尽量定义在底层对象上
表单尽量做成类
渲染与处理尽量用同一个 action这就是它的政务大厅感。
同一份规则,多个办事入口都能共享。