Jak zaktualizować konfigurację modułu?

33

Buduję niestandardowy moduł w Drupal 8. Zawiera on kilka plików konfiguracyjnych YAML.

W miarę rozwoju muszę zmienić i dodać do konfiguracji, np. Aby dodać kolejne pole do mojej niestandardowej encji.

Obecnie jedynym sposobem, w jaki udało mi się przekonać Drupala do zauważenia zmian, jest odinstalowanie modułu i jego ponowna instalacja.

Czy istnieje sposób, aby Drupal sprawdził, czy pliki konfiguracyjne dostarczone przez moduły są takie same jak aktywna konfiguracja, a jeśli nie, zaktualizował aktywną konfigurację? Jak obsługiwane są aktualizacje modułów? W D7 hook_update_Nmożna by dodawać pola za pomocą PHP, ale wygląda na to, że CM powinien to obsłużyć w D8?

Rzeczy, które próbowałem po aktualizacji plików yml w module:

  1. drush cr, config sync.

  2. ręczne kopiowanie wszystkich zaktualizowanych plików konfiguracyjnych do sites/default/files/config_XXX/staging/- ale to powoduje błąd: „Nie można zaimportować konfiguracji etapowej, ponieważ pochodzi ona z innej witryny niż ta witryna. Możesz synchronizować konfigurację tylko między klonowanymi instancjami tej witryny”. .

  3. ręczne importowanie plików jeden po drugim za pomocą menedżera konfiguracji. To działa, ale oczywiście musi istnieć bardziej automatyczny sposób.

  4. [EDYCJA] ręcznie użyj modułu config_update , aby sprawdzić zmiany i „przywrócić” do konfiguracji modułu. Znowu jest to instrukcja.

EDYCJA: Z Zarządzania konfiguracją - zrób i nie

NIE DOTYCZY

Spróbuj zmienić aktywną konfigurację w swojej witrynie, zmieniając pliki w katalogu config / install modułu. To NIE zadziała, ponieważ Drupal będzie czytał z tego katalogu tylko po zainstalowaniu modułu.

... ale nie zmienia się dzieje się zdarzyć, chyba że moduły są zobowiązane do tego, co config one potrzebne w ich pierwszym w historii zwolnić, a może nigdy nie zaktualizować lub dodać config.

Z góry dziękuję.

artfulrobot
źródło
Wydaje mi się, że wcześniej zapytano o coś bardzo podobnego (nie mogę tego teraz całkiem znaleźć) i wydaje mi się, że odpowiedź była taka, że ​​domyślna konfiguracja jest sprawdzana tylko w czasie instalacji, więc ponowna instalacja jest najlepszym rozwiązaniem. Nie cytuj mnie jednak :)
Clive
1
'k, ale jak zaktualizować moduł? Moduły mogą pobierać aktualizacje w D8, prawda ;-)? Musi istnieć sposób (a la config_update) na powiedzenie modułów „Drupal! Teraz potrzebuję tej dodatkowej konfiguracji, spójrz i proszę ją scalić”.
artfulrobot
Menedżer aktualizacji konfiguracji wykonuje to zadanie, ale zgadzam się, że wydaje się, że powinien istnieć natywny sposób na zrobienie tego. Coś hook_update_Nwezmę, ale nie jestem pewien, co
Clive
2
Wow, myślę, że odpowiedzią może być „nie możesz”! Nigdy tego nie widziałem! Powrót do hook_update_N. Doskonały artykuł o Drupal 8 dla małych witryn (i część 2 ). W D8 „strony mają swoją konfigurację, a nie moduły” .
artfulrobot
Chciałbym dodać, że doskonałym przykładem użycia tego jest konfiguracja obejmująca wiele witryn, w której chcesz udostępnić określone duże fragmenty konfiguracji, ale nie wszystkie i wdrożyć to. Mogą to być niestandardowe moduły. W przypadku pojedynczej witryny byłby to po prostu eksport / import konfiguracji, wiele witryn nie byłoby tak proste.
Ambidex,

Odpowiedzi:

24

Jak wspomniano w pierwotnym pytaniu i komentarzach uzupełniających, istnieje wiele modułów contrib i metod ręcznych, aby to osiągnąć.

Aby zrobić to automatycznie lub w niestandardowy sposób, myślę, że hook_update_N()jest to prawdopodobnie najbardziej opłacalna opcja.

Na przykład jest to przykład z Head 2 Head do aktualizacji w system.sitecelu ustawienia default_langcode:

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

Możesz także czytać w config (zalecane tylko w celu dodania nowej konfiguracji, niekoniecznie aktualizacji lub przesłaniania konfiguracji, która mogła zostać dostosowana):

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

gdzie $pathjest bezwzględna ścieżka do my_config.foo.ymlpliku.

jhedstrom
źródło
1
Kiedy podążam za drugim podejściem, konfiguracja jest zapisywana do Drupala, ale nie otrzymuje UUID, nawet gdy eksportuję ją do katalogu konfiguracji. Doprowadziło mnie to do problemu, w którym próbowałem tego z niestandardowym widokiem. Strona przeglądu widoków zwróciła mi błąd krytyczny, ponieważ identyfikator UUID dla encji Config był niedostępny.
Sebastian,
9

Ponieważ doszedłem do tego pytania, ale tak naprawdę nie znalazłem tutaj właściwej odpowiedzi na moją sytuację, chciałbym dodać inną odpowiedź.

Uwaga: Anty-wzór przed nami!

Przypadek użycia

Podczas opracowywania projektów stale aktualizujemy nasze środowisko testowe / akceptacyjne o nowe aktualizacje konfiguracji. Weźmy na przykład prosty fikcyjny moduł wiadomości, chcielibyśmy dodać typ zawartości do modułu i wdrożyć go w naszym środowisku akceptacyjnym. Po sprawdzeniu stwierdziliśmy, że brakuje kilku pól i innych elementów związanych z konfiguracją. Ponieważ wiemy, że środowisko akceptacji nie jest aktualizowane w config, tak naprawdę chcemy przeładować całą konfigurację z modułu, dodając nową funkcjonalność i nie przeszkadzać nam w importowaniu każdego zmienionego .ymlpliku.

Potrzebujemy naszej konfiguracji tylko w modułach, gdy rozwijamy wiele witryn. W przypadku pojedynczych witryn korzystamy głównie z wyeksportowanej konfiguracji witryny, w której kolejny krok jest niepotrzebny.

Reimportuj konfigurację całkowicie (anti-pattern!)

Odkryliśmy, że za pomocą usługi ConfigInstaller jesteśmy w stanie ponownie zaimportować pełną konfigurację z określonego modułu.

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

Używaj ostrożnie!

Chciałbym dodać, że spowoduje to zastąpienie każdej aktywnej zawartości, która została zmieniona w środowisku. Dlatego używaj tego rozwiązania tylko wtedy, gdy masz pewność, że możesz bezpiecznie zastąpić aktywną konfigurację. Nigdy nie użyjemy tego w środowisku produkcyjnym i będziemy go stosować tylko na wczesnym etapie rozwoju.

Najpierw wypróbuj rozwiązanie @ jhedstrom, zanim zaczniesz zastanawiać się nad tym.

Ambidex
źródło
9

Znalazłem Gist na GitHub, który przywraca / przeładowuje konfigurację modułu przy użyciu drush:

drush cim -y --partial --source=modules/path/to/module/config/install/
Елин Й.
źródło
2

Na podstawie mojego komentarza: Jak zaktualizować konfigurację modułu?

Kiedy podążam za drugim podejściem, konfiguracja jest zapisywana do Drupala, ale nie otrzymuje UUID, nawet gdy eksportuję ją do katalogu konfiguracji. Doprowadziło mnie to do problemu, w którym próbowałem tego z niestandardowym widokiem. Strona przeglądu widoków zwróciła mi błąd krytyczny, ponieważ identyfikator UUID dla encji Config był niedostępny.

Stworzyłem małą funkcję, która mi w tym pomaga, oto mój przykładowy kod:

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.';
}
Sebastian
źródło
1

Powyższa odpowiedź (pełny ponowny import) również działała w moim przypadku użycia, ale najpierw poświęciłem trochę czasu, szukając bardziej selektywnego ponownego importu. Oto kod, który miałem, który działał jak hak aktualizacji i był oparty na kodzie w module 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,
  ];
}
pwolanina
źródło
1

Moduł synchronizacji konfiguracji pomaga rozwiązać ten problem w przyjemny sposób. Ten zestaw 7 modułów wydaje się nieco przesadny tylko w tym przypadku (jego celem jest głównie bezpieczne łączenie aktualizacji bez zastępowania dostosowań), ale ze względu na swoją koncepcję pozwala również śledzić i importować zmiany konfiguracji z modułu / instalacji i / foldery opcjonalne szybko.

Zasadniczo możesz to przetestować w następujący sposób:

  • utwórz i włącz swój moduł niestandardowy w środowisku lokalnym z niektórymi „domyślnymi” elementami konfiguracji umieszczonymi jak zwykle w folderze / config / install
  • zainstaluj i włącz moduł config_sync i wszystkie jego moduły zależne
  • wprowadź zmiany w elemencie konfiguracji modułu w folderze / config / install
  • access / admin / config / development / configuration / distro. Powinieneś zobaczyć swoją zmianę i mieć możliwość jej zaimportowania do aktywnej konfiguracji (tryb scalania ma na celu zachowanie zmian klienta, tryb resetowania wymusza import) - podczas programowania najczęściej używam trybu resetowania, ale tryb scalania powinien również działać, chyba że dokonał jakichkolwiek ręcznych zmian w tej samej konfiguracji równolegle

Uwaga: jeśli tylko chcesz używać config_sync celu przyspieszenia importu config podczas rozwoju modułu (i nie dbam o łączenie się z aktualizacji klientów), to wystarczy, że ten pakiet zainstalowany i włączony tylko na lokalnym (rozwoju) Środowisko ( zakładając, że Twój moduł przejdzie do wyższych środowisk po sfinalizowaniu i użyjesz zarządzania konfiguracją rdzenia D8, aby opublikować jego konfigurację w wyższych środowiskach).

Mirsoft
źródło