Drupal 10:通过示例和用例将第三方设置添加到配置实体

Drupal 10:向Drupal配置实体添加第三方设置

注意:这篇文章发布已超过两年,因此其中包含的信息可能已过时。如果您发现有问题,请留下评论,我公司会尽力更正。

2023年9月3日 - 阅读时长20分钟

Drupal具有强大的模块化系统,通过插件、实体和配置,能为网站添加各式各样的扩展功能。

借助Drupal的模块化配置系统,还可以把自定义配置注入到现有的配置实体中。这样做既能扩展现有实体的功能,又能让所有自定义设置都保存在系统配置里。而且,能在不改变实体现有配置架构的情况下增强任何配置实体,毕竟更改配置架构可能会对其他模块产生连锁反应。

要是您处理过Drupal配置,可能在配置中看到过 third_party_settings 块。核心的快捷方式(Shortcut)模块就是个很好的例子,使用标准安装配置文件安装网站时,它会在已安装的主题中添加第三方设置。比如,Claro主题安装后会有如下默认配置。

third_party_settings:
  shortcut:
    module_link: true

这使得快捷方式模块能检测在页面使用该主题时是否应显示链接。这个设置无需单独存储在一个配置实体中(若单独存储,导出时会保存为一个单独的文件),它可以直接作为主题配置的一部分进行加载。

在本文中,我公司将介绍如何使用额外设置向现有配置实体添加自定义配置项,同时也会介绍一些使用此技术的实际案例。

一、创建配置架构文件

第一步是创建一个配置架构文件,这或许是整个过程中最具挑战性的部分。其实它是一个“小型”配置架构,命名空间为“third_party”,用于扩展想要增强的现有配置命名空间。

架构名称应采用以下格式:

<配置实体名称>.third_party.<模块名称>:
  type: config_entity
  label "第三方设置"
  mapping:
    <你的配置架构>

为了便于说明,我公司将向内容类型(即“节点”配置实体),特别是“文章”内容类型添加第三方设置。因为“文章”内容类型在Drupal的标准安装配置文件中存在,所以大多数网站都会有该类型。节点内容实体的架构定义为“node.type.*”,这也是我们添加第三方设置的起点。

以下架构定义添加到一个名为“third_party_settings_example”的自定义模块中,该文件位于config/schema目录下,文件名为 third_party_settings_example.schema.yml,用于存储模块的所有架构设置。

node.type.*.third_party.third_party_settings_example:
  type: config_entity
  label: "示例设置"
  mapping:
    text_field:
      type: text
      label: "文本字段"

这将为内容配置实体添加一个包含单个文本字段的第三方设置。

完成上述步骤后,就可以开始使用这些设置来增强内容配置实体了。

二、注入设置

由于要向节点类型实体添加架构设置,所以需要使用hook_form_FOMR_ID_alter()钩子来修改节点类型表单。访问“/admin/structure/types/manage/article”路径时看到的就是这个表单,它用于配置预览设置、发布选项、显示设置和菜单选项等。

以下是该钩子的代码示例,应放在.module文件中。

/**
 * 实现hook_form_FORM_ID_alter()钩子。
 */
function third_party_settings_example_form_node_type_edit_form_alter(&$form, \Drupal\Core\Form\FormStateInterface $form_state, $form_id) {
  $entity = $form_state->getFormObject()->getEntity();
  if ($entity && $entity->id() === 'article') {
    $exampleTextField = $entity->getThirdPartySetting('third_party_settings_example', 'text_field');

    $form['example_settings'] = [
      '#type' => 'fieldset',
      '#tree' => FALSE,
      '#title' => t('第三方示例设置'),
      '#description' => t('仅用于示例目的。'),
    ];

    $form['example_settings']['text_field'] = [
      '#type' => 'textfield',
      '#title' => t('示例文本字段'),
      '#default_value' => $exampleTextField,
    ];

    $form['#entity_builders'][] = 'third_party_settings_example_node_form_builder';
  }
}

上述代码主要做了以下操作:

  • 确保处理的是“文章”内容实体类型。
  • 获取我们模块中“text_field”的现有第三方设置。
  • 在内容实体表单中添加额外字段,以便修改该设置。

最后一步是将一个名为“third_party_settings_example_node_form_builder”的自定义回调函数注入到 '#entity_builders' 表单设置中,该回调函数将在表单保存时触发。此自定义回调函数用于将添加的第三方设置保存到配置中,如果设置为空,则清除该设置。

/**
 * 节点:文章实体的实体构建器。
 */
function third_party_settings_example_node_form_builder($entity_type, $entity, &$form, \Drupal\Core\Form\FormStateInterface $form_state) {
  if ($form_state->getValue('text_field')) {
    $textFieldValue = $form_state->getValue('text_field');
    $entity->setThirdPartySetting('third_party_settings_example', 'text_field', $textFieldValue);
    return;
  }
  $entity->unsetThirdPartySetting('third_party_settings_example', 'text_field');
}

完成上述操作后,就可以编辑“文章”内容类型配置页面,并将一些文本注入到配置实体中。

实际上,获取和操作第三方设置涉及多个方法,这些方法都在Drupal\Core\Config\Entity\ThirdPartySettingsInterface接口中定义。由于核心的Drupal\Core\Config\Entity\ConfigEntityInterface接口继承了该接口,并且该接口在Drupal中广泛使用,所以许多不同的配置实体都可以使用这些方法。

  • setThirdPartySetting($module, $key, $value) - 设置第三方设置的值。
  • getThirdPartySetting($module, $key, $default = NULL) - 获取第三方设置的值。
  • getThirdPartySettings($module) - 获取指定模块的所有第三方设置。
  • unsetThirdPartySetting($module, $key) - 取消设置第三方设置。
  • getThirdPartyProviders() - 获取存储信息的第三方列表。

至此,系统已能正常工作。接下来,看看在配置中包含此设置时导出配置会发生什么。

三、导出第三方配置

当导出增强后的配置时,Drupal会检测到第三方设置的存在,并将其注入到生成的配置架构中。

以下是“文章”内容类型的新配置文件(存储在node.type.article.yml文件中)。

uuid: ebc525b7-eeb3-4a86-9113-4a9019744a64
langcode: en
status: true
dependencies:
  module:
    - menu_ui
    - third_party_settings_example
third_party_settings:
  menu_ui:
    available_menus:
      - main
    parent: 'main:'
  third_party_settings_example:
    text_field: '一些示例文本。'
_core:
  default_config_hash: AeW1SEDgb1OTQACAWGhzvMknMYAJlcZu0jljfeU3oso
name: 文章
type: article
description: '使用 <em>文章</em> 发布时效性内容,如新闻、新闻稿或博客文章。'
help: ''
new_revision: true
preview_mode: 1
display_submitted: true

从这个示例可以看出,已经使用菜单用户界面(Menu UI)模块向third_party_settings部分注入了一些配置。我们的模块通过添加自己的配置对该部分进行了更新。

四、使用方法

现在已经为“文章”内容实体添加了自定义配置,那么如何使用这些配置呢?

由于此更改影响的是配置实体,所以需要从内容实体中提取这些配置才能使用。可以通过内容实体来完成此操作,因为它存储了定义它的配置实体的相关信息。

例如,以下对hook_preprocess_HOOK()的实现将向“文章”内容实体注入一些内容。从与内容项关联的配置实体中提取我们模块的第三方设置,并将其注入到主题内容中。

/**
 * 实现hook_preprocess_HOOK()钩子。
 */
function third_party_settings_example_preprocess_node(&$variables) {
  /** @var \Drupal\node\NodeInterface $node */
  $node = $variables['elements']['#node'];
  if ($node->bundle() == 'article') {
    $additionalSetting = $node->type->entity->getThirdPartySetting('third_party_settings_example', 'text_field');

    $variables['content']['additional_title'] = [
      '#markup' => $additionalSetting,
    ];
  }
}

完成上述操作后,会看到添加到文章配置实体中的文本显示在所有文章上。这虽然是个不太实用的示例,但展示了该技术的实际应用。虽然文本字段的设置只添加到了一处,但却改变了网站上所有文章的显示方式。

需要注意的是,这里添加对“文章”内容实体的检查只是为了便于示例说明,并非必要操作。可以在不检查文章内容类型的情况下以相同的方式构建此模块,此时所有内容实体都将获得此设置,并能够像这样注入文本。

五、使用额外设置的模块

以下是几个使用第三方设置将配置存储在其他配置实体中的模块。如果想了解该技术的实际应用,这些模块是很好的起点。

一、调度器(Scheduler)

调度器模块是一个常用的Drupal模块,它允许在指定时间发布节点。该模块在第三方设置中为每个内容类型存储一些配置,同时也允许以相同的方式配置媒体项、商业产品和分类术语。

二、最大长度(MaxLength)

最大长度模块允许为文本字段设置字符限制,同时还会显示字符计数。该模块的设置会注入到实体的字段配置中,所以可以添加到网站的任何文本字段上。

三、允许格式(Allowed Formats)

允许格式模块能使网站隐藏文本字段下方常出现的关于文本输入格式的复杂信息。这是另一个可以添加到网站任何文本字段的字段级配置项。由于其架构设置较为复杂,值得深入研究字段级第三方设置。

六、总结

本文讨论的第三方设置技术有很多应用场景,特别是当您希望将设置与网站配置一起导出时。少量的配置项可以轻松注入到任何配置实体中,它们会与您的网站一起存在,并且可以像其他配置项一样进行部署。在Drupal模块开发Drupal开发,甚至Drupal升级过程中,第三方设置技术都可能发挥重要作用。并且随着Drupal 11的不断发展,该技术也会不断得到优化与完善。

并非 所有 配置实体都支持第三方设置,但如果某个类实现了Drupal\Core\Config\Entity\ThirdPartySettingsInterface接口,那么它应该具备此功能。

虽然这项技术很有用,但不必每次都使用第三方设置系统。实际上,如果自定义配置会影响多种不同类型的实体,或者包含大量自定义配置,那么可能应该考虑将配置存储在单独的配置文件中。尽管第三方设置在配置实体内有单独的命名空间,但仍应尊重父实体,尽量避免用大量数据对其进行修改。

另外,请记住,第三方设置是与配置实体相关联的,而非 内容实体。这意味着添加的任何设置都会影响该内容实体的所有实例。如果想为单个实体(如某些文章或分类术语)添加自定义配置,那么可能应该使用字段API来实现。

在最近的一个项目中,我公司利用这项技术为消息模板配置实体(来自消息模块)添加了额外设置。我公司需要一种方法为一些消息实体添加额外数据,同时又不想为每种消息模板添加单独的字段,第三方设置正好满足了这一需求。如果消息模板配置实体中存在额外数据,在创建消息时会执行额外操作;否则,将正常创建消息,不执行任何额外步骤。这些额外配置随后可以使用Drupal配置工作流轻松导出和部署。

本文中使用的所有代码都可以在GitHub上的一个示例第三方设置Drupal模块中找到。您可以自由使用该模块来创建自己的第三方设置。

此外,Drupal.org上有一个关于此技术的文档页面,如果您想了解更多相关信息,值得一读。