Como atualizo a configuração de um módulo?

33

Estou construindo um módulo personalizado no Drupal 8. Ele inclui alguns arquivos de configuração YAML.

À medida que desenvolvo, preciso alterar e adicionar à configuração, por exemplo, para adicionar outro campo à minha entidade personalizada.

No momento, a única maneira que encontrei para fazer o Drupal perceber as alterações é desinstalar o módulo e reinstalá-lo.

Existe uma maneira de fazer com que o Drupal verifique se os arquivos de configuração fornecidos pelos módulos são iguais aos da configuração ativa e, caso contrário, atualize a configuração ativa? Como são tratadas as atualizações do módulo? Em D7 hook_update_Nseria usado para adicionar campos usando PHP, mas parece que isso deve ser tratado pelo CM em D8?

Coisas que tentei após atualizar os arquivos yml no módulo:

  1. drush cr, sincronização de configuração.

  2. copiar manualmente todos os arquivos de configuração atualizados para sites/default/files/config_XXX/staging/- mas isso gera este erro "A configuração em estágios não pode ser importada, porque se origina de um site diferente desse site. Você só pode sincronizar a configuração entre instâncias clonadas deste site". .

  3. importando manualmente os arquivos um por um usando o gerenciador de configuração. Isso funciona, mas obviamente deve haver uma maneira mais automática.

  4. [EDIT] use manualmente o módulo config_update para inspecionar alterações e 'reverter' para a configuração do módulo. Novamente, isso é manual.

EDIT: Do gerenciamento de configuração - faça e não faça

NÃO É

Tente alterar a configuração ativa no seu site alterando os arquivos no diretório config / install de um módulo. Isso NÃO funcionará porque o Drupal somente lerá esse diretório quando o módulo estiver instalado.

... mas muda há vai acontecer, a menos que os módulos estão ligados a qualquer config-se necessária em seu primeiro lançamento, e pode nunca atualizar ou adicionar configuração.

Desde já, obrigado.

artfulrobot
fonte
Eu acho que algo muito semelhante foi perguntado antes (não consigo encontrá-lo agora) e acho que a resposta foi que a configuração padrão é consultada apenas no momento da instalação, portanto, reinstalar é o caminho a seguir. Não me cite embora :)
Clive
1
, mas como um módulo seria atualizado? Módulos podem receber atualizações no D8, certo ;-)? Deve haver uma maneira (a la config_update) para os módulos dizerem "Drupal! Agora eu exijo essa configuração extra, dê uma olhada e mescle-a por favor".
Artfulrobot
O Configuration Update Manager faz o trabalho, mas concordo que parece haver uma maneira nativa de fazer isso. Algo em hook_update_Nque eu suponho, mas não sei ao certo o que
Clive
2
Uau, acho que a resposta pode acabar sendo "você não pode"! Nunca vi isso chegando! Voltar para hook_update_N. Excelente artigo sobre o Drupal 8 para sites pequenos (e parte 2 ). No D8 "os sites possuem sua configuração, não os módulos" .
Artfulrobot
Gostaria de acrescentar que um excelente caso de uso para isso é uma configuração multisite, na qual você deseja compartilhar grandes partes específicas da configuração, mas não todas, e implantá-las. Isso pode incluir módulos personalizados. Para um único site, seria simplesmente uma exportação / importação de configuração, um multisite não seria tão simples.
Ambidex 10/10

Respostas:

24

Conforme mencionado na pergunta original e nos comentários de acompanhamento, há uma variedade de módulos de contribuição e métodos manuais para fazer isso.

Para fazer isso automaticamente, ou de uma maneira personalizada, acho que hook_update_N()ainda é provavelmente a opção mais viável.

Por exemplo, este é um exemplo do Head 2 Head para atualizar system.sitee definir o default_langcode:

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

Você também pode ler a configuração (recomendado apenas para adicionar nova configuração, não necessariamente atualizando ou substituindo a configuração que pode ter sido personalizada):

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

Onde $pathé o caminho absoluto para o my_config.foo.ymlarquivo.

jhedstrom
fonte
1
Quando estou seguindo a segunda abordagem, a configuração é gravada no Drupal, mas não está recebendo um UUID, mesmo quando eu a exporto para o diretório de configuração. Isso me levou a um problema em que tentei isso com uma Visualização personalizada. A página de visão geral do Views retornou um erro fatal, pois o uuid da entidade Config não estava disponível.
Sebastian
9

Como cheguei nessa questão também, mas não encontrei a resposta correta para minha situação aqui, gostaria de adicionar outra resposta.

Atenção: Antipadrão à frente!

Caso de uso

Quando estamos desenvolvendo projetos, atualizamos constantemente nosso ambiente de teste / aceitação com novas atualizações de configuração. Tomemos, por exemplo, um módulo de notícias fictício simples, gostaríamos de adicionar um tipo de conteúdo ao módulo e implantá-lo em nosso ambiente de aceitação. Após a revisão, concluímos que faltam alguns campos e outras coisas relacionadas à configuração. Como sabemos que o ambiente de aceitação não está sendo atualizado na configuração, realmente queremos apenas recarregar toda a configuração do módulo enquanto adicionamos novas funcionalidades e não nos incomodamos importando todos os .ymlarquivos alterados .

Nós só precisamos da nossa configuração em módulos quando estamos desenvolvendo multisites. Para sites únicos, geralmente usamos apenas a configuração do site exportado, na qual a próxima etapa é desnecessária.

Reimporte totalmente a configuração (anti-padrão!)

Descobrimos que, usando o serviço ConfigInstaller , podemos reimportar a configuração completa novamente de um módulo específico.

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

Use com cuidado!

Gostaria de acrescentar que isso substituirá qualquer conteúdo ativo que tenha sido alterado no ambiente. Portanto, use esta solução somente quando tiver certeza de que é seguro substituir a configuração ativa. Nunca usaremos isso em um ambiente de produção e aplicaremos apenas no desenvolvimento inicial.

Primeiro experimente a solução da @ jhedstrom antes de começar a considerar esta.

Ambidex
fonte
9

Eu encontrei este Gist no GitHub, que reverte / recarrega a configuração do módulo usando o drush:

drush cim -y --partial --source=modules/path/to/module/config/install/
Елин Й.
fonte
2

Com base no meu comentário: Como atualizo a configuração de um módulo?

Quando estou seguindo a segunda abordagem, a configuração é gravada no Drupal, mas não está recebendo um UUID, mesmo quando eu a exporto para o diretório de configuração. Isso me levou a um problema em que tentei isso com uma Visualização personalizada. A página de visão geral do Views retornou um erro fatal, pois o uuid da entidade Config não estava disponível.

Eu criei uma pequena função que me ajuda com isso, aqui meu código de exemplo:

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
fonte
1

A resposta acima (reimportação completa) também funcionou no meu caso de uso, mas primeiro passei um tempo procurando uma reimportação mais seletiva. Aqui está o código que eu tinha, que parecia funcionar como um gancho de atualização e foi baseado no código do módulo 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
fonte
1

O módulo Sincronizador de configuração ajuda a resolver esse problema de uma maneira agradável. Esse conjunto de 7 módulos parece um pouco exagerado apenas para esse caso (sua intenção é mesclar com segurança atualizações sem substituir as personalizações), mas devido ao seu conceito, ele também permite rastrear e importar alterações de configuração dos módulos / instalação e instalação. / pastas opcionais rapidamente.

Basicamente, você pode testá-lo da seguinte maneira:

  • crie e ative seu módulo personalizado em seu ambiente local com alguns itens de configuração "padrão" colocados na pasta / config / install como de costume
  • instalar e ativar o módulo config_sync e todos os seus módulos dependentes
  • faça algumas edições no item de configuração do seu módulo dentro da pasta / config / install
  • acesse / admin / config / development / configuration / distro. Você deve ver sua alteração e ter a opção de importá-la para a configuração ativa (o modo Mesclar visa preservar as alterações do cliente, o modo Redefinir força a importação) - durante o desenvolvimento, usarei principalmente o modo Redefinir, mas o modo mesclado deve funcionar também, a menos que você fez alterações manuais na mesma configuração em paralelo

Nota: se você única quiser usar o config_sync para acelerar a importação de configuração durante o desenvolvimento do módulo (e você não se preocupam com a fusão com atualizações do cliente), é o suficiente para ter esta suite instalado e ativado apenas em seu ambiente (desenvolvimento) local ( supondo que seu módulo vá para ambientes superiores após a finalização e você use o gerenciamento de configuração do núcleo do D8 para postar sua configuração em ambientes superiores).

Mirsoft
fonte