모듈의 구성을 어떻게 업데이트합니까?


33

Drupal 8에서 커스텀 모듈을 만들고 있습니다. 여기에는 YAML 구성 파일이 포함되어 있습니다.

개발함에 따라 구성을 변경하고 추가해야합니다 (예 : 사용자 지정 엔터티에 다른 필드 추가).

현재 Drupal이 변경 사항을 알도록하는 유일한 방법은 모듈을 제거하고 다시 설치하는 것입니다.

Drupal이 모듈이 제공 한 구성 파일이 활성 구성과 동일한 지 확인하고 그렇지 않은 경우 활성 구성을 업데이트하는 방법이 있습니까? 모듈 업데이트는 어떻게 처리됩니까? D7에서는 hook_update_NPHP를 사용하여 필드를 추가하는 데 사용되지만 D8의 CM에서 처리 해야하는 것처럼 보입니까?

모듈에서 yml 파일을 업데이트 한 후 시도한 사항 :

  1. drush cr, 구성 동기화.

  2. 업데이트 된 모든 구성 파일을 수동으로 복사 sites/default/files/config_XXX/staging/-이 오류는 "이 사이트와 다른 사이트에서 시작되었으므로 준비된 구성을 가져올 수 없습니다.이 사이트의 복제 된 인스턴스간에 만 구성을 동기화 할 수 있습니다." .

  3. 구성 관리자를 사용하여 파일을 하나씩 수동으로 가져옵니다. 이것은 작동하지만 분명히 더 자동적 인 방법이 있어야합니다.

  4. [편집] 수동으로 config_update 모듈을 사용하여 변경 사항을 검사하고 모듈 구성으로 '되돌아 가십시오 '. 다시, 이것은 수동입니다.

편집 : 에서 구성 관리 - 것과하지 말아야 할

하지마

모듈의 config / install 디렉토리에서 파일을 변경하여 사이트의 활성 구성을 변경하십시오. Drupal은 모듈이 설치 될 때 해당 디렉토리에서만 읽을 수 있기 때문에 작동하지 않습니다.

...하지만이 변경이되어 가고 모듈은 그들의 최초의 해제에 필요한 config (설정) 어떤 결합하지 않는 한, 일이, 이제까지 업데이트하거나 설정을 추가 않을 수도 있습니다.

미리 감사드립니다.


나는 전에 질문을 받았다 매우 비슷한 일이 (아주 지금 그것을 찾을 수없는) 생각하고, 내가 생각하는 답은 재 설치가 갈 수있는 방법입니다, 그래서 설치시 협의되는 기본 설정했다. 나를 인용하지 마라 :)
Clive

1
'k,하지만 모듈은 어떻게 업데이트됩니까? 모듈은 D8에서 업데이트를받을 수 있습니다. ;-)? 모듈이 "Drupal!이라고 말할 수있는 방법 (la config_update)이 있어야합니다. 이제이 추가 구성이 필요합니다. 살펴보고 병합하십시오."
artfulrobot

Configuration Update Manager 가 작업을 수행하지만 기본 방법이 필요하다고 생각합니다. hook_update_N나는 뭔가를 가정했지만 확실하지 않습니다
Clive

2
와우, 나는 대답이 "당신이 할 수 없다"고 생각할 것입니다! 오는 것을 본 적이 없어! 위로 hook_update_N. 작은 사이트 (및 2 부 )를 위한 Drupal 8에 대한 훌륭한 기사 . D8에서 "사이트는 모듈이 아닌 구성을 소유합니다" .
artfulrobot

이에 대한 훌륭한 유스 케이스는 멀티 사이트 설정이며, 특정 대규모 구성을 공유하고 싶지는 않지만이를 배포하려는 다중 사이트 설정이라고 덧붙이고 싶습니다. 여기에는 사용자 정의 모듈이 포함될 수 있습니다. 단일 사이트의 경우 단순히 구성 내보내기 / 가져 오기 일 뿐이며 다중 사이트는 그렇게 간단하지 않습니다.
Ambidex

답변:


24

원래 질문과 후속 의견에서 언급했듯이이를 달성하기위한 다양한 제어 모듈과 수동 방법이 있습니다.

자동으로 또는 사용자 정의 방식으로 수행하려면 hook_update_N()여전히 가장 실용적인 옵션 일 것입니다.

예를 들어, 다음 을 설정 하기 위해 업데이트 할 헤드 2 헤드 의 예입니다 .system.sitedefault_langcode

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

구성에서 읽을 수도 있습니다 (사용자 정의되었을 수있는 구성을 반드시 업데이트하거나 재정의 할 필요는 없지만 새 구성을 추가하는 경우에만 권장 됨).

  $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에 작성되지만 구성 디렉토리로 내보내도 UUID가 표시되지 않습니다. 이로 인해 사용자 정의보기로 시도했을 때 문제가 발생했습니다. 구성 엔터티에 대한 UUID를 사용할 수 없으므로 뷰 개요 페이지에서 치명적인 오류가 반환되었습니다.
세바스찬

9

이 질문에 착륙했지만 여기서 내 상황에 맞는 정답을 찾지 못 했으므로 다른 답변을 추가하고 싶습니다.

참고 : 안티 패턴 앞서!

사용 사례

프로젝트를 개발할 때 새로운 구성 업데이트로 테스트 / 수락 환경을 지속적으로 업데이트합니다. 간단한 가상의 뉴스 모듈을 예로 들어, 모듈에 컨텐츠 유형을 추가하고이를 수용 환경에 배치하려고합니다. 검토 결과, 누락 된 필드 및 기타 구성 관련 항목이 몇 가지 있다고 결론을 내 렸습니다. 승인 환경이 구성에서 업데이트되지 않는다는 것을 알고 있으므로 실제로는 새로운 기능을 추가하면서 모듈에서 전체 구성을 다시로드하고 변경된 모든 항목을 가져와도 신경 쓰지 않습니다..yml 파일 .

다중 사이트를 개발할 때 모듈의 구성 만 필요합니다. 단일 사이트의 경우 대부분 다음 단계가 필요하지 않은 내 보낸 사이트 구성 만 사용합니다.

구성을 완전히 다시 가져 오기 (패턴 안티!)

ConfigInstaller 서비스 를 사용하면 특정 모듈에서 전체 구성을 다시 가져올 수 있습니다.

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

주의해서 사용하십시오!

환경 내에서 변경된 모든 활성 컨텐츠를 덮어 쓰게됩니다. 따라서 활성 구성을 덮어 쓰는 것이 안전한 경우에만이 솔루션을 사용하십시오. 프로덕션 환경에서는이 기능을 사용하지 않으며 초기 개발에만 적용됩니다.

먼저 이것을 고려하기 전에 @jhedstrom의 솔루션을 사용해보십시오.


9

GitHub 에서이 요점 을 찾았습니다 .dist를 사용하여 주어진 모듈의 구성을 되돌 리거나 다시로드합니다.

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

2

내 의견을 바탕으로 : 모듈 구성을 어떻게 업데이트합니까?

두 번째 접근 방식을 따르면 구성이 Drupal에 작성되지만 구성 디렉토리로 내보내도 UUID가 표시되지 않습니다. 이로 인해 사용자 정의보기로 시도했을 때 문제가 발생했습니다. 구성 엔터티에 대한 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 코어 구성 관리를 사용하여 구성을 더 높은 환경에 게시한다고 가정합니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.