Symfony gives you a wide variety of ways to customize how a form is rendered. In this guide, you'll learn how to customize every possible part of your form with as little effort as possible whether you use Twig or PHP as your templating engine.
Symfony为您提供了各式各样的方式来定制表单渲染,在本指南中,无论您使用的是Twig还是PHP模板引擎,您都将花极少的精力就可以学到如何定制您表单中的每个部分。
Recall that the label, error and HTML widget of a form field can easily be rendered by using the form_row
Twig function or the row
PHP helper method:
回想一下,表单域的标签(label)、错误(error)以及HTML小部件都能够很方便地通过Twig的form_row方法或PHP的row助手方法来进行渲染。
- {{ form_row(form.age) }}
You can also render each of the three parts of the field individually:
您也可以单独渲染该表单域三部分中的每一部分:
- <div>
- {{ form_label(form.age) }}
- {{ form_errors(form.age) }}
- {{ form_widget(form.age) }}
- </div>
In both cases, the form label, errors and HTML widget are rendered by using a set of markup that ships standard with Symfony. For example, both of the above templates would render:
在上述两个示例中,表单标签(label)、错误(error)和HTML小部件是通过使用一系列Symfony的标准标签来进行渲染的。比如上述两个模板都将渲染成:
- <div>
- <label for="form_age">Age</label>
- <ul>
- <li>This field is required</li>
- </ul>
- <input type="number" id="form_age" name="form[age]" />
- </div>
To quickly prototype and test a form, you can render the entire form with just one line:
为了对表单进行快速原型设计和测试,您可以仅用一行代码来渲染整个表单:
- {{ form_widget(form) }}
The remainder of this recipe will explain how every part of the form's markup can be modified at several different levels. For more information about form rendering in general, see Rendering a Form in a Template.
本文接下来将阐述表单标签的每一部分在几个不同层次上是如何被修改。更多关于表单常用渲染,请参见:在模板中渲染表单。
Symfony uses form fragments - a small piece of a template that renders just one part of a form - to render every part of a form - - field labels, errors, input
text fields, select
tags, etc
Symfony利用表单片段(一小段仅对表单一部分进行渲染的模板)来渲染表单的每个部分。如:标签(label)、错误(error)、输入文本框(input)和选择(select)标识等。
The fragments are defined as blocks in Twig and as template files in PHP.
表单片段在在Twig引擎中被称为区块,在PHP引擎中被称为模板文件。
A theme is nothing more than a set of fragments that you want to use when rendering a form. In other words, if you want to customize one portion of how a form is rendered, you'll import a theme which contains a customization of the appropriate form fragments.
主题无非是您想用来渲染表单的一组表单片段。换句话说,如果您想定制部分表单的渲染方式,您应该导入一个主题,其中包含了定制的表单片断。
Symfony comes with a default theme (form_div_layout.html.twig in Twig and FrameworkBundle:Form
in PHP) that defines each and every fragment needed to render every part of a form.
Symfony自带一个缺省的主题(Twig引擎的缺省主题是form_div_layout.html.twig文件,而在PHP引擎的缺省主题则位于是FrameworkBundle:Form名称空间中),其中定义了渲染表单各部分时需要用到的所有表单片段。
In the next section you will learn how to customize a theme by overriding some or all of its fragments.
下一节,您将学到如何通过重写主题的部分或全部片断来定制该主题。
For example, when the widget of a integer
type field is rendered, an input
number
field is generated
例如,当一个整数型表单域小部件被渲染时,将生成一个数字类型的输入框。
- {{ form_widget(form.age) }}
renders:
渲染结果:
- <input type="number" id="form_age" name="form[age]" required="required" value="33" />
Internally, Symfony uses the integer_widget
fragment to render the field. This is because the field type is integer
and you're rendering its widget
(as opposed to its label
or errors
).
在内部,symfony采用integer_widget片断来渲染该表单域。这是因为该表单域的类型是整型,且您正在渲染它的小部件(相对于label或errror来说)。
In Twig that would default to the block integer_widget
from the form_div_layout.html.twig template.
Twig引擎将缺省从form_div_layout.html.twig模板中取出inter_widget区块 。
In PHP it would rather be the integer_widget.html.php
file located in FrameworkBundle/Resources/views/Form
folder.
PHP引擎则是从FrameworkBundle/Resources/views/Form文件夹中取出integer_widget.html.php文件
The default implementation of the integer_widget
fragment looks like this:
integer_widget片段的缺省实现如下所示:
- {# integer_widget.html.twig #}
- {% block integer_widget %}
- {% set type=type|default('number') %}
- {{ block('field_widget') }}
- {% endblock integer_widget %}
As you can see, this fragment itself renders another fragment - field_widget
:
如您所见,该片断本身是由另一个片断(field_widget)来渲染:
(译者:field_widget将被form_widget所替代,目前保留还保留field_widget区块,将于2.3版时删除)
- {# FrameworkBundle/Resources/views/Form/field_widget.html.twig #}
- {% block field_widget %}
- {% set type=type|default('text') %}
- <input type="{{ type }}" {{ block('widget_attributes') }} value="{{ value }}" />
- {% endblock field_widget %}
The point is, the fragments dictate the HTML output of each part of a form. To customize the form output, you just need to identify and override the correct fragment. A set of these form fragment customizations is known as a form "theme". When rendering a form, you can choose which form theme(s) you want to apply.
可以看出,这些片段决定了表单各部分的HTML输出。要对表单输出进行定制,您只需要定义并覆盖正确的片断即可。这样一组定制的表单片断就被称为表单“主题”。这样当渲染表单时,您就可以选择您想应用的表单主题了。
In Twig a theme is a single template file and the fragments are the blocks defined in this file.
对于Twig引擎而言,一个主题就是单个的模板文件,同时表单片断对应该文件中的区块。
In PHP a theme is a folder and the fragments are individual template files in this folder.
而对于PHP引擎而方,一个主题则是一个文件夹,同时每个表单片断则对应于该文件夹中的单个模板文件。
To see the power of form theming, suppose you want to wrap every input number
field with a div
tag. The key to doing this is to customize the integer_widget
fragment.
为了体现表单主题的强大,让我们假定您想把所有的number表单域放到div标签里。要做到这一点,对integer_widget片断进行定制是关键。
When customizing the form field block in Twig, you have two options on where the customized form block can live:
在Twig引擎中定制form域区块时,对于将定制的表单区块放置何处,您有两种选择:
Method 放置方式 | Pros 优点 | Cons 缺点 |
---|---|---|
Inside the same template as the form 放置在当前模板中 |
Quick and easy 简单快速 |
Can't be reused in other templates 不能被其它模板重用 |
Inside a separate template 放置在独立模板中 |
Can be reused by many templates 可以被多个模板重用 |
Requires an extra template to be created 需要创建一个额外模板 |
Both methods have the same effect but are better in different situations.
这两种放置方式的效果是相同的,分别适用于不同的情形。
The easiest way to customize the integer_widget
block is to customize it directly in the template that's actually rendering the form.
定制integer_widget区块最简单的方法是直接在实际渲染表单的模板中定制。
- {% extends '::base.html.twig' %}
- {% form_theme form _self %}
- {% block integer_widget %}
- <div class="integer_widget">
- {% set type=type|default('number') %}
- {{ block('field_widget') }}
- </div>
- {% endblock %}
- {% block content %}
- {# ... render the form #}
- {{ form_row(form.age) }}
- {% endblock %}
By using the special {% form_theme form _self %}
tag, Twig looks inside the same template for any overridden form blocks. Assuming the form.age
field is an integer
type field, when its widget is rendered, the customized integer_widget
block will be used.
通过使用特殊的{% form_theme form _self %}标签,Twig引擎可以在当前模板中覆写任意的表单区块。假定form.age域是一个整数类型的表单域,当它的小部件被渲染时,将使用定制的integer_widget区块。
The disadvantage of this method is that the customized form block can't be reused when rendering other forms in other templates. In other words, this method is most useful when making form customizations that are specific to a single form in your application. If you want to reuse a form customization across several (or all) forms in your application, read on to the next section.
这种方式的弊端是该定制表单区域不能在其它模板中渲染其它表单时重用。换句话说,这种方式在您应用程序中对特定的单个表单进行定制时最为有用。如果您想在您应用程序的部分或全部表单重复使用一个表单定制的话,那么请继续阅读下一节。
You can also choose to put the customized integer_widget
form block in a separate template entirely. The code and end-result are the same, but you can now re-use the form customization across many templates:
您也可以选择将定制的integer_widget表单区块放在一个完全独立的模板中,代码和最终结果是一样的,但现在您可以在许多模板中重用这个表单定制了。
- {# src/Acme/DemoBundle/Resources/views/Form/fields.html.twig #}
- {% block integer_widget %}
- <div class="integer_widget">
- {% set type=type|default('number') %}
- {{ block('field_widget') }}
- </div>
- {% endblock %}
Now that you've created the customized form block, you need to tell Symfony to use it. Inside the template where you're actually rendering your form, tell Symfony to use the template via the form_theme
tag:
现在您已经定制了表单区块,您需要告诉Symfony使用它。在实际渲染您表单的模板中,可以通过form_theme标签来告诉Symfony使用它:
- {% form_theme form 'AcmeDemoBundle:Form:fields.html.twig' %}
- {{ form_widget(form.age) }}
When the form.age
widget is rendered, Symfony will use the integer_widget
block from the new template and the input
tag will be wrapped in the div
element specified in the customized block.
当form.age小组件被渲染,Symfony将从新模板中使用integer_widget区块,input标签将被包含定制区块指定的div元素中。
When using PHP as a templating engine, the only method to customize a fragment is to create a new template file - this is similar to the second method used by Twig.
The template file must be named after the fragment. You must create a integer_widget.html.php
file in order to customize the integer_widget
fragment.
- <!-- src/Acme/DemoBundle/Resources/views/Form/integer_widget.html.php -->
- <div class="integer_widget">
- <?php echo $view['form']->renderBlock('field_widget', array('type' => isset($type) ? $type : "number")) ?>
- </div>
Now that you've created the customized form template, you need to tell Symfony to use it. Inside the template where you're actually rendering your form, tell Symfony to use the theme via the setTheme
helper method:
- <?php $view['form']->setTheme($form, array('AcmeDemoBundle:Form')) ;?>
- <?php $view['form']->widget($form['age']) ?>
When the form.age
widget is rendered, Symfony will use the customized integer_widget.html.php
template and the input
tag will be wrapped in the div
element.
So far, to override a particular form block, the best method is to copy the default block from form_div_layout.html.twig, paste it into a different template, and then customize it. In many cases, you can avoid doing this by referencing the base block when customizing it.
到目前为止,要覆写特定的表单区块,最好的方法是将form_div_layout.html.twig中的缺省区块,复制到不同的模板中,然后定制它。在很多情况下,您可以在定制时通过引用基本区块来避免这么做。
This is easy to do, but varies slightly depending on if your form block customizations are in the same template as the form or a separate template.
这很容易做到,但略有不同,这取决于您是在当前模板还是在独立模板中定制表单区块。
Import the blocks by adding a use
tag in the template where you're rendering the form:
您可以通过在渲染表单的模板中添加use标签来导入区块:
- {% use 'form_div_layout.html.twig' with integer_widget as base_integer_widget %}
Now, when the blocks from form_div_layout.html.twig are imported, the integer_widget
block is called base_integer_widget
. This means that when you redefine the integer_widget
block, you can reference the default markup via base_integer_widget
:
现在,当区块从form_div_layout.html.twig模板中导入时,integer_widget区块被称为base_integer_widget。这意味着在您重定义integer_widget区块时,您可以通过base_integer_widget来引用缺省标记:
- {% block integer_widget %}
- <div class="integer_widget">
- {{ block('base_integer_widget') }}
- </div>
- {% endblock %}
If your form customizations live inside an external template, you can reference the base block by using the parent()
Twig function:
如果您在外部模板中进行表单定制,那么您可以使用Twig引擎的parent()函数来引用基本区块:
- {# src/Acme/DemoBundle/Resources/views/Form/fields.html.twig #}
- {% extends 'form_div_layout.html.twig' %}
- {% block integer_widget %}
- <div class="integer_widget">
- {{ parent() }}
- </div>
- {% endblock %}
It is not possible to reference the base block when using PHP as the templating engine. You have to manually copy the content from the base block to your new template file. |
If you'd like a certain form customization to be global to your application, you can accomplish this by making the form customizations in an external template and then importing it inside your application configuration:
如果您想要在您应用程序范围中使用某个表单定制,您可以将其写入一个外部模板,并在您应用程序配置中导入。
By using the following configuration, any customized form blocks inside the AcmeDemoBundle:Form:fields.html.twig
template will be used globally when a form is rendered.
通过使用以下配置,任何在AcmeDemoBundle:Form:fields.html.twig模板中定制的表单区块在全局范围内都将在表单渲染时使用。
- # app/config/config.yml
- twig:
- form:
- resources:
- - 'AcmeDemoBundle:Form:fields.html.twig'
- # ...
By default, Twig uses a div layout when rendering forms. Some people, however, may prefer to render forms in a table layout. Use the form_table_layout.html.twig
resource to use such a layout:
一般来说,Twig引擎在渲染表单时使用div标签布局。然而对有些人来说,他们更喜欢使用table标签布局的方式来渲染表单,这需要使用form_table_layout.html.twig资源。
- # app/config/config.yml
- twig:
- form:
- resources: ['form_table_layout.html.twig']
- # ...
If you only want to make the change in one template, add the following line to your template file rather than adding the template as a resource:
如果您只想应用到一个模板中,那么在您的模板文件中添加以下语句,而无须将该模板作为资源添加:
- {% form_theme form 'form_table_layout.html.twig' %}
Note that the form
variable in the above code is the form view variable that you passed to your template.
注意,在以上代码中的表单变量是由您传递到模板中的表单视图变量。
By using the following configuration, any customized form fragments inside the src/Acme/DemoBundle/Resources/views/Form
folder will be used globally when a form is rendered.
- # app/config/config.yml
- framework:
- templating:
- form:
- resources:
- - 'AcmeDemoBundle:Form'
- # ...
By default, the PHP engine uses a div layout when rendering forms. Some people, however, may prefer to render forms in a table layout. Use the FrameworkBundle:FormTable
resource to use such a layout:
- # app/config/config.yml
- framework:
- templating:
- form:
- resources:
- - 'FrameworkBundle:FormTable'
If you only want to make the change in one template, add the following line to your template file rather than adding the template as a resource:
- <?php $view['form']->setTheme($form, array('FrameworkBundle:FormTable')); ?>
Note that the $form
variable in the above code is the form view variable that you passed to your template.
So far, you've seen the different ways you can customize the widget output of all text field types. You can also customize individual fields. For example, suppose you have two text
fields - first_name
and last_name
- but you only want to customize one of the fields. This can be accomplished by customizing a fragment whose name is a combination of the field id attribute and which part of the field is being customized. For example:
到目前为止,您所看到的都是定制整个text域类型小部件输出的不同方法,您也可以定制单个表单域。例如,假设您有两个text域(first_name和last_name),但您只想定制其中的一个,这可以通过定制一个片断(片断名由表单域的id和被定制的表单域部分组成)来实现。如下所示:
- {% form_theme form _self %}
- {% block _product_name_widget %}
- <div class="text_widget">
- {{ block('field_widget') }}
- </div>
- {% endblock %}
- {{ form_widget(form.name) }}
Here, the _product_name_widget
fragment defines the template to use for the field whose id is product_name
(and name is product[name]
).
在这里,_product_name_widget片断定义了用于id是product_name表单域的模板。
The |
You can also override the markup for an entire field row using the same method:
您也可以采用相同的方式来覆写整个表单域行的标识:
- {# _product_name_row.html.twig #}
- {% form_theme form _self %}
- {% block _product_name_row %}
- <div class="name_row">
- {{ form_label(form) }}
- {{ form_errors(form) }}
- {{ form_widget(form) }}
- </div>
- {% endblock %}
So far, this recipe has shown you several different ways to customize a single piece of how a form is rendered. The key is to customize a specific fragment that corresponds to the portion of the form you want to control (see naming form blocks).
到目前为止,本文档已经向您展示了如何对表单渲染进行定制的几种不同的方法。关键在于定制一个特定的片断,该片断对应于您想控制的表单部分。(详见命名表单区块)
In the next sections, you'll see how you can make several common form customizations. To apply these customizations, use one of the methods described in the Form Theming section.
在下一节,您将看到时如何制作几个常见的表单定制。要应用这些定制,请采用在表单主题一节所示的方式之一。
The form component only handles how the validation errors are rendered, and not the actual validation error messages. The error messages themselves are determined by the validation constraints you apply to your objects. For more information, see the chapter on validation. 该表单组件只处理验证错误如何渲染,而不是实际的验证错误信息。错误信息本身则取决于应用于对象的验证限制。更多信息,请参见验证一章。 |
There are many different ways to customize how errors are rendered when a form is submitted with errors. The error messages for a field are rendered when you use the form_errors
helper:
当出错表单被提交时,有很多方法来定制错误如何渲染。当您使用form_error助手函数时,表单域的错误信息将被渲染。
- {{ form_errors(form.age) }}
By default, the errors are rendered inside an unordered list:
一般情况下,错误将被渲染在一个无序列表:
- <ul>
- <li>This field is required</li>
- </ul>
To override how errors are rendered for all fields, simply copy, paste and customize the field_errors
fragment.
要覆写所有表单域的错误渲染,只需要简单地复制、粘贴并定制field_errers片断即可。
- {# fields_errors.html.twig #}
- {% block field_errors %}
- {% spaceless %}
- {% if errors|length > 0 %}
- <ul class="error_list">
- {% for error in errors %}
- <li>{{ error.messageTemplate|trans(error.messageParameters, 'validators') }}</li>
- {% endfor %}
- </ul>
- {% endif %}
- {% endspaceless %}
- {% endblock field_errors %}
See Form Theming for how to apply this customization. 如何应用该定制,请参见表单主题 |
You can also customize the error output for just one specific field type. For example, certain errors that are more global to your form (i.e. not specific to just one field) are rendered separately, usually at the top of your form:
您也可以为某些特定域类型定制错误输出。举个例子,将某些针对您表单全局的错误(而非特定某个表单域的错误)单独渲染,通常出现在您表单的顶部:
- {{ form_errors(form) }}
To customize only the markup used for these errors, follow the same directions as above, but now call the block form_errors
(Twig) / the file form_errors.html.php
(PHP). Now, when errors for the form
type are rendered, your customized fragment will be used instead of the default field_errors
.
要定制仅用于这些错误的标识,请遵循上述说明,但现在被称为form_errors区块(Twig)/form_errors文件(PHP)。现在,当表单类型的错误被渲染时,您的定制片断将被使用,用于代替缺省的field_errors。
When you can manage it, the easiest way to render a form field is via the form_row
function, which renders the label, errors and HTML widget of a field. To customize the markup used for rendering all form field rows, override the field_row
fragment. For example, suppose you want to add a class to the div
element around each row:
当您要管理它时,最简单的方式就是通过form_row函数来渲染表单域,该函数渲染一个表单域的标签(label)、错误(errors)和HTML小部件(widget)。要定制用于渲染所有表单域行的标识,请覆写field_row片断。举个例子,假定您想要添加一个类让div元素包含每个表单域行:
- {# field_row.html.twig #}
- {% block field_row %}
- <div class="form_row">
- {{ form_label(form) }}
- {{ form_errors(form) }}
- {{ form_widget(form) }}
- </div>
- {% endblock field_row %}
See Form Theming for how to apply this customization. 如何应用该定制,请参见表单主题 |
If you want to denote all of your required fields with a required asterisk (*
), you can do this by customizing the field_label
fragment.
如果您想要通过一个必需星号(*)标示您所有的必需表单域,您可以通过定制field_label片断来实现。
In Twig, if you're making the form customization inside the same template as your form, modify the use
tag and add the following:
使用Twig引擎,如果您在当前模板中实现表单定制的话,那么使用use标签并添加下列语句:
- {% use 'form_div_layout.html.twig' with field_label as base_field_label %}
- {% block field_label %}
- {{ block('base_field_label') }}
- {% if required %}
- <span class="required" title="This field is required">*</span>
- {% endif %}
- {% endblock %}
In Twig, if you're making the form customization inside a separate template, use the following:
使用Twig引擎,如果您是在独立模板中实现表单定制的话,那么使用下列语句:
- {% extends 'form_div_layout.html.twig' %}
- {% block field_label %}
- {{ parent() }}
- {% if required %}
- <span class="required" title="This field is required">*</span>
- {% endif %}
- {% endblock %}
When using PHP as a templating engine you have to copy the content from the original template:
当使用PHP作为模板引擎时,您不得不从原始模板中复制内容:
- <!-- field_label.html.php -->
- <!-- original content -->
- <label for="<?php echo $view->escape($id) ?>" <?php foreach($attr as $k => $v) { printf('%s="%s" ', $view->escape($k), $view->escape($v)); } ?>><?php echo $view->escape($view['translator']->trans($label)) ?></label>
- <!-- customization -->
- <?php if ($required) : ?>
- <span class="required" title="This field is required">*</span>
- <?php endif ?>
See Form Theming for how to apply this customization. 如何应用该定制,请参见表单主题 |
You can also customize your form widgets to have an optional "help" message. In Twig, If you're making the form customization inside the same template as your form, modify the use tag and add the following:
您也可以定制您的表单小部件,使之具有一个可选的“帮助”信息。在Twig模板中,如果您在当前模板中实现表单定制的话,那么使用use标签并添加下列语句:
- {% use 'form_div_layout.html.twig' with field_widget as base_field_widget %}
- {% block field_widget %}
- {{ block('base_field_widget') }}
- {% if help is defined %}
- <span class="help">{{ help }}</span>
- {% endif %}
- {% endblock %}
In twig, If you're making the form customization inside a separate template, use the following:
使用Twig引擎,如果您是在独立模板中实现表单定制的话,那么使用下列语句:
- {% extends 'form_div_layout.html.twig' %}
- {% block field_widget %}
- {{ parent() }}
- {% if help is defined %}
- <span class="help">{{ help }}</span>
- {% endif %}
- {% endblock %}
When using PHP as a templating engine you have to copy the content from the original template:
当使用PHP作为模板引擎时,您不得不从原始模板中复制内容:
- <!-- field_widget.html.php -->
- <!-- Original content -->
- <input
- type="<?php echo isset($type) ? $view->escape($type) : "text" ?>"
- value="<?php echo $view->escape($value) ?>"
- <?php echo $view['form']->renderBlock('attributes') ?>
- />
- <!-- Customization -->
- <?php if (isset($help)) : ?>
- <span class="help"><?php echo $view->escape($help) ?></span>
- <?php endif ?>
To render a help message below a field, pass in a help
variable:
要在表单域后渲染帮助信息,需要发送help变量:
- {{ form_widget(form.title, {'help': 'foobar'}) }}
See Form Theming for how to apply this customization. 如何应用该定制,请参见表单主题 |