在 Drupal 用语中,用于创建表单的函数叫做表单构造函数(constructor)。术语 constructor 没有特别的意义,与面向对象编程中的同名术语无关。
表单构造函数创建出一个复杂的数据结构,用于构造、验证、管理表单。其使用方式类似于钩子:遵循特定的命名约定,所有表单构造函数都返回同一类型的数据结构。
在研究表单构造函数之前,先看一下我们想要表单具备的特性的列表:
现在我们可以把这个任务清单转换成表单构造器了。这个函数有点长,因为需要创建很多域。不过,阅读起来并不复杂:
function emailusers_compose_form($context, $account) { // This is a value only -- equivalent to a hidden field, except // that it is never rendered into the HTML. $form['to'] = array( '#type' => 'value', '#value' => $account, ); // Create a fieldset for the body: $form['message'] = array( '#type' => 'fieldset', '#title' => t('Compose the Message'), ); // Textfield for subject of the body $form['message']['subject'] = array( '#type' => 'textfield', '#title' => t('Subject'), '#size' => 50, '#maxlengh' => 255, '#description' => t('The subject of the email message.'), ); // And a text area for the body. $form['message']['body'] = array( '#type' => 'textarea', '#title' => t('Message'), '#cols' => 50, '#rows' => 5, '#description' => t('The body of the email message.'), ); // Create a fieldset for details $form['details'] = array( '#type' => 'fieldset', '#title' => t("Details"), ); // Checkbox: if checked, CC the author, too. $form['details']['cc_me'] = array( '#type' => 'checkbox', '#title' => t('BCC Yourself'), '#default_value' => 1, '#description' => t('If this is checked, the message will also be sent to you.'), ); // Finally, a submit button: $form['submit'] = array( '#type' => 'submit', '#value' => t('Send Mail'), ); return $form; }
这个函数的一般结构并不陌生。它与其它 Drupal 函数很相似。变量 $form 是个关联数组,其中每一项对应(大体上)一个表单元素。一共有 7 个这样的表单元素,我们稍后详细分析。
$form 数组的每一项的值都是另一个关联数组,包含相应元素的配置信息。
此函数构造出 $form 数组,并把它返回。其它函数,比如 drupal_get_form(),调用这个函数,用这个表单定义达到各种目的,包括创建表单的HTML 表示形式,或进行基本的表单验证。
让我们详细分析一下 $form 数组的各个项。第一项存储账户信息:
$form['to'] = array( '#type' => 'value', '#value' => $account, );
这一项是个 value 域。与我们将要看到的其它元素不同,它对应的不是一个可见的表单元素。它只是保存了某个 value 的引用(在本例中,是 $account 变量的引用),表单 API 在整个处理过程中都可以使用此引用。
因为这是个简短的定义,从它开始讲解比较好。在本例中,$forms 数组的 key 用于引用关于此表单的数据。以 HTML 表单为例,这大体上相当与表单元素的name 属性。
这一项的值是一个关联数组,它提供了关于此表单元素的信息。由于表单的复杂性,这个数组中可以使用的值有一大票,但这里的两个 key 对于很多表单域都是相同的。
提示:所支持参数的完整列表,参加表单 API 参考网页:http://api.drupal.org/api/file/developer/topics/forms_api_reference.html/6 除参数列表外,这个网页中还包含了指明哪个表单元素支持哪个参数的矩阵。
第一个,#type,表明这个表单元素为何种类型。每个 HTML 表单元素都有一个类型。还有几个特殊类型的表单域,value 类型即为其中之一,在很多方面,它与 <input type="hidden"/> HTML 表单域完成相似的功能。这使得用户可以为表单附加信息,而不必为用户提供修改信息的方式。但是,有一个很重要的方面是不同的:value 永远不会发送给客户端。它只是存储在服务器端。
提示:所有用于定义表单项的参数都以 # 开头。这用来区分参数和嵌套表单域。稍后关于 field set 的讨论中有相关示例。
下一个参数是 #value。这个参数也可以用于好几个不同的表单类型。它保存内容不能被用户修改的表单类型的域的值。在本例中,它引用 $account 数据。
提示:多数情况下,#value 域的值应该是一种易于打印的类型(比如字符串或整数)。不过当 #type 被设置为 value 时,#value 的引用可以是任何对象。
提示:在这里,对术语 'value' 的重复使用可能导致混淆。有一种 #type 叫做 value,它说明了所描述的是哪一种表单元素。数组中还有一个 #value 元素,它说明了表单元素应该被设置为什么值。在我们的示例中,就出现了一种不幸的情况,我们可以这样表述:元素 value 的值是 #value 的值。(the value of the value element is #value's value.)
我们大体上介绍了如何定义表单元素。现在让我们快速浏览一下 $form 数组中的其它项:
// Create a fieldset for the body: $form['message'] = array( '#type' => 'fieldset', '#title' => t('Compose the Message'), ); // Textfield for subject of the body $form['message']['subject'] = array( '#type' => 'textfield', '#title' => t('Subject'), '#size' => 50, '#maxlengh' => 255, '#description' => t('The subject of the email message.'), ); // And a text area for the body. $form['message']['body'] = array( '#type' => 'textarea', '#title' => t('Message'), '#cols' => 50, '#rows' => 5, '#description' => t('The body of the email message.'), );
这三项加在一起定义了一个 field set,包括一个 Subject 文本框和 Message 文本输入区。
三项中的第一项定义了一个 field set(注意 #type 的值为 fieldset)。参数 #title 可以用于大多数元素,它用来创建 field set 的说明以及其它表单域的标签(label)。
提示:与平常一样,传递给用户的普通文本应该放在 t() 转换函数之内。
下一项定义了 Subject 文本输入区。为了表明这个域属于 field set,subject 数组嵌入到 $form['message'] 数组之中,成为 $form['message']['subject']。使用这种嵌套功能,就可以有逻辑地构造出复杂的表单。当 Drupal 收到并处理提交的表单数据时,你就可以用上述数据结构访问它。
textfield 类型在 HTML 中显示为单行文本输入框(<input type="text"/>)。
在本例中,#size 和 #maxlength 参数用于设置可见区域的大小和域中可以包含的最大字符数。对于 #maxlength,不仅用于生成表单,当表单数据上传时,Drupal 在服务器端还要进行检查,以确保用户提交的数据不超过限制长度。#description 显示为输入区域的帮助文本。
与文本框一样,用于邮件正文(body)的文本区域定义为 $form['message'] 数组的子元素。文本输入区也被显示在 field set 之内。这里用了两个参数:#rows 和 #cols。它们与 HTML<textarea/> 元素的同名属性相对应。
至此,我们学习了几个 field,让我们看看 Drupal 如何把这个数据结构转换成表单。显示为 HTML 时,这三个项目如下所示:

插图 6-2
对于 field set,标题被嵌入边框中。对于另外两个元素,标题显示在域的上方,描述文本显示在域的下面。
最后三项定义作用是类似的:
// Create a fieldset for details $form['details'] = array( '#type' => 'fieldset', '#title' => t('Details'), ); // Checkbox: if checked, CC the author, too. $form['details']['cc_me'] = array( '#type' => 'checkbox', '#title' => t('BCC Yourself'), '#default_value' => 1, '#description' => t('If this is checked, the message will also be sent to you.'), ); // Finally, a submit button: $form['submit'] = array( '#type' => 'submit', '#value' => t('Send Mail'), );
在本节中,为 details 创建了一个新的 field set。说明应该把邮件BBC 给发送者的单选框被加入了 details field set 之中。在 cc_me 单选框定义中,#default_value 说明单选框默认处于选中状态(1 = 选中, 0 = 未选中)。但是 #default_value 与 #value 有一点重要的差异,#default_value 说明用户可以根据自己的意愿选择不同的值,而用户不能改变 #value 的值。
最后,在带有单选框的 field set 之后是提交按钮。此处再次使用了 #value——这次是为了给提交按钮添加标签。
至此,如果我们用一个 URL 访问这个页面:http://example.com/drupal?q=admin/emailusers/compose/3 会出现什么情况呢?首先 emailusers_menu() 钩子致使 Drupal 使用emailusers_compose() 回调函数(传入整数 3)。这个函数装入用户的账户信息($account)、生成一行文本,然后把控制器交给 drupal_get_form()。drupal_get_form() 函数从 emailusers_compose_form() 中装入表单构造函数并把它显示为 HTML,然后再发送给用户。
提示:调试时,应清空缓存。表单以及支撑表单的对象是被缓存的。有时候,这种缓存会干扰开发。使用 Devel 模块中 Devel 区块的”清空缓存(Empty cache)链接可以避免这种困扰。
最终显示给用户的是如下截图所示的页面:

插图 6-3
但是,提交这个表单时,会发生什么呢?
评论
发表新评论