支付宝扫一扫付款
微信扫一扫付款
(微信为保护隐私,不显示你的昵称)
本字段类型,用于输出某些字段或表单的“集合(collection)”。从最简单的意义上说,它可以是一个 TextType
字段的数组,装载着 emails
值。在复杂场合下,你可以内嵌整个表单,这在创建one-to-many(一对多)关联的表单时(如,在一个产品表单中,你可以管理关联的多张产品图片),十分有用。
Rendered as 输出为 |
depends on the entry_type option 取决于 entry_type 选项 |
Options 选项 |
|
Inherited options 继承的选项 |
|
Parent type 父类型 |
FormType |
Class 类 |
CollectionType |
Note
如果你正在使用Doctrine entity的一个collection,要特别注意 allow_add, allow_delete 和 by_reference 选项。你可以在 如何嵌入表单(字段)集合 一文中看到完整示例。
当你在表单中需要管理一组相似元素时要用到这个类型。例如,假设你有一个 emails
字段,对应着一组邮箱地址。在表单中,你希望把每一个邮箱地址展现成它自己的input文本框:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
// ...
$builder->add('emails', CollectionType::class, array(
// each entry in the array will be an "email" field
// 数组中的每一个入口都是一个 "email" 字段类型
'entry_type' => EmailType::class,
// these options are passed to each "email" type
// 这些选项将被传入到每一个 "email" 字段类型之中
'entry_options' => array(
'attr' => array('class' => 'email-box')
),
)); |
最简单的办法是一次输出全部:
1 | {{ form_row(form.emails) }} |
1 | <?php echo $view['form']->row($form['emails']) ?> |
一个灵活得多的方法是下面这种:
1 2 3 4 5 6 7 8 9 10 11 | <?php echo $view['form']->label($form['emails']) ?>
<?php echo $view['form']->errors($form['emails']) ?>
<ul>
<?php foreach ($form['emails'] as $emailField): ?>
<li>
<?php echo $view['form']->errors($emailField) ?>
<?php echo $view['form']->widget($emailField) ?>
</li>
<?php endforeach ?>
</ul> |
两种情况下,每个输入框都不会输出内容,除非你的 emails
data数组已经包含有一些邮箱。
在这个简单的例子中,是不可能添加或删除已有的邮箱的。使用 prototype 选项) (参考下面的例子)。从 emails
数组中删除邮箱可以通过 allow_delete 选项来完成。
如果 allow_add 被设置为 true
,那么如果有任何未被识别的元素被提交,它们将被无缝添加到数组元素中。这在理论上来说很不错,但实践中要付出更多努力来令客户端JavaScript运行正确。
继续遵循上例,假设你在一开始的 emails
数组的值中有两个邮箱。这时,两个 input 字段会被输出,看上去像下面这种 (具体取决于你的表单的name):
要让你的用户能够添加另一个邮箱,只需设置 allow_add 为 true
,并且 - 通过 JavaScript - 以 form[emails][2]
命名来输出其他字段 (以此类推至更多)。
为了让事情简单,把 prototype 选项设为 true
可以让你输出一个 "template" field(模板字段),你可以在后面的 JavaScript 中来使用,以便帮助你动态地创建这些新字段。一个已输出的 prototype field 可能是下面这样的:
1 2 3 4 5 | <input type="email"
id="form_emails___name__"
name="form[emails][__name__]"
value=""
/> |
通过用一些唯一的值 (如 2
) 来替换 __name__
,你可以构建并插入新的HTML字段到表单中。
使用 jQuery 时,一个简单的示例可能是下面这样。你可以一次输出全部的集合字段 (即 form_row(form.emails)
),后面的事情会因为 data-prototype
属性为你带来的自动输出 (小有不同 - 见下面) 而变得愈发简单,你要做的全部事情就是一些 JavaScript 而已:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | {{ form_start(form) }}
{# ... #}
{# store the prototype on the data-prototype attribute #}
{# 在 data-prototype 属性中存入 prototype #}
<ul id="email-fields-list"
data-prototype="{{ form_widget(form.emails.vars.prototype)|e }}">
{% for emailField in form.emails %}
<li>
{{ form_errors(emailField) }}
{{ form_widget(emailField) }}
</li>
{% endfor %}
</ul>
<a href="#" id="add-another-email">Add another email</a>
{# ... #}
{{ form_end(form) }}
<script type="text/javascript">
// keep track of how many email fields have been rendered
// 跟踪已经渲染出来的email字段之数量
var emailCount = '{{ form.emails|length }}';
jQuery(document).ready(function() {
jQuery('#add-another-email').click(function(e) {
e.preventDefault();
var emailList = jQuery('#email-fields-list');
// grab the prototype template
// 抓取 prototype 模板
var newWidget = emailList.attr('data-prototype');
// replace the "__name__" used in the id and name of the prototype
// with a number that's unique to your emails
// end name attribute looks like name="contact[emails][2]"
// 把 prototype 中的 "__name__" 用一个相对于邮箱是“唯一”的数字来替换
// 最终的 name 属性看上去像是 name="contact[emails][2]" 这种
newWidget = newWidget.replace(/__name__/g, emailCount);
emailCount++;
// create a new list element and add it to the list
// 创建一个新的 li 元素,并把它添加到列表中
var newLi = jQuery('<li></li>').html(newWidget);
newLi.appendTo(emailList);
});
})
</script> |
Tip
如果你要一次输出整个collection,那么 prototype 在包裹着 collection 的元素 (如 div
或 table
) 中的 data-prototype
属性上是自动可用的。唯一的区别是,整个 "form row"(表单行)因你而输出,意味着你不必把它封装到类似上例中的任何一个“容器元素(container element)”之中。
值类型: boolean
默认值: false
如果设为 true
,则如果有未识别的元素被提交到集合的话,它们将被添加到新元素中。末级数组中将会包含这些已存在的元素以及这些一并提交过来的新元素。参考上例以了解细节。
prototype 选项可以用于辅助输出prototype元素,用于 - 和 JavaScript 一起 - 在客户端中动态地创建新元素。更多信息请参考上例,以及 使用“Prototype” 来允许“新”标签。
Caution
如果你内嵌的整个表单对应的是一个 one-to-many 一对多的数据库映射,你需要手动确保这些新对象的外键被正确设置。若你使用的是Doctrine,这一切不会自动发生。参考上述链接以了解细节。(译注:有机会的话,我们会把这里讲清楚)。
值类型: boolean
默认值: false
如果设为 true
,则如果已经存在的元素没有被包括在已提交的数据中的话,它们将正确地在最终的数组元素中“被缺席”。这意味着通过 JavaScript 你可以实现一个“delete”删除键,从DOM中简单的删除一个表单元素。当用户提交表单时,从已提交的数据中“缺席”,代表它将在最终的数组中被删除掉。
参考 允许删除Tag 以了解更多。
Caution
当你内嵌了一组对象集合时,使用此选项要小心。本例中,如果任何内嵌的表单被删除,它们 将会 正确地从最终的对象数组中“被消失”。只是,根据你的程序逻辑,当某个对象被移除时,你可能还希望删除它或至少从主力对象中删掉它的那个外键。所有这些统统不会自动完成。更多信息,参考 允许删除Tag。
值类型: boolean
默认值: false
如果你希望显式地从表单中移除集合中的全部空字段,需要设置此选项。但是,已经存在的集合字段 ,只会在你开启了 allow_delete 选项的时候才会被删除。 否则,空值将会保留。
Caution
The delete_empty
选项只会删除normalized(标准化了的)值是 null
的元素。如果嵌套的 entry_type 是一个
compound 表单类型,你只能要么设置 required
选项为 false
,要么设置 empty_data
选项为 null
。这两个选项可以在
entry_options 中进行设置。参考 FormType 的 empty_data 选项 以了解为何这样做是必要的。
值类型: array
默认值: array()
这个数组要传到在 entry_type 中所指定的表单类型中。例如,如果你用的把 ChoiceType 用作 entry_type 选项 (如,用于一个下拉菜单的collection),那么你至少要把 choices
选项传入到底层类型中:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\ChoiceType;
// ...
$builder->add('favorite_cities', CollectionType::class, array(
'entry_type' => ChoiceType::class,
'entry_options' => array(
'choices' => array(
'Nashville' => 'nashville',
'Paris' => 'paris',
'Berlin' => 'berlin',
'London' => 'london',
),
),
)); |
值类型: string
or FormTypeInterface
必填项
此即集合中的每一个元素的字段类型 (如 TextType
, ChoiceType
, 等)。例如,如果你有一个邮箱地址的数组,你可使用 EmailType。如果你希望内嵌一组其他类型的字段,创建一个新的form type实例,再把它传入到这个选项中。
值类型: boolean
默认值: true
本选项在使用了 allow_add 选项时有用。如果设为 true
(同时 allow_add 也被设为 true
),就能够使用一个特殊的 "prototype" 属性,以便你能在页上中输出一个 "template" 模板样例,令其呈现出一个新元素的样子。提供给该元素的 name
属性是 __name__
。这令你能够通过 JavaScript 去添加一个 "add another" 按钮, 它会读取 prototype,并把 __name__
用一些唯一的名称或数字给替换掉,然后在你的表单里输出。当提交时,它会被添加到你的由 allow_add 选项所决定的底层数组中。
prototype 字段可通过 collection 字段中的 prototype
进行输出:
1 | {{ form_row(form.emails.vars.prototype) }} |
1 | <?php echo $view['form']->row($form['emails']->vars['prototype']) ?> |
注意
Tip
如果你要一次输出全部的collection集合字段,那么 prototype from row(表单行)将在包裹着 collection 的元素 (如 div
或 table
) 中的 data-prototype
属性上自动可用。
真正使用此选项时的细节,除上例之外,亦可参考 使用“Prototype” 来允许“新”标签。
值类型: mixed
默认值: null
让你为 prototype 定义特定的值。每一个新行在初始化时将包含设置在此选项中的值。默认时,通过 entry_options 选项配置给所有行(all entries)的数据,将被使用。
1 2 3 4 5 6 7 8 9 10 | use Symfony\Component\Form\Extension\Core\Type\CollectionType;
use Symfony\Component\Form\Extension\Core\Type\TextType;
// ...
$builder->add('tags', CollectionType::class, array(
'entry_type' => TextType::class,
'allow_add' => true,
'prototype' => true,
'prototype_data' => 'New Tag Placeholder',
)); |
值类型: boolean
默认值: true
如果你在表单中拥有若干个collection,甚至是更糟糕的 nested collection(内嵌集合),你可能希望改变占位符,以便不相关的占位符不会被同一个值所替代。
以下选项继承自 FormType 字段类型。并未列出全部选项 - 仅是最适用于 CollectionType 的部分:
值类型: boolean
默认值: false
多数情况下,如果你有一个 author
字段,你会预期 setAuthor()
在底层对象(underlying object)中被调用。但在某些时候,setAuthor()
可能 不会 被调用。将 by_reference
设置为 false
以确保 setter 在所有情况下皆被调用。
为说明此一功能,这里有一个简单的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 | use Symfony\Component\Form\Extension\Core\Type\TextType;
use Symfony\Component\Form\Extension\Core\Type\EmailType;
use Symfony\Component\Form\Extension\Core\Type\FormType;
// ...
$builder = $this->createFormBuilder($article);
$builder
->add('title', TextType::class)
->add(
$builder->create('author', FormType::class, array('by_reference' => ?))
->add('name', TextType::class)
->add('email', EmailType::class)
) |
如果 by_reference
是 true,当你对表单调用 submit()
(或 handleRequest()
) 时,在程序背后,会发生如下情形:
1 2 3 | $article->setTitle('...');
$article->getAuthor()->setName('...');
$article->getAuthor()->setEmail('...'); |
注意 setAuthor()
未被调用。author 是根据引用而修改的(modified by reference)。
如果你设置 by_reference
为 false,提交之后会像下面这样:
1 2 3 4 5 | $article->setTitle('...');
$author = clone $article->getAuthor();
$author->setName('...');
$author->setEmail('...');
$article->setAuthor($author); |
所以,by_reference=false
所真正做的全部事情,就是迫使框架调用父级对象上的 setter。
类似的,若你正使用 CollectionType 字段,底层集合中的数据(collection data)是一个对象 (一如使用了Doctrine的 ArrayCollection
),那么 by_reference
必须设置为 false
,如果你需要 adder 和 remover 被调用的话 (即 addAuthor()
和 removeAuthor()
)。
值类型: mixed
默认值是 array()
(空数组)。
本选项决定集合字段将要 返回 什么值,若提交的值是空的 (或丢失) 的时候。当表单在view层输出时,如果什么也没提供的话,本选项不去设置一个初始值。
这可以帮你处理用表单提交空字段。例如,当没有值被选中时,如果你希望 name
字段能够被显式地设置为 John Doe
,你可以这么做:
1 2 3 4 | $builder->add('name', null, array(
'required' => false,
'empty_data' => 'John Doe',
)); |
这仍将输出一个空文本框,但是随着提交,John Doe
这个值将被设置。使用 data
或 placeholder
选项,在输出的表单中显示这个初始值。
如果表单是 compound 的,你可以把 empty_data
设为数组、对象或 closure。参考 如何为一个表单类配置空数据 以了解关于此选项的更多细节。
Note
如果你希望为你的整个表单类设置 empty_data
选项,参考 如何为一个表单类配置空数据 一文。
Tip
Form data transformers(表单的数据转换器)仍将适用于 empty_data
值。这意味着一个空字符串将被抛为 null
。可使用一个自定义的data transformer,如果你明确地需要返回一个空字符串的话。
值类型: boolean
默认值: true
如果是 true
,该字段的任何错误信息将被传到父级字段或表单中。例如,如果对一个常规字段设置为 true
,该字段的任何错误将被附着到主表单上,而不是该字段本身。
值类型: array
默认值: array()
本选项让你修改一条验证错误信息(validation error)的目标。
设想你有一个自定义的方法,名为 matchingCityAndZipCode()
,它用于验证城市与邮编是是否匹配。不幸的是,你的表单中并没有一个 "matchingCityAndZipCode" 字段,所以Symfony能做的全部事情也就是在表单顶部显示错误而已。
有了自定义的错误信息映射,你可以做得更好:把错误映射到city字段,以便在那上面来显示错误:
下面是映射关系中的左侧和右侧之规则:
propertyName
;array
或 ArrayAccess
对象的入口点生成了错误信息,则 property path 就是 [indexName]
;addresses[work].matchingCityAndZipCode
;默认时,任何没有被映射的属性,其错误信息将被顶到父级表单。你可以在左侧使用“点” (.
) 来映射 city
字段的全部错误信息,使用:
值类型: string
默认值: The label is "guessed" from the field name
设置用来输出字段的 label。设置为 false 将不显示 label。label 也可直接在模板中设置:
1 | {{ form_label(form.name, 'Your name') }} |
1 2 3 4 | echo $view['form']->label(
$form['name'],
'Your name'
); |
值类型: array
默认值: array()
设置 <label>
元素的 HTML 属性,用来输出字段的 label。它可以是关联数组,以HTML属性为键。该属性可直接在模板中设置:
值类型: string
默认值: null
配置用于字段的 label 中的字符串,如果 label
选项未被设置的话。这在使用了 关键字翻译信息 时是有用的。
如果你使用了关键字翻译源(keyword message)作为 labels,你常常会因为在同一个label上有多个关键字翻译源而被迫终止 (如 profile_address_street
, invoice_address_street
)。这是因为 label 就是为每一个字段“路径”(path)而设的(This is because the label is build for each "path" to a field)。要避免重复的关键字翻译源,可以把label format(标签格式)设为一个静态值,如下:
本选项是子类型继承过来的。使用上面的代码,两个表单的 street
字段的 label 将使用 form.address.street
键翻译源。
在label format中有两个可用的变量:
%id%
profile_address_street
);%name%
street
).默认值 (null
) 导致了字段名称的一个 "humanized" 版本。
Note
The label_format
选项在表单主题(form theme)中被解析。如果你 自定义表单主题 的话,确对模板进行更新。
值类型: boolean
默认值: true
如果你希望字段在“对象进行读写时”被忽略,可以设置 mapped
选项为 false
。
值类型: boolean
默认值: true
如果是 true,一个 HTML5 required attribute 会被输出。其对应的 label
也将随一个 required
class 而输出。
这是浅表限制,且独立在validation验证系统之外。最好是,如果你让 Symfony 猜测字段类型的话,则本选项的值将从你的validation infomation(验证信息)中进行猜测。
Note
这个 required 也会影响每个字段的空值(empty data)将被如何处理。参考 empty_data 选项以了解细节。
Variable(变量) | Type(类型) | Usage(作用) |
---|---|---|
allow_add | boolean |
allow_add 选项的值。 |
allow_delete | boolean |
allow_delete 选项的值。 |
本文,包括例程代码在内,采用的是 Creative Commons BY-SA 3.0 创作共用授权。