配置重写系统
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 Warn 或 Config 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 以启用基于语言的配置覆盖 中添加。