表单基础 #

一、表单概述 #

1.1 表单组件 #

Symfony表单组件提供完整的表单处理功能:

text
┌─────────────────────────────────────────────────────┐
│                  表单组件功能                        │
├─────────────────────────────────────────────────────┤
│  • 表单创建和配置                                   │
│  • 自动渲染HTML                                     │
│  • 数据绑定和转换                                   │
│  • 验证和错误处理                                   │
│  • CSRF保护                                        │
│  • 文件上传处理                                     │
└─────────────────────────────────────────────────────┘

1.2 安装表单组件 #

bash
# 安装表单组件
composer require form validator

# 安装Maker Bundle(用于生成表单)
composer require --dev maker

二、创建表单 #

2.1 使用Maker创建表单 #

bash
# 创建表单类
php bin/console make:form UserType User

# 输出
created: src/Form/UserType.php

2.2 表单类定义 #

php
<?php

namespace App\Form;

use App\Entity\User;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class UserType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('name')
            ->add('email')
            ->add('active')
        ;
    }

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

2.3 控制器中使用表单 #

php
<?php

namespace App\Controller;

use App\Entity\User;
use App\Form\UserType;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;

class UserController extends AbstractController
{
    public function new(Request $request, EntityManagerInterface $entityManager): Response
    {
        $user = new User();
        
        $form = $this->createForm(UserType::class, $user);
        
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $entityManager->persist($user);
            $entityManager->flush();

            return $this->redirectToRoute('app_user_list');
        }

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

    public function edit(Request $request, User $user, EntityManagerInterface $entityManager): Response
    {
        $form = $this->createForm(UserType::class, $user);
        
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $entityManager->flush();

            return $this->redirectToRoute('app_user_list');
        }

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

三、渲染表单 #

3.1 基本渲染 #

twig
{# templates/user/new.html.twig #}
{% extends 'base.html.twig' %}

{% block title %}创建用户{% endblock %}

{% block body %}
<h1>创建用户</h1>

{{ form_start(form) }}
    {{ form_widget(form) }}
    <button type="submit">保存</button>
{{ form_end(form) }}
{% endblock %}

3.2 表单函数 #

twig
{# 完整表单渲染 #}
{{ form(form) }}

{# 分步渲染 #}
{{ form_start(form) }}
    {{ form_errors(form) }}
    
    {{ form_row(form.name) }}
    {{ form_row(form.email) }}
    {{ form_row(form.active) }}
    
    <button type="submit">保存</button>
{{ form_end(form) }}

{# 更细粒度控制 #}
{{ form_start(form, {'attr': {'class': 'form'}}) }}
    <div class="form-group">
        {{ form_label(form.name, '用户名') }}
        {{ form_widget(form.name, {'attr': {'class': 'form-control'}}) }}
        {{ form_errors(form.name) }}
    </div>
    
    <div class="form-group">
        {{ form_label(form.email) }}
        {{ form_widget(form.email) }}
        {{ form_help(form.email) }}
        {{ form_errors(form.email) }}
    </div>
    
    <button type="submit" class="btn btn-primary">保存</button>
{{ form_end(form) }}

3.3 表单函数说明 #

函数 说明
form() 渲染完整表单
form_start() 渲染表单开始标签
form_end() 渲染表单结束标签
form_row() 渲染一行(label + widget + errors)
form_label() 渲染标签
form_widget() 渲染控件
form_errors() 渲染错误信息
form_help() 渲染帮助文本

四、表单字段 #

4.1 文本字段 #

php
<?php

$builder
    ->add('name', TextType::class, [
        'label' => '用户名',
        'required' => true,
        'attr' => [
            'placeholder' => '请输入用户名',
            'class' => 'form-control',
        ],
    ])
    ->add('email', EmailType::class, [
        'label' => '邮箱',
        'help' => '请输入有效的邮箱地址',
    ])
    ->add('password', PasswordType::class, [
        'label' => '密码',
        'always_empty' => false,
    ])
    ->add('bio', TextareaType::class, [
        'label' => '个人简介',
        'required' => false,
        'attr' => ['rows' => 5],
    ])
;

4.2 选择字段 #

php
<?php

use Symfony\Component\Form\Extension\Core\Type\ChoiceType;

$builder
    ->add('status', ChoiceType::class, [
        'choices' => [
            '活跃' => 'active',
            '禁用' => 'inactive',
            '待审核' => 'pending',
        ],
        'placeholder' => '请选择状态',
    ])
    ->add('roles', ChoiceType::class, [
        'choices' => [
            '管理员' => 'ROLE_ADMIN',
            '编辑' => 'ROLE_EDITOR',
            '用户' => 'ROLE_USER',
        ],
        'multiple' => true,
        'expanded' => true,
    ])
    ->add('gender', ChoiceType::class, [
        'choices' => [
            '男' => 'male',
            '女' => 'female',
        ],
        'expanded' => true,
    ])
;

4.3 日期时间字段 #

php
<?php

use Symfony\Component\Form\Extension\Core\Type\DateType;
use Symfony\Component\Form\Extension\Core\Type\DateTimeType;
use Symfony\Component\Form\Extension\Core\Type\TimeType;

$builder
    ->add('birthday', DateType::class, [
        'widget' => 'single_text',
        'format' => 'yyyy-MM-dd',
        'years' => range(date('Y') - 100, date('Y')),
    ])
    ->add('publishedAt', DateTimeType::class, [
        'widget' => 'single_text',
        'html5' => true,
    ])
    ->add('openTime', TimeType::class, [
        'widget' => 'choice',
        'hours' => range(8, 20),
    ])
;

4.4 数字字段 #

php
<?php

use Symfony\Component\Form\Extension\Core\Type\IntegerType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\RangeType;

$builder
    ->add('quantity', IntegerType::class, [
        'attr' => ['min' => 1, 'max' => 100],
    ])
    ->add('price', NumberType::class, [
        'scale' => 2,
        'attr' => ['step' => '0.01'],
    ])
    ->add('rating', RangeType::class, [
        'attr' => [
            'min' => 1,
            'max' => 5,
        ],
    ])
;

4.5 文件上传 #

php
<?php

use Symfony\Component\Form\Extension\Core\Type\FileType;

$builder
    ->add('avatar', FileType::class, [
        'label' => '头像',
        'mapped' => false,
        'required' => false,
        'constraints' => [
            new File([
                'maxSize' => '2M',
                'mimeTypes' => [
                    'image/jpeg',
                    'image/png',
                ],
                'mimeTypesMessage' => '请上传有效的图片文件',
            ]),
        ],
    ])
;

五、表单选项 #

5.1 常用选项 #

php
<?php

$builder->add('name', TextType::class, [
    'label' => '用户名',
    'label_attr' => ['class' => 'form-label'],
    'required' => true,
    'disabled' => false,
    'attr' => [
        'class' => 'form-control',
        'placeholder' => '请输入用户名',
        'maxlength' => 100,
    ],
    'help' => '用户名长度为2-100个字符',
    'help_attr' => ['class' => 'form-text'],
    'data' => '默认值',
    'empty_data' => '',
    'trim' => true,
    'mapped' => true,
    'row_attr' => ['class' => 'form-group'],
]);

5.2 表单级选项 #

php
<?php

public function configureOptions(OptionsResolver $resolver): void
{
    $resolver->setDefaults([
        'data_class' => User::class,
        'csrf_protection' => true,
        'csrf_field_name' => '_token',
        'csrf_token_id' => 'user_item',
        'attr' => ['class' => 'user-form'],
        'method' => 'POST',
        'action' => '/user/new',
        'validation_groups' => ['Default', 'registration'],
    ]);
}

六、表单处理 #

6.1 处理流程 #

php
<?php

public function new(Request $request, EntityManagerInterface $entityManager): Response
{
    $user = new User();
    
    $form = $this->createForm(UserType::class, $user);
    
    $form->handleRequest($request);

    if ($form->isSubmitted()) {
        if ($form->isValid()) {
            $entityManager->persist($user);
            $entityManager->flush();
            
            $this->addFlash('success', '用户创建成功');
            
            return $this->redirectToRoute('app_user_list');
        }
        
        $this->addFlash('error', '表单验证失败');
    }

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

6.2 获取表单数据 #

php
<?php

public function submit(Request $request): Response
{
    $form = $this->createForm(UserType::class);
    $form->handleRequest($request);

    if ($form->isSubmitted() && $form->isValid()) {
        $user = $form->getData();
        
        $name = $form->get('name')->getData();
        $email = $form->get('email')->getData();
        
        $allData = $form->all();
        
        $extraField = $form->get('extra_field')?->getData();
    }
}

6.3 手动提交表单 #

php
<?php

public function manualSubmit(): Response
{
    $user = new User();
    $form = $this->createForm(UserType::class, $user);
    
    $form->submit([
        'name' => 'John Doe',
        'email' => 'john@example.com',
        'active' => true,
    ]);

    if ($form->isValid()) {
        // 处理数据
    }
}

七、表单主题 #

7.1 使用Bootstrap主题 #

yaml
# config/packages/twig.yaml
twig:
    form_themes: ['bootstrap_5_layout.html.twig']

7.2 自定义表单主题 #

twig
{# templates/form/fields.html.twig #}
{% block text_widget %}
    <input type="text" {{ block('widget_attributes') }} value="{{ value }}" class="custom-input">
{% endblock %}

{% block textarea_widget %}
    <textarea {{ block('widget_attributes') }} class="custom-textarea">{{ value }}</textarea>
{% endblock %}

{% block form_row %}
    <div class="form-group mb-3">
        {{ form_label(form) }}
        {{ form_widget(form) }}
        {{ form_errors(form) }}
        {% if help is defined %}
            <small class="form-text text-muted">{{ help }}</small>
        {% endif %}
    </div>
{% endblock %}

7.3 应用自定义主题 #

twig
{# 在模板中应用 #}
{% form_theme form 'form/fields.html.twig' %}

{{ form(form) }}

八、CSRF保护 #

8.1 自动CSRF保护 #

php
<?php

public function configureOptions(OptionsResolver $resolver): void
{
    $resolver->setDefaults([
        'csrf_protection' => true,
        'csrf_field_name' => '_token',
        'csrf_token_id' => 'user_form',
    ]);
}

8.2 禁用CSRF保护 #

php
<?php

$form = $this->createForm(UserType::class, $user, [
    'csrf_protection' => false,
]);

九、表单事件 #

9.1 表单事件监听 #

php
<?php

use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;

class UserType extends AbstractType
{
    public function buildForm(FormBuilderInterface $builder, array $options): void
    {
        $builder
            ->add('name')
            ->add('email')
        ;

        $builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
            $user = $event->getData();
            $form = $event->getForm();

            if ($user && $user->getId()) {
                $form->add('active', CheckboxType::class, [
                    'required' => false,
                ]);
            }
        });

        $builder->addEventListener(FormEvents::POST_SUBMIT, function (FormEvent $event) {
            $user = $event->getData();
            
            if ($user->getName()) {
                $user->setName(ucfirst($user->getName()));
            }
        });
    }
}

十、总结 #

本章学习了:

  • 表单组件概述
  • 创建表单类
  • 控制器中使用表单
  • 渲染表单
  • 表单字段类型
  • 表单选项配置
  • 表单处理流程
  • 表单主题
  • CSRF保护
  • 表单事件

下一章将学习 表单类型

最后更新:2026-03-28