PHP项目Symfony form与表单扩展

wen PHP项目 2

本文目录导读:

PHP项目Symfony form与表单扩展

  1. Symfony Form 基础
  2. 表单扩展机制
  3. 高级表单扩展示例
  4. 表单主题扩展
  5. 表单事件扩展
  6. 最佳实践

我来详细介绍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表单系统变得非常灵活和强大,可以根据项目需求进行各种定制和扩展。

抱歉,评论功能暂时关闭!