表单是网站最重要的组成部分,以用户交互为主的站点,表单几乎无处不在。所以,表单的生成、验证、提交,也是程序中很重要的部分,成熟的程序,几乎都有自成体系的表单机制。drupal 也不例外。drupal 的表单系统相当强大,可以让程序员不写任何 html 代码,完成表单的生成、验证、提交、入库等一系列流程。在模块开发中,几乎都会和表单打交道。
这篇教程是 drupal 表单的基本应用实例,涉及表单的定义、验证、提交等基本环节。
本系列教程,都以 drupal 6 为基础,以模块为载体,每篇教程即为一个模块,以后不再做特殊说明。
web 表单交互的基本流程大致是:用户访问某个页面,输出表单界面,用户填写表单,提交,程序验证,通过验证或让用户重新填写。流程基本固定,但实现的方式有许多种,在 drupal 中,这整个流程均可以在模块中完成,甚至不用写一行 html 代码。下面开始创建模块。
模块名称:myform。模块名称根据需要自定义,一般来说,不与现有的第三方模块重名。这个模块我们命名为:myform。
实现功能:定义一个页面,生成一个表单,让所有可以提交信息。
权限设置:不做权限验证,所有人均可提交。本教程重点在表单的基本应用,权限部分留待以后。
以下是详细步骤:
第一步:定义模块信息,添加模块信息文件:myform.info
// $Id$
name = myform
description = 用户反馈表单
core = "6.x"
第二步:创建数据表,添加模块安装文件:myform.install
1、实现 hook_schema,drupal 为了实现多种数据库的兼容,有自成体系的数据库抽象层,包括创建表、增删查,均有详细接口。此部分留待后文讲述。
查看代码 打印代码?
function myform_schema() {
$schema['myform'] = array(
'description' => '反馈表单字段',
'fields' => array(
'id' => array(
'type' => 'serial',
'unsigned' => true,
'not null' => true,
'description' => '自增主键'
),
'title' => array(
'type' => 'varchar',
'length' => 255,
'not null' => true,
'default' => '',
19 'description' => '标题', 20 ), 21 'name' => array( 22 'type' => 'varchar', 23 'length' => 64, 24 'not null' => true, 25 'default' => '', 26 'description' => '昵称', 27 ), 28 'mail' => array( 29 'type' => 'varchar', 30 'length' => 255, 31 'not null' => true, 32 'default' => '', 33 'description' => '邮件', 34 ), 35 'timestamp' => array( 36 'type' => 'int', 37 'not null' => true, 38 'default' => 0, 39 'description' => '留言时间' 40 ), 41 'body' => array( 42 'description' => '留言内容', 43 'type' => 'text', 44 'not null' => false, 45 'size' => 'big', 46 'serialize' => true 47 ), 48 ), 49 'indexes' => array( 50 'timestamp' => array('timestamp'), 51 ), 52 'primary key' => array('id'), 53 ); 54 return $schema; 55 }
2、实现 hook_install。在模块被安装时,将调用此钩子。通常,若以 hook_schema 定义了数据表结构,将使用 drupal_install_schema 来完成安装。同时,可以在 hook_install 进行其它操作,比如写入一些默认数据等。
查看代码
打印代码?
1
4 function myform_install() {
5 drupal_install_schema('myform');
6 }
3、实现 hook_uninstall。在模块卸载时,将调用此钩子。这并不是必须的,但推荐实现,若你的模块创建了数据表,可调用 drupal_uninstall_schema 删除表。注意:用户禁用模块,并不会调用此钩子。
查看代码
打印代码?
1
4 function myform_uninstall() {
5 drupal_uninstall_schema('myform');
6 }
第三步:创建模块主文件,添加模块主文件:myform.module
根据我们的需要,需要定义一个页面,使用 hook_menu 完成。drupal 是一个单入口程序,所有请求均由 index.php 解析分发,hook_menu 定义的路径,实际上就相当于 $_GET['q'],即:index.php?q={path}。用户发起访问请求,系统判断请求路径由哪个模块定义,根据最大匹配原则,找到最接近的定义者。
比如:用户请求的路径为:test/abcd,匹配的顺序大致如:test/abcd -> test/% -> test。如以上三个路径都没有被定义,则返回错误,无法找到页面。以下是本模块需要定义路径。 hook_menu 将被写入数据库 menu 表中,并被缓存,所以,有任何关于 hook_menu 的改动,建议先清除缓存。
注意:如果两个模块定义了相同的路径,将根据 weight 来决定使用谁的配置。
hook_menu 是一个相当重要的钩子,几乎每个模块都要用到,但不是本篇教程的重点,只做以上简单介绍。
查看代码
打印代码?
01
04 function myform_menu() {
05 $item = array();
06
07 $item['myform'] = array(
08 'description' => '用户反馈',
09 'page callback' => 'drupal_get_form',
10 'page arguments' => array('myform_page_form'),
11 'access callback' => true,
12 'file' => 'myform.page.inc',
13 'type' => MENU_CALLBACK,
14 );
15
16 return $item;
17 }
第四步:创建表单,创建文件:myform.page.inc
为什么要将表单代码写在另一个文件呢?drupal 在每次初始化时,将加载每个模块的主文件(如:myform.module),我们可以把只在特定路径使用到的代码,写在另一个文件里,减少主模块文件的大小,以加速系统运行。当然,这所带来的效率提升可能是微乎其微,甚至没有,但养成这个习惯也不是坏事,另外这也让模块的文件结构更清晰,易于维护。
根据 hook_menu 里的定义,drupal_get_form 是页面回调函数,相当于:drupal_get_form('myform_page_form')。我们只需要按规定格式定义表单,然后调用 drupal_get_form,便能得 html 代码,输出到页面即可。
在 myform.page.inc 中创建表单函数:myform_page_form。
查看代码
打印代码?
1
5 function myform_page_form(&$form_state) {
6 }
这儿有一个参数: &$form_state,这是 drupal_get_form 自动传递的,其中包含表单 id、token 等信息,若处于验证或提交环节,包含所有用户输入值,即 $_POST。使用 drupal_get_form 也可以传递自定义参数,比如:drupal_get_form('myform_page_form', $test),那么 myform_page_form 函数则要这样:
1
7 function myform_page_form(&$form_state, $test) {
8 }
此处不做深入讨论,回到本教程,创建表单的全部代码如下:
查看代码
打印代码?
01
05 function myform_page_form(&$form_state) {
06
07 $form['title'] = array(
08 '#title' => '主题', // 字段名称
09 '#type' => 'textfield', // 字段类型,此处为单行文本
10 '#required' => true, // 是否必填
11 '#default_value' => '', // 默认值,可选
12 );
13
14 $form['name'] = array(
15 '#title' => '昵称',
16 '#type' => 'textfield',
17 '#required' => true,
18 '#description' => '请输入您的昵称,不低于 2 个字符'
19 );
20
21 $form['mail'] = array(
22 '#title' => '邮件',
23 '#type' => 'textfield',
24 '#required' => true,
25 '#description' => '请输入您的常用邮件,便于我们联系您'
26 );
27
28 $form['body'] = array(
29 '#title' => '内容',
30 '#type' => 'textarea', // 多行文本
31 '#required' => true,
32 );
33
34
41 $form['#validate'] = array('myform_page_form_validate');
42
43
50 $form['submit'] = array(
51 '#type' => 'submit', // 提交按钮
52 '#value' => '确认提交', // 按钮值
53 '#submit' => array('myform_page_form_submit'), // 提交函数
54 );
55
56 return $form;
57 }
接下来是表单验证函数。刚接触 drupal 的朋友可能有这个疑惑,drupal 为什么要把表单的验证和提交环节分开呢,这不是影响效率吗,验证完毕不是可以直接做入库等提交操作吗?实际上,这是为了让表单机制更灵活,假设验证和提交环节不分开,某个模块定义了表单,其它模块也想要验证这个表单,怎么办?验证和提交分离,其它模块可以轻松的通过 hook_form_alter 添加一个验证函数,在该函数中,可阻止表单提交。国内很多程序,均不考虑这些需要,所以,如果要进行二次开发,只有在源代码上进行修改,缺乏灵活性。myform_page_form 表单的验证函数代码如下:
查看代码
打印代码?
01
08 function myform_page_form_validate(&$form, &$form_state) {
09
10
15 if (drupal_strlen($form_state['values']['name']) < 2) {
16
20 form_set_error('name', '昵称不能少于 2 个字符');
21 } else if (drupal_strlen($form_state['values']['title']) > 255) {
22 form_set_error('title', '标题长度不能大于 255 个字符');
23 }
24
25 // 如验证通过,将进入表单提交环节
26 }
以上只验证了长度是否合法,实际应用中,可能还需要验证电子邮件是否合法等。验证通过后,进行提交环节,代码如下:
查看代码
打印代码?
01
06 function myform_page_form_submit(&$form, &$form_state) {
07
10 db_query("INSERT INTO {myform} (id, title, name, mail, timestamp, body)
11 VALUES (null, '%s', '%s', '%s', %d, '%s')", $form_state['values']['title'],
12 $form_state['values']['name'], $form_state['values']['mail'],
13 $_SERVER['REQUEST_TIME'], $form_state['values']['body']);
14
15 // 获取上一条 insert sql 返回的主键,以此判断是否执行成功
16 if (db_last_insert_id('myform', 'id')) {
17
18
22 drupal_set_message('感谢您,反馈已经提交,我们将尽快和您取得联系');
23
24 // 如果想将用户重定向到一个页面,可修改 $form['#redirect'] 值,如:
25
26 $form['#redirect'] = 'test/abcde';
27
28
32
33 } else {
34
35 // 数据写入失败了
36 drupal_set_message('抱歉,遇到问题,提交失败了', 'error');
37
38 }
39
40 }
表单成功写入数据库,整个表单的流程就走完了。很简单吧?因为规则不熟悉,可能不明白为什么要这样写,实际上,刚开始做模块开发,只需要知道要这样即可以,不必深究为什么要这样。倘若先拿一本书,务必把每个术语搞清楚,理解透彻,没有实际开发经验,我想还是比较困难的。好读书不求甚解,大概也是这个道理吧。做多了,自然就明白了,可能我是实践主义者。
以上就是本篇教程的全部内容,下一篇将继续研究表单,说说关于文件上传。
启用后,访问 http://www.mysite.com/?q=myform 即可提交表单。