Drupal 7:更新列表的字段定义

我最近不得不更新 Drupal 7 站点,需要将列表字段的字段键更改为不同的值。这在 Drupal 中是不可能的,因为它会进行预检查以确保密钥不存在。如果它发现数据库中存在具有该键的任何值,则它将拒绝更改。这是绝对正确的,但是当您需要更新这些值时会导致一些问题。

解决方案是通过直接在数据库中更改字段定义和字段数据来更改它们。以下函数提供了一种巧妙的小方法,只需传入字段名称和需要更改的键值即可对字段执行此操作。

/** 
 * 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 string $field_name
 *   The field to act upon.
 * @param array $search_replace
 *   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';
  // 获取现有的字段配置。
  $field_definition = db_query("SELECT id, data FROM field_config WHERE field_name = :field_name LIMIT 1", array(':field_name' => $field_name))->fetchAll();
  // 更改 allowed_values 元素中的数据。
  $data = unserialize($field_definition[0]->data);
  foreach ($search_replace as $value => $key) {
    unset($data['settings']['allowed_values'][$value]);
    $data['settings']['allowed_values'][$key] = $value;
  }
 
  // 将新的字段配置保存到数据库中。
  db_update('field_config')
    ->condition('field_name', $field_name)
    ->fields(['data' => serialize($data)])
    ->execute();
  unset($field_definition);
 
  // 从表中获取所有数据。
  $field_values = db_select($field_table, 't')
   ->fields('t', ['entity_id', $field_table_value])
   ->execute()
   ->fetchAll();
 
  foreach ($field_values as $field_value) {
    // 如果该值存在于我们的搜索/替换数组中,则交换它。
    if (isset($search_replace[$field_value->$field_table_value])) {
      $field_key = $search_replace[$field_value->$field_table_value];
    } else {
      continue;
    }
 
    // 将字段数据中的更改保存到数据库中。
    db_update($field_table)
      ->condition('entity_id', $field_value->entity_id)
      ->fields([$field_table_value => $field_key])
      ->execute();
  }
}

此函数采用字段名称并首先更改字段定义以使其正确。然后它将从数据库加载该字段的所有数据并更新值以与新值一致。它不考虑任何字段修订,因此如果您希望更改所有当前和过去的值,则需要将此功能添加到函数中。此外,该功能不使用事务,因此如果中途出现任何问题,您手上将有一堆数据。如果确实发生这种情况,只需重新运行该功能,它就会完成工作。

这个功能非常容易使用。只需创建一个数组,允许将旧键转换为新键,并将其mymodule_update_field_definition()与字段名称一起传递给函数。

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

在上面的例子中,在配置和字段数据表中,字段“field_my_field”的“Key 1”的所有键都更改为1。