Symfony为您提供了几种自定义表单渲染的方法。在本文中,您将学习如何对表单的一个或多个字段进行单一自定义。如果您需要以相同的方式自定义所有表单,请创建表单主题 或 使用任何内置主题,例如Symfony表单 的 Bootstrap主题 。
1、表单渲染函数
单个调用Twig函数 form()就足以呈现整个表单,包括其所有字段和错误消息:
{# form is a variable passed from the controller and created
by calling to the $form->createView() method #}
{{ form(form) }}
下一步是使用form_start(), form_end(), form_errors()和 form_row() Twig函数来呈现不同的表单部分,以便您可以自定义添加HTML元素和属性:
该form_row()
函数输出整个字段内容,包括标签,帮助消息,HTML元素和错误消息。所有这些都可以使用其他Twig函数进一步自定义,如下图所示:
该form_label() , form_widget() , form_help()和 form_errors()小枝功能,可以让您对每个表单域的渲染进行控制,这样你就可以完全自定义它们:
<div class="form-control">
<i class="fa fa-calendar"></i> {{ form_label(form.dueDate) }}
{{ form_widget(form.dueDate) }}
<small>{{ form_help(form.dueDate) }}</small>
<div class="form-error">
{{ form_errors(form.dueDate) }}
</div>
</div>
在本文的后面部分,您可以找到这些 Twig函数 的完整参考,以及更多用法示例。
2、表单渲染变量
上一节中提到的一些Twig函数,允许传递变量来配置它们的行为。例如,该form_label()
函数允许您定义自定义标签,以覆盖表单中定义的标签:
{{ form_label(form.task, 'My Custom Task Label') }}
某些 表单字段类型 具有可以传递给 widget 的其它渲染选项。每种类型都记录了这些选项,但有一个常见选项attr
,它允许您修改表单元素上的HTML属性。以下将名字为 task_field 的
CSS类添加到已经渲染的文本类型的输入字段task:
{{ form_widget(form.task, {'attr': {'class': 'task_field'}}) }}
如果您一次渲染整个表单(或整个嵌入表单),则该variables
参数(即form_widget的第2个参数)将仅应用于表单本身,而不是其子项。换句话说,以下内容不会将 “foo” class属性传递给表单中的所有子字段:
{# does **not** work - the variables are not recursive #}
{{ form_widget(form, { 'attr': {'class': 'foo'} }) }}
如果需要 “手动”渲染表单字段,则可以使用其属性访问字段(例如id
,name
和label
)的 各个vars
值。例如,得到id
:
{{ form.task.vars.id }}
在本文后面,您可以找到这些Twig变量及其描述的完整参考。
3、如何使用表单主题
前面部分中显示的Twig函数和变量,详细可见symfony学习笔记之模板渲染-----twig总结,可以帮助您自定义表单的一个或多个字段。但是,此自定义无法应用于表单的其余部分。
本文将介绍,如何在您的应用程序中使用Symfony提供的任何表单主题,以及如何创建自己的自定义表单主题。
1)Symfony内置表格主题
Symfony带有几个内置的表单主题,使用一些最流行的CSS框架时,您的表单看起来很棒。每个主题都在一个Twig模板中定义:
- DIV布局 form_div_layout.html.twig,将每个表单字段包装在一个
<div>
元素中,它是Symfony应用程序中默认使用的主题,除非您按照本文后面的说明对其进行配置。 - TABLE布局 form_table_layout.html.twig,将整个表单包装在
<table>
元素内,并将每个表单字段包含在<tr>
元素内。 - Bootstrap3布局 bootstrap_3_layout.html.twig,使用
<div>元素包裹每一个表单字段,并应用
CSS类和Bootstrap 3 CSS框架样式 。 - Bootstrap3水平布局 bootstrap_3_horizontal_layout.html.twig,它类似于前一个主题,但应用的CSS类实现表单中标签和小部件同行显示)。
- Bootstrap4布局 bootstrap_4_layout.html.twig,与Bootstrap 4 CSS框架样式相同
bootstrap_3_layout.html.twig
,但已更新。 - Bootstrap4水平布局 bootstrap_4_horizontal_layout.html.twig,与
bootstrap_3_horizontal_layout.html.twig
Bootstrap 4样式相同 但已更新。 - foundation_5_layout.html.twig,使用
<div>元素包裹每一个表单字段
,同时应用Foundation CSS框架的默认样式 。
提示:
阅读有关Bootstrap 4 Symfony表单主题的文章, 以了解有关它的更多信息。
2)将主题应用于所有表单
Symfony默认使用 form_div_layout.html.twig
主题。如果要对应用的所有形式使用其他主题,请在 twig.form_themes
选项中进行配置 :
# config/packages/twig.yaml
twig:
form_themes: ['bootstrap_4_horizontal_layout.html.twig']
# ...
<!-- config/packages/twig.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:twig="http://symfony.com/schema/dic/twig"
xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/twig http://symfony.com/schema/dic/twig/twig-1.0.xsd">
<twig:config>
<twig:form-theme>bootstrap_4_horizontal_layout.html.twig</twig:form-theme>
<!-- ... -->
</twig:config>
</container>
// config/packages/twig.php
$container->loadFromExtension('twig', [
'form_themes' => [
'bootstrap_4_horizontal_layout.html.twig',
],
// ...
]);
您可以将多个主题传递给此选项,因为有时表单主题仅重新定义了一些元素。这样,如果某个主题没有覆盖某个元素,Symfony会查找其他主题。twig.form_themes
选项中主题的顺序很重要。每个主题都会覆盖所有以前的主题,因此您必须将最重要的主题放在列表的末尾。
3)将主题应用于单个表单
虽然大多数时候,您都会在全局范围内应用表单主题,但您可能只需要将一个主题应用于某个特定表单。你可以使用Twig标签 form_theme 来做到这 一点:
{# this form theme will be applied only to the form of this template #}
{% form_theme form 'foundation_5_layout.html.twig' %}
{{ form_start(form) }}
{# ... #}
{{ form_end(form) }}
form_theme
标记的第一个参数(在此示例中的form参数
)是存储 表单视图对象 的变量的名称。第二个参数是定义表单主题的Twig模板的路径。
4)将多个主题应用于单个表单
还可以通过应用多个主题,来自定义表单。为此,使用 with
关键字 将所有Twig模板的路径作为数组传递(它们的顺序很重要,因为每个主题都会覆盖所有以前的主题):
{# apply multiple form themes but only to the form of this template #}
{% form_theme form with [
'foundation_5_layout.html.twig',
'forms/my_custom_theme.html.twig'
] %}
{# ... #}
5)将不同主题应用于子表单
您还可以将表单主题,应用于表单的特定子项:
{% form_theme form.a_child_form 'form/my_custom_theme.html.twig' %}
当您希望为嵌套表单,创建一个与主表单不同的自定义主题时,这非常有用。指定两个主题:
{% form_theme form 'form/my_custom_theme.html.twig' %}
{% form_theme form.a_child_form 'form/my_other_theme.html.twig' %}
6)禁用单个表单的全局主题
应用程序中定义的全局表单主题,始终应用于所有表单,甚至是那些使用 form_theme
标记应用自己主题的表单。您可能希望禁用此功能,例如在为某个bundle创建管理界面时,且这个bundle也可以安装到其它应用程序上(因此您无法控制全局启用的主题)。为此,请在表单主题列表后,添加关键字 only
:
{# 可能被应用于不同应用程序中 #}
{% form_theme form with ['foundation_5_layout.html.twig'] only %}
{# ... #}
使用 only
关键字 时,不会应用Symfony的内置表单主题(form_div_layout.html.twig
等)。为了正确渲染表单,您需要自己提供功能齐全的表单主题,或者使用Twig的 use
关键字 扩展其中一个内置表单主题,而不是 extends
重复使用原始主题内容。
{# templates/form/common.html.twig #}
{% use "form_div_layout.html.twig" %}
{# ... #}
4、创建自己的表单主题
Symfony使用 Twig块 来渲染表单的每个部分 - 字段标签,错误errors, <input>
文本字段,<select>
标签等。主题 是一个Twig模板,其中包含着您在渲染表单时要使用的一个或多个的这样的块。
例如,考虑渲染一个整数属性的表单字段 age
。如果将其添加到模板:
{{ form_widget(form.age) }}
生成的HTML内容将是这样的,(它将根据您的应用程序中启用的表单主题而有所不同):
<input type="number" id="form_age" name="form[age]" required="required" value="33" />
Symfony使用一个称为 “integer_widget
” 的
Twig块来渲染该字段。这是因为字段类型是integer,
并且您正在渲染它的widget小部件
(而不是它label
或errors
或help
)。创建表单主题的第一步是,知道要覆盖哪个Twig块,如以下部分所述。
1)表单片段命名
表单片段的命名,取决于您的需求:
- 如果要自定义相同类型的所有字段(例如全部
<textarea>
),请使用field-type_field-part
模式(例如textarea_widget
)。 - 如果您只想自定义一个特定字段(例如,
<textarea>
用于description
编辑产品的表单字段),请使用该_field-id_field-part
模式(例如_product_description_widget
)。
在这两种情况下,field-part
可以是以下任何有效的表单字段部分(以下各部分都称为Twig块或片段,):
2)相同类型的所有字段的片段命名
这些片段名称遵循type_part的模式
,其中type
对应于被渲染的字段类型(例如textarea
,checkbox
, date
,等等),并且part
对应于正在渲染的(例如 label
,widget
等)
片段名称的一些示例是:
form_row
- 由form_row()用于渲染大多数的字段;textarea_widget
- 由form_widget() 用于渲染textarea
字段类型控件;form_errors
- 由form_errors() 用于渲染字段的错误信息;
3)单个字段的片段命名
这些片段名称遵循_field-id_part模式
,其中 field-id
对应于字段的id
属性(含表单名称和字段名称,例如product_description
, user_age
等),并且part
对应于正在渲染的(例如label
,widget
等)
该field-id,包含表单名称和字段名称(例如 product_price
)。表单名称可以手动设置,也可以根据表单类型名称自动生成(例如ProductType
等同于product
)。如果您不确定表单名称是什么,请查看为表单渲染的HTML代码。您还可以使用以下 [block_name
选项] 显式定义此 [字段名称值] :
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\Form\Extension\Core\Type\TextType;
public function buildForm(FormBuilderInterface $builder, array $options)
{
// ...
$builder->add('name', TextType::class, [
'block_name' => 'custom_name',
]); # 'block_name'用于定义 基于表单字段的Twig块的名字
}
在此示例中,片段名称将_product_custom_name_widget
取代默认值_product_name_widget(默认field-id为product_name)
。
4)表单集合的片段命名
使用表单集合时,每个集合项(即表单)的片段都要遵循预定义的模式。例如,考虑以下复杂示例,其中 TaskManagerType
的集合 TaskListType,
又包含以下集合TaskType
:
class TaskManagerType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options = array())
{
// ...
$builder->add('taskLists', CollectionType::class, array(
'entry_type' => TaskListType::class, # 一个entry如taskLists,可能包含好几个entry_type即entry
'block_name' => 'task_lists',
));
}
}
class TaskListType extends AbstractType
{
public function buildForm(FormBuilderInterface $builder, array $options = array())
{
// ...
$builder->add('tasks', CollectionType::class, array(
'entry_type' => TaskType::class, # 一个entry如tasks,可能包含好几个entry_type即entry
));
}
}
class TaskType
{
public function buildForm(FormBuilderInterface $builder, array $options = array())
{
$builder->add('name');
// ...
}
}
然后你得到以下所有定制的块(其中,*
可以被替换 row
,widget
,label
,或help
):
{% block _task_manager_task_lists_* %}
{# the collection field of TaskManager #}
{% endblock %}
{% block _task_manager_task_lists_entry_* %}
{# the inner TaskListType #}
{% endblock %}
{% block _task_manager_task_lists_entry_tasks_* %}
{# the collection field of TaskListType #}
{% endblock %}
{% block _task_manager_task_lists_entry_tasks_entry_* %}
{# the inner TaskType #}
{% endblock %}
{% block _task_manager_task_lists_entry_tasks_entry_name_* %}
{# the field of TaskType #}
{% endblock %}
5)模板片段继承
每个字段类型都有父类型(例如,textarea
父类型为 text
,text
父类型为 form
),如果基本片段不存在,Symfony将使用片段 fragement 作为父类型。
当Symfony渲染一个textarea类型的错误时,它首先要渲染textarea_errors
片段,然后依次回溯地渲染text_errors
和form_errors
片段。
Symfony支持的字段类型有:
a)文本字段 Text Fields:
b)选择字段 Choice Fields:
c)日期和时间字段 Date and Time Fields:
d)其他字段 Other Fields:
e)字段组 Field Groups:
f)隐藏字段 Hidden Fields:
g)按钮 Buttons:
h)基本字段 Base Fields:
6)单个表单模板的特殊化主题
在对应用中的 单个表单 进行特定的自定义时,建议使用此方法。例如更改表单的所有<textarea>
元素,或自定义一个将使用JavaScript处理的非常特殊的表单字段。
您只需要将特殊的{% form_theme form _self %}
标记添加到渲染表单的模板中,并且将这个模板继承自你的基本模板。这使得任何被覆盖的表单块仅在当前模板中被重新定义:
{% extends 'base.html.twig' %}
{% form_theme form _self %}
{# this overrides the widget of any field of type integer, but only in the
forms rendered inside this template #}
{% block integer_widget %}
<div class="...">
{# ... render the HTML element to display this field ... #}
</div>
{% endblock %}
{# this overrides the entire row of the field whose "id" = "product_stock" (and whose
"name" = "product[stock]") but only in the forms rendered inside this template #}
{% block _product_stock_row %}
<div class="..." id="...">
{# ... render the entire field contents, including its errors ... #}
</div>
{% endblock %}
{# ... render the form ... #}
这种方法的主要缺点是,它只有在你的模板扩展另一个时才有效(在前面的例子中扩展自'base.html.twig'
)。如果您的模板没有扩展时,则必须指向form_theme
单独的模板,如下一节中所述。
另一个缺点是,在其它他模板中渲染其它表单时,无法重用自定义表单块。如果这就是您所需要的,请在单独的模板中创建表单主题,如下一节中所述。
7)在单独的模板中创建表单主题
在创建整个应用程序中使用的表单主题,或甚至在不同的Symfony应用程序中重用时,建议使用此方法。您只需要在某处创建一个Twig模板,并按照 表单片段命名 规则来定义Twig块。
例如,如果表单主题很简单,并且您只想覆盖 <input type="integer">
元素,请创建此模板:
{# templates/form/my_theme.html.twig #}
{% block integer_widget %}
{# ... add all the HTML, CSS and JavaScript needed to render this field #}
{% endblock %}
现在您需要告诉Symfony,使用此表单主题而不是(或除此之外)默认主题。如本文前面部分所述,如果要将全局主题应用于所有表单,请定义 twig.form_themes
选项:
# config/packages/twig.yaml
twig:
form_themes: ['form/my_theme.html.twig']
# ...
<!-- config/packages/twig.xml -->
<?xml version="1.0" encoding="UTF-8" ?>
<container xmlns="http://symfony.com/schema/dic/services"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:twig="http://symfony.com/schema/dic/twig"
xsi:schemaLocation="http://symfony.com/schema/dic/services
http://symfony.com/schema/dic/services/services-1.0.xsd
http://symfony.com/schema/dic/twig http://symfony.com/schema/dic/twig/twig-1.0.xsd">
<twig:config>
<twig:form-theme>form/my_theme.html.twig</twig:form-theme>
<!-- ... -->
</twig:config>
</container>
// config/packages/twig.php
$container->loadFromExtension('twig', [
'form_themes' => [
'form/my_theme.html.twig',
],
// ...
]);
如果您只想将其应用于某些特定表单,请使用以下form_theme
标记:
{% form_theme form 'form/my_theme.html.twig' %}
{{ form_start(form) }}
{# ... #}
{{ form_end(form) }}
8)部分重用表单的内置主题
创建完整的表单主题需要大量的工作,因为有太多不同的表单字段类型。您可以只定义您感兴趣的块,然后在您的应用或模板中配置多个表单主题,而不是定义所有这些Twig块。这样做是因为,在渲染未在自定义主题中覆盖的块时,Symfony会回退到其他主题。
另一种解决方案是,使用Twig“use”标签而不是extends
标签,使表单主题模板从其中一个内置主题扩展,这样您就可以继承其所有块(如果您不确定,则从默认 form_div_layout.html.twig
主题扩展 ):
{# templates/form/my_theme.html.twig #}
{% use 'form_div_layout.html.twig' %}
{# ... override only the blocks you are interested in #}
最后,您还可以使用 Twig parent()函数 重用内置主题的原始内容。当您只想进行微小更改时,这很有用,例如使用某个元素包装生成的HTML:
{# templates/form/my_theme.html.twig #}
{% use 'form_div_layout.html.twig' %}
{% block integer_widget %}
<div class="some-custom-class">
{{ parent() }}
</div>
{% endblock %}
在渲染表单的模板中定义表单主题时,此技术也有效。但是,从内置主题导入块有点复杂:
{% form_theme form _self %}
{# import a block from the built-in theme and rename it so it doesn't
conflict with the same block defined in this template #}
{% use 'form_div_layout.html.twig' with integer_widget as base_integer_widget %}
{% block integer_widget %}
<div class="some-custom-class">
{{ block('base_integer_widget') }}
</div>
{% endblock %}
{# ... render the form ... #}
9)自定义表单验证错误
如果为对象定义验证规则,则在提交的数据无效时,您将看到一些验证错误消息。这些消息与form_errors()函数一起显示,并且可以使用 form_errors
对
任何表单主题中的Twig块进行自定义,如前面部分所述。
需要考虑的一个重要事项是,某些错误与整个表单而不是特定字段相关联。为了区分全局错误和本地错误,请使用 表单中的一个名为 compound
的变量。如果是true
,则意味着当前渲染的是字段集合(例如整个表单),而不仅仅是单个字段:
{# templates/form/my_theme.html.twig #}
{% block form_errors %}
{% if errors|length > 0 %}
{% if compound %}
{# ... display the global form errors #}
<ul>
{% for error in errors %}
<li>{{ error.message }}</li>
{% endfor %}
</ul>
{% else %}
{# ... display the errors for a single field #}
{% endif %}
{% endif %}
{% endblock form_errors %}
本作品(包括代码示例),遵循 Creative Commons BY-SA 3.0 许可。
参考 1:如何自定义表单渲染 https://symfony.com/doc/current/form/form_customization.html
参考 2:如何去控制表单渲染 http://www.symfonychina.com/doc/current/form/rendering.html
参考 3:如何自定义表单渲染 http://www.symfonychina.com/doc/current/form/form_customization.html