There are two kinds of updates we need when updating between 8.x versions:

1) Entity updates (added field, removed field, etc)
Pretty similar to D7, plenty of examples in core

2) Config updates
Nobody's doing it yet in contrib yet.
D8 CMI makes the assumption that the user owns the config, not the module, so after the module is installed, that's it, no updates.
This is the complete opposite of the way config worked in D7, so we need to be careful.

An update needs to:
a) Install new config
b) Delete removed config (rare)
c) Update changed config

a) and b) are easy to do manually. c) is the tricky one.
Ideally we'd be able to specify the semantics of config ownership: "leave this config alone" VS "keep refreshing this config" VS "refresh this config only until the user customizes it". I don't see many use cases for always refreshing the config, guessing "refresh until the user customizes it" would be the default.
The config_update module has an API that we might be able to use.
Related core issue: #2656370: Create a method to discover if configuration matches the shipped configuration.

Comments

bojanz created an issue. See original summary.

bojanz’s picture

Note that the various D8 config update modules insist on having a UI that pushes the responsibility/complexity of reviewing updates to the end-user.
The average end-user will run away screaming.

Instead, we as module maintainers need to make the decision ourselves, in our update hooks. Having an API to use in update hooks currently feels better than trying to automate it, but let's see how to code turns out.

We'll also want to document common cases such as:
- Updated view
- Added configurable field
etc

bojanz’s picture

Assigned: Unassigned » mglaman

mglaman has made good progress on the config update helper class.

https://github.com/drupalcommerce/commerce/pull/347

bojanz’s picture

Status: Active » Fixed

After many iterations we've created the ConfigUpdater service that covers the needed functionality:
https://github.com/drupalcommerce/commerce/commit/af4e4bb3507cc1119e92ea...

Verbose usage example:


/**
 * Placed in commerce.post_update.php
 */
function commerce_post_update_example() {
  $config_updater = \Drupal::service('commerce.config_updater');

  // The 'views.view.commerce_products' view was updated in the module, and
  // should be updated on the site unless the user has already modified it.
  $first_result = $config_updater->revert(['views.view.commerce_products']);

  // The 'commerce_product.commerce_product_type.default' product type was
  // updated in the module and should be updated on the site regardless
  // of whether the user has already modified it
  $second_result = $config_updater->revert(['commerce_product.commerce_product_type.default'], FALSE);

  // The 'system.action.commerce_publish_product' action was removed from the
  // module, and should be removed from the site.
  $third_result = $config_updater->delete(['system.action.commerce_publish_product']);

  // Let's pretend this is a more generic replacement for the old publish action.
  $fourth_result = $config_updater->import(['system.action.commerce_superpublish_product']);

  $message = '';
  // Each updater call allows multiple config names, and returns a result
  // with success and failure messages. Merge them, and show them to the user.
  $success_results = array_merge(
    $first_result->getSucceeded(),
    $second_result->getSucceeded(),
    $third_result->getSucceeded(),
    $fourth_result->getSucceeded()
  );
  $failure_results = array_merge(
    $first_result->getFailed(),
    $second_result->getFailed(),
    $third_result->getFailed(),
    $fourth_result->getFailed()
  );

  if ($success_results) {
    $message = t('Succeeded:');
    $message .= '<ul>';
    foreach ($success_results as $success_message) {
      $message .= '<li>' . $success_message . '</li>';
    }
    $message .= '</ul>';
  }
  if ($failure_results) {
    $message .= t('Failed:');
    $message .= '<ul>';
    foreach ($failure_results as $failure_message) {
      $message .= '<li>' . $failure_message . '</li>';
    }
    $message .= '</ul>';
  }

  return $message;
}

Matt will do a blog post soon.

agoradesign’s picture

Thank you very much for this great work! Looks very very interesting :-)

Have you thought of moving the ConfigUpdated into a seperate project? Looks useful for any kind of project, not only Commerce ones

bojanz’s picture

@agoradesign
I wouldn't want to duplicate the config_update module (which has already settled on its own API).
In my opinion this code needs to live in core.

mglaman is adding the isModified() helper to #2656370: Create a method to discover if configuration matches the shipped configuration and I've opened #2684081: Modules need a way to keep their configuration up to date for the entire thing.

agoradesign’s picture

You're absolutely right... I've commented on your core issue, trying to clarify that the existence of such helper functions only have advantages, as you can never fully prevent module authors from doing stupid things...

Status: Fixed » Closed (fixed)

Automatically closed - issue fixed for 2 weeks with no activity.