logo

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

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

演示 EBT 模块 下载 EBT 模块

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

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

演示 EPT 模块 滚动

滚动
03/10/2025, by Ivan

Drupal 8 的配置系统以统一的方式处理配置。默认情况下,Drupal 将配置数据存储在数据库中,但它们可以导出为 YAML 文件,从而能够通过版本控制来管理配置。然而,在某些情况下需要为特定目的覆盖配置值。在 Drupal 7 中,有一个全局变量 global $conf,它通常在 settings.php 中填充条件覆盖的配置值。该系统的主要缺点是覆盖会进入实际的配置中。当保存包含覆盖值的配置表单时,条件覆盖就会写入实际的配置存储。

Drupal 8 引入了配置覆盖系统,该系统:

  • 将这些覆盖作为临时层叠在标准配置值之上
  • 不将覆盖值用于配置表单
  • 可以与其他配置文件一起存储覆盖,从而支持预生产和版本控制(例如,语言覆盖,见下文)。

Drupal 7 中的全局变量 $conf 在 Drupal 8 中重命名为 $config,并在默认的配置系统中启用。

全局覆盖

Drupal 8 保留了使用 global $config 的功能。配置系统通过 Drupal\Core\Config\ConfigFactory::get() 的实现合并这些覆盖值。当您从配置中获取值时,全局变量 $config 会有机会更改返回值:

// 获取系统站点维护消息文本。该值可能会被
// 来自 global $config 的默认值覆盖(以及翻译,见下文)。
$message = \Drupal::config('system.maintenance')->get('message');

要在全局 $config 中覆盖配置值(例如 settings.php 中),请引用它们的配置键:

$config['system.maintenance']['message'] = 'Sorry, our site is down now.';

对于嵌套值,使用嵌套数组键:

$config['system.performance']['css']['preprocess'] = 0;

在 settings.php 之外使用 $config 时,请使用之前的 global $config;

您可以通过以下方式之一确定可用的配置变量:

  • 使用「配置管理器」模块的用户界面查看,路径:/admin/config/development/configuration/single/export
  • 直接检查站点配置的 YML 文件
  • 或者通过 drush 查询。
drush config-list
drush config-get system.performance --include-overridden

请注意,通过 settings.php 中的 $config 覆盖的值 无法在 Drupal 管理界面中查看(直到 #2408549:配置表单中未显示覆盖值 修复之前,您可以使用 Configuration Override WarnConfig Override Inspector),也无法通过 drush 检查(除非添加 --include-overridden 标志)。管理界面显示的是配置中保存的值,这样您就可以在没有覆盖的其他环境中进行更改。

一个出于安全考虑覆盖 API 密钥的示例参见:覆盖 Commerce Gateway 的 API 密钥

避免覆盖

您可以在没有覆盖的情况下获取配置,以访问原始配置值(例如,即使全局覆盖也禁用)。这对于编写配置表单特别有用。使用无覆盖环境对表单很重要,以避免将覆盖值写入配置中。如果您的代码在多语言环境中使用,这尤为有用,因为配置值通常被覆盖为翻译。

以下是获取带有覆盖和不带覆盖的配置示例:

// 获取带有覆盖的站点名称。
$site_name = \Drupal::config('system.site')->get('name');

// 获取不带覆盖的站点名称。
$site_name = \Drupal::config('system.site')->getOriginal('name', FALSE);
// 注意,可变配置始终不包含覆盖。
$site_name = \Drupal::configFactory()->getEditable('system.site')->get('name');

您还可以通过 config.storage 服务直接访问配置存储,它实现了 StorageInterface::read()。但这种方式很少是访问配置的正确方法。

语言覆盖

例如,为用户发送电子邮件时,配置必须使用用户的语言,而不是页面的语言。记住之前使用的语言,根据用户设置正确的语言,基于配置执行操作,然后再恢复语言:

// 加载 language_manager 服务
$language_manager = \Drupal::service('language_manager');

// 获取目标语言对象
$langcode = $account->getPreferredLangcode();
$language = $language_manager->getLanguage($langcode);

// 在此操作前记住原始语言
$original_language = $language_manager->getConfigOverrideLanguage();
// 在配置工厂上设置目标翻译语言
$language_manager->setConfigOverrideLanguage($language);

$mail_config  = \Drupal::config('user.mail');

// 现在基于正确语言的 $mail_config 发送邮件

// 将配置语言恢复为原始语言
$language_manager->setConfigOverrideLanguage($original_language);

语言覆盖系统同样使用配置存储保存覆盖(不同于基于 $config 的全局覆盖)。语言覆盖存储在以其主文件命名的文件中。例如,对于上面的 user.mail 配置文件,如果有语言特定的覆盖文件,它将命名为 language.config.$langcode.user.mail。覆盖文件的命名格式为 language.config. 前缀 + 语言代码 + 原始配置键。与普通配置文件一起存储可实现逐步翻译,并可像基础配置一样修改。

这些语言覆盖文件是如何生成的?Locale 模块通过系统事件生成基于 配置架构信息 的翻译文件。核心的配置翻译模块提供了一个通用用户界面,用于基于架构翻译配置。它既适用于内置配置,也适用于自定义配置,并与相同的语言覆盖文件一起工作。

模块提供的覆盖

模块也可以提供自己的配置覆盖。虽然 Drupal 核心支持全局覆盖和语言覆盖,但在其他情况下也可能需要覆盖,例如基于用户角色、上下文、域或群组等。模块可以定义自己的条件来应用覆盖。

当 ConfigFactory 收集模块提供的覆盖时,它会调用任何带有 config.factory.override 标签的服务:

config_example.services.yml
services:
  config_example.overrider:
    class: Drupal\config_example\Config\ConfigExampleOverrides
    tags:
      - {name: config.factory.override, priority: 5}

设置订阅者优先级以确定覆盖的优先顺序。相同配置名的情况下,优先级高的覆盖将生效。

src/Config/ConfigExampleOverrides.php
namespace Drupal\config_example\Config;

use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Config\ConfigFactoryOverrideInterface;
use Drupal\Core\Config\StorageInterface;

/**
 * 示例配置覆盖。
 */
class ConfigExampleOverrides implements ConfigFactoryOverrideInterface {

  /**
   * {@inheritdoc}
   */
  public function loadOverrides($names) {
    $overrides = array();
    if (in_array('system.site', $names)) {
      $overrides['system.site'] = ['name' => 'Overridden site name!'];
    }
    return $overrides;
  }

  /**
   * {@inheritdoc}
   */
  public function getCacheSuffix() {
    return 'ConfigExampleOverrider';
  }
  
  /**
   * {@inheritdoc}
   */
  public function getCacheableMetadata($name) {
    return new CacheableMetadata();
  }

  /**
   * {@inheritdoc}
   */
  public function createConfigObject($name, $collection = StorageInterface::DEFAULT_COLLECTION) {
    return NULL;
  }

}

配置覆盖系统在三个不同层级上工作:language、modules 和 settings.php,其中 settings.php 拥有最高优先级。settings.php 文件中的覆盖优先于模块提供的值,模块提供的覆盖优先于语言覆盖。模块覆盖的事件订阅者优先级仅影响模块之间的覆盖优先级,无法高于语言或 settings.php 覆盖。

请注意,Drupal 核心中的配置表单不会使用覆盖值。在上面的模块覆盖示例中,您在 /admin/config/system/site-information 中不会看到“Overridden site name!”。

需要强调的是,如果您在覆盖中读取原始配置值(例如用于比较或合并),必须从 \Drupal::configFactory 加载,而不是 \Drupal::config,以避免循环调用:

$original = \Drupal::configFactory()->getEditable('system.site')->getOriginal('name', FALSE);

更多参考信息

配置覆盖系统的最新/当前形式在 #2098119: 用内置的本地化支持和基于单一事件的覆盖替换配置上下文系统 中被引入。

历史/已废弃信息请参见 #1646580: 为本地化配置实现配置事件和监听器及存储上下文,以及 #1763640: 引入配置上下文以提供原始配置和其他覆盖的访问,语言特定的覆盖后来在 #2020361: 创建 LanguageConfigContext 以启用基于语言的配置覆盖 中添加。