译者:老葛 ESKALATE科技公司
主题化表单
Drupal拥有内置的函数,用以处理你定义的表单数据结构,并将其翻译或者说是呈现成HTML代码。然后,许多时候你可能需要修改Drupal生成的输出,或者你可能想其安全的控制整个流程。幸运的是,Drupal很容易做到这一点。
使用#prefix、#suffix和#markup
如果你的主体化需求非常简单,那么你就可以使用属性#prefix和#suffix来为表单元素前面和/或后面添加HTML代码,从而满足需求:
$form['color'] = array(
'#prefix' => '<hr />',
'#title' => t('Color'),
'#type' => 'fieldset',
'#suffix' => '<div class="privacy-warning">' .
t('This information will be displayed publicly!') . '</div>';
);
该代码在颜色(color)字段集上方添加了一条水平线,在其下方添加了一条私有消息。你甚至可以在你的表单中使用类型#markup来声明标示字体。任何一个没有属性#type的表单元素默认为类型#markup。
$form['blinky'] = array(
'#type' = 'markup',
'#value' = '<blink>Hello!</blink>'
);
注意: 本方法向你的表单中引入了HTML标示字体,一般认为该方法与使用<blink>标签效果差不多好。但是与编写一个主题函数相比,它不够干净利落,同时也增加了你网站设计人员的工作量。
使用主题函数
主题化表单的最灵活的方式是为表单或者表单元素单独构建一个主题函数。Drupal默认的主题函数名称为“theme_”加上你的表单ID的名称。在我们的例子中,使用了theme_formexample_nameform()。下面的主题函数将被调用并生成完全一样的输出
function theme_formexample_nameform($form) {
$output = drupal_render($form);
return $output;
}
拥有我们自己的主题函数的好处是,我们可以按照我们的意愿对变量$output进行解析、混合、添加等操作。我们可以快速的讲一个特定元素放到表单的最前面。例如在下面的例子中,我们把颜色字段集放在了最前面。
function theme_formexample_nameform($form) {
// Always put the the color selection at the top.
$output = drupal_render($form['color']);
// Then add the rest of the form.
$output .= drupal_render($form);
return $output;
}
告诉Drupal使用哪一个主题函数
通过为一个表单声明属性#theme,你可以命令Drupal使用给定的主题函数,无论它是否匹配“theme_”加表单ID的格式。
// Now our form will be themed by the function theme_formexample_special_theme().
$form['#theme'] = 'formexample_special_theme';
或者,你也可以让Drupal为一个表单元素使用一个特定的主题函数:
// Theme this fieldset element with theme_formexample_coloredfieldset().
$form['color'] = array(
'#title' => t('Color'),
'#type' => 'fieldset',
'#theme' => 'formexample_coloredfieldset'
);
注意:Drupal将在你设定的#theme属性的字符串前面添加前缀“theme_”,所以我们将#theme设置为formexample_coloredfieldset而不是theme_formexample_coloredfieldset,尽管后者是函数名称。为什么这样呢?请参看第8章。
用钩子函数hook_forms()声明验证和提交函数
有时,你回遇到一种特殊的情况,你想让许多不同的表单共有同一个验证或者提交函数。这就是所谓的代码复用。在该情况下,这是一个不错的主意。例如,在节点模块中,所有的节点类型的表单都共有一个验证和提交函数。那么我们就需要一种方式,以将表单ID映射到验证和提交函数上。让我们进入到hook_forms()中。
在Drupal回显表单时,它首先查找基于表单ID定义表单的函数(正因为这样,在我们的代码中,我们输用函数formexample_nameform())。如果找不到该函数,它将触发hook_forms(),该钩子函数在所有的模块中查找匹配的表单ID以进行回调。例如,在node.module中,使用下面的代码将不同类型的节点表单ID映射到同一个处理器上:
/**
* Implementation of hook_forms(). All node forms share the same form handler.
*/
function node_forms() {
foreach (array_keys(node_get_types()) as $type) {
$forms[$type .'_node_form']['callback'] = 'node_form';
}
return $forms;
}
在我们的例子中,我们也可以使用钩子函数hook_forms()以将另一个表单ID映射到我们现有的代码上:
/**
* Implementation of hook_forms().
*/
function formexample_forms() {
$forms['formexample_alternate'] = array(
'callback' => 'formexample_nameform');
return $forms;
}
现在,如果我们调用drupal_get_form(“formexample_alternate”),,Drupal将调用formexample_nameform()来得到表单定义。然后试图分别调用formexample_alternate_validate()和formexample_alternate_submit()以进行验证和提交。
主题、验证、提交函数的调用次序
你已经看到,有多个地方可以为Drupal放置你的主题、验证、提交函数。拥有这么多的可选项会让人感到困惑的,到底要调用哪个函数呢?下面是一个drupal所查找位置的次序小结。这里给定一个主题函数,假定你使用基于PHPTemplate的名为bluebeach的主题,表单定义将可选的属性$form['#base']设为foo,并且你调用函数drupal_get_form('formexample_nameform')。Drupal使用它找到的第一个主题函数。
1. $form['#theme'] // A function defined in the element form definition.
2. bluebeach_formexample_nameform() // Theme function provided by theme.
3. phptemplate_formexample_nameform() // Theme function provided by theme engine.
4. theme_formexample_nameform() // 'theme_' plus the form ID.
5. bluebeach_formexample_foo() // Theme name plus $form['#base']
6. phptemplate_formexample_foo() // Theme engine name plus $form['#base']
7. theme_formexample_foo() // 'theme_' plus the $form['#base']
在验证期间,表单验证器的调用次序如下:
1. 由 $form['#validate']定义的函数
2. formexample_nameform_validate() // 表单 ID + 'validate'.
3. formexample_foo_validate() // $form['#base'] + 'validate'.
当需要查找处理提交的函数时,查找位置的次序如下:
1. 由$form['#submit'] 定义的函数
2. formexample_nameform_submit() // 表单 ID + 'submit'.
3. formexample_foo_submit() // $form['#base'] + 'submit'.
什么时候需要设置$form[‘#base’]呢?当你拥有多个表单,而它们共有同一个验证和提交函数时,设置它。
编写一个验证函数
Drupal拥有一个内置的机制,能对未通过验证的表单元素进行高亮显示,并为用户展示一条错误消息。检查一下我们例子中的验证函数来看一下它是怎么工作的:
/**
* Validate the form.
*/
function formexample_nameform_validate($form_id, $form_values) {
if ($form_values['user_name'] == 'King Kong') {
// We notify the form API that this field has failed validation.
form_set_error('user_name',
t('King Kong is not allowed to use this form.'));
}
}
注意form_set_error()的使用。当King Kong访问我们的表单并用他的巨大的键盘键入他的姓名的时候,它将会在页面的顶部看到一条错误消息,而包含错误的字段被高亮显示,如图10-5所示:
图10-5 验证失败后为用户给出提示
当然,他也有可能仅仅输入他的名字(不包含姓)。我们在这里仅仅拿他作为一个例子,来说明form_set_error()为我们的表单设置了一条错误消息,并使得验证失败。
验证函数应该专注于验证。作为一条一般规则,它不应该改变数据。然而,它们可以为$form_values数组添加信息,如我们接下来所讲的。
使用form_set_value()传递数据