Last Updated: August 20, 2016
·
5.058K
· fluxsauce

Updating the storage definition of entities that already have content in Drupal 8

Recently, I was asked to increase the length of a text field. Relatively straight forward, except there was already content.

The SQL storage cannot change the schema for an existing field

I understand the reasoning behind prohibiting an automatic operation, but if I'm making the change explicitly in code, it should just work. Here's how I got around it:

/**
 * Increase FIELD size to 50.
 */
function MODULE_update_8XXX() {
  $database = \Drupal::database();
  // Retrieve existing field data.
  $entity_type = 'ENTITY_TYPE';
  $field = 'FIELD_NAME';
  $tables = [
    "{$entity_type}__$field",
    "{$entity_type}_revision__$field",
  ];
  $existing_data = [];
  foreach ($tables as $table) {
    // Get the old data.
    $existing_data[$table] = $database->select($table)
      ->fields($table)
      ->execute()
      ->fetchAll(PDO::FETCH_ASSOC);

    // Wipe it.
    $database->truncate($table)->execute();
  }

  $field_storage_configs = \Drupal::entityTypeManager()
    ->getStorage('field_storage_config')
    ->loadByProperties([
      'field_name' => $field,
    ]);
  foreach ($field_storage_configs as $field_storage) {
    $new_field_storage = $field_storage->toArray();
    $new_field_storage['settings']['max_length'] = 50;

    $new_field_storage = FieldStorageConfig::create($new_field_storage);
    $new_field_storage->original = $new_field_storage;
    $new_field_storage->enforceIsNew(FALSE);

    $new_field_storage->save();
  }

  // Restore the data.
  foreach ($tables as $table) {
    $insert_query = $database
      ->insert($table)
      ->fields(array_keys(end($existing_data[$table])));
    foreach ($existing_data[$table] as $row) {
      $insert_query->values(array_values($row));
    }
    $insert_query->execute();
  }
}

Loosely inspired by https://www.drupal.org/node/2554097