Drupal 7: Update Field Definitions For Lists

Thursday, November 17, 2016 - 18:24

I had to update a Drupal 7 site recently and needed to change the field keys of a list field to be different values. This wasn't possible from within Drupal as it does a pre-check to make sure that the key doesn't already exist. If it finds any values present in the database with that key then it will reject the change. This is absolutely correct but causes a little bit of an issue when you need to update these values.

The solution to this is to alter both the field definition and the field data via directly altering them in the database. The following function provides a neat little way of doing this to a field by just passing in the field name and what the key values need to change to.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/**
 * Updates the definitions and data of a multi field based on a search replace array.
 *
 * For the $search_replace array, the keys must be the current values of the field and the values must be the new values
 * to map the existing values to.
 *
 * @param $field_name string
 *   The field to act upon.
 * @param $search_replace array
 *   The values to replace.
 */
function mymodule_update_field_definition($field_name, $search_replace) {
  $field_table = 'field_data_' . $field_name;
  $field_table_value = $field_name . '_value';
 
  // Get the existing field config.
  $field_definition = db_query("SELECT id, data FROM field_config WHERE field_name = :field_name LIMIT 1", array(':field_name' => $field_name))->fetchAll();
 
  // Change the data in the allowed_values element.
  $data = unserialize($field_definition[0]->data);
  foreach ($search_replace as $value => $key) {
    unset($data['settings']['allowed_values'][$value]);
    $data['settings']['allowed_values'][$key] = $value;
  }
 
  // Save the new field config to the database.
  db_update('field_config')
    ->condition('field_name', $field_name)
    ->fields(['data' => serialize($data)])
    ->execute();
 
  unset($field_definition);
 
  // Get all the data from the table.
  $field_values = db_select($field_table, 't')
    ->fields('t', ['entity_id', $field_table_value])
    ->execute()
    ->fetchAll();
 
  foreach ($field_values as $field_value) {
 
    // If the value exists in our search/replace array then swap it.
    if (isset($search_replace[$field_value->$field_table_value])) {
      $field_key = $search_replace[$field_value->$field_table_value];
    }
    else {
      continue;
    }
 
    // Save the change in field data to the database.
    db_update($field_table)
      ->condition('entity_id', $field_value->entity_id)
      ->fields([$field_table_value => $field_key])
      ->execute();
  }
}

This function takes the field name and first alters the field definitions to be correct. It will then load all data for the field from the database and update the values to be in line with the new values. It doesn't take into account any field revisions and so if you are looking to change all current and past values then you'll need to add this functionality to the function. Also, the function doesn't use transactions and as such if anything fails half-way through you'll have a mess of data on your hands. If this does happen just re-run the function and it will finish things off.

This function is pretty easy to use. Just create an array that allows the old keys to be translated to the new keys and pass this to the mymodule_update_field_definition() function along with the field name.

1
2
3
4
5
6
$search_replace = array(
  'Key 1' => 1,
  'Key 2' => 2,
);
$field_name = 'field_my_field';
mymodule_update_field_definition($field_name, $search_replace);

In the above example, all keys of "Key 1" for the field "field_my_field" are changed to 1 in the configuration and the the field data table.

Category: 
philipnorton42's picture

Philip Norton

Phil is the founder and administrator of #! code and is an IT professional working in the North West of the UK.
Google+ | Twitter

Add new comment