logo

额外区块类型 (EBT) - 全新的布局构建器体验❗

额外区块类型 (EBT) - 样式化、可定制的区块类型:幻灯片、标签页、卡片、手风琴等更多类型。内置背景、DOM Box、JavaScript 插件的设置。立即体验布局构建的未来。

演示 EBT 模块 下载 EBT 模块

❗额外段落类型 (EPT) - 全新的 Paragraphs 体验

额外段落类型 (EPT) - 类似的基于 Paragraph 的模块集合。

演示 EPT 模块 滚动

滚动

9.8. 在 Drupal 中使用表单。通过代码添加配置表单。

17/10/2025, by Ivan

在本教程中,我们将处理 Drupal Form API,并为模块创建一个设置表单。我们已经创建了用于显示页面和块的模块,现在让我们创建一个配置表单,用于存储连接到某个条件服务的数据。假设我们需要在站点上存储 API 密钥和 API 客户端 ID,例如用于 Google Maps API。

代码示例可以在 GitHub 上查看:
https://github.com/levmyshkin/drupalbook8

我们可以将这些数据存储在 settings.php 中,并将这些设置添加到 git 中。但这样做不安全,存储对服务的访问权限最好存在数据库中。

Settings form

现在让我们为表单添加另一个路由:

modules/custom/drupalbook/drupalbook.routing.yml

drupalbook.settings:
  path: '/admin/structure/drupalbook/settings'
  defaults:
    _form: '\Drupal\drupalbook\Form\DrupalbookSettingsForm'
    _title: 'DrupalBook Settings form'
  requirements:
    _permission: 'administer site configuration'

与之前的路由不同,在 defaults 中我们指定的是 _form,而不是 _controller。原因是我们将为表单创建一个表单类,而不是控制器类。现在让我们为表单类创建一个文件:

modules/custom/drupalbook/src/Form/DrupalbookSettingsForm.php

你需要在 src 文件夹中为表单创建一个单独的 Form 文件夹。这允许你将模块代码分离到不同的文件夹中,并且可以根据文件夹名称轻松找到所需的代码。

添加以下表单代码,我们将分析每个代码块以及它的工作原理:

<?php

namespace Drupal\drupalbook\Form;
 
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;
 
/**
 * Configure example settings for this site.
 */
class DrupalbookSettingsForm extends ConfigFormBase {
  /**
   * {@inheritdoc}
   */
  public function getFormId() {
    return 'drupalbook_admin_settings';
  }
 
  /**
   * {@inheritdoc}
   */
  protected function getEditableConfigNames() {
    return [
      'drupalbook.settings',
    ];
  }
 
  /**
   * {@inheritdoc}
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    $config = $this->config('drupalbook.settings');
 
    $form['drupalbook_api_key'] = array(
      '#type' => 'textfield',
      '#title' => $this->t('API Key'),
      '#default_value' => $config->get('drupalbook_api_key'),
    );
 
    $form['drupalbook_api_client_id'] = array(
      '#type' => 'textfield',
      '#title' => $this->t('API Client ID'),
      '#default_value' => $config->get('drupalbook_api_client_id'),
    );
 
    return parent::buildForm($form, $form_state);
  }
 
  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    // Retrieve the configuration
    $this->configFactory->getEditable('drupalbook.settings')
      // Set the submitted configuration setting
      ->set('drupalbook_api_key', $form_state->getValue('drupalbook_api_key'))
      // You can set multiple configurations at once by making
      // multiple calls to set()
      ->set('drupalbook_api_client_id', $form_state->getValue('drupalbook_api_client_id'))
      ->save();
 
    parent::submitForm($form, $form_state);
  }
}

我们已经了解了命名空间(namespace)和使用(use)操作符,以及 Drupal 如何自动连接仅需要的类:

\drupalbook\Form;
 
use Drupal\Core\Form\ConfigFormBase;
use Drupal\Core\Form\FormStateInterface;

为了创建配置表单,你需要继承 ConfigFormBase 类,ConfigFormBase 表示你将把数据从表单保存到配置中。

extends ConfigFormBase {

接下来,指定表单 ID,它必须对每个表单都是唯一的。如果你将表单 ID 以模块名称开头,确保你的 ID 是唯一的:

function getFormId() {
  return 'drupalbook_admin_settings';
}

指定我们将存储数据的配置组:

function getEditableConfigNames() {
  return [
    'drupalbook.settings',
  ];
}

现在,让我们来看一下我们如何创建表单字段。你可以通过阅读文档来评估 Form API 的功能,以及你可以显示哪些字段:

https://api.drupal.org/api/drupal/elements/8.5.x

我们目前仅使用了 textfield 字段:

https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Render%21Element%21Textfield.php/class/Textfield/8.5.x

但是你经常需要处理下拉列表:

https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Render%21Element%21Select.php/class/Select/8.5.x

复选框和单选按钮:

https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Render%21Element%21Checkboxes.php/class/Checkboxes/8.5.x

https://api.drupal.org/api/drupal/core%21lib%21Drupal%21Core%21Render%21Element%21Radios.php/class/Radios/8.5.x

尝试将你的字段添加到表单中,而不限于文本字段。

function buildForm(array $form, FormStateInterface $form_state) {
  $config = $this->config('drupalbook.settings');

我们在 $form 中构建表单数组,然后从中生成 HTML。变量 $form_state 存储表单提交的所有数据,这里包括所有字段的值、表单 ID、用于防止自动提交的 CSRF token。$form_state 还允许你在多步表单的各个步骤之间传递数据,我们将在接下来的课程中使用这个功能。每次表单通过 AJAX 提交时,表单都会被重新构建,而 $form_state 使你能够构建提交按钮点击前的表单副本。如果表单因为错误没有提交,例如某个字段未填,所有字段的值都会保存到 $form_state 中。

在这里,我们还从配置中加载数据。如果你已经在 drupalbook.settings 中保存了一些数据,那么 $config 就不再是空的,这将允许你通过 #default_value 为每个文本字段设置当前值,并使用 get() 方法从配置中获取数据。

['drupalbook_api_key'] = array(
  '#type' => 'textfield',
  '#title' => $this->t('API Key'),
  '#default_value' => $config->get('drupalbook_api_key'),
);
 
$form['drupalbook_api_client_id'] = array(
  '#type' => 'textfield',
  '#title' => $this->t('API Client ID'),
  '#default_value' => $config->get('drupalbook_api_client_id'),
);

在方法结束时,我们返回 $form 和 $form_state,以便表单能够被构建。

::buildForm($form, $form_state);

接下来是 submitForm() 方法,它会在表单提交且没有发生错误时触发。如果你确实有一个必填字段未填写,并且 Drupal 抛出一个错误,那么 submitForm 将不会执行。如果你想检查提交的数据值,那么你需要使用 validateForm(),即使表单中有错误,validate 也会生效,使用 validate 你可以取消表单的提交,并且如果数据不符合你的要求,可以引发错误。我们将在接下来的表单教程中覆盖验证功能。

在 submitForm() 方法中,我们遍历所有字段,收集它们的值并更新 drupalbook.settings 配置:

function submitForm(array &$form, FormStateInterface $form_state) {
  // Retrieve the configuration
  $this->configFactory->getEditable('drupalbook.settings')
    // Set the submitted configuration setting
    ->set('drupalbook_api_key', $form_state->getValue('drupalbook_api_key'))
    // You can set multiple configurations at once by making
    // multiple calls to set()
    ->set('drupalbook_api_client_id', $form_state->getValue('drupalbook_api_client_id'))
    ->save();
 
  parent::submitForm($form, $form_state);
}

我们还调用了父方法 submitForm,它会显示表单提交成功的消息。你可以注释掉这行并写自己的消息:

//parent::submitForm($form, $form_state);
drupal_set_message($this->t('My Cool Form have been saved!'));

记得清理缓存以应用你的路由。现在你可以尝试你的表单了。当你需要加载 API 密钥时,可以使用以下代码:

$config = \Drupal::config('example.settings');
$api_key =$config->get('drupalbook_api_key');
$api_client_id = $config->get('drupalbook_api_client_id');

这段代码将在任何模块或预处理函数中工作,因为 Drupal 中有一个统一的配置系统。

这就是本节关于表单的内容,接下来的课程我们将分析如何制作多步表单。

代码示例可以在 GitHub 上查看:
https://github.com/levmyshkin/drupalbook8