Change field type with existing data on Drupal 8

On this blog post, you will learn how to change a field type with existing data on Drupal.

Sometimes, you might be asked to perform this kind of changes on a content type, even if you have considered all use cases for the content types on the CMS or how the usage in the long term will be.

If you try to do this change from the admin pages located at:

Home > Administration > Structure > Content types > {Content type name} > Manage fields > {Field name}

You will probably get this error:

The field can’t be changed since already has data

One way to do this is by using a hook_update_N, on my example, the change will be from “Text (plain)” to “Text (formatted, long)” type. This is one of the usual cases when a client wants to add more than 255 characters on the text field.

function mymodule_update_8001() {
  $database = \Drupal::database();
  $entityType = 'node';
  $fieldName = 'field_article';
  $table = $entityType . '__' . $fieldName;
  $currentRows = NULL;
  $newFieldsList = [];
  $fieldStorage = FieldStorageConfig::loadByName($entityType, $fieldName);

  if (is_null($fieldStorage)) {
    return;
  }

  // Get all current data from DB.
  if ($database->schema()->tableExists($table)) {
    // The table data to restore after the update is completed.
    $currentRows = $database->select($table, 'n')
      ->fields('n')
      ->execute()
      ->fetchAll();
  }

  // Use existing field config for new field.
  foreach ($fieldStorage->getBundles() as $bundle => $label) {
    $field = FieldConfig::loadByName($entityType, $bundle, $fieldName);
    $newField = $field->toArray();
    $newField['field_type'] = 'text_long';
    $newField['settings'] = [];
    $newFieldsList[] = $newField;
  }

  // Deleting field storage which will also delete bundles(fields).
  $newFieldStorage = $fieldStorage->toArray();
  $newFieldStorage['type'] = 'text_long';
  $newFieldStorage['settings'] = [];

  $fieldStorage->delete();

  // Purge field data now to allow new field and field_storage with same name
  // to be created.
  field_purge_batch(40);

  // Create new field storage.
  $newFieldStorage = FieldStorageConfig::create($newFieldStorage);
  $newFieldStorage->save();

  // Create new fields.
  foreach ($newFieldsList as $nfield) {
    $nfieldConfig = FieldConfig::create($nfield);
    $nfieldConfig->save();
  }

  // Restore existing data in new table.
  if (!is_null($currentRows)) {
    foreach ($currentRows as $row) {
      $database->insert($table)
        ->fields((array) $row)
        ->execute();
    }
  }
}

These are some steps to follow:

  1. Look for the machine name of the field.
  2. Go to `admin/reports/fields` and search for the field targeted to be changed. It’s important to know how many content types are using it.
  3. Look for the table on the DB used by the field and check how many rows there are.
  4. Add the snippet in a {module_name}.install from one of your custom modules.
  5. Replace the $entityType and $fieldName variables according to the entity and field  targeted on your case.
  6. Prepare a database backup.
  7. Run the update by going to /update.php on your browser or by running the command drush updb on your terminal.
  8. Check if the update was successfully performed.
  9. Use the information from steps 2) and 3) and check if the field content is still there.

If you are using Configuration Management feature on your site, here are some additional steps:

  1. After you verify that the new update works fine, export the new configuration by running drush config:export sync (on my case, “sync” is the name of the configuration directory)
  2. When you need to run this update on other environments, here is the order on how the commands will be executed:
    1. drush updb -y
    2. drush config:import sync
  3. Run: drush updb -y

You can find more info about updating entities and fields in Drupal 8 on this page of the official documentation.



Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.