> Symfony中文手册 > 如何自定义表单渲染

如何自定义表单渲染

Symfony提供了很多方式来自定义如何输出表单。在本章中,你将学习如何花费尽可能少的努力来自定义你的表单的每一个可能的部分,无论你的模板引擎用的是Twig还是PHP。

表单输出基础 ¶

回想一下,使用 form_row Twig函数或 row PHP helper方法来输出某个表单字段的label,错误提示以及HTML widget是非常简单的。

1
{{ form_row(form.age) }}
1
<?php echo $view['form']->row($form['age']); ?>

你也可以分别输出该字段三个部分中的每一个:

1
2
3
4
5
<div>
    {{ form_label(form.age) }}
    {{ form_errors(form.age) }}
    {{ form_widget(form.age) }}
</div>
1
2
3
4
5
<div>
    <?php echo $view['form']->label($form['age']); ?>
    <?php echo $view['form']->errors($form['age']); ?>
    <?php echo $view['form']->widget($form['age']); ?>
</div>

在两个例子中,表单的label,错误提示和HTML widget(控件)都是通过符合Symofny标准的标记(markup)来输出的。上面的两个模板都将输出:

1
2
3
4
5
6
7
<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>

要快速呈现表单原型或测试一个表单,只需要一行代码即可输出整个表单:

1
2
3
4
5
6
{# renders all fields / 输出全部字段 #}
{{ form_widget(form) }}
 
{# renders all fields *and* the form start and end tags #}
{# 输出全部字段  *和* form的开始与结束标签 #}
{{ form(form) }}
1
2
3
4
5
<!-- renders all fields / 输出全部字段  -->
<?php echo $view['form']->widget($form) ?>
 
<!-- 输出全部字段  *和* form的开始与结束标签 -->
<?php echo $view['form']->form($form) ?>

本文剩余部分将解释表单标记语言的每一个部分是如何能在几个不同的层面被修改的。关于表单渲染的更多基本信息,参考 如何控制表单输出

Form Widget: 译为表单控件,即,负责输出表单(或表单中的某个字段)的HTML代码,以呈现表单之页面效果。

什么是表单主题? ¶

Symfony使用表单片段 - 即一个“仅用来渲染表单的某一个部分”的模板小码段 - 来输出表单的每一个部分 - 像是字段的label,错误信息,inpput 文本字段,select 标签,等等。

这些片段在 Twig 中被定义为区块(block),在 PHP 中则被定义为模板文件。

一个 主题,就是在输出表单时,你希望使用的的一组片段集合。换句话说,如果你希望自定义某一部分的表单输出,你要导入一个 主题,这个主题包含了相对应的个性化表单片段。

Symfony​​自带了一些 内置表单主题,它定义了在输出表单的每一个部分时所需要的每一个片段:

  • form_div_layout.html.twig,在一个 <div> 元素中封装了每一个表单字段。

  • form_table_layout.html.twig,在一个 <table> 元素中打包了整个表单,其中每一个字段都放在一个 <tr> 元素中。

  • bootstrap_3_layout.html.twig,使用合适的class来应用默认的 Bootstrap 3 CSS 框架 样式,将每一个表单字段封装在一个 <div> 元素中。

  • bootstrap_3_horizontal_layout.html.twig,类似于前面那个主题,但是所使用的CSS class是那些用于将表单进行水平显示的(即,labelinput 处于同一行)

  • foundation_5_layout.html.twig,使用合适的class来应用默认的 Foundation CSS 框架 样式,将每一个表单字段封装一个 <div> 元素中。

Caution

当你使用Bootstrap表单主题并手动输出字段时,调用 checkbox/radio 字段的 form_label() 时不会显示任何东西。这是因为在Bootstrap内部,label已经通过 form_widget() 来显示。

在下面的小节中,你将学习如何通过覆写一些或全部的表单片段,来自定义一个表单主题。

例如,当一个 integer 字段类型的控件被输出时,一个input number 字段会被生成:

1
{{ form_widget(form.age) }}
1
<?php echo $view['form']->widget($form['age']) ?>

将输出:

1
<input type="number" id="form_age" name="form[age]" required="required" value="33" />

在内部,Symfony使用了 integer_widget 片段来输出这个字段。这是因为此字段的类型是 integer,同时你输出的它的 widget(相对于 labelerrors 来说)。

在Twig中将默认指向 form_div_layout.html.twig 文件夹的 integer_widget 区块。

在 PHP 中则直接是位于 FrameworkBundle/Resources/views/Form 文件夹的 integer_widget.html.php 文件。

integer_widget 片段的默认实现如下:

1
2
3
4
5
{# form_div_layout.html.twig #}
{% block integer_widget %}
    {% set type = type|default('number') %}
    {{ block('form_widget_simple') }}
{% endblock integer_widget %}
1
2
<!-- integer_widget.html.php -->
<?php echo $view['form']->block($form, 'form_widget_simple', array('type' => isset($type) ? $type : "number")) ?>

正如你看到的,这个片段自身输出了其他的片段 - form_widget_simple

1
2
3
4
5
{# form_div_layout.html.twig #}
{% block form_widget_simple %}
    {% set type = type|default('text') %}
    <input type="{{ type }}" {{ block('widget_attributes') }} {% if value is not empty %}value="{{ value }}" {% endif %}/>
{% endblock form_widget_simple %}
1
2
3
4
5
6
<!-- FrameworkBundle/Resources/views/Form/form_widget_simple.html.php -->
<input
    type="<?php echo isset($type) ? $view->escape($type) : 'text' ?>"
    <?php if (!empty($value)): ?>value="<?php echo $view->escape($value) ?>"<?php endif ?>
    <?php echo $view['form']->block($form, 'widget_attributes') ?>
/>

关键在于,片段指示的是,表单的每个部分的html输出。为了定义表单输出,你只需去识别和覆写正确的片段。这样的一组表单片段的集合就是大家所熟知的表单“主题”了。当输出表单时,你可以选择想要使用的表单主题。

在Twig中,一个主题是一个单独的模板文件,片段则是定义在这个文件的block(区块)。

在PHP中,一个主题是一个文件夹,片段是这个文件夹中的一个个独立的模板文件。

明确将要自定义的是哪个区块

本例中,将被自定义的片段名称是 integer_widget,这是因为你要为所有的 intger 字段类型去覆写HTML widget (控件)。如果你需要定制 textarea 字段,那么你需要去自定义 textarea_widget

integer 部分来自于类名:基于(命名)标准,IntegerType 就会是 integer

你已看到,片段的名称是由字段类型和该字段所要输出的是哪一部分(如,widget, label, errors, row)组合而成。同理,如果你想控制text文本输入框的错误信息是如何输出的,你应该去自定义 text_errors 片段。

然而更常见的是,你想要个性化全体字段的错误显示方式。你可以通过自定义 form_errors 片段来达到这个目的。这利用了字段类型的继承。具体来说,由于 text 类型扩展自 form 类型,如果它不存在,在回滚到父级片段名称之前(如 form_errors),表单组件将首先寻找“特定类型”的片段(即 text_errors)。

此话题的更多信息请参考 表单片段的命名。

使用表单主题 ¶

要了解表单主题的威力,假设你想要在每个input number 字段外包裹一个 div 标签。要做到这一点,关键是要自定义 integer_widget 片段。

在Twig中使用表单主题 ¶

当在twig中自定义表单字段的区块时,对于表单区块存放于 何处,你有两个选项:

方法 优点 缺点
和表单在同一个模板中 快速而容易 不可复用于其他模板之中
在独立的模板之中 可被许多模板复用 需要创建一个额外的模板

这两种方法具有相同的效果,但是在不同的情况下各有所长。

方法1:和表单在同一个模板中使用 ¶

自定义 integer_widget 区块的最简单的方式,是在实际输出表单的模板中,直接进行自定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{% extends 'base.html.twig' %}
 
{% form_theme form _self %}
 
{% block integer_widget %}
    <div class="integer_widget">
        {% set type = type|default('number') %}
        {{ block('form_widget_simple') }}
    </div>
{% endblock %}
 
{% block content %}
    {# ... render the form / 输出表单 #}
 
    {{ form_row(form.age) }}
{% endblock %}

通过使用特殊的 {% form_theme form _self %} 标签,Twig在同一模板内寻找覆写了的表单区块。假设 form.age 字段是一个 integer 类型的字段,当它的html控件被输出时,这个自定义的 integer_widget 区块将被使用。

这种方法的缺点是,当在其他的模板输出另外的表单时,这个自定义的表单区块不能复用。换句话说,在你程序中对某个单一表单进行自定义时,这种方式最常用。如果你想在多个(或全部)表单中复用某一个表单(区块)的自定义(效果),请继续阅读。

方法2:在一个独立的模板中使用 ¶

你也可以选择把自定义的 integer_widget 表单区块整个放进一个单独的模板中。其代码以及最终的结果都是一样的,但是你现在可以在多个模板中复用这个自定义的表单了:

1
2
3
4
5
6
7
{# app/Resources/views/form/fields.html.twig #}
{% block integer_widget %}
    <div class="integer_widget">
        {% set type = type|default('number') %}
        {{ block('form_widget_simple') }}
    </div>
{% endblock %}

现在你已经创建了自定义的表单区块,你需要告诉Symfony去使用它。在你实际输出的表单模板内,通过 form_theme 标签告之Symfony来使用这个模板。

1
2
3
{% form_theme form 'form/fields.html.twig' %}
 
{{ form_widget(form.age) }}

form.age 控件被输出时,Symfony将使用新模板中的 integer_widget 区块,在这个自定义的区块中指定了将 input 标签包容到 div 元素里面。

多个模板 ¶

一个表单,也可以通过多个模板来自定义。要做到这一点,使用 with 关键字,将所有模板的名字作为一个数组传递进来:

1
2
3
{% form_theme form with ['common.html.twig', 'form/fields.html.twig'] %}
 
{# ... #}

这些模板可以位于不同的bundle中,使用“功能性名称”来引用这些模板,如 AcmeFormExtraBundle:form:fields.html.twig

子表单 ¶

你也可以把表单主题应用到你的某个表单的子表单中:

1
{% form_theme form.child 'form/fields.html.twig' %}

当你想把一个自定义主题作用到一个内嵌表单中时,这就有用了。只要指明两个主题即可:

1
2
3
{% form_theme form 'form/fields.html.twig' %}
 
{% form_theme form.child 'form/fields_child.html.twig' %}

在PHP中使用表单主题 ¶

当使用PHP作为模板引擎时,自定义表单片段只能是创建一个全新模板文件 - 这个和使用Twig时的第二种方式类似。

模板文件的命名要在片段名称之后。你必须创建一个 integer_widget.html.php 文件才能自定义integer_widget 片段。

1
2
3
4
5
6
7
8
<!-- app/Resources/views/form/integer_widget.html.php -->
<div class="integer_widget">
    <?php echo $view['form']->block(
        $form,
        'form_widget_simple',
        array('type' => isset($type) ? $type : "number")
    ) ?>
</div>

现在你已经创建了自定义的表单模板,你要通知Symofny使用它。在实际输出表单的模板中,通过 setTheme helper方法告诉 Symfony 来使用它:

1
2
3
<?php $view['form']->setTheme($form, array(':form')); ?>
 
<?php $view['form']->widget($form['age']) ?>

form.age html控件被输出时,Symfony将使用自定义的 integer_widget.html.php 模板,同时input标签将被封装在 div 元素内。

如果你想把主题应用到特定的子表单上,可将其传到 setTheme 方法中:

1
<?php $view['form']->setTheme($form['child'], ':form'); ?>

Note

:form 语法基于模板的“功能性名称(functional names)”:Bundle:Directory。因为表单位于 app/Resources/views 目录下,Bundle 部分是空的,所以是 :form

引用表单的基本区块(Twig引擎专用) ¶

到目前为止,覆写一个特定的表单区块,最好的办法是从 form_div_layout.html.twig 中复制出默认的区块,再粘贴到其他模板中,然后自定义它。很多时候,在需要定制表单时,你可以通过引用基本区块来避免这样做。

这个很容易做,但随着你的表单区块是在同一模板还是独立模板中进行定制,也会略有不同。

在表单区块的模板之内引用区块 ¶

在你要输出表单的模板中输入 use 标签来导入区块:

1
{% use 'form_div_layout.html.twig' with integer_widget as base_integer_widget %}

现在,当从 form_div_layout.html.twig 引入这个块儿后,integer_widget 块儿被称为了 base_integer_widget。这表示你可以通过 base_integer_widget 来引用默认的html码段。

1
2
3
4
5
{% block integer_widget %}
    <div class="integer_widget">
        {{ block('base_integer_widget') }}
    </div>
{% endblock %}

从外部模板中引用表单基本块儿 ¶

如果你的表单自定义部分位于外部模板,你可以通过使用 Twig 的 parent() 函数来引用基本块儿: s

1
2
3
4
5
6
7
8
{# app/Resources/views/Form/fields.html.twig #}
{% extends 'form_div_layout.html.twig' %}
 
{% block integer_widget %}
    <div class="integer_widget">
        {{ parent() }}
    </div>
{% endblock %}

Note

当使用 PHP 作为模板引擎的时候,无法引用基本块。你必须在基本块中手动把内容复制到你的新模板文件中。

展开程序级别的自定义 ¶

如果你希望表单的定制部分能成为程序全局级别的,通过在外部模板中完成表单定制,再将它导入到程序级别的配置中,即可实现:

Twig ¶

通过使用以下配置,当一个模板被输出时,任何在form/fields.html.twig模板中的定义表单块都可以被全局使用。

1
2
3
4
5
# app/config/config.yml
twig:
    form_themes:
        - 'form/fields.html.twig'
    # ...
1
2
3
4
5
<!-- app/config/config.xml -->
<twig:config>
    <twig:form-theme>form/fields.html.twig</twig:form-theme>
    <!-- ... -->
</twig:config>
1
2
3
4
5
6
7
8
// app/config/config.php
$container->loadFromExtension('twig', array(
    'form_themes' => array(
        'form/fields.html.twig',
    ),
 
    // ...
));

在输出表单时 Twig 默认使用 div 布局。然而,有些人可能更喜欢用 table 布局来输出表单。使用 form_table_layout.html.twig 资源来调用这样的布局:

1
2
3
4
5
# app/config/config.yml
twig:
    form_themes:
        - 'form_table_layout.html.twig'
    # ...
1
2
3
4
5
<!-- app/config/config.xml -->
<twig:config>
    <twig:form-theme>form_table_layout.html.twig</twig:form-theme>
    <!-- ... -->
</twig:config>
1
2
3
4
5
6
7
8
// app/config/config.php
$container->loadFromExtension('twig', array(
    'form_themes' => array(
        'form_table_layout.html.twig',
    ),
 
    // ...
));

如果你只想在一个模板中做出修改,在你的模板文件中添加这样一行代码就可以了,而不必把模板作为一个资源来添加(到配置文件中):

1
{% form_theme form 'form_table_layout.html.twig' %}

注意上面代码中的 form 变量是你传入到自己模板中的view variable(视图变量)。

PHP ¶

通过使用以下配置,当一个表单被输出时,任何在 app/Resources/views/Form 文件夹中自定义表单片段,都会被全局使用。

1
2
3
4
5
6
7
# app/config/config.yml
framework:
    templating:
        form:
            resources:
                - 'AppBundle:Form'
    # ...
1
2
3
4
5
6
7
8
9
<!-- app/config/config.xml -->
<framework:config>
    <framework:templating>
        <framework:form>
            <resource>AppBundle:Form</resource>
        </framework:form>
    </framework:templating>
    <!-- ... -->
</framework:config>
1
2
3
4
5
6
7
8
9
10
11
12
13
// app/config/config.php
// PHP
$container->loadFromExtension('framework', array(
    'templating' => array(
        'form' => array(
            'resources' => array(
                'AppBundle:Form',
            ),
        ),
     ),
 
     // ...
));

默认情况下,输出表单时,PHP引擎使用的是 div 布局。有些人可能倾向于 table 布局来输出表单。使用 FrameworkBundle:FormTable 资源即可使用此布局:

1
2
3
4
5
6
# app/config/config.yml
framework:
    templating:
        form:
            resources:
                - 'FrameworkBundle:FormTable'
1
2
3
4
5
6
7
8
9
<!-- app/config/config.xml -->
<framework:config>
    <framework:templating>
        <framework:form>
            <resource>FrameworkBundle:FormTable</resource>
        </framework:form>
    </framework:templating>
    <!-- ... -->
</framework:config>
1
2
3
4
5
6
7
8
9
10
11
12
// app/config/config.php
$container->loadFromExtension('framework', array(
    'templating' => array(
        'form' => array(
            'resources' => array(
                'FrameworkBundle:FormTable',
            ),
        ),
    ),
 
     // ...
));

如果你只想在一个模板中做出修改,在你的模板文件中添加这样一行代码就可以了,而不必把模板作为一个资源来添加(到配置文件中):

1
<?php $view['form']->setTheme($form, array('FrameworkBundle:FormTable')); ?>

注意上面代码中的 form 变量是你传入到自己模板中的view variable(视图变量)。

如何自定义一个单独的字段 ¶

目前你已经看到,利用不同方式,你可以对全体“文本”字段类型进行html控件的自定义输出。你也可以自定义一个单独的字段。例如,假设在 product 这个表单中有两个 text 字段 - namedescription - 但是你只想自定义其中的一个字段。通过自定义一个表单片段,该片段名称是由该字段的 id 属性以及“该字段的哪个部分要被自定义”组合而成,就能够实现。例如,仅自定义 name 字段:

1
2
3
4
5
6
7
8
9
{% form_theme form _self %}
 
{% block _product_name_widget %}
    <div class="text_widget">
        {{ block('form_widget_simple') }}
    </div>
{% endblock %}
 
{{ form_widget(form.name) }}
1
2
3
4
5
6
7
8
9
<!-- Main template -->
<?php echo $view['form']->setTheme($form, array(':form')); ?>
 
<?php echo $view['form']->widget($form['name']); ?>
 
<!-- app/Resources/views/Form/_product_name_widget.html.php -->
<div class="text_widget">
    <?php echo $view['form']->block('form_widget_simple') ?>
</div>

在这里,_product_name_widget 片段定义的是模板(区块),它使用于 idproduct_name 的表单字段(其名称是product[name])。

Tip

字段的 product 部分是表单的名称,可以是手动设置的,也可以根据表单类型的名称来自动生成(如,ProductType 等同于 product)。如果你不确定表单的名字,只需查看所生成的表单的源代码。

如果你想要改变这个 product_product_name_widget 区块的 name 部分,可以在form type类中设置 block_name 选项:

1
2
3
4
5
6
7
8
9
10
11
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, array(
        'block_name' => 'custom_name',
    ));
}

然后这个区块的名称将是 _product_custom_name_widget

你也可以使用相同的方法来覆写该字段全部的“row”的html标记:

1
2
3
4
5
6
7
8
9
10
11
{% form_theme form _self %}
 
{% block _product_name_row %}
    <div class="name_row">
        {{ form_label(form) }}
        {{ form_errors(form) }}
        {{ form_widget(form) }}
    </div>
{% endblock %}
 
{{ form_row(form.name) }}
1
2
3
4
5
6
7
8
9
10
11
<!-- Main template -->
<?php echo $view['form']->setTheme($form, array(':form')); ?>
 
<?php echo $view['form']->row($form['name']); ?>
 
<!-- app/Resources/views/Form/_product_name_row.html.php -->
<div class="name_row">
    <?php echo $view['form']->label($form) ?>
    <?php echo $view['form']->errors($form) ?>
    <?php echo $view['form']->widget($form) ?>
</div>

如何自定义一个Collection prototype(表单集合原型) ¶

当使用 表单集合 时,通过覆写区块,可以用一个完全自定义的prototype,将集合原型覆写。例如,如果你的表单字段被命名为 tasks,你可以像下面这样改变每一个 task(字段) 控件:

1
2
3
4
5
6
7
8
{% form_theme form _self %}
 
{% block _tasks_entry_widget %}
    <tr>
        <td>{{ form_widget(form.task) }}</td>
        <td>{{ form_widget(form.dueDate) }}</td>
    </tr>
{% endblock %}
1
2
3
4
5
<!-- src/AppBundle/Resources/views/Form/_tasks_entry_widget.html.php -->
<tr>
    <td><?php echo $view['form']->widget($form->task) ?></td>
    <td><?php echo $view['form']->widget($form->dueDate) ?></td>
</tr>

你不光可以覆写用于输出的html控件,更能改变完整的表单行(form row)或标签(label)。上面给出了 tasks 字段,下面给出区块的相关名称:

表单组成部分 Block名称
label _tasks_entry_label
widget _tasks_entry_widget
row _tasks_entry_row

其他常见的自定义 ¶

到目前为止,本文已展示给你若干不同的方式,来完成“待输出的表单的某一个单独部分”的自定义。关键就是要自定义一个特定的片段,它对应着你想要控制的表单的相应部分。(参考 表单块的命名)。

在下面的小节,你会看到几种常见的表单定制方式。为了应用这些自定义,需要用到 使用表单主题 小节中提到的方法。

自定义错误输出 ¶

Note

表单组件只处理验证错误(validation errors)是 如何 输出的,而不是validation错误的“实际内容”。这个错误信息本身,取决于你对象的验证约束(validation constraints)。更多信息,参考 验证 一文。

当一个表单提交后出现错误信息时,有很多不同的方法可以自定义这些信息的显示。当你使用 form_errors 助手时,字段的错误信息就会被输出:

1
{{ form_errors(form.age) }}
1
<?php echo $view['form']->errors($form['age']); ?>

默认情况下,错误信息被输出到一个无序列表中:

1
2
3
<ul>
    <li>This field is required</li>
</ul>

对于 全体 字段来说,要去覆写“如何输出错误信息”,只要直接复制、粘贴并自定义 form_errors 片段就可以了:

1
2
3
4
5
6
7
8
9
10
11
12
{# form_errors.html.twig #}
{% block form_errors %}
    {% spaceless %}
        {% if errors|length > 0 %}
        <ul>
            {% for error in errors %}
                <li>{{ error.message }}</li>
            {% endfor %}
        </ul>
        {% endif %}
    {% endspaceless %}
{% endblock form_errors %}
1
2
3
4
5
6
7
8
<!-- form_errors.html.php -->
<?php if ($errors): ?>
    <ul>
        <?php foreach ($errors as $error): ?>
            <li><?php echo $error->getMessage() ?></li>
        <?php endforeach ?>
    </ul>
<?php endif ?>

Tip

参考 使用表单主题 以了解如何应用这个自定义。

你也可以仅为某个特定的字段类型来自定义错误输出。仅去自定义那些用于这些错误的标记,可遵循和上面相同的方法,但是把内容放到相关的 _errors 区块(或者文件,若使用PHP模板的话)之中。例如,text_errors (或 text_errors.html.php)。

Tip

参考 表单片段的命名 即可找到你需要定义的特定代码区块或文件。

表单中特定的全局错误(如,并非针对某一个字段)将被独立地输出,通常在你的表单顶部:

1
{{ form_errors(form) }}
1
<?php echo $view['form']->render($form); ?>

要仅对这些错误所使用的html标记进行自定义,遵循和上面相同的方法。但是现在要检查 compound 变量是否设置成了 true。如果是 true,意味着当前输出的是一个字段集合(如,整个表单),而不仅仅是某个单一字段:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
{# form_errors.html.twig #}
{% block form_errors %}
    {% spaceless %}
        {% if errors|length > 0 %}
            {% if compound %}
                <ul>
                    {% for error in errors %}
                        <li>{{ error.message }}</li>
                    {% endfor %}
                </ul>
            {% else %}
                {# ... display the errors for a single field #}
            {% endif %}
        {% endif %}
    {% endspaceless %}
{% endblock form_errors %}
1
2
3
4
5
6
7
8
9
10
11
12
<!-- form_errors.html.php -->
<?php if ($errors): ?>
    <?php if ($compound): ?>
        <ul>
            <?php foreach ($errors as $error): ?>
                <li><?php echo $error->getMessage() ?></li>
            <?php endforeach ?>
        </ul>
    <?php else: ?>
        <!-- ... render the errors for a single field -->
    <?php endif ?>
<?php endif ?>

自定义“Form Row(表单行)” ¶

当你能够管理它的时候,渲染一个表单字段的最简单方式,就是通过 form_row 模板函数,它能输出字段的label,错误信息,和HTML控件。为了自定义输出 所有 表单的字段行之html标记,可覆写 form_row 片段。例如,你想在每一个表单行周围的 div 元素中添加一个class属性:

1
2
3
4
5
6
7
8
{# form_row.html.twig #}
{% block form_row %}
    <div class="form_row">
        {{ form_label(form) }}
        {{ form_errors(form) }}
        {{ form_widget(form) }}
    </div>
{% endblock form_row %}
1
2
3
4
5
6
<!-- form_row.html.php -->
<div class="form_row">
    <?php echo $view['form']->label($form) ?>
    <?php echo $view['form']->errors($form) ?>
    <?php echo $view['form']->widget($form) ?>
</div>

Tip

参考 使用表单主题 以了解如何应用这些自定义。

为字段的Label添加一个“必填”星标 ¶

如果你想为所有必填字段加上必填星标(*),你可以通过定制 form_label 片段来完成。

在 Twig 中,如果你正在表单自身的模板中进行自定义,修改 use 标签并且添加下列代码:

1
2
3
4
5
6
7
8
9
{% use 'form_div_layout.html.twig' with form_label as base_form_label %}
 
{% block form_label %}
    {{ block('base_form_label') }}
 
    {% if required %}
        <span class="required" title="This field is required">*</span>
    {% endif %}
{% endblock %}

在 Twig 中,如果你正在一个独立模板中进行着表单自定义,使用下列代码:

1
2
3
4
5
6
7
8
9
{% extends 'form_div_layout.html.twig' %}
 
{% block form_label %}
    {{ parent() }}
 
    {% if required %}
        <span class="required" title="This field is required">*</span>
    {% endif %}
{% endblock %}

若使用 PHP 作为模板引擎,你必须从原始模板中把内容拷出来:

1
2
3
4
5
6
7
8
9
10
11
12
<!-- form_label.html.php -->
 
<!-- original content -->
<?php if ($required) { $label_attr['class'] = trim((isset($label_attr['class']) ? $label_attr['class'] : '').' required'); } ?>
<?php if (!$compound) { $label_attr['for'] = $id; } ?>
<?php if (!$label) { $label = $view['form']->humanize($name); } ?>
<label <?php foreach ($label_attr as $k => $v) { printf('%s="%s" ', $view->escape($k), $view->escape($v)); } ?>><?php echo $view->escape($view['translator']->trans($label, array(), $translation_DOMain)) ?></label>
 
<!-- customization -->
<?php if ($required) : ?>
    <span class="required" title="This field is required">*</span>
<?php endif ?>

Tip

参考 使用表单主题 以了解如何应用这个自定义。

仅通过 CSS 来实现

默认时,必填字段的 label 标签,是通过一个required CSS class来输出的。因此,你也可以仅通过 CSS 来添加星标:

1
2
3
label.required:before {
    content: "* ";
}

添加“帮助”信息 ¶

你也可以定义你的表单控件,以拥有一个可选的“帮助”信息。

在 Twig 中,如果你正在表单自身的模板进行自定义,修改 use 标签并且添加下列代码:

1
2
3
4
5
6
7
8
9
{% use 'form_div_layout.html.twig' with form_widget_simple as base_form_widget_simple %}
 
{% block form_widget_simple %}
    {{ block('base_form_widget_simple') }}
 
    {% if help is defined %}
        <span class="help">{{ help }}</span>
    {% endif %}
{% endblock %}

在 Twig 中,如果你是在独立的模板中对表单展开自定义,使用以下代码:

1
2
3
4
5
6
7
8
9
{% extends 'form_div_layout.html.twig' %}
 
{% block form_widget_simple %}
    {{ parent() }}
 
    {% if help is defined %}
        <span class="help">{{ help }}</span>
    {% endif %}
{% endblock %}

若使用 PHP 作为模板引擎,你必须从原始模板中把内容拷出来:

1
2
3
4
5
6
7
8
9
10
11
12
13
<!-- form_widget_simple.html.php -->
 
<!-- Original content -->
<input
    type="<?php echo isset($type) ? $view->escape($type) : 'text' ?>"
    <?php if (!empty($value)): ?>value="<?php echo $view->escape($value) ?>"<?php endif ?>
    <?php echo $view['form']->block($form, 'widget_attributes') ?>
/>
 
<!-- Customization -->
<?php if (isset($help)) : ?>
    <span class="help"><?php echo $view->escape($help) ?></span>
<?php endif ?>

要在字段下方显示帮助信息,需要传入一个 help 模板变量:

1
{{ form_widget(form.title, {'help': 'foobar'}) }}
1
<?php echo $view['form']->widget($form['title'], array('help' => 'foobar')) ?>

Tip

参考 使用表单主题 以了解如何应用这个自定义。

使用表单变量 ¶

可用于输出表单不同部分(例如表单widget,表单label,表单errors等等)的大部分函数,都允许你直接实现某种自定义。看下面这个例子:

1
2
{# render a widget, but add a "foo" class to it #}
{{ form_widget(form.name, { 'attr': {'class': 'foo'} }) }}
1
2
3
4
5
6
<!-- render a widget, but add a "foo" class to it -->
<?php echo $view['form']->widget($form['name'], array(
    'attr' => array(
        'class' => 'foo',
    ),
)) ?>

作为第二个参数传递的数组,包含的是表单“变量”(form "variables")。参考 Form Variables/表单变量 以了解Twig中此一概念的更多细节。