Symfony中的表单助手
可以说, 表单占据了开发人员编写模板的大部分时间, 而且表单一般都设计得相当糟糕. 由于涉及默认值, 数据格式, 验证, 重填, 表单处理等许
多内容, 开发者常常忽略了表单中的一些重要细节. 而symfony恰恰对这个问题给予了特别的关注. 本章介绍了为加速表单开发而设计的可以自动完
成多种要求的开发工具:
* 表单辅助函数提供了一种比较快地在模板中编写表单输入的方法, 特别是在编写诸如日期,下拉列表和富文本之类复杂的元素时.
* 如果要用一个表单去编辑一个对象的属性时, 利用对象表单辅助函数可以进一步加速模板的编写.
* YAML验证文件可以方便表单验证和重填.
* 验证器集成了用于验证输入数据的代码, symfony绑定了满足最常用的需求的验证器, 开发人员也很容易定制自己的验证器.
Form Helpers 表单辅助函数
-----------------------
在模板中, 表单元素的HTML标记常常和PHP代码混杂在一起. symfony中的form 辅助函数就是为了减少这种情形的发生并且避免在
`>input<`标记中不断重复`>?php echo`标记.
### Main Form Tag主要的表单标记
根据前面章节的介绍, 你必须用`form_tag()` 辅助函数创建表单, 因为它可以将用参数表示的动作转换为经路由过的URL. 第二个参数还
可以支持额外的选项---例如,可以改变缺省的`method`
例10-1 `form_tag()` 辅助函数
[php]
>?php echo form_tag('test/save') ?<
=< >form method="post" action="/path/to/save"<
>?php echo form_tag('test/save', 'method=get multipart=true
class=simpleForm') ?<
=< >form method="get" enctype="multipart/form-data"
class="simpleForm"action="
因为没有必要提供表单结束辅助函数, 所以尽管看起来不怎么美观, 你仍旧需要加上HTML的>/form
<标记.>
### Standard Form Elements标准的表单元素
有了表单辅助函数, 表单中的每个元素都会默认以元素名作为其id属性. 这个约定很有用. 例10-2给出了所有标准表单 辅助函数及相关的选
项.
例10-2 标准表单辅助函数语法.
[php]
// Text field (input)文本域
>?php echo input_tag('name', 'default value') ?<
=< >input type="text" name="name" id="name" value="default
value" /<
// All form helpers accept an additional options parameter所有表单辅助函数都
接受一个额外的选项参数.
// It allows you to add custom attributes to the generated tag它允许你为
生成的标记加上定制的属性.
>?php echo input_tag('name', 'default value', 'maxlength=20') ?<
=< >input type="text" name="name" id="name" value="default
value"maxlength="20" /<
// Long text field (text area)长文本域
>?php echo textarea_tag('name', 'default content', 'size=10x20') ?
<
=< >textarea name="name" id="name" cols="10" rows="20"<
default content
>/textarea<
// Check box复选框
>?php echo checkbox_tag('single', 1, true) ?<
>?php echo checkbox_tag('driverslicense', 'B', false) ?<
=< >input type="checkbox" name="single" id="single"
value="1"checked="checked" /<
>input type="checkbox" name="driverslicense"
id="driverslicense"value="B" /<
// Radio button单选按钮
>?php echo radiobutton_tag('status[]', 'value1', true) ?<
>?php echo radiobutton_tag('status[]', 'value2', false) ?<
=< >input type="radio" name="status[]" id="status_value1"
value="value1"checked="checked
>input type="radio" name="status[]" id="status_value2"
value="value2" /<
// Dropdown list (select)下拉列表
>?php echo select_tag('payment',
'>option selected="selected"
>option
/option<
>option
/option<')
?<
=< >select name="payment" id="payment"<
>option selected="selected"
>option
/option<
>option
/option<
>/select<
// List of options for a select tag可选项列表
>?php echo options_for_select(array('Visa
'Mastercard'), 0) ?<
=< >option value="0" selected="selected"
>option value="1"
/option<
>option value="2"
/option<
// Dropdown helper combined with a list of options混合了可选项的下拉列表辅助函数
>?php echo select_tag('payment', options_for_select(array(
'Visa',
'Eurocard',
'Mastercard'
), 0)) ?<
=< >select name="payment" id="payment"<
>option value="0" selected="selected"
>option value="1"
/option<
>option value="2"
/option<
>/select<
// To specify option names, use an associative array 用关联数组指明选项名称
>?php echo select_tag('name', options_for_select(array(
'Steve' =< 'Steve',
'Bob' =< 'Bob',
'Albert' =< 'Albert',
'Ian' =< 'Ian',
'Buck' =< 'Buck'
), 'Ian')) ?<
=< >select name="name" id="name"<
>option value="Steve"
/option<
>option value="Bob"
/option<
>option value="Albert"
/option<
>option value="Ian" selected="selected"
>option value="Buck"
/option<
>/select<
// Dropdown list with multiple selection (selected values can be
an array)
>?php echo select_tag('payment', options_for_select(
array('Visa' =< 'Visa', 'Eurocard' =< 'Eurocard', 'Mastercard'
=< 'Mastercard'),
array('Visa', 'Mastecard'),
), array('multiple' =< true))) ?<
=< >select name="payment[]" id="payment" multiple="multiple"<
>option value="Visa" selected="selected"
>option value="Eurocard"
>option value="Mastercard"
>/select<
// Drop-down list with multiple selection (selected values can be
an array)可复选的下拉列表(可选值可以是一个数组)
>?php echo select_tag('payment', options_for_select(
array('Visa' =< 'Visa', 'Eurocard' =< 'Eurocard', 'Mastercard'
=< 'Mastercard'),
array('Visa', 'Mastecard')
), 'multiple=multiple') ?<
=< >select name="payment" id="payment" multiple="multiple"<
>option value="Visa" selected="selected"<
>option value="Eurocard"
>option value="Mastercard" selected="selected"
option<
>/select<
// Upload file field上传文件域
>?php echo input_file_tag('name') ?<
=< >input type="file" name="name" id="name" value="" /<
// Password field密码域
>?php echo input_password_tag('name', 'value') ?<
=< >input type="password" name="name" id="name" value="value" /<
// Hidden field隐藏域
>?php echo input_hidden_tag('name', 'value') ?<
=< >input type="hidden" name="name" id="name" value="value" /<
// Submit button (as text)提交按钮(文本格式)
>?php echo submit_tag('Save') ?<
=< >input type="submit" name="submit" value="Save" /<
// Submit button (as image)提交按钮(图片格式)
>?php echo submit_image_tag('submit_img') ?<
=< >input type="image" name="submit" src="/images/
submit_img.png" /<
submit_image_tag() 辅助函数使用的语法和image_tag()相同, 也具有相同的优点.
<**NOTE**
.因为你需要有多个同名的单选按钮标记,以便达到"选中一个就自动去除另一个"的目的, 这样一来,id和name取同样值的约定将会导致在页面中有多个
-
<**SIDEBAR**
<
$elementName)` to get the value.
<如何取得用户通过表单提交的数据呢? 数据在请求参数中, 所以动作只要调用$this-eter($elementName)就可以取得数据.
<
.根据被调用的表单模板或被处理的表单的请求方法(GET或POST),请求会被重定向到另一个动作 .
<
< [php]
< // In mymodule/actions/actions.class
< public function executeEditAuthor()
< {
< if ($this-
< {
< // Display the form
< return sfView::SUCCESS;
< }
< else
< {
< // Handle the form submission
< $name = $this-
< ...
< $this-
< }
< }
<
<要能正常工作, 表单处理必须和表单显示是同一个动作.
< [php]
< // In mymodule/templates/editAuthorS
< >?php echo form_tag('mymodule/editAuthor'
<
< ...
symfony还提供了为后台进行异步处理的专门表单 辅助函数.下一章有关AJAX的介绍将会提供更详细的信息.
### Date Input Widgets数据输入控件.
表单常用于检索日期. 日期格式错误常常是表单提交失败的主要原因.如果你将rich选
函数可以用一个交互式JavaScript日历来帮助用户输入日
图10-1 富日期输入标记
![Rich date input tag](/images/book/F1001.png "Rich date input tag")
如果未设置rich选项, 则辅助函数将会输出三个select标记, 数据则包括月,日和年的范围.这三个标记有对应的辅助函数, 即
select_day_tag(), select_month_tag()和select_year
分别显示下拉列表.这些元素的缺省值是当前的年月日。例10-3演示了日期输入辅助函数的应用。
例10-3 日期输入辅助函数
[php]
>?php echo input_date_tag('dateofbirth', '2005-05-03',
'rich=true') ?<
=< a text input tag together with a calendar widget
// The following helpers require the Date helper group
>?php use_helper('Date') ?<
>?php echo select_day_tag('day', 1, 'include_custom=Choose a
day') ?<
=< >select name="day" id="day"<
>option value=""
/option<
>option value="1" selected="selected"<01>/option
>option value="2"<02>/option<
...
>option value="31"<31>/option<
>/select<
>?php echo select_month_tag('month', 1, 'include_custom=Choose a
month use_short_month=true') ?<
=< >select name="month" id="month"<
>option value=""
/option<
>option value="1" selected="selected"
>option value="2"
/option<
...
>option value="12"
/option<
>/select<
>?php echo select_year_tag('year', 2007, 'include_custom=Choose a
year year_end=2010') ?<
=< >select name="year" id="year"<
>option value=""
/option<
>option value="2006"<2006>/option<
>option value="2007" selected="selected"<2007>
...
>/select<
input_date_tag() 辅助函数可接受的日期值是PHP函数strtotime(
的, 而例10-5则指出哪些格式是要严格禁止使用的。
例10-4 日期辅助函数可以接受的日期格式
[php]
// Work fine
>?php echo input_date_tag('test', '2006-04-01', 'rich=true') ?<
>?php echo input_date_tag('test', 1143884373, 'rich=true') ?<
>?php echo input_date_tag('test', 'now', 'rich=true') ?<
>?php echo input_date_tag('test', '23 October 2005', 'rich=true') ?
<
>?php echo input_date_tag('test', 'next tuesday', 'rich=true') ?<
>?php echo input_date_tag('test', '1 week 2 days 4 hours 2
seconds', 'rich=true') ?<
// Return null
>?php echo input_date_tag('test', null, 'rich=true') ?<
>?php echo input_date_tag('test', '', 'rich=true') ?<
例10-5 日期辅助函数的错误日期格式
[php]
// Date zero = 01/01/1970
>?php echo input_date_tag('test', 0, 'rich=true') ?<
// Non-English date formats don't work
>?php echo input_date_tag('test', '01/04/2006', 'rich=true') ?<
### Rich Text Editing编辑富文本
因为集成了TinyMCE和FCKEditor插件, `>textarea<`标记的文本可以进行富文本编辑, 也即可以用类似字处理器的按钮来将文
本格式化为粗体,斜体或其它样式, 见图10-2.
图10-2 编辑复文本
![Rich text editing](/images/book/F1002
这两种插件都需要手工安装。 因为安装方法相同, 这里只介绍TinyMCEDE 的安装方法。 你可以从该项目的网站([http://
tinymce.moxiecode.com/](http:/
后将`tinymce/jscripts/tiny_mce/
`settings.yml`中设置指向这个库的路径,如例10
例10-6 设置TinyMCE库路径
all:
.settings:
rich_text_js_dir: js/tiny_mce
设置好后, 再加入`rich=true`选项, 就可以再文本区内进行复文本编辑了。 你也可以用`tinymce_options`选项为
Javascript编辑器指定定制的选项。 参见例10-7
例 10-7 富文本区域
[php]
>?php echo textarea_tag('name', 'default content', 'rich=true
size=10x20')) ?<
=< a rich text edit zone powered by TinyMCE
>?php echo textarea_tag('name', 'default content', 'rich=true
size=10x20tinymce_options
<
=< a rich text edit zone powered by TinyMCE with custom parameters
### Country and Language Selection选择国家和语言
你可能会需要一个可以选择国家的域。因为在不同的语言中国家名也
culture的详细介绍)。 select_country_tag() 辅助函数可以完成所有工作, 它会显示国家名, 并用ISO标准的国家代码
来作为选项的值,参见例10-8。
例10-8 国家列表辅助函数
[php]
>?php echo select_country_tag('country', 'AL') ?<
=< >select name="country" id="country"<
>option value="AF"
/option
>option value="AL" selected="selected"
>option value="DZ"
/option<
>option value="AS"
/option<
...
类似于select_country_tag() 辅助函数, select_language_tag() 辅助函数可以显示一个语言列表, 见例
10-9
例10-9 语言列表辅助函数
[php]
>?php echo select_language_tag('language'
=< >select name="language" id="language"<
...
>option value="elx"
/option<
>option value="en" selected="selected"
>option value="enm"
/option<
>option value="ang"
/option<
>option value="myv"
/option<
>option value="eo"
/option<
...
Form Helpers for Objects对象表单辅助函数
------------------------------
如果用表单的元素去编辑对象的属性, 那么用标准的链接辅助函数去写代码非常费时. 比如, 要编辑Customer对象的telephone属性,
应该写成:
[php]
>?php echo input_tag('telephone', $customer-
=< >input type="text" name="telephone" id="telephone"
value="0123456789" /<
为了避免重复地写属性名, symfony为每个表单辅助函数另外提供了一个对象表单辅助函
字和缺省值. 前面用过的input_tag()可以变换为如下形式:
[php]
>?php echo object_input_tag($customer, 'getTelephone') ?<
=< >input type="text" name="telephone" id="telephone"
value="0123456789" /<
虽然 object_input_tag()看上去并不省事, 但是, 每个标准的表单辅助函数都有一个对应的对象表单辅助函数, 并且它们的语法都一
样, 这样要生成表单就非常简单. 这也是为什么在symfony生成的框架和后台管理都广泛使用了
展示了对象表单辅助函数的用法.
例10-10 对象表单辅助函数语法
[php]
>?php echo object_input_tag($object, $method, $options) ?<
>?php echo object_input_date_tag($object, $method, $options) ?<
>?php echo object_input_hidden_tag(
>?php echo object_textarea_tag($object, $method, $options) ?<
>?php echo object_checkbox_tag($object, $method, $options) ?<
>?php echo object_select_tag($object, $method, $options) ?<
>?php echo object_select_country_tag(
<
>?php echo object_select_language_tag(
<
因为为一个密码元素预先设定缺省值不是一个好主意, 所以没有提供object_password_tag() 辅助函数.
<**CAUTION**
'Object' 辅助函数组后才能使用对象表单辅助函数组.
对象表单辅助函数中最有趣的就是objects_for
### Populating Drop-Down Lists with Objects生成对象的下拉列表.
例10-11中用options_for_select() 辅助函数和其他标准辅助函数, 将一个PHP关联数组转化为一个选项列表.
例10-11 用options_for_select()产生一个基于数组的
[php]
>?php echo options_for_select(array(
'1' =< 'Steve',
'2' =< 'Bob',
'3' =< 'Albert',
'4' =< 'Ian',
'5' =< 'Buck'
), 4) ?<
=< >option value="1"
/option<
>option value="2"
/option<
>option value="3"
/option<
>option value="4" selected="selected"
>option value="5"
/option<
假设你已经有了一个从Propel查询得到的类Author的对
id和name项, 如例10-12所示.
例10-12 用options_for_select()产生一个基于对象数
[php]
// In the action
$options = array();
foreach ($authors as $author)
{
$options[$author-
}
$this-
// In the template
>?php echo options_for_select($options, 4) ?<
因为经常需要进行这类处理, 所以symfony提供了一个objects_for
表。 这个辅助函数需要两个另外的参数:用于遍历值的方法名和生成
[php]
>?php echo objects_for_select($authors, 'getId', 'getName', 4) ?<
这样已经既快又好, 但是当你要处理外键列时, symfony还提供了更多的方便。
### Creating a Drop-Down List Based on a Foreign Key Column创建一个基于外键列的下拉
列表。
外键列可取的值是外键所在的表的记录的主键的键值。 例如, 如果article表有一个author_id列, 它是author表的的外键, 那么
这个列的可能值就是author表的所有id列的值。通常, 用于编辑一篇文章的作者的下拉列表看上去如例10-13所示。
例10-13 用objects_for_select()创建一个基于外键的
[php]
>?php echo select_tag('author_id', objects_for_select(
AuthorPeer::doSelect(new Criteria()),
'getId',
'__toString()',
$article-
)) ?<
=< >select name="author_id" id="author_id"<
>option value="1"
/option<
>option value="2"
/option<
>option value="3"
/option<
>option value="4" selected="selected"
>option value="5"
/option<
>/select<
实际上,仅仅用 object_select_tag()就可以完成所有的工作。 它显示一个下拉列表, 其选项为外键所在表的所有可能记录值。 这个
辅助函数可以根据数据库模式文件猜出外键列和外键所在的表, 因此它的语法非常简洁。以下代码和例10-13完成同样的功能:
[php]
>?php echo object_select_tag($article, 'getAuthorId') ?<
object_select_tag() 辅助函数根据作为参数传递的方法名就可以得到相关的peer类名
是,你可以在第3个参数relate_class中设定你自己的
录名(如果$author-<_toString(
得出的结果中取得的, 它根据记录的创建时间排序。如果你想按指定的顺序显示记录的一个
件的对象数组, 然后你就可以设定peer_method选项。最后, 你可以通过设定include_blank或include
项,在下拉列表的顶部加一个空的选项或某个定制的选项。例10
例10-14 object_select_tag() 辅助函数的选项
[php]
// Base syntax
>?php echo object_select_tag($article, 'getAuthorId') ?<
// Builds the list from AuthorPeer::doSelect(new Criteria())
// Change the peer class used to retrieve the possible values
>?php echo object_select_tag($article, 'getAuthorId',
'related_class=Foobar') ?<
// Builds the list from FoobarPeer::doSelect(new Criteria())
// Change the peer method used to retrieve the possible values
>?php echo object_select_tag($article,
'getAuthorId','peer_method
// Builds the list from AuthorPeer::getMostFamousAutho
Criteria())
// Add an >option value=""< >/option< at the top of the list
>?php echo object_select_tag($article, 'getAuthorId',
'include_blank=true') ?<
// Add an >option value=""
/option< at the top of
the list
>?php echo object_select_tag($article, 'getAuthorId',
'include_custom=Choose an author') ?<
### Updating Objects更新对象
如果利用对象辅助函数编写专用于编辑对象属性的表单, 就比在动作中进行处理容易。 例如, 如果你有一个类Author包含name, age,
address等属性, 你就可以如10-15那样编写表单:
例10-15 仅用对象辅助函数的表单
[php]
>?php echo form_tag('author/update') ?<
>?php echo object_input_hidden_tag(
Name: >?php echo object_input_tag($author, 'getName') ?<>br /<
Age: >?php echo object_input_tag($author, 'getAge') ?<>br /<
Address: >br /<
>?php echo object_textarea_tag($author, 'getAddress') ?<
>/form<
提交表单时, 将调用author模块的update动作, 该动作只要调用由Propel生成的fromArray(
10-16.
例10-16 基于对象表单辅助函数的表单提交处理函数。
[php]
public function executeUpdate ()
{
$author = AuthorPeer::retrieveByPk($this
$this-
$author-
$author-
return $this-
?id='.$author-
}
Form Validation表单验证
-----------------------
第六章介绍过如何在动作类中用validateXXX(
的代码. symfony提供了一种技术, 不用动作类中的PHP代码, 而仅仅是用一个YAML文件就可以验证表单.
为了说明表单验证的特性, 让我们先看一下例10-17中的表单示例. 这是一个典型的联系人表单, 包括name, email, age和
message域。
例 10-17 联系人表单示例. 文件路径为php `modules/contact/templates
[php]
>?php echo form_tag('contact/send') ?<
Name: >?php echo input_tag('name') ?<>br /<
Email: >?php echo input_tag('email') ?<>br /<
Age: >?php echo input_tag('age') ?<>br /<
Message: >?php echo textarea_tag('message') ?<>br /<
>?php echo submit_tag() ?<
>/form<
表单验证的要求是: 如果用户想提交一个输入了无效数据的表单, 浏览器将显示包含出错信息的网页. 我们先对上面的表单定义什么样的数据才是有效
的.
*name域是必需的, 且必须是2到100个字符的文本.
*email域是必需的, 且必须是2到100个字符的文本, 并是一个有效的电子邮件地址
*age域是必需的, 且必须是0-100之间的整数.
*message域是必需的.
你当然可以为联系人表单定义更复杂的验证规则, 不过在本例中有了上面定义的规则就足以说明问题了.
<**NOTE**