本文目录导读:

我来详细介绍Symfony框架中的表单系统与主题定制。
Symfony表单基础
创建表单
// src/Form/ProductType.php
namespace App\Form;
use Symfony\Component\Form\AbstractType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\NumberType;
use Symfony\Component\Form\Extension\Core\Type\SubmitType;
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;
class ProductType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('name', TextType::class, [
'label' => '产品名称',
'attr' => ['class' => 'form-control']
])
->add('price', NumberType::class, [
'label' => '价格',
'attr' => ['class' => 'form-control']
])
->add('save', SubmitType::class, [
'label' => '保存',
'attr' => ['class' => 'btn btn-primary']
]);
}
public function configureOptions(OptionsResolver $resolver): void
{
$resolver->setDefaults([
'data_class' => Product::class,
]);
}
}
控制器中使用
// src/Controller/ProductController.php
namespace App\Controller;
use App\Entity\Product;
use App\Form\ProductType;
use Doctrine\ORM\EntityManagerInterface;
use Symfony\Bundle\FrameworkBundle\Controller\AbstractController;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\Routing\Annotation\Route;
class ProductController extends AbstractController
{
#[Route('/product/new', name: 'product_new')]
public function new(Request $request, EntityManagerInterface $entityManager): Response
{
$product = new Product();
$form = $this->createForm(ProductType::class, $product);
$form->handleRequest($request);
if ($form->isSubmitted() && $form->isValid()) {
$entityManager->persist($product);
$entityManager->flush();
return $this->redirectToRoute('product_success');
}
return $this->render('product/new.html.twig', [
'form' => $form->createView(),
]);
}
}
表单主题定制
全局主题配置
# config/packages/twig.yaml
twig:
form_themes:
- 'bootstrap_5_layout.html.twig' # Bootstrap 5主题
- 'tailwind_2_layout.html.twig' # Tailwind CSS主题
- 'form/my_custom_theme.html.twig' # 自定义主题
- 'foundation_5_layout.html.twig' # Foundation CSS主题
内置主题选项
# config/packages/framework.yaml
framework:
form:
legacy_error_messages: false
csrf_protection: true
自定义主题模板
{# templates/form/my_custom_theme.html.twig #}
{% block form_row %}
<div class="custom-form-group">
{{ form_label(form) }}
{{ form_widget(form) }}
{{ form_errors(form) }}
</div>
{% endblock %}
{% block form_label %}
{% if label is not same as(false) %}
<label class="custom-label">
{{ label }}
{% if required %}
<span class="required">*</span>
{% endif %}
</label>
{% endif %}
{% endblock %}
{% block form_errors %}
{% if errors|length > 0 %}
<div class="custom-error">
<ul>
{% for error in errors %}
<li>{{ error.message }}</li>
{% endfor %}
</ul>
</div>
{% endif %}
{% endblock %}
每个表单单独使用主题
// 控制器中
$form = $this->createForm(ProductType::class, $product, [
'theme' => 'form/special_theme.html.twig'
]);
{# 模板中指定主题 #}
{% form_theme form 'form/special_theme.html.twig' %}
{# 或多个主题 #}
{% form_theme form with ['form/theme1.html.twig', 'form/theme2.html.twig'] %}
字段级别主题覆盖
{# 为特定字段自定义 #}
{% block _product_name_widget %}
<div class="input-group">
<span class="input-group-text">📦</span>
{{ parent() }}
</div>
{% endblock %}
{% block _product_price_row %}
<div class="price-field">
{{ form_label(form) }}
<div class="input-group">
<span class="input-group-text">¥</span>
{{ form_widget(form) }}
</div>
{{ form_errors(form) }}
</div>
{% endblock %}
常用表单主题示例
Bootstrap 5 主题
{# templates/product/form.html.twig #}
{% form_theme form 'bootstrap_5_layout.html.twig' %}
{{ form_start(form) }}
<div class="row">
<div class="col-md-6">
{{ form_row(form.name) }}
</div>
<div class="col-md-6">
{{ form_row(form.price) }}
</div>
</div>
<div class="row mt-3">
<div class="col-12">
{{ form_row(form.description) }}
</div>
</div>
<div class="row mt-4">
<div class="col-12">
{{ form_row(form.save, {
'attr': {'class': 'btn btn-success'}
}) }}
</div>
</div>
{{ form_end(form) }}
Tailwind CSS 主题
{# templates/product/tailwind_form.html.twig #}
{% form_theme form 'tailwind_2_layout.html.twig' %}
{{ form_start(form, {'attr': {'class': 'space-y-6'}}) }}
<div class="grid grid-cols-1 gap-6">
<div>
{{ form_label(form.name, null, {
'label_attr': {'class': 'block text-sm font-medium text-gray-700'}
}) }}
{{ form_widget(form.name, {
'attr': {
'class': 'mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500'
}
}) }}
{{ form_errors(form.name) }}
</div>
<div>
{{ form_label(form.price, null, {
'label_attr': {'class': 'block text-sm font-medium text-gray-700'}
}) }}
{{ form_widget(form.price, {
'attr': {
'class': 'mt-1 block w-full rounded-md border-gray-300 shadow-sm focus:border-indigo-500 focus:ring-indigo-500'
}
}) }}
{{ form_errors(form.price) }}
</div>
</div>
<div class="flex justify-end">
{{ form_widget(form.save, {
'attr': {
'class': 'ml-3 inline-flex justify-center py-2 px-4 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-indigo-600 hover:bg-indigo-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500'
}
}) }}
</div>
{{ form_end(form) }}
自定义字段渲染
{# templates/form/custom_fields.html.twig #}
{% block text_widget %}
<div class="custom-text-input">
<input type="text"
{{ block('widget_attributes') }}
value="{{ value }}"
class="form-input {{ errors|length > 0 ? 'is-invalid' : '' }}"
/>
{% if help %}
<small class="form-text text-muted">{{ help }}</small>
{% endif %}
</div>
{% endblock %}
{% block choice_widget_collapsed %}
<div class="custom-select-wrapper">
<select {{ block('widget_attributes') }}
class="form-select {{ errors|length > 0 ? 'is-invalid' : '' }}">
{% if placeholder is not none %}
<option value="" {{ value is empty ? 'selected' : '' }}>
{{ placeholder }}
</option>
{% endif %}
{% for choice in choices %}
<option value="{{ choice.value }}"
{{ choice.value == value ? 'selected' : '' }}>
{{ choice.label }}
</option>
{% endfor %}
</select>
</div>
{% endblock %}
表单帮助文本和验证
{# 在表单类型中添加帮助文本 #}
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder
->add('email', EmailType::class, [
'label' => '电子邮箱',
'help' => '请输入有效的邮箱地址',
'attr' => [
'placeholder' => 'example@domain.com',
'class' => 'form-control'
]
])
->add('password', RepeatedType::class, [
'type' => PasswordType::class,
'invalid_message' => '两次输入的密码不一致',
'options' => ['attr' => ['class' => 'form-control']],
'required' => true,
'first_options' => ['label' => '密码'],
'second_options' => ['label' => '确认密码'],
]);
}
表单事件和动态修改
// 表单类型中添加事件监听
public function buildForm(FormBuilderInterface $builder, array $options): void
{
$builder->addEventListener(FormEvents::PRE_SET_DATA, function (FormEvent $event) {
$product = $event->getData();
$form = $event->getForm();
// 根据产品类型动态添加字段
if ($product && $product->getType() === '电子') {
$form->add('warranty', IntegerType::class, [
'label' => '保修期(月)'
]);
}
});
}
最佳实践
主题模板组织
templates/
├── form/
│ ├── themes/
│ │ ├── bootstrap_5_custom.html.twig
│ │ ├── tailwind_custom.html.twig
│ │ └── admin_theme.html.twig
│ ├── fields/
│ │ ├── custom_text.html.twig
│ │ └── custom_select.html.twig
│ └── layout.html.twig
└── product/
└── form.html.twig
性能优化
# config/packages/twig.yaml
twig:
form_themes: ['bootstrap_5_layout.html.twig']
cache: '%kernel.cache_dir%/twig' # 启用Twig缓存
表单主题继承
{# 继承内置主题并扩展 #}
{% extends 'bootstrap_5_layout.html.twig' %}
{% block form_row %}
<div class="mb-3 custom-row">
{{ form_label(form) }}
<div class="input-wrapper">
{{ form_widget(form) }}
{{ form_errors(form) }}
</div>
</div>
{% endblock %}
这些方法可以帮助你创建美观、一致的表单界面,同时保持代码的可维护性,选择合适的主题系统可以显著提升开发效率和用户体验。