本文目录导读:

我来详细介绍Symfony表单系统及其扩展机制。
Symfony Form 基础
创建表单类
// src/Form/UserType.php
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder
->add('username', TextType::class, [
'label' => '用户名',
'attr' => ['class' => 'form-control'],
'required' => true,
])
->add('email', EmailType::class, [
'label' => '邮箱',
'attr' => ['placeholder' => '请输入邮箱'],
])
->add('save', SubmitType::class, [
'label' => '保存',
'attr' => ['class' => 'btn btn-primary']
]);
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'data_class' => User::class,
'csrf_protection' => true,
'csrf_field_name' => '_token',
'csrf_token_id' => 'user_item',
]);
}
}
控制器中使用
// src/Controller/UserController.php
use App\Form\UserType;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
class UserController extends AbstractController
{
public function create(Request $request)
{
$user = new User();
$form = $this->createForm(UserType::class, $user, [
'action' => $this->generateUrl('user_create'),
'method' => 'POST',
]);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
// 处理表单数据
$entityManager = $this->getDoctrine()->getManager();
$entityManager->persist($user);
$entityManager->flush();
return $this->redirectToRoute('user_success');
}
return $this->render('user/create.html.twig', [
'form' => $form->createView(),
]);
}
}
表单扩展机制
创建自定义表单扩展
// src/Form/Extension/HelpFormExtension.php
namespace App\Form\Extension;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\FormType;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormView;
use Symfony\Component\OptionsResolver\OptionsResolver;
class HelpFormExtension extends AbstractTypeExtension
{
public static function getExtendedTypes(): iterable
{
return [FormType::class];
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'help' => null,
]);
}
public function buildView(FormView $view, FormInterface $form, array $options)
{
if ($options['help']) {
$view->vars['help'] = $options['help'];
}
}
}
注册扩展为服务
# config/services.yaml
services:
App\Form\Extension\HelpFormExtension:
tags:
- { name: form.type_extension, extended_type: Symfony\Component\Form\Extension\Core\Type\FormType }
高级表单扩展示例
文件上传扩展
// src/Form/Extension/FileUploadExtension.php
namespace App\Form\Extension;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\FileType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\FormEvents;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\OptionsResolver\OptionsResolver;
use Symfony\Component\Validator\Constraints\File;
class FileUploadExtension extends AbstractTypeExtension
{
public static function getExtendedTypes(): iterable
{
return [FileType::class];
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'max_size' => '2M',
'allowed_mime_types' => ['image/jpeg', 'image/png'],
]);
}
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addEventListener(FormEvents::PRE_SUBMIT, function (FormEvent $event) use ($options) {
$data = $event->getData();
if ($data) {
$constraints = [
new File([
'maxSize' => $options['max_size'],
'mimeTypes' => $options['allowed_mime_types'],
])
];
$event->getForm()->add('file', FileType::class, [
'constraints' => $constraints
]);
}
});
}
}
动态下拉列表扩展
// src/Form/Extension/DynamicChoiceExtension.php
namespace App\Form\Extension;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Component\Form\AbstractTypeExtension;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
use Symfony\Component\OptionsResolver\OptionsResolver;
class DynamicChoiceExtension extends AbstractTypeExtension
{
private $entityManager;
public function __construct(EntityManagerInterface $entityManager)
{
$this->entityManager = $entityManager;
}
public static function getExtendedTypes(): iterable
{
return [ChoiceType::class];
}
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'entity_class' => null,
'entity_property' => 'name',
]);
$resolver->setNormalizer('choices', function ($options, $value) {
if ($options['entity_class']) {
$repository = $this->entityManager
->getRepository($options['entity_class']);
$entities = $repository->findAll();
$choices = [];
foreach ($entities as $entity) {
$getter = 'get' . ucfirst($options['entity_property']);
$choices[(string) $entity->$getter()] = $entity->getId();
}
return $choices;
}
return $value;
});
}
}
表单主题扩展
自定义表单主题
{# templates/form/custom_theme.html.twig #}
{% block form_row %}
<div class="form-group{% if errors|length > 0 %} has-error{% endif %}">
{{ form_label(form) }}
{{ form_widget(form, {'attr': {'class': 'form-control'}}) }}
{{ form_errors(form) }}
{% if help is defined %}
<small class="form-text text-muted">{{ help }}</small>
{% endif %}
</div>
{% endblock %}
{% block _user_username_widget %}
<div class="input-group">
<span class="input-group-addon">
<i class="fa fa-user"></i>
</span>
{{ block('form_widget_simple') }}
</div>
{% endblock %}
应用自定义主题
# config/packages/twig.yaml
twig:
form_themes:
- 'form/custom_theme.html.twig'
或者在控制器中指定:
// 在控制器中
$form = $this->createForm(UserType::class, $user, [
'theme' => 'form/custom_theme.html.twig'
]);
表单事件扩展
监听表单事件
// src/Form/EventListener/UserFormListener.php
namespace App\Form\EventListener;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
class UserFormListener implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
FormEvents::PRE_SET_DATA => 'onPreSetData',
FormEvents::POST_SUBMIT => 'onPostSubmit',
FormEvents::SUBMIT => 'onSubmit',
];
}
public function onPreSetData(FormEvent $event)
{
$user = $event->getData();
$form = $event->getForm();
if ($user && $user->getId()) {
// 编辑模式:添加额外字段
$form->add('updatedAt', DateType::class, [
'widget' => 'single_text',
'disabled' => true,
]);
} else {
// 新建模式:添加密码字段
$form->add('password', PasswordType::class);
}
}
public function onPostSubmit(FormEvent $event)
{
$user = $event->getData();
if ($user && $user->getEmail()) {
$user->setEmail(strtolower($user->getEmail()));
}
}
public function onSubmit(FormEvent $event)
{
$data = $event->getData();
$form = $event->getForm();
// 验证逻辑
if ($data['password'] !== $data['confirmPassword']) {
$form->get('confirmPassword')->addError(
new FormError('密码不匹配')
);
}
}
}
在表单类中使用
// src/Form/UserType.php
use App\Form\EventListener\UserFormListener;
class UserType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options)
{
$builder->addEventSubscriber(new UserFormListener());
// ... 其他字段
}
}
最佳实践
表单类型扩展注册
# config/services.yaml
services:
# 注册表单类型扩展
form.type_extension.form:
class: App\Form\Extension\HelpFormExtension
tags:
- { name: form.type_extension, extended_type: Symfony\Component\Form\Extension\Core\Type\FormType }
# 注册表单类型
App\Form\CustomType:
tags: ['form.type']
# 注册表单事件监听器
App\Form\EventListener\UserFormListener:
tags:
- { name: kernel.event_listener, event: form.post_submit, method: onPostSubmit }
表单验证分组
class UserType extends AbstractType
{
public function configureOptions(OptionsResolver $resolver)
{
$resolver->setDefaults([
'validation_groups' => function (FormInterface $form) {
$data = $form->getData();
if ($data && $data->getId()) {
return ['Default', 'update'];
}
return ['Default', 'create'];
},
]);
}
}
表单数据转换器
// src/Form/DataTransformer/DateTimeToTimestampTransformer.php
namespace App\Form\DataTransformer;
use Symfony\Component\Form\DataTransformerInterface;
class DateTimeToTimestampTransformer implements DataTransformerInterface
{
public function transform($value)
{
if ($value instanceof \DateTime) {
return $value->getTimestamp();
}
return null;
}
public function reverseTransform($value)
{
if ($value) {
return (new \DateTime())->setTimestamp($value);
}
return null;
}
}
这些扩展机制让Symfony表单系统变得非常灵活和强大,可以根据项目需求进行各种定制和扩展。