On this page
- General notes
- Core issue discussion
- Example: adding a new base field to an entity type
- Example: updating a field from an obsolete type to a new type
- Updating field storage config items
- Updating field config items
- Updating entity view display configs
- Updating entity form configs
- Other examples
- Updating a base field type
- Deleting a base field type
- Useful drush commands while developing your hook_update_N
- Checking the current schema version of a module
- Manually setting the current schema version of a module
Updating Entities and Fields in Drupal 8
This documentation needs work. See "Help improve this page" in the sidebar.
If your module is making a data model change related to entities and fields, then you will need to write a hook_update_N() function that will update the sites for existing users of your module who already had it installed before you made the change, so that they can continue to function. This is described below.
General notes
Some notes on hook_update_N() functions:
- The hook_update_N() skeleton section on the parent page tells how/where to create your hook_update_N() function.
- Combine all your current data model updates into one hook_update_N() function, but don't combine them with other updates that were done previously.
Core issue discussion
A discussion to add helpers for better fields DX into core can be found here: #937442: Field type modules cannot maintain their field schema (field type schema change C(R)UD is needed)
Example: adding a new base field to an entity type
The field definition has to be repeated from the entity class, as the definition in the entity class can't be relied on during the update process:
function example_update_8701() {
$field_storage_definition = BaseFieldDefinition::create('boolean')
->setLabel(t('Revision translation affected'))
->setDescription(t('Indicates if the last edit of a translation belongs to current revision.'))
->setReadOnly(TRUE)
->setRevisionable(TRUE)
->setTranslatable(TRUE);
\Drupal::entityDefinitionUpdateManager()
->installFieldStorageDefinition('revision_translation_affected', 'block_content', 'block_content', $field_storage_definition);
}
Example: updating a field from an obsolete type to a new type
As one example of a more drastic change that wasn't handled by the automatic update system that handles entity and field schema updates, on issue #1847596: Remove Taxonomy term reference field in favor of Entity reference, the Taxonomy Reference field was removed, in place of using the more generic Entity Reference field. This change was not a simple schema update, so the automatic system didn't take care of it. At the time of that change, Drupal Core was not attempting to provide updates for data model changes, but the contributed HEAD to HEAD module was.
So, the HEAD to HEAD project provided a function called head2head_1847596() (in head2head.module) to update an existing site's taxonomy reference fields to be generic entity reference fields.
This function is a bit complex, and consists of several parts. Each part does operations similar to those shown on the page about updating configuration in Drupal 8.
Updating field storage config items
In this section, the function locates all the config items of type 'taxonomy_term_reference', and updates them to be entity reference fields instead.
if (!$field_storage_configs = \Drupal::entityTypeManager()->getStorage('field_storage_config')->loadByProperties(array('type' => 'taxonomy_term_reference'))) {
return;
}
foreach ($field_storage_configs as $field_storage) {
// Since the usual workflow for field storages do not allow changing the
// field type, we have to work around it in this case.
$new_field_storage = $field_storage->toArray();
$new_field_storage['type'] = 'entity_reference';
$new_field_storage['module'] = 'core';
$new_field_storage['settings']['target_type'] = 'taxonomy_term';
$vocabulary_name = $new_field_storage['settings']['allowed_values'][0]['vocabulary'];
unset($new_field_storage['settings']['allowed_values']);
$new_field_storage = FieldStorageConfig::create($new_field_storage);
$new_field_storage->original = $new_field_storage;
$new_field_storage->enforceIsNew(FALSE);
$new_field_storage->save();
Updating field config items
In this section, the function locates all field config items for the storage config item it's working on, and updates these.
$field_name = $field_storage->getName();
if (!$fields = \Drupal::entityTypeManager()->getStorage('field_config')->loadByProperties(['field_name' => $field_name])) {
continue;
}
foreach ($fields as $field) {
$new_field = $field->toArray();
$new_field['field_type'] = 'entity_reference';
$new_field['settings'] = [
'handler' => 'default:taxonomy_term',
'handler_settings' => [
'target_bundles' => [$vocabulary_name => $vocabulary_name],
// Enable auto-create.
'auto_create' => TRUE,
],
];
$new_field = FieldConfig::create($new_field);
$new_field->original = $field;
$new_field->enforceIsNew(FALSE);
$new_field->save();
Updating entity view display configs
In this section, the function locates all entity views display config items that are displaying the field being worked on, and updates them.
$properties = [
'targetEntityType' => $field->getTargetEntityTypeId(),
'bundle' => $field->getTargetBundle()
];
if ($view_displays = \Drupal::entityTypeManager()->getStorage('entity_view_display')->loadByProperties($properties)) {
foreach ($view_displays as $view_display) {
if ($component = $view_display->getComponent($field_name)) {
// Map taxonomy term reference formatters to entity reference ones.
switch ($component['type']) {
case 'taxonomy_term_reference_plain':
$type = 'entity_reference_label';
$settings = [
'link' => FALSE,
];
break;
case 'taxonomy_term_reference_link':
$type = 'entity_reference_label';
$settings = [
'link' => TRUE,
];
break;
case 'taxonomy_term_reference_rss_category':
$type = 'entity_reference_rss_category';
$settings = [];
break;
}
$view_display->setComponent($field_name, [
'type' => $type,
'settings' => $settings,
] + $component)->save();
}
}
}
Note: Ensure your schema definitions are updated to match the new settings.
Updating entity form configs
In this section, the function locates all entity form config items that contain widgets for the field being worked on, and updates them.
$properties = array(
'targetEntityType' => $field->getTargetEntityTypeId(),
'bundle' => $field->getTargetBundle()
);
if ($form_displays = \Drupal::entityTypeManager()->getStorage('entity_form_display')->loadByProperties($properties)) {
foreach ($form_displays as $form_display) {
if ($component = $form_display->getComponent($field_name)) {
$form_display->setComponent($field_name, [
'type' => 'entity_reference_autocomplete_tags',
'settings' => [
'match_operator' => 'CONTAINS',
'size' => '60',
'placeholder' => '',
],
] + $component)->save();
}
}
}
}
}
Note: Ensure your schema definitions are updated to match the new settings.
Example: Updating name and description of Entity programatically.
// To update name and description of content type Page
$configEntity = \Drupal::entityTypeManager()
->getStorage('node_type')
->load('page');
if (!$configEntity) {
return NULL;
}
$configEntity->set('name', 'New name page');
$configEntity->set('description', 'New updated description');
$configEntity->save();Other examples
Updating a base field type
If you need to update the type of a custom BaseFieldDefinition, the general process after having updated the entity class itself is:
- Store the existing values for the field
- Include revision values if the field is revisionable - for brevity and clarity an example of this is not included here, see https://drupal.stackexchange.com/questions/311803/how-to-change-a-custom... for more information.
- Clear out the values from the field (required to uninstall a field)
- Uninstall the field
- Create a new BaseFieldDefinition reflecting the entity class update
- Install the new definition
- Restore the values from step 1
For example, changing the type from `boolean` to `string`:
$database = \Drupal::database();
$transaction = $database->startTransaction();
$entity_type_manager = \Drupal::entityTypeManager();
$bundle_of = 'node';
$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();
$definition_manager = \Drupal::entityDefinitionUpdateManager();
// Store the existing values.
$status_values = $database->select($table_name)
->fields($table_name, [$id_key, 'status_field'])
->execute()
->fetchAllKeyed();
// Clear out the values.
$database->update($table_name)
->fields(['status_field' => NULL])
->execute();
// Uninstall the field.
$field_storage_definition = $definition_manager->getFieldStorageDefinition('status_field', $bundle_of);
$definition_manager->uninstallFieldStorageDefinition($field_storage_definition);
// Create a new field definition.
$new_status_field = BaseFieldDefinition::create('string')
->setLabel(t('Status field'))
->setDescription(t('The status - either no, yes or skip.'))
->setDefaultValue('no')
->setRevisionable(FALSE)
->setTranslatable(FALSE);
// Install the new definition.
$definition_manager->installFieldStorageDefinition('status_field', $bundle_of, $bundle_of, $new_status_field);
// Restore the values.
$value_map = [
'1' => 'yes',
'0' => 'no',
];
foreach ($status_values as $id => $value) {
$database->update($table_name)
->fields(['status_field' => $value_map[$value]])
->condition($id_key, $id)
->execute();
}
// Commit transaction.
unset($transaction);
Deleting a base field type
If you need to delete a custom field from a BaseFieldDefinition the general process is:
- Delete the code from the EntityClass::BaseFieldDefinition
- Delete the field from
entity_keysin entity definition notation if it is defined there - Uninstall the field
- Run cron
For example:
$update_manager = \Drupal::entityDefinitionUpdateManager();
$definition = $update_manager->getFieldStorageDefinition('name_of_old_field_to_delete', 'entity_type');
$update_manager->uninstallFieldStorageDefinition($definition);
Note that while the field data will be gone from the database, the field widget may still show up on entity edit pages until you've run cron.
See the change record on Write update functions for entity schema updates, automation removed for other similar examples.
Useful drush commands while developing your hook_update_N
Checking the current schema version of a module
Drupal 8
drush php-eval "echo drupal_get_installed_schema_version('my_module');"
Drupal 9+
drush php-eval "echo \Drupal::service('update.update_hook_registry')->getInstalledVersion('my_module');"
Manually setting the current schema version of a module
Drupal 8
drush php-eval "echo drupal_set_installed_schema_version('my_module', '8000');"
Drupal 9+
drush php-eval "\Drupal::service('update.update_hook_registry')->setInstalledVersion('my_module', '9000');"
Help improve this page
You can:
- Log in, click Edit, and edit this page
- Log in, click Discuss, update the Page status value, and suggest an improvement
- Log in and create a Documentation issue with your suggestion
Still on Drupal 7? Security support for Drupal 7 ended on 5 January 2025. Please visit our Drupal 7 End of Life resources page to review all of your options.