You are here

class Updater in Update helper 2.x

Same name and namespace in other branches
  1. 8 src/Updater.php \Drupal\update_helper\Updater

Helper class to update configuration.

Hierarchy

Expanded class hierarchy of Updater

1 string reference to 'Updater'
update_helper.services.yml in ./update_helper.services.yml
update_helper.services.yml
1 service uses Updater
update_helper.updater in ./update_helper.services.yml
Drupal\update_helper\Updater

File

src/Updater.php, line 19

Namespace

Drupal\update_helper
View source
class Updater implements UpdaterInterface {
  use StringTranslationTrait;

  /**
   * Site configFactory object.
   *
   * @var \Drupal\Core\Config\ConfigFactoryInterface
   */
  protected $configFactory;

  /**
   * Module installer service.
   *
   * @var \Drupal\Core\Extension\ModuleInstallerInterface
   */
  protected $moduleInstaller;

  /**
   * Config reverter service.
   *
   * @var \Drupal\config_update\ConfigRevertInterface
   */
  protected $configReverter;

  /**
   * Configuration handler service.
   *
   * @var \Drupal\update_helper\ConfigHandler
   */
  protected $configHandler;

  /**
   * Logger service.
   *
   * Note: Instead of using this service directly, use provided wrappers for it:
   * - logWarning - for logging warnings, it will mark executed update as failed
   * - logInfo    - for logging information.
   *
   * @var \Drupal\update_helper\UpdateLogger
   */
  protected $logger;

  /**
   * Event dispatcher.
   *
   * @var \Symfony\Component\EventDispatcher\EventDispatcherInterface
   */
  protected $eventDispatcher;

  /**
   * Constructs the PathBasedBreadcrumbBuilder.
   *
   * @param \Drupal\Core\Config\ConfigFactoryInterface $config_factory
   *   Config factory service.
   * @param \Drupal\Core\Extension\ModuleInstallerInterface $module_installer
   *   Module installer service.
   * @param \Drupal\config_update\ConfigRevertInterface $config_reverter
   *   Config reverter service.
   * @param \Drupal\update_helper\ConfigHandler $config_handler
   *   Configuration handler service.
   * @param \Drupal\update_helper\UpdateLogger $logger
   *   Update logger.
   * @param \Symfony\Component\EventDispatcher\EventDispatcherInterface $event_dispatcher
   *   Event dispatcher.
   */
  public function __construct(ConfigFactoryInterface $config_factory, ModuleInstallerInterface $module_installer, ConfigRevertInterface $config_reverter, ConfigHandler $config_handler, UpdateLogger $logger, EventDispatcherInterface $event_dispatcher) {
    $this->configFactory = $config_factory;
    $this->moduleInstaller = $module_installer;
    $this->configReverter = $config_reverter;
    $this->configHandler = $config_handler;
    $this->logger = $logger;
    $this->eventDispatcher = $event_dispatcher;
  }

  /**
   * Keeps the record of total warnings occurred during update execution.
   *
   * @var int
   */
  protected $warningCount = 0;

  /**
   * {@inheritdoc}
   */
  public function logger() {
    return $this->logger;
  }

  /**
   * Log warning message with internal count for reporting update failures.
   *
   * @param string $message
   *   The message used for logging of warning.
   */
  protected function logWarning($message) {
    $this->warningCount++;
    $this->logger
      ->warning($message);
  }

  /**
   * Log information message, that will be displayed on update execution.
   *
   * @param string $message
   *   The message used for logging of info.
   */
  protected function logInfo($message) {
    $this->logger
      ->info($message);
  }

  /**
   * {@inheritdoc}
   */
  public function executeUpdate($module, $update_definition_name) {
    $this->warningCount = 0;
    $update_definitions = $this->configHandler
      ->loadUpdate($module, $update_definition_name);
    if (isset($update_definitions[UpdateDefinitionInterface::GLOBAL_ACTIONS])) {
      $this
        ->executeGlobalActions($update_definitions[UpdateDefinitionInterface::GLOBAL_ACTIONS]);
      unset($update_definitions[UpdateDefinitionInterface::GLOBAL_ACTIONS]);
    }
    if (!empty($update_definitions)) {
      $this
        ->executeConfigurationActions($update_definitions);
    }

    // Dispatch event after update has finished.
    $event = new ConfigurationUpdateEvent($module, $update_definition_name, $this->warningCount);
    $this->eventDispatcher
      ->dispatch(UpdateHelperEvents::CONFIGURATION_UPDATE, $event);
    return $this->warningCount === 0;
  }

  /**
   * Get array with defined global actions.
   *
   * Global actions can be:
   * - install_modules: list of modules to install
   * - import_configs: list of configurations to import.
   *
   * @param array $global_actions
   *   Array with list of global actions.
   */
  protected function executeGlobalActions(array $global_actions) {
    if (isset($global_actions[UpdateDefinitionInterface::GLOBAL_ACTION_INSTALL_MODULES])) {
      $this
        ->installModules($global_actions[UpdateDefinitionInterface::GLOBAL_ACTION_INSTALL_MODULES]);
    }
    if (isset($global_actions[UpdateDefinitionInterface::GLOBAL_ACTION_IMPORT_CONFIGS])) {
      $this
        ->importConfigs($global_actions[UpdateDefinitionInterface::GLOBAL_ACTION_IMPORT_CONFIGS]);
    }
  }

  /**
   * Execute configuration update definitions for configurations.
   *
   * @param array $update_definitions
   *   List of configurations with update definitions for them.
   */
  protected function executeConfigurationActions(array $update_definitions) {
    foreach ($update_definitions as $configName => $configChange) {
      if ($this
        ->updateConfig($configName, $configChange['update_actions'], $configChange['expected_config'])) {
        $this
          ->logInfo($this
          ->t('Configuration @configName has been successfully updated.', [
          '@configName' => $configName,
        ]));
      }
      else {
        $this
          ->logWarning($this
          ->t('Unable to update configuration for @configName.', [
          '@configName' => $configName,
        ]));
      }
    }
  }

  /**
   * Apply configuration changes on configuration array.
   *
   * Order is following:
   * 1. Delete
   *   - this options is first, in that way, it's possible to delete bigger
   *     configuration block before adding new one.
   * 2. Add
   *   - add is seconds, in that way we can add whole block deleted before.
   * 3. Change
   *   - change is last, because we want to apply smaller modifications after
   *     all bigger changes are done.
   *
   * @param array $base_config
   *   The base configuration.
   * @param array $update_actions
   *   The configuration update actions.
   *
   * @return array
   *   Returns modified configuration.
   */
  protected function applyConfigActions(array $base_config, array $update_actions) : array {

    // 1. Define configuration keys that should be deleted.
    if (isset($update_actions['delete'])) {
      $delete_keys = $this
        ->getFlatKeys($update_actions['delete']);
      foreach ($delete_keys as $key_path) {
        NestedArray::unsetValue($base_config, $key_path);
      }
    }

    // 2. Add configuration that is added.
    if (isset($update_actions['add'])) {
      $base_config = NestedArray::mergeDeep($base_config, $update_actions['add']);
    }

    // 3. Add configuration that is changed.
    if (isset($update_actions['change'])) {
      $base_config = NestedArray::mergeDeep($base_config, $update_actions['change']);
    }
    return $base_config;
  }

  /**
   * Installs modules.
   *
   * @param array $modules
   *   List of module names.
   */
  protected function installModules(array $modules) {
    foreach ($modules as $module) {
      try {
        if ($this->moduleInstaller
          ->install([
          $module,
        ])) {
          $this
            ->logInfo($this
            ->t('Module @module is successfully enabled.', [
            '@module' => $module,
          ]));
        }
        else {
          $this
            ->logWarning($this
            ->t('Unable to enable @module.', [
            '@module' => $module,
          ]));
        }
      } catch (MissingDependencyException $e) {
        $this
          ->logWarning($this
          ->t('Unable to enable @module because of missing dependencies.', [
          '@module' => $module,
        ]));
      }
    }
  }

  /**
   * Imports configurations.
   *
   * @param array $config_list
   *   List of full configuration names.
   */
  protected function importConfigs(array $config_list) {

    // Import configurations.
    foreach ($config_list as $full_config_name) {
      $config_name = ConfigName::createByFullName($full_config_name);
      if (!empty($this->configFactory
        ->get($full_config_name)
        ->getRawData())) {
        $this
          ->logWarning($this
          ->t('Importing of @full_name config will be skipped, because configuration already exists.', [
          '@full_name' => $full_config_name,
        ]));
        continue;
      }
      if (!$this->configReverter
        ->import($config_name
        ->getType(), $config_name
        ->getName())) {
        $this
          ->logWarning($this
          ->t('Unable to import @full_name config, because configuration file is not found.', [
          '@full_name' => $full_config_name,
        ]));
        continue;
      }
      $this
        ->logInfo($this
        ->t('Configuration @full_name has been successfully imported.', [
        '@full_name' => $full_config_name,
      ]));
    }
  }

  /**
   * Get flatten array keys as list of paths.
   *
   * Example:
   *   $nestedArray = [
   *      'a' => [
   *          'b' => [
   *              'c' => 'c1',
   *          ],
   *          'bb' => 'bb1'
   *      ],
   *      'aa' => 'aa1'
   *   ]
   *
   * Result: [
   *   ['a', 'b', 'c'],
   *   ['a', 'bb']
   *   ['aa']
   * ]
   *
   * @param array $nested_array
   *   Array with nested keys.
   *
   * @return array
   *   List of flattened keys.
   */
  protected function getFlatKeys(array $nested_array) {
    $keys = [];
    foreach ($nested_array as $key => $value) {
      if (is_array($value) && !empty($value)) {
        $list_of_sub_keys = $this
          ->getFlatKeys($value);
        foreach ($list_of_sub_keys as $subKeys) {
          $keys[] = array_merge([
            $key,
          ], $subKeys);
        }
      }
      else {
        $keys[] = [
          $key,
        ];
      }
    }
    return $keys;
  }

  /**
   * Update configuration.
   *
   * It's possible to provide expected configuration that should be checked,
   * before new configuration is applied in order to ensure existing
   * configuration is expected one.
   *
   * @param string $config_name
   *   Configuration name that should be updated.
   * @param array $update_actions
   *   Configuration update actions.
   * @param array $expected_configuration
   *   Only if current config is same like old config we are updating.
   *
   * @return bool
   *   Returns TRUE if update of configuration was successful.
   */
  protected function updateConfig($config_name, array $update_actions, array $expected_configuration = []) {
    $config = $this->configFactory
      ->getEditable($config_name);
    $config_data = $config
      ->get();

    // Check that configuration exists before executing update.
    if (empty($config_data)) {
      return FALSE;
    }

    // Apply configuration update actions.
    $update_config_data = $this
      ->applyConfigActions($config_data, $update_actions);

    // Check if configuration is already in new state.
    if (empty(DiffArray::diffAssocRecursive($config_data, $update_config_data)) && empty(DiffArray::diffAssocRecursive($update_config_data, $config_data))) {
      return TRUE;
    }
    if (!empty($expected_configuration) && DiffArray::diffAssocRecursive($expected_configuration, $config_data)) {
      return FALSE;
    }
    $config
      ->setData($update_config_data);
    $config
      ->save();
    return TRUE;
  }

}

Members

Namesort descending Modifiers Type Description Overrides
StringTranslationTrait::$stringTranslation protected property The string translation service. 4
StringTranslationTrait::formatPlural protected function Formats a string containing a count of items.
StringTranslationTrait::getNumberOfPlurals protected function Returns the number of plurals supported by a given language.
StringTranslationTrait::getStringTranslation protected function Gets the string translation service.
StringTranslationTrait::setStringTranslation public function Sets the string translation service to use. 2
StringTranslationTrait::t protected function Translates a string to the current language or to a given language.
Updater::$configFactory protected property Site configFactory object.
Updater::$configHandler protected property Configuration handler service.
Updater::$configReverter protected property Config reverter service.
Updater::$eventDispatcher protected property Event dispatcher.
Updater::$logger protected property Logger service.
Updater::$moduleInstaller protected property Module installer service.
Updater::$warningCount protected property Keeps the record of total warnings occurred during update execution.
Updater::applyConfigActions protected function Apply configuration changes on configuration array.
Updater::executeConfigurationActions protected function Execute configuration update definitions for configurations.
Updater::executeGlobalActions protected function Get array with defined global actions.
Updater::executeUpdate public function Execute update of configuration from update definitions. Overrides UpdaterInterface::executeUpdate
Updater::getFlatKeys protected function Get flatten array keys as list of paths.
Updater::importConfigs protected function Imports configurations.
Updater::installModules protected function Installs modules.
Updater::logger public function Get update logger service. Overrides UpdaterInterface::logger
Updater::logInfo protected function Log information message, that will be displayed on update execution.
Updater::logWarning protected function Log warning message with internal count for reporting update failures.
Updater::updateConfig protected function Update configuration.
Updater::__construct public function Constructs the PathBasedBreadcrumbBuilder.