问:如何校验和提交表单?
答:Drupal允许定义默认的表单校验处理函数和提交处理函数。

function practice_demo_form($form, &$form_state) {
... ...
return $form;
} function practice_demo_form_validate($form, &$form_state) {
if (...) {
form_set_error(...);
}
} function practice_demo_form_submit($form, &$form_state) {
... ...
}

这些函数在drupal_prepare_form()中查找:

function drupal_prepare_form($form_id, &$form, &$form_state) {
... if (!isset($form['#validate'])) {
// Ensure that modules can rely on #validate being set.
$form['#validate'] = array();
// Check for a handler specific to $form_id.
// 默认的表单校验函数以_validate结尾
if (function_exists($form_id . '_validate')) {
$form['#validate'][] = $form_id . '_validate';
}
// Otherwise check whether this is a shared form and whether there is a
// handler for the shared $form_id.
elseif (isset($form_state['build_info']['base_form_id']) && function_exists($form_state['build_info']['base_form_id'] . '_validate')) {
$form['#validate'][] = $form_state['build_info']['base_form_id'] . '_validate';
}
} if (!isset($form['#submit'])) {
// Ensure that modules can rely on #submit being set.
$form['#submit'] = array();
// Check for a handler specific to $form_id.
// 默认的表单提交函数以_submit结尾
if (function_exists($form_id . '_submit')) {
$form['#submit'][] = $form_id . '_submit';
}
// Otherwise check whether this is a shared form and whether there is a
// handler for the shared $form_id.
elseif (isset($form_state['build_info']['base_form_id']) && function_exists($form_state['build_info']['base_form_id'] . '_submit')) {
$form['#submit'][] = $form_state['build_info']['base_form_id'] . '_submit';
}
} ...
}

问:$form_state['build_info']['args']是什么用途?
答:$form_state['build_info']['args']是page arguments中除第一个参数外其它所有的参数,第一个参数是表单标识$form_id。

function practice_menu() {
$items['practice/demo'] = array(
'title' => 'Demo',
'page callback' => 'drupal_get_form',
// 第一个参数是表单标识practice_demo_from
// 第二个参数是ultramen,第三个参数是25
'page_arguments' => array('practice_demo_from', 'ultramen', 25),
'access callback' => TRUE,
);
return $items;
} function practice_demo_form($form, &$form_state, $name, $age) {
... ...
} function drupal_get_form($form_id) {
$form_state = array(); $args = func_get_args();
// Remove $form_id from the arguments.
array_shift($args);
$form_state['build_info']['args'] = $args; return drupal_build_form($form_id, $form_state);
}

问:form_build_id是什么?
答:Drupal为每个表单定义一个唯一的form build id,是一组随机字符串。form build id在客服端浏览器以hidden field的方式呈现,表单提交时回传给Drupal。

function drupal_prepare_form($form_id, &$form, &$form_state) {
... // Generate a new #build_id for this form, if none has been set already. The
// form_build_id is used as key to cache a particular build of the form. For
// multi-step forms, this allows the user to go back to an earlier build, make
// changes, and re-submit.
// @see drupal_build_form()
// @see drupal_rebuild_form()
// Drupal为每个表单定义一个唯一的form_build_id
if (!isset($form['#build_id'])) {
$form['#build_id'] = 'form-' . drupal_random_key();
}
// form_buikd_id会在浏览器表单中以hidden的形式出现
$form['form_build_id'] = array(
'#type' => 'hidden',
'#value' => $form['#build_id'],
'#id' => $form['#build_id'],
'#name' => 'form_build_id',
// Form processing and validation requires this value, so ensure the
// submitted form value appears literally, regardless of custom #tree
// and #parents being set elsewhere.
'#parents' => array('form_build_id'),
); // 同form_build_id
if (isset($form_id)) {
$form['form_id'] = array(
'#type' => 'hidden',
'#value' => $form_id,
'#id' => drupal_html_id("edit-$form_id"),
// Form processing and validation requires this value, so ensure the
// submitted form value appears literally, regardless of custom #tree
// and #parents being set elsewhere.
'#parents' => array('form_id'),
);
}
if (!isset($form['#id'])) {
$form['#id'] = drupal_html_id($form_id);
} ...
}


form build id用来做什么?Drupal用它来标识缓存的表单数据。

function drupal_build_form($form_id, &$form_state) {
... // If the incoming input contains a form_build_id, we'll check the cache for a
// copy of the form in question. If it's there, we don't have to rebuild the
// form to proceed. In addition, if there is stored form_state data from a
// previous step, we'll retrieve it so it can be passed on to the form
// processing code.
// 用$form_state['input']['form_id']标识表单,例如practice_demo_form
// 用$form_state['input']['form_build_id']标识缓存
$check_cache = isset($form_state['input']['form_id']) && $form_state['input']['form_id'] == $form_id && !empty($form_state['input']['form_build_id']);
if ($check_cache) {
$form = form_get_cache($form_state['input']['form_build_id'], $form_state);
} ...
} function drupal_process_form($form_id, &$form, &$form_state) {
... // After processing the form, the form builder or a #process callback may
// have set $form_state['cache'] to indicate that the form and form state
// shall be cached. But the form may only be cached if the 'no_cache' property
// is not set to TRUE. Only cache $form as it was prior to form_builder(),
// because form_builder() must run for each request to accommodate new user
// input. Rebuilt forms are not cached here, because drupal_rebuild_form()
// already takes care of that.
// 用$form_state['cache']和$form_state['no_cache']标识是否允许表单缓存
if (!$form_state['rebuild'] && $form_state['cache'] && empty($form_state['no_cache'])) {
form_set_cache($form['#build_id'], $unprocessed_form, $form_state);
} ...
}

问:form token是什么?
答:Drupal为每个表单定义一个form token,与当前session会话关联在一起。form token的存在是为了避免处理长时间未提交的表单。

function drupal_prepare_form($form_id, &$form, &$form_state) {
... // Add a token, based on either #token or form_id, to any form displayed to
// authenticated users. This ensures that any submitted form was actually
// requested previously by the user and protects against cross site request
// forgeries.
// This does not apply to programmatically submitted forms. Furthermore, since
// tokens are session-bound and forms displayed to anonymous users are very
// likely cached, we cannot assign a token for them.
// During installation, there is no $user yet.
if (!empty($user->uid) && !$form_state['programmed']) {
// Form constructors may explicitly set #token to FALSE when cross site
// request forgery is irrelevant to the form, such as search forms.
if (isset($form['#token']) && $form['#token'] === FALSE) {
unset($form['#token']);
}
// Otherwise, generate a public token based on the form id.
else {
$form['#token'] = $form_id;
// 将form token发送到客户端
$form['form_token'] = array(
'#id' => drupal_html_id('edit-' . $form_id . '-form-token'),
'#type' => 'token',
'#default_value' => drupal_get_token($form['#token']),
// Form processing and validation requires this value, so ensure the
// submitted form value appears literally, regardless of custom #tree
// and #parents being set elsewhere.
'#parents' => array('form_token'),
);
}
} ...
} function drupal_validate_form($form_id, &$form, &$form_state) {
... // If the session token was set by drupal_prepare_form(), ensure that it
// matches the current user's session.
// $form['#token']是表单标识,例如practice_demo_form
if (isset($form['#token'])) {
if (!drupal_valid_token($form_state['values']['form_token'], $form['#token'])) {
$path = current_path();
$query = drupal_get_query_parameters();
$url = url($path, array('query' => $query)); // Setting this error will cause the form to fail validation.
form_set_error('form_token', t('The form has become outdated. Copy any unsaved work in the form below and then <a href="@link">reload this page</a>.', array('@link' => $url))); // Stop here and don't run any further validation handlers, because they
// could invoke non-safe operations which opens the door for CSRF
// vulnerabilities.
$validated_forms[$form_id] = TRUE;
return;
}
} ...
}

问:$form_state['input']是怎么来的?
答:$form_state['input']来自与$_GET或者$_POST,取决于$form_state['method']。

function drupal_build_form($form_id, &$form_state) {
... if (!isset($form_state['input'])) {
$form_state['input'] = $form_state['method'] == 'get' ? $_GET : $_POST;
} ...
}

问:$form_state['values']是怎么来的?
答:$form_state['values']是$form_state['input']经过过滤处理后的结果。$form_state['input']是原始数据,$form_state['values']是过滤后的数据。

问:$form_state['rebuild']是用来做什么的?
答:不清楚。

问:$form_state['redirect']是用来做什么的?
答:redirect用在表单提交成功后,表示需要重定向的新地址。等同于调用drupal_goto()。

function practice_demo_form_submit($form, &$form_state) {
$form_state['redirect'] = 'admin/people/expiration';
// 等同 drupal_goto('admin/people/expiration');
} function drupal_redirect_form($form_state) {
// Skip redirection for form submissions invoked via drupal_form_submit().
if (!empty($form_state['programmed'])) {
return;
}
// Skip redirection if rebuild is activated.
if (!empty($form_state['rebuild'])) {
return;
}
// Skip redirection if it was explicitly disallowed.
if (!empty($form_state['no_redirect'])) {
return;
}
// Only invoke drupal_goto() if redirect value was not set to FALSE.
if (!isset($form_state['redirect']) || $form_state['redirect'] !== FALSE) {
if (isset($form_state['redirect'])) {
if (is_array($form_state['redirect'])) {
call_user_func_array('drupal_goto', $form_state['redirect']);
}
else {
// This function can be called from the installer, which guarantees
// that $redirect will always be a string, so catch that case here
// and use the appropriate redirect function.
$function = drupal_installation_attempted() ? 'install_goto' : 'drupal_goto';
$function($form_state['redirect']);
}
}
drupal_goto(current_path(), array('query' => drupal_get_query_parameters()));
}
}

问:$form_state['submitted']是用来做什么的?
答:不清楚。

问:$form_state['executed']是用来做什么的?
答:不清楚。

问:$form_state['programmed']是用来做什么的?
答:不清楚。

问:$form_state['groups']是用来做什么的?
答:不清楚。

问:$form_state['buttons']是用来做什么的?
答:buttons是表单中所有的按钮。什么样的元素是按钮?在hook_element_info()中用button_type标识。

// If the form was submitted by the browser rather than via Ajax, then it
// can only have been triggered by a button, and we need to determine which
// button within the constraints of how browsers provide this information.
if (isset($element['#button_type'])) {
// All buttons in the form need to be tracked for
// form_state_values_clean() and for the form_builder() code that handles
// a form submission containing no button information in $_POST.
$form_state['buttons'][] = $element;
if (_form_button_was_clicked($element, $form_state)) {
$form_state['triggering_element'] = $element;
}
} function system_element_info() {
$types['submit'] = array(
'#input' => TRUE,
'#name' => 'op',
'#button_type' => 'submit',
'#executes_submit_callback' => TRUE,
'#limit_validation_errors' => FALSE,
'#process' => array('ajax_process_form'),
'#theme_wrappers' => array('button'),
);
$types['button'] = array(
'#input' => TRUE,
'#name' => 'op',
'#button_type' => 'submit',
'#executes_submit_callback' => FALSE,
'#limit_validation_errors' => FALSE,
'#process' => array('ajax_process_form'),
'#theme_wrappers' => array('button'),
);
}

问:$form_state['triggering_element']是用来做什么的?
答:triggering_element用来标识是哪个元素触发的表单提交事件。通常是按钮。

问:可不可以为某个表单定制主题?
答:可以的。Drupal默认用表单标识作为该表单的主题。

function drupal_prepare_form($form_id, &$form, &$form_state) {
... // If no #theme has been set, automatically apply theme suggestions.
// theme_form() itself is in #theme_wrappers and not #theme. Therefore, the
// #theme function only has to care for rendering the inner form elements,
// not the form itself.
if (!isset($form['#theme'])) {
$form['#theme'] = array($form_id); // 表单默认主题
if (isset($form_state['build_info']['base_form_id'])) {
$form['#theme'][] = $form_state['build_info']['base_form_id'];
}
} ...
}

问:可不可以用alter钩子修改表单?
答:可以的。Drupal构造表单时,允许为表单定义通用和专用两种alter钩子。

function drupal_prepare_form($form_id, &$form, &$form_state) {
... // Invoke hook_form_alter(), hook_form_BASE_FORM_ID_alter(), and
// hook_form_FORM_ID_alter() implementations.
$hooks = array('form'); // 表单通用钩子
if (isset($form_state['build_info']['base_form_id'])) {
$hooks[] = 'form_' . $form_state['build_info']['base_form_id'];
}
$hooks[] = 'form_' . $form_id; // 表单专用钩子
drupal_alter($hooks, $form, $form_state, $form_id); ...
}

Drupal Form问题汇总的更多相关文章

  1. drupal form 中图片上传

    1.创建url 链接到form $items['qianfeng/add'] = array(     'title' => t('加入信息'),     'page callback' =&g ...

  2. Drupal 远程命令执行漏洞(CVE-2018-7600)

    名称: Drupal 远程命令执行漏洞 CVE-ID: CVE-2018-7600 Poc: https://paper.seebug.org/578/ EXPLOIT-DB: https://www ...

  3. SAP 财务模块 FI-TV 差旅管理

    SAP 财务模块 FI-TV 差旅管理 Travel Management差旅管理事务码              描述PRT3      Trip Costs: Maintain Postings ...

  4. ASP.NET检测到有潜在危险的 Request.Form 值解决方案汇总

    ASP.NET检测到有潜在危险的 Request.Form 值解决方案汇总 当我们在网站中使用CKEditor等富文本编辑器时,大多都会遇到这样的到警告 这是因为ASP.NET默认开启对页面提交内容的 ...

  5. Form实现主从块金额汇总

    1.FORM使用app_calculate.running_total汇总行金额,行上有编码重复验证. 情况一:当录入多个编码重复的行并保存时,报错,清除一个重复行再保存(头行金额一致),报错&quo ...

  6. ajax提交form表单资料详细汇总

    一.ajax提交form表单和不同的form表单的提交主要区别在于,ajax提交表单是异步提交的,而普通的是同步提交的表单.通过在后台与服务器进行少量数据交换,ajax 可以使网页实现异步更新.这意味 ...

  7. drupal 7在一个form新增或者修改一个字段

    例如在以下页面新增一个工号字段 在模块文件里面通过hook_form_FORM_ID_alter 函数修改,比如 图中的helloworld是模块名称,也就是你在哪个模块里面写这个函数那么名称就是什么 ...

  8. form表单类标签汇总

    <form action="form_action.asp" method="get"> First name: <input type=&q ...

  9. form in drupal

    qin_form_ajax_example_form($form, &$form_state)类似函数的参数永远都是一样的,最多把$form前面也加上& 当没有实现页面跳转时,$for ...

随机推荐

  1. Selenium2+python自动化57-捕获异常(NoSuchElementException)

    前言 在定位元素的时候,经常会遇到各种异常,为什么会发生这些异常,遇到异常又该如何处理呢? 本篇通过学习selenium的exceptions模块,了解异常发生的原因. selenium+python ...

  2. 第二章 ActionScript 3.0学习之画星星(鼠标及键盘事件)

    今天觉得学到的比较有趣,所以记录之......~~~ 下面这段就是画出星星的代码:StarShape.as package { import flash.display.Shape; import f ...

  3. style="visibility: hidden"和 style=“display:none”之间的区别

    style=“display:none” 隐藏页面元素: <html> <head> <script type="text/javascript"&g ...

  4. html调用servlet(JDBC在Servlet中的使用)(2)

    5.修改数据 5.1编写查询条件页面 修改单条数据的时候,首先是查询出单个数据的详细信息,然后根据实际需要部分修改或者全部修改.修改之后,数据会提交到数据库,数据库中保存更新以后的数据. 查询出单条数 ...

  5. Objective-C:分类(Category、extension)

    分类(Category .Extension) (一)分类的划分     (2) 1.(命名的类别)类别Category:只能添加新的方法,不能添加新变量.           2.(未命名的类别)类 ...

  6. PreparedStatement 使用like 模糊查询

    PreparedStatement 使用like 在使用PreparedStatement进行模糊查询的时候废了一番周折,以前一直都没有注意这个问题. 一般情况下我们进行精确查询,sql语句类似:se ...

  7. Page Redirect Speed Test

    现在,有两种方法可以实现网页的自动跳转. (1) 用html自带的<meta>标签(如下)可以实现网页的自动跳转,而且可以控制跳转的延时. <meta http-equiv=&quo ...

  8. 【用jQuery来判断浏览器的类型】及【javascript获取用户ip地址】

    用jQuery来判断浏览器的类型,主要是使用$.browser这个工具类,使用方法: $.browser.['浏览器关键字'] //谷歌浏览器.360浏览器等其他一些浏览器,没有专门的判断 funct ...

  9. C#_文件读写常用类介绍

    首先要熟悉.NET中处理文件和文件夹的操作.File类和Directory类是其中最主要的两个类.了解它们将对后面功能的实现提供很大的便利.      本节先对和文件系统相关的两个.NET类进行简要介 ...

  10. 直接修改class文件内容即使是文本会导致App异常,正确方式是修改java再用生成的class替换掉原有的class

    前几天来了个小任务,把某项目中某人的邮件地址改了下. 由于对项目不熟悉,于是采用find方式找出app中所有包含某人邮件地址的文件都找出来了. xml,properties大约三四个,还有两个clas ...