• Drupal Form问题汇总


    问:如何校验和提交表单?
    答: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);
    
      ...
    }
  • 相关阅读:
    Win7 配置Android开发环境 狼人:
    Windows Phone 7 Tips (3) 狼人:
    Windows Phone 7 Tips (1) 狼人:
    探索移动Web网页编码设计 狼人:
    初探AIR for Android开发 狼人:
    Android设计趋势分析10则 狼人:
    Android与服务器端数据交互 狼人:
    Android UI基本测验:线性布局 狼人:
    Android用户界面设计:线性布局 狼人:
    Eclipse开发Android应用程序入门:重装上阵 狼人:
  • 原文地址:https://www.cnblogs.com/eastson/p/3727601.html
Copyright © 2020-2023  润新知