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

  1. function practice_demo_form($form, &$form_state) {
  2. ... ...
  3. return $form;
  4. }
  5.  
  6. function practice_demo_form_validate($form, &$form_state) {
  7. if (...) {
  8. form_set_error(...);
  9. }
  10. }
  11.  
  12. function practice_demo_form_submit($form, &$form_state) {
  13. ... ...
  14. }

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

  1. function drupal_prepare_form($form_id, &$form, &$form_state) {
  2. ...
  3.  
  4. if (!isset($form['#validate'])) {
  5. // Ensure that modules can rely on #validate being set.
  6. $form['#validate'] = array();
  7. // Check for a handler specific to $form_id.
  8. // 默认的表单校验函数以_validate结尾
  9. if (function_exists($form_id . '_validate')) {
  10. $form['#validate'][] = $form_id . '_validate';
  11. }
  12. // Otherwise check whether this is a shared form and whether there is a
  13. // handler for the shared $form_id.
  14. elseif (isset($form_state['build_info']['base_form_id']) && function_exists($form_state['build_info']['base_form_id'] . '_validate')) {
  15. $form['#validate'][] = $form_state['build_info']['base_form_id'] . '_validate';
  16. }
  17. }
  18.  
  19. if (!isset($form['#submit'])) {
  20. // Ensure that modules can rely on #submit being set.
  21. $form['#submit'] = array();
  22. // Check for a handler specific to $form_id.
  23. // 默认的表单提交函数以_submit结尾
  24. if (function_exists($form_id . '_submit')) {
  25. $form['#submit'][] = $form_id . '_submit';
  26. }
  27. // Otherwise check whether this is a shared form and whether there is a
  28. // handler for the shared $form_id.
  29. elseif (isset($form_state['build_info']['base_form_id']) && function_exists($form_state['build_info']['base_form_id'] . '_submit')) {
  30. $form['#submit'][] = $form_state['build_info']['base_form_id'] . '_submit';
  31. }
  32. }
  33.  
  34. ...
  35. }

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

  1. function practice_menu() {
  2. $items['practice/demo'] = array(
  3. 'title' => 'Demo',
  4. 'page callback' => 'drupal_get_form',
  5. // 第一个参数是表单标识practice_demo_from
  6. // 第二个参数是ultramen,第三个参数是25
  7. 'page_arguments' => array('practice_demo_from', 'ultramen', 25),
  8. 'access callback' => TRUE,
  9. );
  10. return $items;
  11. }
  12.  
  13. function practice_demo_form($form, &$form_state, $name, $age) {
  14. ... ...
  15. }
  16.  
  17. function drupal_get_form($form_id) {
  18. $form_state = array();
  19.  
  20. $args = func_get_args();
  21. // Remove $form_id from the arguments.
  22. array_shift($args);
  23. $form_state['build_info']['args'] = $args;
  24.  
  25. return drupal_build_form($form_id, $form_state);
  26. }

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

  1. function drupal_prepare_form($form_id, &$form, &$form_state) {
  2. ...
  3.  
  4. // Generate a new #build_id for this form, if none has been set already. The
  5. // form_build_id is used as key to cache a particular build of the form. For
  6. // multi-step forms, this allows the user to go back to an earlier build, make
  7. // changes, and re-submit.
  8. // @see drupal_build_form()
  9. // @see drupal_rebuild_form()
  10. // Drupal为每个表单定义一个唯一的form_build_id
  11. if (!isset($form['#build_id'])) {
  12. $form['#build_id'] = 'form-' . drupal_random_key();
  13. }
  14. // form_buikd_id会在浏览器表单中以hidden的形式出现
  15. $form['form_build_id'] = array(
  16. '#type' => 'hidden',
  17. '#value' => $form['#build_id'],
  18. '#id' => $form['#build_id'],
  19. '#name' => 'form_build_id',
  20. // Form processing and validation requires this value, so ensure the
  21. // submitted form value appears literally, regardless of custom #tree
  22. // and #parents being set elsewhere.
  23. '#parents' => array('form_build_id'),
  24. );
  25.  
  26. // 同form_build_id
  27. if (isset($form_id)) {
  28. $form['form_id'] = array(
  29. '#type' => 'hidden',
  30. '#value' => $form_id,
  31. '#id' => drupal_html_id("edit-$form_id"),
  32. // Form processing and validation requires this value, so ensure the
  33. // submitted form value appears literally, regardless of custom #tree
  34. // and #parents being set elsewhere.
  35. '#parents' => array('form_id'),
  36. );
  37. }
  38. if (!isset($form['#id'])) {
  39. $form['#id'] = drupal_html_id($form_id);
  40. }
  41.  
  42. ...
  43. }


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

  1. function drupal_build_form($form_id, &$form_state) {
  2. ...
  3.  
  4. // If the incoming input contains a form_build_id, we'll check the cache for a
  5. // copy of the form in question. If it's there, we don't have to rebuild the
  6. // form to proceed. In addition, if there is stored form_state data from a
  7. // previous step, we'll retrieve it so it can be passed on to the form
  8. // processing code.
  9. // 用$form_state['input']['form_id']标识表单,例如practice_demo_form
  10. // 用$form_state['input']['form_build_id']标识缓存
  11. $check_cache = isset($form_state['input']['form_id']) && $form_state['input']['form_id'] == $form_id && !empty($form_state['input']['form_build_id']);
  12. if ($check_cache) {
  13. $form = form_get_cache($form_state['input']['form_build_id'], $form_state);
  14. }
  15.  
  16. ...
  17. }
  18.  
  19. function drupal_process_form($form_id, &$form, &$form_state) {
  20. ...
  21.  
  22. // After processing the form, the form builder or a #process callback may
  23. // have set $form_state['cache'] to indicate that the form and form state
  24. // shall be cached. But the form may only be cached if the 'no_cache' property
  25. // is not set to TRUE. Only cache $form as it was prior to form_builder(),
  26. // because form_builder() must run for each request to accommodate new user
  27. // input. Rebuilt forms are not cached here, because drupal_rebuild_form()
  28. // already takes care of that.
  29. // 用$form_state['cache']和$form_state['no_cache']标识是否允许表单缓存
  30. if (!$form_state['rebuild'] && $form_state['cache'] && empty($form_state['no_cache'])) {
  31. form_set_cache($form['#build_id'], $unprocessed_form, $form_state);
  32. }
  33.  
  34. ...
  35. }

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

  1. function drupal_prepare_form($form_id, &$form, &$form_state) {
  2. ...
  3.  
  4. // Add a token, based on either #token or form_id, to any form displayed to
  5. // authenticated users. This ensures that any submitted form was actually
  6. // requested previously by the user and protects against cross site request
  7. // forgeries.
  8. // This does not apply to programmatically submitted forms. Furthermore, since
  9. // tokens are session-bound and forms displayed to anonymous users are very
  10. // likely cached, we cannot assign a token for them.
  11. // During installation, there is no $user yet.
  12. if (!empty($user->uid) && !$form_state['programmed']) {
  13. // Form constructors may explicitly set #token to FALSE when cross site
  14. // request forgery is irrelevant to the form, such as search forms.
  15. if (isset($form['#token']) && $form['#token'] === FALSE) {
  16. unset($form['#token']);
  17. }
  18. // Otherwise, generate a public token based on the form id.
  19. else {
  20. $form['#token'] = $form_id;
  21. // 将form token发送到客户端
  22. $form['form_token'] = array(
  23. '#id' => drupal_html_id('edit-' . $form_id . '-form-token'),
  24. '#type' => 'token',
  25. '#default_value' => drupal_get_token($form['#token']),
  26. // Form processing and validation requires this value, so ensure the
  27. // submitted form value appears literally, regardless of custom #tree
  28. // and #parents being set elsewhere.
  29. '#parents' => array('form_token'),
  30. );
  31. }
  32. }
  33.  
  34. ...
  35. }
  36.  
  37. function drupal_validate_form($form_id, &$form, &$form_state) {
  38. ...
  39.  
  40. // If the session token was set by drupal_prepare_form(), ensure that it
  41. // matches the current user's session.
  42. // $form['#token']是表单标识,例如practice_demo_form
  43. if (isset($form['#token'])) {
  44. if (!drupal_valid_token($form_state['values']['form_token'], $form['#token'])) {
  45. $path = current_path();
  46. $query = drupal_get_query_parameters();
  47. $url = url($path, array('query' => $query));
  48.  
  49. // Setting this error will cause the form to fail validation.
  50. 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)));
  51.  
  52. // Stop here and don't run any further validation handlers, because they
  53. // could invoke non-safe operations which opens the door for CSRF
  54. // vulnerabilities.
  55. $validated_forms[$form_id] = TRUE;
  56. return;
  57. }
  58. }
  59.  
  60. ...
  61. }

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

  1. function drupal_build_form($form_id, &$form_state) {
  2. ...
  3.  
  4. if (!isset($form_state['input'])) {
  5. $form_state['input'] = $form_state['method'] == 'get' ? $_GET : $_POST;
  6. }
  7.  
  8. ...
  9. }

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

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

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

  1. function practice_demo_form_submit($form, &$form_state) {
  2. $form_state['redirect'] = 'admin/people/expiration';
  3. // 等同 drupal_goto('admin/people/expiration');
  4. }
  5.  
  6. function drupal_redirect_form($form_state) {
  7. // Skip redirection for form submissions invoked via drupal_form_submit().
  8. if (!empty($form_state['programmed'])) {
  9. return;
  10. }
  11. // Skip redirection if rebuild is activated.
  12. if (!empty($form_state['rebuild'])) {
  13. return;
  14. }
  15. // Skip redirection if it was explicitly disallowed.
  16. if (!empty($form_state['no_redirect'])) {
  17. return;
  18. }
  19. // Only invoke drupal_goto() if redirect value was not set to FALSE.
  20. if (!isset($form_state['redirect']) || $form_state['redirect'] !== FALSE) {
  21. if (isset($form_state['redirect'])) {
  22. if (is_array($form_state['redirect'])) {
  23. call_user_func_array('drupal_goto', $form_state['redirect']);
  24. }
  25. else {
  26. // This function can be called from the installer, which guarantees
  27. // that $redirect will always be a string, so catch that case here
  28. // and use the appropriate redirect function.
  29. $function = drupal_installation_attempted() ? 'install_goto' : 'drupal_goto';
  30. $function($form_state['redirect']);
  31. }
  32. }
  33. drupal_goto(current_path(), array('query' => drupal_get_query_parameters()));
  34. }
  35. }

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

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

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

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

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

  1. // If the form was submitted by the browser rather than via Ajax, then it
  2. // can only have been triggered by a button, and we need to determine which
  3. // button within the constraints of how browsers provide this information.
  4. if (isset($element['#button_type'])) {
  5. // All buttons in the form need to be tracked for
  6. // form_state_values_clean() and for the form_builder() code that handles
  7. // a form submission containing no button information in $_POST.
  8. $form_state['buttons'][] = $element;
  9. if (_form_button_was_clicked($element, $form_state)) {
  10. $form_state['triggering_element'] = $element;
  11. }
  12. }
  13.  
  14. function system_element_info() {
  15. $types['submit'] = array(
  16. '#input' => TRUE,
  17. '#name' => 'op',
  18. '#button_type' => 'submit',
  19. '#executes_submit_callback' => TRUE,
  20. '#limit_validation_errors' => FALSE,
  21. '#process' => array('ajax_process_form'),
  22. '#theme_wrappers' => array('button'),
  23. );
  24. $types['button'] = array(
  25. '#input' => TRUE,
  26. '#name' => 'op',
  27. '#button_type' => 'submit',
  28. '#executes_submit_callback' => FALSE,
  29. '#limit_validation_errors' => FALSE,
  30. '#process' => array('ajax_process_form'),
  31. '#theme_wrappers' => array('button'),
  32. );
  33. }

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

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

  1. function drupal_prepare_form($form_id, &$form, &$form_state) {
  2. ...
  3.  
  4. // If no #theme has been set, automatically apply theme suggestions.
  5. // theme_form() itself is in #theme_wrappers and not #theme. Therefore, the
  6. // #theme function only has to care for rendering the inner form elements,
  7. // not the form itself.
  8. if (!isset($form['#theme'])) {
  9. $form['#theme'] = array($form_id); // 表单默认主题
  10. if (isset($form_state['build_info']['base_form_id'])) {
  11. $form['#theme'][] = $form_state['build_info']['base_form_id'];
  12. }
  13. }
  14.  
  15. ...
  16. }

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

  1. function drupal_prepare_form($form_id, &$form, &$form_state) {
  2. ...
  3.  
  4. // Invoke hook_form_alter(), hook_form_BASE_FORM_ID_alter(), and
  5. // hook_form_FORM_ID_alter() implementations.
  6. $hooks = array('form'); // 表单通用钩子
  7. if (isset($form_state['build_info']['base_form_id'])) {
  8. $hooks[] = 'form_' . $form_state['build_info']['base_form_id'];
  9. }
  10. $hooks[] = 'form_' . $form_id; // 表单专用钩子
  11. drupal_alter($hooks, $form, $form_state, $form_id);
  12.  
  13. ...
  14. }

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. 使用docker exec命令

    这个命令使用exit命令后,不会退出后台,一般使用这个命令,使用方法如下   docker exec -it db3 /bin/sh 或者 docker exec -it d48b21a7e439 / ...

  2. 解决“listView点击一个Item,另外几个Item也跟着改变”的问题

    如图所看到的: 我点击Item,右边的checkBox就会对应的变化.可是当我第一次做的时候.点击第一个Item,右边的checkBox变为绿色,可是当我listView往下拉的时候,发现以下也有是绿 ...

  3. 如何记录linux终端下的操作日志

    如何记录linux终端下的操作日志 在linux终端下,为方便检查操作中可能出现的错误,以及避免屏幕滚屏的限制,我们可以把操作日志记录下来.常用的工具有 screen,script,以及tee等,通过 ...

  4. 论文神器Latex30分钟快速入门教程-只需9步向学神看齐

    小E说:工欲善其事,必先利其器.立志做个安静的美学霸的你,学会Latex,一定能使你的论文写作事半功倍. 1.LaTeX软件的安装和使用 方法A(自助):在MikTeX的官网下载免费的MikTeX编译 ...

  5. jenkins error: "no valid crumb was included in the request"

    一.问题描述(Problem Description): 在jenkins中创建新任务时候选择“拷贝已存在任务”,点击OK,跳转到下一步时候弹出如下错误信息:"No valid crumb ...

  6. 混沌数学之二维logistic模型

    上一节讲了logistic混沌模型,这一节对其扩充一下讲二维 Logistic映射.它起着从一维到高维的衔接作用,对二维映射中混沌现象的研究有助于认识和预测更复杂的高维动力系统的性态.通过构造一次藕合 ...

  7. IIS HTTP 错误 404.17 - Not Found 解决方法

    错误提示如下图: 出现这种情况的原因通常是因为先安装了Framework,后安装的IIS: 运行cmd,输入:   C:\Windows\Microsoft.NET\Framework\V4.0.30 ...

  8. leetcode414-第三大的数

    给定一个非空数组,返回此数组中第三大的数.如果不存在,则返回数组中最大的数.要求算法时间复杂度必须是O(n). 示例 1: 输入: [3, 2, 1] 输出: 1 解释: 第三大的数是 1. 示例 2 ...

  9. 20个令人惊叹的音乐应用程序UI,值得收藏

    我们无法想象世界上没有手机.他们已经成为日常生活中不可缺失的一部分.今天的手机可以让你不只是拨打电话和发送消息.它可以让你浏览网页空间,拍照,看书,听音乐等等. 回顾一下互联网,你会看到不同的音乐AP ...

  10. 安装Oracle之后解决掉的问题分享

    TNS-03505: 无法解析名称                                                            在测试tnsping的时候始终显示这么个问题. ...