install

my_module.install
<?php

/**
 * Reset all search_api.index configs to their values from config/sync.
 */
function myzak_customs_post_update_10004() {
  // Resolve the sync config directory.
  $sync_path = \Drupal\Core\Site\Settings::get('config_sync_directory');

  // Fallback: derive from DRUPAL_ROOT.
  if (empty($sync_path)) {
    $sync_path = DRUPAL_ROOT . '/../config/sync';
  }

  $sync_path = rtrim($sync_path, '/');
  $messages  = [];

  // Find all search_api.index yml files.
  $files = glob($sync_path . '/search_api.index.*.yml');

  if (empty($files)) {
    return "No search_api.index yml files found in: {$sync_path}";
  }

  foreach ($files as $file) {
    $yaml_content = file_get_contents($file);

    if ($yaml_content === FALSE) {
      $messages[] = "Could not read file: {$file}";
      continue;
    }

    // Parse YAML.
    $config_data = \Symfony\Component\Yaml\Yaml::parse($yaml_content);

    if (empty($config_data)) {
      $messages[] = "Skipped " . basename($file) . ": empty or invalid YAML.";
      continue;
    }

    $config_name = basename($file, '.yml');

    // Load the active config.
    $active_config = \Drupal::configFactory()->getEditable($config_name);

    if ($active_config->isNew()) {
      // Config doesn't exist in active config yet, create it fresh.
      $active_config->setData($config_data)->save();
      $messages[] = "Created {$config_name}: did not exist in active config.";
      continue;
    }

    // Overwrite the entire config with the sync values.
    $active_config->setData($config_data)->save();
    $messages[] = "Reset {$config_name}: active config overwritten with sync values.";
  }

  // Invalidate caches for search_api so changes take effect.
  \Drupal::service('cache.config')->invalidateAll();

  // Log results.
  \Drupal::logger('myzak_customs')->notice(implode("\n", $messages));

  return implode("\n", $messages);
}

15.05.2026 | Dominik Wille

Import config in update hook

switch_roles.install
<?php

use Drupal\user\RoleInterface;

/**
 * Remove the old roles and replace them with the new ones.
 */
function MY_MODULE_update_9041(): void {
  $em = \Drupal::entityTypeManager();
  $user_storage = $em->getStorage('user');

  // Ensure the new role exists. If not, create it first.
  $role_storage = $em->getStorage('user_role');
  if ($role_storage->load('new_role') === NULL) {
    /** @var \Drupal\user\RoleInterface $role */
    $role = $role_storage->create([
      'id' => 'new_role',
      'label' => 'New role',
    ]);
    $role->save();
  }

  $uids = \Drupal::entityQuery('user')
    ->accessCheck(FALSE)
    ->execute();

  if (empty($uids)) {
    return;
  }

  /** @var \Drupal\user\UserInterface[] $users */
  $users = $user_storage->loadMultiple($uids);

  foreach ($users as $user) {
    if ($user->hasRole('old_role')) {
      $user->addRole('new_role');
      $user->removeRole('old_role');
    }

    $user->save();
  }
}
02.04.2026 | Lothar Ferreira Neumann

Updating user roles using a module update hook

my_module.install
<?php

/**
 * Change my_basefield max_length to 255.
 */
function my_module_update_10001() {
  $entity_type_id = 'my_entity';
  $field_name = 'my_basefield';
  $field_length = 255;

  /** @var \Drupal\Core\Entity\EntityLastInstalledSchemaRepositoryInterface $schema_repository */
  $schema_repository = \Drupal::service('entity.last_installed_schema.repository');
  /** @var \Drupal\Core\Entity\EntityFieldManager $entity_field_manager */
  $entity_field_manager = \Drupal::service('entity_field.manager');
  /** @var Drupal\Core\Field\BaseFieldDefinition[] $base_field_definitions */
  $base_field_definitions = $entity_field_manager->getBaseFieldDefinitions($entity_type_id);
  $schema_repository->setLastInstalledFieldStorageDefinition($base_field_definitions[$field_name]);
  $field_storage_definitions = $schema_repository->getLastInstalledFieldStorageDefinitions($entity_type_id);

  // Update the serialized schema property.
  $rc = new \ReflectionClass($field_storage_definitions[$field_name]);
  $schema_property = $rc->getProperty('schema');
  $schema_property->setAccessible(TRUE);
  $schema = $field_storage_definitions[$field_name]->getSchema();
  $schema['columns']['value']['length'] = $field_length;
  $schema_property->setValue($field_storage_definitions[$field_name], $schema);

  // Update the field definition in the last installed schema repository.
  $schema_repository->setLastInstalledFieldStorageDefinitions($entity_type_id, $field_storage_definitions);

  // Update the storage schema.
  $key_value = \Drupal::keyValue('entity.storage_schema.sql');
  $key_name = $entity_type_id . '.field_schema_data.' . $field_name;
  $storage_schema = $key_value->get($key_name);
  // Update all tables where the field is present.
  foreach ($storage_schema as &$table_schema) {
    $table_schema['fields'][$field_name]['length'] = $field_length;
  }
  $key_value->set($key_name, $storage_schema);

  // Update the database tables where the field is part of.
  $db = Drupal::database();
  foreach ($storage_schema as $table_name => $table_schema) {
    $db->schema()->changeField($table_name, $field_name, $field_name, $table_schema['fields'][$field_name]);
  }

}