0

In a custom module, I have a content entity in which I have defined this field:

$fields['formheader'] = BaseFieldDefinition::create('string')
  ->setLabel(t('Header'))
  ->setSettings([
    'max_length' => 255,
    'text_processing' => 0,
  ])
  ->setDefaultValue('')
  ->setDisplayOptions('view', [
    'label' => 'above',
    'type' => 'string',
    'weight' => $weight,
  ])
  ->setDisplayOptions('form', [
    'type' => 'string_textfield',
    'weight' => $weight,
  ])
  ->setDisplayConfigurable('form', TRUE)
  ->setDisplayConfigurable('view', TRUE);

I'd like to change its type from string to string_long and max_length from 255 to 1024.
How can I do that without uninstalling my module, changing my source code and installing my module again (there is already content in this entity)?

1

You don't need to uninstall the module, you can write some update hooks instead.

Since it's not possible to update the field type, you have to manually move the field values to a new field.

First create a new field for the cloned values:

$fields['formheader_clone'] = BaseFieldDefinition::create('string_long')
  ->setLabel(t('Header'))
  ->setSettings([
    'max_length' => 1024,
    'text_processing' => 0,
  ])
  ->setDefaultValue('')
  ->setDisplayOptions('view', [
    'label' => 'above',
    'type' => 'string',
    'weight' => $weight,
  ])
  ->setDisplayOptions('form', [
    'type' => 'string_textfield',
    'weight' => $weight,
  ])
  ->setDisplayConfigurable('form', TRUE)
  ->setDisplayConfigurable('view', TRUE);

Then add this update hook:

function mymodule_update_8000() {
  $entity_definition_update_manager = \Drupal::service('entity.definition_update_manager');

  // Migrate the fields and create the new field.
  $field_definition = BaseFieldDefinition::create('string_long')
    ->setLabel(t('Header'))
    ->setSettings([
      'max_length' => 1024,
      'text_processing' => 0,
    ])
    ->setDefaultValue('')
    ->setDisplayOptions('view', [
      'label' => 'above',
      'type' => 'string',
      'weight' => $weight,
    ])
    ->setDisplayOptions('form', [
      'type' => 'string_textfield',
      'weight' => $weight,
    ])
    ->setDisplayConfigurable('form', TRUE)
    ->setDisplayConfigurable('view', TRUE);
  $entity_definition_update_manager->installFieldStorageDefinition('formheader_clone', 'my_entity', 'my_module', $field_definition);

  drupal_flush_all_caches();

  $entities = \Drupal::entityTypeManager()->getStorage('my_entity')->loadMultiple();
  foreach ($entities as $entity) {
    $entity->formheader_clone->value = $entity->formheader->value;
    $entity->save();
  }

  // Delete the storage definition if it was defined before.
  $storage_definition = $entity_definition_update_manager->getFieldStorageDefinition('formheader', 'my_entity');
  if ($storage_definition) {
    $entity_definition_update_manager->uninstallFieldStorageDefinition($storage_definition);
  }
}

Now run drush updb

Now you have to update the formheader field in your custom entity, this is what it must look like:

$fields['formheader'] = BaseFieldDefinition::create('string_long')
  ->setLabel(t('Header'))
  ->setSettings([
    'max_length' => 1024,
    'text_processing' => 0,
  ])
  ->setDefaultValue('')
  ->setDisplayOptions('view', [
    'label' => 'above',
    'type' => 'string',
    'weight' => $weight,
  ])
  ->setDisplayOptions('form', [
    'type' => 'string_textfield',
    'weight' => $weight,
  ])
  ->setDisplayConfigurable('form', TRUE)
  ->setDisplayConfigurable('view', TRUE);

Now add this update hook:

function mymodule_update_8001() {
  $entity_definition_update_manager = \Drupal::service('entity.definition_update_manager');

  $field_definition = BaseFieldDefinition::create('string_long')
    ->setLabel(t('Header'))
    ->setSettings([
      'max_length' => 1024,
      'text_processing' => 0,
    ])
    ->setDefaultValue('')
    ->setDisplayOptions('view', [
      'label' => 'above',
      'type' => 'string',
      'weight' => $weight,
    ])
    ->setDisplayOptions('form', [
      'type' => 'string_textfield',
      'weight' => $weight,
    ])
    ->setDisplayConfigurable('form', TRUE)
    ->setDisplayConfigurable('view', TRUE);
  $entity_definition_update_manager->installFieldStorageDefinition('formheader', 'my_entity', 'my_module', $field_definition);

  drupal_flush_all_caches();

  $entities = \Drupal::entityTypeManager()->getStorage('my_entity')->loadMultiple();
  foreach ($entities as $entity) {
    $entity->formheader->value = $entity->formheader_clone->value;
    $entity->save();
  }

  // Delete the storage definition if it was defined before.
  $storage_definition = $entity_definition_update_manager->getFieldStorageDefinition('formheader_clone', 'my_entity');
  if ($storage_definition) {
    $entity_definition_update_manager->uninstallFieldStorageDefinition($storage_definition);
  }
}

Now run drush updb again

It's important to do it in the correct order

13
  • When I execute this, I get this error: Cannot access protected property Drupal\Core\Field\BaseFieldDefinition::$type in amap_update_8001() (line 12 of /Users/efalzon/Sites/JdP8/web/modules/custom/amap/amap.install).. How can I update this protected value?
    – gbmapo
    Jan 9 '20 at 16:14
  • I updated my answer. Please make a backup of your DB before trying to do this. I'm not sure if it will nuke the data :)
    – user72672
    Jan 9 '20 at 16:25
  • Now I get this error: Call to undefined method Drupal\Core\Field\BaseFieldDefinition::setFieldStorageDefinition() in amap_update_8001() (line 10 of /Users/efalzon/Sites/JdP8/web/modules/custom/amap/amap.install).
    – gbmapo
    Jan 9 '20 at 16:44
  • Try setFieldDefinition instead of setFieldStorageDefinition
    – user72672
    Jan 9 '20 at 16:47
  • The error is now: Call to undefined method Drupal\Core\Field\BaseFieldDefinition::setFieldDefinition() in amap_update_8001() (line 11 of /Users/efalzon/Sites/JdP8/web/modules/custom/amap/amap.install).
    – gbmapo
    Jan 9 '20 at 16:57
0

Finally with help from Jdrupal and this article, I made it work:

function amap_update_8001(&$sandbox)
{
  $entity_type_manager = \Drupal::entityTypeManager();
  $bundle_of = 'contract_type';

  $storage = $entity_type_manager->getStorage($bundle_of);
  $bundle_definition = $entity_type_manager->getDefinition($bundle_of);
//Sometimes the primary key isn't 'id'. e.g. 'eid' or 'item_id'.
  $id_key = $bundle_definition->getKey('id');
//If there is no data table defined then use the base table.
  $table_name = $storage->getDataTable() ?: $storage->getBaseTable();
  $database = \Drupal::database();
  $definition_manager = \Drupal::entityDefinitionUpdateManager();

//Store the existing values.
  $header_values = $database->select($table_name)
    ->fields($table_name, [$id_key, 'formheader'])
    ->execute()
    ->fetchAllAssoc($id_key);

//Clear out the values.
  $database->update($table_name)
    ->fields(['formheader' => NULL])
    ->execute();

//Uninstall the field.
  $field_storage_definition = $definition_manager->getFieldStorageDefinition('formheader', $bundle_of);
  $definition_manager->uninstallFieldStorageDefinition($field_storage_definition);

//Create a new field definition.
  $new_formheader = BaseFieldDefinition::create('string_long')
    ->setLabel(t('Header for Form'))
    ->setSettings([
      'max_length' => 1024,
      'text_processing' => 0,
    ])
    ->setDefaultValue('')
    ->setDisplayOptions('view', [
      'label' => 'above',
      'type' => 'string_long',
    ])
    ->setDisplayOptions('form', [
      'type' => 'string_textarea',
    ])
    ->setDisplayConfigurable('form', TRUE)
    ->setDisplayConfigurable('view', TRUE);

//Install the new definition.
  $definition_manager->installFieldStorageDefinition('formheader', $bundle_of, $bundle_of, $new_formheader);

//Restore the values.
  foreach ($header_values as $id => $value) {
    $database->update($table_name)
      ->fields(['formheader' => $value->formheader])
      ->condition($id_key, $id)
      ->execute();
  }

  drupal_flush_all_caches();

  \Drupal::messenger()->addMessage('Module AMAP Update # 8001 was executed successfully.');

}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.