如何更新模块的配置?


33

我正在Drupal 8中构建一个自定义模块。它包含一些YAML配置文件。

在开发过程中,我需要更改并添加到配置中,例如,将另一个字段添加到我的自定义实体中。

目前,我发现让Drupal注意到更改的唯一方法是卸载模块并重新安装。

有没有办法让Drupal检查模块提供的配置文件是否与活动配置相同,如果不相同,则更新活动配置?如何处理模块更新?在D7 hook_update_N中将使用PHP添加字段,但是看起来这应该由D8中的CM处理吗?

更新模块中的yml文件后我尝试过的事情:

  1. drush cr,配置同步。

  2. 手动将所有更新的配置文件复制到sites/default/files/config_XXX/staging/-但这会出现此错误“无法导入暂存的配置,因为它源自与该站点不同的站点。您只能在此站点的克隆实例之间同步配置。”

  3. 使用配置管理器手动导入文件。这可行,但是显然必须有一种更自动的方法。

  4. [EDIT]手动使用config_update模块检查更改并“还原”到模块的配置。同样,这是手动的。

编辑:管理配置-做和不做

不要

尝试通过更改模块的config / install目录中的文件来更改站点上的活动配置。这将不起作用,因为Drupal仅在安装模块时从该目录读取。

...但是将会发生变化,除非模块绑定到他们在第一个发行版本中所需的任何配置,并且可能永远不会更新或添加配置。

提前致谢。


我认为以前曾询问过非常类似的内容(现在无法完全找到它),并且我认为答案是仅在安装时参考默认配置,因此重新安装是必经之路。不过不要引用我的话:)
Clive

1
'k,但是如何更新模块?允许模块在D8中获取更新,对吧;-)?模块必须有一种方式(la config_update),说“ Drupal!我现在需要这个额外的配置,请看一下并合并进去。”
artfulrobot

Configuration Update Manager可以完成这项工作,但是我同意应该有一种本机的方式来执行此操作。东西hook_update_N我会承担,但我不知道是什么
克莱夫

2
哇,我认为答案可能最终是“你做不到”!从来没有看到那来!回到hook_update_N。关于Drupal 8的优秀文章,适用于小型站点(以及第2部分)。在D8中,“站点拥有其配置,而不是模块”
artfulrobot

我想补充一点,一个很好的用例是多站点安装,您要在其中共享特定的大型配置而不是全部共享并部署它。这些可能包括自定义模块。对于单个站点,它只是配置导出/导入,而对多站点则不是那么简单。
Ambidex

Answers:


24

如原始问题和后续注释中所述,有多种contrib模块和手动方法可以完成此任务。

自动执行或以自定义方式执行此操作,我认为hook_update_N()仍然可能是最可行的选择。

例如,这是从Head 2 Head更新system.site以设置的示例default_langcode

  $config_factory = \Drupal::configFactory();
  $langcode = $config_factory->get('system.site')->get('langcode');
  $config_factory->getEditable('system.site')->set('default_langcode', $langcode)->save();

您还可以读入config(仅建议添加新配置,而不必更新或覆盖可能已自定义的配置):

  $source = new FileStorage($path);
  /** @var \Drupal\Core\Config\StorageInterface $active_storage */
  $active_storage = \Drupal::service('config.storage');
  $active_storage->write($name, $source->read($name));

文件$path的绝对路径在哪里my_config.foo.yml


1
当我采用第二种方法时,将配置写入Drupal,但是即使将其导出到config目录也没有得到UUID。这导致我遇到一个问题,在该问题中我使用自定义视图进行了尝试。“视图”概述页面返回了一个致命错误,因为Config实体的uuid不可用。
塞巴斯蒂安

9

当我也着手解决这个问题,但并没有真正找到适合我的情况的正确答案时,我想添加另一个答案。

请注意:前面有反模式!

用例

在开发项目时,我们会不断使用新的配置更新来更新测试/接受环境。以一个简单的虚拟新闻模块为例,我们想向该模块添加一个内容类型,并将其部署到我们的接受环境中。经过审查,我们得出结论,缺少一些字段以及其他与配置相关的内容。因为我们知道接受环境不会在config中更新,所以我们实际上只想从模块中重新加载整个配置,同时添加新功能,而不会因导入每个更改的.yml文件而受到困扰。

在开发多站点时,我们仅需要模块中的配置。对于单个站点,我们通常只使用导出的站点配置,而无需执行下一步。

完全重新导入配置(反模式!)

我们发现使用ConfigInstaller服务,我们可以再次从特定模块重新导入完整的配置。

// Implement in a update_N hook. 
\Drupal::service('config.installer')->installDefaultConfig('module', $module);

请谨慎使用!

我想补充一点,这将覆盖环境中已更改的所有活动内容。因此,仅在确定可以安全覆盖活动配置时才使用此解决方案。我们永远不会在生产环境中使用它,而只会在早期开发中使用。

在开始考虑此方法之前,请先尝试@jhedstrom的解决方案。


9

我在GitHub上找到了这个Gist,它使用drush还原/重新加载给定模块的配置:

drush cim -y --partial --source=modules/path/to/module/config/install/

2

根据我的评论:如何更新模块的配置?

当我采用第二种方法时,将配置写入Drupal,但是即使将其导出到config目录也没有得到UUID。这导致我遇到一个问题,在该问题中我使用自定义视图进行了尝试。“视图”概述页面返回了一个致命错误,因为Config实体的uuid不可用。

我创建了一个小函数来帮助我,在这里我的示例代码:

function _example_views_update_config($configsNames) {
  $config_path    = drupal_get_path('module', 'example') . '/config/install';
  $source         = new FileStorage($config_path);
  $config_storage = \Drupal::service('config.storage');
  $config_factory = \Drupal::configFactory();
  $uuid_service = \Drupal::service('uuid');

  foreach ($configsNames as $name) {
    $config_storage->write($name, $source->read($name));
    $config_factory->getEditable($name)->set('uuid', $uuid_service->generate())->save();
  }
}

/**
 * Add new action configurations.
 */
function example_update_8003() {
  $configsNames = [
    'config-1',
    'config-2',
  ];

  _example_views_update_config($configsNames);
  return 'Added new configurations.';
}

1

上面的答案(完全重新导入)也适用于我的用例,但是首先我花了一些时间研究选择性更高的重新导入。这是我所拥有的代码,它似乎是作为一个更新挂钩,并且基于config_update模块中的代码:

/**
 * Update all my config.
 *
 * This can be more selective than calling installDefaultConfig().
 */
function MYMODULE_update_8004() {
  $prefixes = [
    'field.storage.node',
    'field.field.node',
    'node.type',
    'core.base_field_override.node',
    'core.entity_view_display'
  ];
  $results = [];
  foreach ($prefixes as $prefix) {
    $results[$prefix] = _update_or_install_config($prefix);
  }
  $return = '';
  foreach ($results as $prefix => $result) {
    $return .= "\n$prefix:\n";
    foreach ($result as $key => $ids) {
      $return .= "$key: " . implode(', ', $ids) . "\n";
    }
  }
  if (function_exists('drush_log')) {
    drush_log($return, \Psr\Log\LogLevel::WARNING);
  }
  return $return;
}


/**
 * Update or install config entities from config/install files.
 *
 * @see \Drupal\config_update\ConfigReverter::import
 * @see \Drupal\config_update\ConfigReverter::revert
 *
 * @param string $prefix
 *   The prefix for YAML files in find, like 'field.storage.node'
 */
function _update_or_install_config($prefix) {
  $updated = [];
  $created = [];
  /** @var \Drupal\Core\Config\ConfigManagerInterface $config_manger */
  $config_manger = \Drupal::service('config.manager');
  $files = glob(__DIR__ . '/config/install/' . $prefix . '.*.yml');
  foreach ($files as $file) {
    $raw = file_get_contents($file);
    $value = \Drupal\Component\Serialization\Yaml::decode($raw);
    if (!is_array($value)) {
      throw new \RuntimeException(sprintf('Invalid YAML file %s'), $file);
    }
    // Lazy hack here since that code ignores the file extension.
    $type = $config_manger->getEntityTypeIdByName(basename($file));
    $entity_manager = $config_manger->getEntityManager();
    $definition = $entity_manager->getDefinition($type);
    $id_key = $definition->getKey('id');
    $id = $value[$id_key];
    /** @var \Drupal\Core\Config\Entity\ConfigEntityStorage $entity_storage */
    $entity_storage = $entity_manager->getStorage($type);
    $entity = $entity_storage->load($id);
    if ($entity) {
      $entity = $entity_storage->updateFromStorageRecord($entity, $value);
      $entity->save();
      $updated[] = $id;
    }
    else {
      $entity = $entity_storage->createFromStorageRecord($value);
      $entity->save();
      $created[] = $id;
    }
  }
  return [
    'updated' => $updated,
    'created' => $created,
  ];
}

1

配置同步器模块可以很好地解决此问题。对于这个案例,这个由7个模块组成的模块套件似乎有点过大了(它的目的主要是安全地合并到更新中而不覆盖自定义项),但是由于其概念,它还允许从模块的/ install和/可选文件夹。

基本上,您可以像下面这样测试它:

  • 像往常一样在/ config / install文件夹中放置一些“默认”配置项,从而在本地环境中创建并启用自定义模块
  • 安装并启用config_sync模块及其所有从属模块
  • 在/ config / install文件夹中的模块配置项中进行一些编辑
  • 访问/ admin / config / development / configuration / distro。您应该看到所做的更改,并可以选择将其导入到活动配置中(合并模式用于保留客户端更改,重置模式用于强制导入)-在开发过程中,我将主要使用重置模式,但除非您在相同的配置中并行进行了任何手动更改

注意:如果你只是想使用config_sync模块的开发过程中,加快配置进口(和你不关心与客户端更新合并),这足以安装了这套房,只在本地(开发)环境中启用(假设您的模块在完成后将转到更高的环境,并且您使用D8核心配置管理将其配置发布到更高的环境)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.