Skip to content

Instantly share code, notes, and snippets.

@swichers
Created November 23, 2017 00:07
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save swichers/d0141a3ab14d93835d09943684480ee0 to your computer and use it in GitHub Desktop.
Save swichers/d0141a3ab14d93835d09943684480ee0 to your computer and use it in GitHub Desktop.
drush 8 command to rebuild Drupal 8 field mappings
<?php
/**
* @file
* Drush command implementation for rebuild-field-map.
*
* This will allow rebuilding the field mapping stored in the key value store
* for a given entity type. This is helpful when trying to repair a corrupted
* field map store without having to add and remove fields.
*/
/**
* Implements HOOK_drush_command().
*/
function rebuild_field_map_drush_command() {
$items = [];
$items['rebuild-field-map'] = [
'description' => dt('Rebuilds the bundle field maps inside the key value store.'),
'arguments' => [
'type' => dt('The entity type to rebuild field maps for.'),
],
'options' => [
'force' => dt('Force rebuilding even if no additions or removals were detected.'),
],
'examples' => [
'drush rfm node --force' => dt('Rebuild the field mappings for nodes and ignore if no changes were detected.'),
'drush rfm paragraph' => dt('Rebuild the field mappings for paragraphs'),
],
'aliases' => ['rfm'],
'command-hook' => 'rebuild',
'required-arguments' => TRUE,
'bootstrap' => DRUSH_BOOTSTRAP_DRUPAL_FULL,
];
return $items;
}
/**
* Process the rebuild command from the user.
*
* @param string $entity_type
* The type of entity to rebuild mappings for.
*
* @return bool
* TRUE on success, FALSE otherwise.
*/
function drush_rebuild_field_map_rebuild($entity_type) {
$bundle_key_value = \Drupal::service('keyvalue')
->get('entity.definitions.bundle_field_map');
$new_mapping = rebuild_field_map_get_bundle_map_by_entity_type($entity_type);
$existing_map = $bundle_key_value->get($entity_type);
$to_be_added = array_diff_key($new_mapping, $existing_map);
$to_be_removed = array_diff_key($existing_map, $new_mapping);
$has_changes = !empty($to_be_added) || !empty($to_be_removed);
drush_print(dt('Rebuilding stored field mapping values for @entity_type.', ['@entity_type' => $entity_type]));
if ($has_changes) {
$rows = [
[
dt('Field'),
NULL,
dt('Change'),
],
];
$rows = array_merge(
$rows,
rebuild_field_map_list_to_table_row($to_be_added, dt('Add')),
rebuild_field_map_list_to_table_row($to_be_removed, dt('Remove'))
);
drush_print_table($rows, TRUE);
if (!empty($to_be_added)) {
drush_print(\Drupal::translation()
->formatPlural(count($to_be_added),
'@count item will be added.',
'@count items will be added.'), 2);
}
if (!empty($to_be_removed)) {
drush_print(\Drupal::translation()
->formatPlural(count($to_be_removed),
'@count item will be removed.',
'@count items will be removed.'), 2);
}
}
else {
drush_log(dt('There are no changes to be made. The stored field mapping matches the generated one.'), 'ok');
}
if ($has_changes || drush_get_option('force', FALSE)) {
if (drush_confirm(dt('Rebuild the field map? This action cannot be undone.'))) {
$bundle_key_value->set($entity_type, $new_mapping);
drupal_flush_all_caches();
drush_log(dt('The field mapping has been rebuilt.'), 'success');
}
else {
return drush_user_abort();
}
}
return TRUE;
}
/**
* Convert a field mapping array into a drush table value.
*
* @param array $map
* The field mapping array keyed by the field name.
* @param string $action
* The action being taken on this set of mappings (Add, Remove).
*
* @return array
* A multivalued array that combines the action with the mapping.
*/
function rebuild_field_map_list_to_table_row(array $map, $action) {
return drush_key_value_to_array_table(
array_combine(
array_keys($map),
array_pad([], count($map), $action)
)
);
}
/**
* Gets the field mappings for the given entity type.
*
* @param string $entity_type
* The type of the entity to generate field mappings for.
*
* @return array
* An array of field mappings ready to store in the key value store.
*/
function rebuild_field_map_get_bundle_map_by_entity_type($entity_type) {
$bundle_field_map = [];
$entity_manager = \Drupal::service('entity_field.manager');
$bundle_info = \Drupal::service('entity_type.bundle.info')
->getBundleInfo($entity_type);
foreach ($bundle_info as $bundle => $info) {
$definitions = $entity_manager->getFieldDefinitions($entity_type, $bundle);
if (empty($definitions)) {
continue;
}
// Filter out the definitions that are not of the target type.
// @todo This filter was based on what was in the current field mapping vs
// what was being returned by getFieldDefinitions(). Is there a way to get
// the list already filtered to this point?
$definitions = array_filter($definitions, function ($definition) {
return $definition instanceof \Drupal\Field\Entity\FieldConfig;
});
if (!empty($definitions)) {
foreach ($definitions as $field_definition) {
// Mimicking Drupal core behavior by pulling bundle and name from the
// definition instead of using the existing bundle and name variables.
$field_bundle = $field_definition->getTargetBundle();
$field_name = $field_definition->getName();
if (!isset($bundle_field_map[$field_name])) {
// This field did not exist yet, initialize it with the type and empty
// bundle list.
$bundle_field_map[$field_name] = [
'type' => $field_definition->getType(),
'bundles' => [],
];
}
$bundle_field_map[$field_name]['bundles'][$field_bundle] = $field_bundle;
}
}
}
return $bundle_field_map;
}
@criley
Copy link

criley commented Sep 2, 2018

Any chance of a Drush 9 version of this?

@matthewboman
Copy link

@tarekchida
Copy link

tarekchida commented Sep 16, 2019

How can I execute this script please ? Drush dose not recognise it :

The drush command 'rebuild-field-map' could not be found

Thank you

@swichers
Copy link
Author

You can add it to a custom module. You would need to name it "rebuild_field_map". Here's an example https://github.com/rakeshjames/custom_drush_command

That set, this was written 2 years ago. Drush has made many changes since then. Those instructions are for Drush 8 when Drush 9 is the current version. This code will not work for Drush 9.

@tarekchida
Copy link

Ok thank you, but this did not remove my bug :/

Table mapping contains invalid field langcode. dans Drupal\Core\Entity\Sql\SqlContentEntityStorage->mapToStorageRecord()

Do you have any idea please ?
Thank you

@anagorn
Copy link

anagorn commented Sep 21, 2021

You can add it to a custom module. You would need to name it "rebuild_field_map". Here's an example https://github.com/rakeshjames/custom_drush_command

That set, this was written 2 years ago. Drush has made many changes since then. Those instructions are for Drush 8 when Drush 9 is the current version. This code will not work for Drush 9.

Wanted to let you know this works perfectly for me at Drush 10 + Drupal 8, so thank you!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment