Общее обнаружение измененных полей в пользовательской форме перед сохранением узла

12

Я добавляю определенные поля из типа контента в пользовательскую форму, используя field_attach_form (). Когда форма отправлена, я обрабатываю эти поля, вызывая field_attach_form_validate () и field_attach_submit () из обратных вызовов #validate и #submit.

В этот момент я хочу сравнить подготовленный объект узла после отправки с исходным узлом и беспокоиться об этом только для node_save (), если какое-либо из полей изменилось. Поэтому я начинаю с загрузки исходного узла с помощью entity_load_unchanged().

К сожалению, массивы полей в исходном объекте узла не соответствуют массивам полей в подготовленном объекте узла, который ожидает сохранения, даже если в поля не было внесено никаких изменений, поэтому просто "$ old_field == $ new_field" «Сравнение невозможно. Например, простое текстовое поле выглядит так в оригинале:

$old_node->field_text['und'][0] = array(
  'value' => 'Test',
  'format' => NULL,
  'safe_value' => 'Test',
);

В то время как в подготовленном узле это выглядит так.

$node->field_text['und'][0] = array(
  'value' => 'Test',
);

Вы можете подумать о том, чтобы просто сравнить ключ «значение», но затем вы столкнетесь с полями, составленными из других элементов, которые не имеют ключей «значение». Например, давайте посмотрим на поле адреса, в котором нет ключа 'value', а в старых и подготовленных узлах есть ключи, которые не имеют аналогов.

Старый узел

$old_node->field_address['und'][0] = array(
  'country' => 'GB',
  'administrative_area' => 'Test',
  'sub_administrative_area' => NULL,
  'locality' => 'Test',
  'dependent_locality' => NULL,
  'postal_code' => 'Test',
  'thoroughfare' => 'Test',
  'premise' => 'Test',
  'sub_premise' => NULL,
  'organisation_name' => 'Test',
  'name_line' => 'Test',
  'first_name' => NULL,
  'last_name' => NULL,
  'data' => NULL,
);

Подготовленный узел

$node->field_address['und'][0] = array(
  'element_key' => 'node|page|field_address|und|0',
  'thoroughfare' => 'Test',
  'premise' => 'Test',
  'locality' => 'Test',
  'administrative_area' => 'Test',
  'postal_code' => 'Test',
  'country' => 'GB',
  'organisation_name' => 'Test',
  'name_line' => 'Test',
);

Для пустых полей есть еще одно несоответствие.

Старый узел

$old_node->field_text = array();

Подготовленный узел

$node->field_text = array(
  'und' => array(),
);

Могу ли я в общем сравнить старое и новое значение любого поля, чтобы определить, изменилось ли оно или нет?
Это просто невозможно?

патологический
источник
Я думаю, что вы можете поиграть с _field_invoke()чем-то или с чем-то связанным, чтобы подготовить полную структуру поля из «подготовленного» узла, визуализировать оба поля и просто сравнить эти строки HTML. Просто идея.
Калабро
@kalabro Да, это определенно верный путь, я не могу не чувствовать, что это будет очень плохо для производительности - чтобы сделать его обобщенным, вам нужно будет загружать каждый бит информации о поле индивидуально, используя отправку формы. Или, я думаю, вы могли бы написать агрегированный запрос для получения данных, но тогда важные хуки могут не сработать. Концептуально это кажется возможным, но я думаю, что реализация будет довольно сложной
Клайв
@kalabro Я не совсем понимаю эту идею. Не могли бы вы опубликовать некоторый псевдокод, чтобы продемонстрировать, как подготовить структуру поля, а затем отобразить ее, как вы описали?
morbiD

Ответы:

9

Это, наконец, должно работать как общее решение. Спасибо Клайву и morbiD за весь вклад.

Передайте обе версии узла следующей функции. Это будет:

  1. Извлеките все редактируемые поля обнаруженного типа контента и их редактируемые столбцы (т. Е. Элементы, которые могут появиться в пользовательской форме) из базы данных в одном запросе.

  2. Игнорировать поля и столбцы, которые полностью пусты в обеих версиях.

  3. Обрабатывайте поле с разным количеством значений в двух версиях как изменение.

  4. Переберите все поля, значения и столбцы и сравните две версии.

  5. Сравнивайте элементы не тождественно (! =), Если они числовые, и тождественно (! ==), если они являются чем-то другим.

  6. Немедленно верните TRUE при первом обнаруженном изменении (так как одного изменения достаточно, чтобы узнать, что нам нужно повторно сохранить узел).

  7. Возвратите FALSE, если никаких изменений не обнаружено после сравнения всех значений.

  8. Рекурсивно сравнивайте коллекции полей, загружая их и их схему и передавая результаты себе. Это ДОЛЖНО даже позволить ему сравнивать вложенные коллекции полей. Код НЕ должен иметь никакой зависимости от модуля Field Collection.

Дайте мне знать, если в этом коде есть еще ошибки или опечатки.

/*
 * Pass both versions of the node to this function. Returns TRUE if it detects any changes and FALSE if not.
 * Pass field collections as an array keyed by field collection ID.
 *
 * @param object $old_entity
 *   The original (stored in the database) node object.
 *   This function may also pass itself a FieldCollectionItemEntity object to compare field collections.
 * @param object $new_entity
 *   The prepared node object for comparison.
 *   This function may also pass itself a FieldCollectionItemEntity object to compare field collections.
 */
function _fields_changed($old_entity, $new_entity) {
  // Check for node or field collection.
  $entity_is_field_collection = (get_class($old_entity) == 'FieldCollectionItemEntity');

  $bundle = ($entity_is_field_collection ? $old_entity->field_name : $old_entity->type);

  // Sanity check. Exit and throw an error if the content types don't match.
  if($bundle !== ($entity_is_field_collection ? $new_entity->field_name : $new_entity->type)) {
    drupal_set_message('Content type mismatch. Unable to save changes.', 'error');
    return FALSE;
  }

  // Get field info.
  $field_read_params = array(
    'entity_type' => ($entity_is_field_collection ? 'field_collection_item' : 'node'),
    'bundle' => $bundle
  );
  $fields_info = field_read_fields($field_read_params);

  foreach($fields_info as $field_name => $field_info) {
    $old_field = $old_entity->$field_name;
    $new_field = $new_entity->$field_name;

    // Check the number of values for each field, or if they are populated at all.
    $old_field_count = (isset($old_field[LANGUAGE_NONE]) ? count($old_field[LANGUAGE_NONE]) : 0);
    $new_field_count = (isset($new_field[LANGUAGE_NONE]) ? count($new_field[LANGUAGE_NONE]) : 0);

    if ($old_field_count != $new_field_count) {
      // The two versions have a different number of values. Something has changed.
      return TRUE;
    } elseif ($old_field_count > 0 && $new_field_count > 0) {
      // Both versions have an equal number of values. Time to compare.

      // See if this field is a field collection.
      if ($field_info['type'] == 'field_collection') {

        foreach ($new_field[LANGUAGE_NONE] as $delta => $values) {
          $old_field_collection = entity_load_unchanged('field_collection_item', $values['entity']->item_id);
          $new_field_collection = $values['entity'];

          if (_fields_changed($old_field_collection, $new_field_collection)) {
            return TRUE;
          }
        }
        unset($delta, $values);

      } else {
        foreach($old_field[LANGUAGE_NONE] as $delta => $value) {
          foreach($field_info['columns'] as $field_column_name => $field_column_info) {
            $old_value = $old_field[LANGUAGE_NONE][$delta][$field_column_name];
            $new_value = $new_field[LANGUAGE_NONE][$delta][$field_column_name];
            $field_column_type = $field_column_info['type'];

            // As with the overall field, exit if one version has a value and the other doesn't.
            if (isset($old_value) != isset($new_value)) {
              return TRUE;
            } elseif (isset($old_value) && isset($new_value)) {
              // The column stores numeric data so compare values non-identically.
              if (in_array($field_column_type, array('int', 'float', 'numeric'))) {
                if ($new_value != $old_value) {
                  return TRUE;
                }
              }
              // The column stores non-numeric data so compare values identically,
              elseif ($new_value !== $old_value) {
                return TRUE;
              }
            } else {
              // Included for clarity. Both values are empty so there was obviously no change.
            }
          } 
          unset($field_column_name, $field_column_info);
        }
        unset($delta, $value);
      }
    } else {
      // Included for clarity. Both values are empty so there was obviously no change.
    }
  }
  unset($field_name, $field_info);
  // End of field comparison loop.

  // We didn't find any changes. Don't resave the node.
  return FALSE;
}

Иногда вам интересно узнать, какие поля изменились. Чтобы узнать это, вы можете использовать эту версию функции:

/*
 * Pass both versions of the node to this function. Returns an array of
 * fields that were changed or an empty array if none were changed.
 * Pass field collections as an array keyed by field collection ID.
 *
 * @param object $old_entity
 *   The original (stored in the database) node object.
 *   This function may also pass itself a FieldCollectionItemEntity object to compare field collections.
 * @param object $new_entity
 *   The prepared node object for comparison.
 *   This function may also pass itself a FieldCollectionItemEntity object to compare field collections.
 */
function _fields_changed($old_entity, $new_entity) {
  // Check for node or field collection.
  $entity_is_field_collection = (get_class($old_entity) == 'FieldCollectionItemEntity');

  $bundle = ($entity_is_field_collection ? $old_entity->field_name : $old_entity->type);

  // Sanity check. Exit and throw an error if the content types don't match.
  if ($bundle !== ($entity_is_field_collection ? $new_entity->field_name : $new_entity->type)) {
    drupal_set_message('Content type mismatch. Unable to save changes.', 'error');
    return FALSE;
  }

  // Get field info.
  $field_read_params = array(
    'entity_type' => ($entity_is_field_collection ? 'field_collection_item' : 'node'),
    'bundle' => $bundle
  );
  $fields_info = field_read_fields($field_read_params);

  $fields_changed = array();

  foreach ($fields_info as $field_name => $field_info) {
    $old_field = $old_entity->$field_name;
    $new_field = $new_entity->$field_name;

    // Check the number of values for each field, or if they are populated at all.
    $old_field_count = (isset($old_field[LANGUAGE_NONE]) ? count($old_field[LANGUAGE_NONE]) : 0);
    $new_field_count = (isset($new_field[LANGUAGE_NONE]) ? count($new_field[LANGUAGE_NONE]) : 0);

    if ($old_field_count != $new_field_count) {
      // The two versions have a different number of values. Something has changed.
      $fields_changed[] = $field_name;
    }
    elseif ($old_field_count > 0 && $new_field_count > 0) {
      // Both versions have an equal number of values. Time to compare.

      // See if this field is a field collection.
      if ($field_info['type'] == 'field_collection') {

        foreach ($new_field[LANGUAGE_NONE] as $delta => $values) {
          $old_field_collection = entity_load_unchanged('field_collection_item', $values['entity']->item_id);
          $new_field_collection = $values['entity'];

          $fields_changed = array_merge($fields_changed, _fields_changed($old_field_collection, $new_field_collection));
        }
        unset($delta, $values);

      }
      else {
        foreach ($old_field[LANGUAGE_NONE] as $delta => $value) {
          foreach ($field_info['columns'] as $field_column_name => $field_column_info) {
            $old_value = $old_field[LANGUAGE_NONE][$delta][$field_column_name];
            $new_value = $new_field[LANGUAGE_NONE][$delta][$field_column_name];
            $field_column_type = $field_column_info['type'];

            // As with the overall field, exit if one version has a value and the other doesn't.
            if (isset($old_value) != isset($new_value)) {
              $fields_changed[] = $old_field;
            }
            elseif (isset($old_value) && isset($new_value)) {
              // The column stores numeric data so compare values non-identically.
              if (in_array($field_column_type, array(
                'int',
                'float',
                'numeric'
              ))) {
                if ($new_value != $old_value) {
                  $fields_changed[] = $field_name;
                }
              }
              // The column stores non-numeric data so compare values identically,
              elseif ($new_value !== $old_value) {
                $fields_changed[] = $field_name;
              }
            }
            else {
              // Included for clarity. Both values are empty so there was obviously no change.
            }
          }
          unset($field_column_name, $field_column_info);
        }
        unset($delta, $value);
      }
    }
    else {
      // Included for clarity. Both values are empty so there was obviously no change.
    }
  }
  unset($field_name, $field_info);
  // End of field comparison loop.

  return $fields_changed;
}

Иногда вы можете захотеть сделать так, чтобы изменение определенных полей узла не приводило к обновлению «измененной» временной метки этого узла. Это может быть реализовано следующим образом:

/**
 * Implements hook_node_presave().
 */
function mymodule_node_presave($node) {
  $fields_changed = _fields_changed($node->original, $node);
  $no_update_timestamp_fields = array('field_subject', 'field_keywords');
  if (!empty($fields_changed) &&
    empty(array_diff($fields_changed, $no_update_timestamp_fields))) {
    // Don't change the $node->changed timestamp if one of the fields has
    // been changed that should not affect the timestamp.
    $node->changed = $node->original->changed;
  }
}

РЕДАКТИРОВАТЬ (30.07.2013) Укреплена поддержка сбора на местах. Добавлена ​​поддержка полей с несколькими значениями.

РЕДАКТИРОВАТЬ (31.07.2015) Добавлена ​​версия функции, которая возвращает, какие поля были изменены, и пример использования.

Эрик Н
источник
Это здорово, я чувствую, что это должен быть какой-то модуль API для разработчиков.
Желе
3

Вот еще один, более простой подход, позволяющий избежать сложных сравнений значений на стороне сервера и работающий с любой формой:

  1. Используйте jQuery, чтобы определить, изменились ли значения формы
  2. Установите значение скрытого элемента, чтобы указать, что форма изменилась.
  3. Проверьте скрытый элемент значения на стороне сервера и обработайте, как требуется.

Вы можете использовать плагин грязной формы jQuery, такой как https://github.com/codedance/jquery.AreYouSure

Хотя другие, которые позволяют слушать измененную форму / грязный статус, также будут работать.

Добавьте слушателя, чтобы установить значение скрытого элемента формы:

Установите для скрытого элемента формы значение по умолчанию «изменено», чтобы сохранить по умолчанию для пользователей с отключенным JavaScript (~ 2%).

например:

// Clear initial state for js-enabled user
$('input#hidden-indicator').val('')
// Add changed listener
$('#my-form').areYouSure({
    change: function() {
      // Set hidden element value
      if ($(this).hasClass('dirty')) {
        $('input#hidden-indicator').val('changed');
      } else {
        $('input#hidden-indicator').val('');
      }
    }
 });

Затем вы можете проверить значение скрытого элемента

if ($form_state['values']['hidden_indicator'] == 'changed') { /* node_save($node) */ }

в вашей форме подтвердить / отправить обработчики.

Дэвид Томас
источник
2
Хорошее решение, хотя, очевидно, есть пользователи без js. Также проверьте Drupal.behaviors.formUpdated в файле misc / form.js ядра drupal. Еще одна вещь, которую стоит отметить, это то, как работают некоторые редакторы wysiwyg и их модули drupal, обнаружение измененного значения не всегда так просто, как должно быть.
Роби
Да, установка значения по умолчанию «изменен» для скрытого элемента сохранит по умолчанию для тех немногих пользователей, у которых не включен js - небольшой процент. Интересное замечание относительно того, что, Drupal.behaviors.formUpdatedвозможно, val()может быть связано с тем, что, хотя, похоже, оно сработает без фактического изменения значения (например, включает в себя событие щелчка), тогда как выделенные плагины лучше обнаруживают измененные значения формы.
Дэвид Томас
0

Я не уверен, что это идеально, но почему бы не взять его наоборот, сравнивая формы вместо узловых объектов ?

Я не уверен, что вы строго находитесь в форме узла, но в любом случае вы можете визуализировать форму с помощью старого узла и нового узла:

module_load_include('inc', 'node', 'node.pages');
node_object_prepare($new_node);
$new_form = drupal_get_form($new_node->node_type . '_node_form', $new_node);
node_object_prepare($old_node);
$old_form = drupal_get_form($old_node->node_type . '_node_form', $old_node);

Сравните ваши формы ...

Я надеюсь, что это хороший трек ... дайте мне знать.

Григорий Капустин
источник
Я уже изучил drupal_get_form (), но я не знал, что вы можете передать ему $ node в качестве второго параметра. Тем не менее, я только что протестировал ваш пример кода выше и, к сожалению, хотя возвращаемые структуры массивов совпадают, значения не совпадают. Взгляните на этот рекурсивный array_diff_assoc () для поля адреса, с которым я тестирую: i.imgur.com/LUDPu1R.jpg
morbiD
Я вижу, что array_diff_assoc, но вы бы успели дать dpm обоих drupal_get_form? Там может быть способ обойти это.
Григорий Капустин
0

Вот метод, использующий hook_node_presave ($ node). Это всего лишь макет, если вы думаете, что он помогает, протестируйте его и улучшите в соответствии с вашими потребностями!

  /**
   * Implements hook_node_presave().
   *
   * Look for changes in node fields, before they are saved
   */
  function mymodule_node_presave($node) {

    $changes = array();

    $node_before = node_load($node->nid);

    $fields = field_info_instances('node', $node->type);
    foreach (array_keys($fields) as $field_name) {

      $val_before = field_get_items('node', $node_before, $field_name);
      $val = field_get_items('node', $node, $field_name);

      if ($val_before != $val) {

        //if new field values has more instances then old one, it has changed
        if (count($val) != count($val_before)) {
          $changes[] = $field_name;
        } else {
          //cycle throught 1 or multiple field value instances
          foreach ($val as $k_i => $val_i) {
            if (is_array($val_i)) {
              foreach ($val_i as $k => $v) {
                if (isset($val_before[$k_i][$k]) && $val_before[$k_i][$k] != $val[$k_i][$k]) {
                  $changes[] = $field_name;
                }
              }
            }
          }
        }
      }
    }
    dpm($changes);
  }

Я предполагаю, что для каждого значения поля экземпляры, которые определены в $ node, должны быть определены и равны в $ node_before. Меня не волнуют поля значений полей, которые находятся в $ node_before и не находятся в $ node, я полагаю, что они остаются прежними.

dxvargas
источник
Может быть, я что-то упустил, но не hook_node_presave () подразумевает, что node_save () был вызван? Мы пытаемся избежать вызова node_save (), если поля не были изменены.
morbiD
Правда, этот хук вызывается внутри node_save (). Но вы все равно можете отменить сохранение, вызвав drupal_goto () внутри mymodule_node_presave ().
dxvargas
2
@hiphip Это действительно не очень хорошая идея, вы оставите узел сохранения в несогласованном состоянии, если перенаправите его в середине
Клайв
0

Это всего лишь некоторый код, который я собрал вместе. Все заслуги должны идти на @eclecto для выполнения всей работы по ногам. Это всего лишь (аналогично непроверенный) вариант, который напрямую принимает объекты узла, немного уменьшает попадания в БД и обеспечивает согласование языка.

function _node_fields_have_changed($old_node, $new_node) {
  // @TODO Sanity checks (e.g. node types match).

  // Get the fields attached to the node type.
  $params = array('entity_type' => 'node', 'bundle' => $old_node->type);
  foreach (field_read_fields($params) as $field) {
    // Get the field data for both nodes.
    $old_field_data = field_get_items('node', $old_node, $field['field_name']);
    $new_field_data = field_get_items('node', $new_node, $field['field_name']);

    // If the field existed on the old node, but not the new, it's changed.
    if ($old_field_data && !$new_field_data) {
      return TRUE;
    }
    // Ditto but in reverse.
    elseif ($new_field_data && !$old_field_data) {
      return TRUE;
    }

    foreach ($field['columns'] as $column_name => $column) {
      // If there's data in both columns we need an equality check.
      if (isset($old_field_data[$column_name]) && isset($new_field_data[$column_name])) {
        // Equality checking based on column type.
        if (in_array($column['type'], array('int', 'float', 'numeric')) && $old_field_data[$column_name] != $new_field_data[$column_name]) {
          return TRUE;
        }
        elseif ($old_field_data[$column_name] !== $new_field_data[$column_name]) {
          return TRUE;
        }
      }
      // Otherwise, if there's data for one column but not the other,
      // something changed.
      elseif (isset($old_field_data[$column_name]) || isset($new_field_data[$column_name])) {
        return TRUE;
      }
    } 
  }

  return FALSE;
}
Клайв
источник
1
Вы заставили меня задуматься о моей новой версии. Я даже включил проверку работоспособности типа узла.
Eric N
0

Приведенный ответ отличный, и он мне помог, но есть кое-что, что я должен был исправить.

// See if this field is a field collection.
if ($field_info['type'] == 'field_collection') {
  foreach ($old_field[LANGUAGE_NONE] as $delta => $values) {
    $old_field_collection = entity_load_unchanged('field_collection_item', $values['entity']->item_id);
    $new_field_collection = $values['entity'];

    $fields_changed = array_merge($fields_changed, erplain_api_fields_changed($old_field_collection, $new_field_collection));
  }
  unset($delta, $values);
}

В foreach()цикле мне пришлось сменить с $new_fieldна $old_field. Я не знаю, является ли это новой версией Drupal или только моим кодом (возможно, из-за другого кода где-то еще), но у меня нет доступа к нему $new_field['entity'].

Doud
источник
Я только что протестировал функцию _fields_changed () на новой установке Drupal 7.41, и сохранение узла с field_collection дает мне это $ old_field и $ new_field . Мне кажется, что вы могли бы вызывать _fields_changed () с параметрами $ old_entity и $ new_entity неправильно (или вы случайно поменяли местами имена переменных в вашем коде).
morbiD
0

Спасибо за пост, действительно сэкономил мне много времени. Я исправил кучу предупреждений и уведомлений, которые выводила функция:

/*
 * Pass both versions of the node to this function. Returns an array of
 * fields that were changed or an empty array if none were changed.
 * Pass field collections as an array keyed by field collection ID.
 *
 * @param object $old_entity
 *   The original (stored in the database) node object.
 *   This function may also pass itself a FieldCollectionItemEntity object to compare field collections.
 * @param object $new_entity
 *   The prepared node object for comparison.
 *   This function may also pass itself a FieldCollectionItemEntity object to compare field collections.
 */
function _fields_changed($old_entity, $new_entity) {
  $fields_changed = array();

  // Check for node or field collection.
  if (is_object($old_entity)) {
    $entity_is_field_collection = (get_class($old_entity) == 'FieldCollectionItemEntity');
    $bundle = !empty($entity_is_field_collection) ? $old_entity->field_name : $old_entity->type;
  }

  // Sanity check. Exit and throw an error if the content types don't match.
  if (is_object($new_entity)) {
    if ($bundle !== (!empty($entity_is_field_collection) ? $new_entity->field_name : $new_entity->type)) {
      drupal_set_message('Content type mismatch. Unable to save changes.', 'error');
      return FALSE;
    }
  }

  // Get field info.
  $field_read_params = array(
    'entity_type' => !empty($entity_is_field_collection) ? 'field_collection_item' : 'node',
  );

  if (!empty($bundle)) {
    $field_read_params['bundle'] = $bundle;
  }

  $fields_info = field_read_fields($field_read_params);

  foreach ($fields_info as $field_name => $field_info) {
    $old_field = isset($old_entity->$field_name) ? $old_entity->$field_name : NULL;
    $new_field = isset($new_entity->$field_name) ? $new_entity->$field_name : NULL;

    // Check the number of values for each field, or if they are populated at all.
    $old_field_count = (isset($old_field[LANGUAGE_NONE]) ? count($old_field[LANGUAGE_NONE]) : 0);
    $new_field_count = (isset($new_field[LANGUAGE_NONE]) ? count($new_field[LANGUAGE_NONE]) : 0);

    if ($old_field_count != $new_field_count) {
      // The two versions have a different number of values. Something has changed.
      $fields_changed[] = $field_name;
    }
    elseif ($old_field_count > 0 && $new_field_count > 0) {
      // Both versions have an equal number of values. Time to compare.

      // See if this field is a field collection.
      if ($field_info['type'] == 'field_collection') {

        foreach ($new_field[LANGUAGE_NONE] as $delta => $values) {
          $old_field_collection = NULL;
          if (!empty($values['entity']->item_id)) {
            $old_field_collection = entity_load_unchanged('field_collection_item', $values['entity']->item_id);
          }

          $new_field_collection = NULL;
          if (isset($values['entity'])) {
            $new_field_collection = $values['entity'];
          }

          $fields_changed = array_merge($fields_changed, _fields_changed($old_field_collection, $new_field_collection));
        }
        unset($delta, $values);

      }
      else {
        foreach ($old_field[LANGUAGE_NONE] as $delta => $value) {
          foreach ($field_info['columns'] as $field_column_name => $field_column_info) {
            $old_value = isset($old_field[LANGUAGE_NONE][$delta][$field_column_name]) ? $old_field[LANGUAGE_NONE][$delta][$field_column_name] : NULL;
            $new_value = isset($new_field[LANGUAGE_NONE][$delta][$field_column_name]) ? $new_field[LANGUAGE_NONE][$delta][$field_column_name] : NULL;
            $field_column_type = $field_column_info['type'];

            // As with the overall field, exit if one version has a value and the other doesn't.
            if (isset($old_value) != isset($new_value)) {
              $fields_changed[] = $old_field;
            }
            elseif (isset($old_value) && isset($new_value)) {
              // The column stores numeric data so compare values non-identically.
              if (in_array($field_column_type, array(
                'int',
                'float',
                'numeric'
              ))) {
                if ($new_value != $old_value) {
                  $fields_changed[] = $field_name;
                }
              }
              // The column stores non-numeric data so compare values identically,
              elseif ($new_value !== $old_value) {
                $fields_changed[] = $field_name;
              }
            }
            else {
              // Included for clarity. Both values are empty so there was obviously no change.
            }
          }
          unset($field_column_name, $field_column_info);
        }
        unset($delta, $value);
      }
    }
    else {
      // Included for clarity. Both values are empty so there was obviously no change.
    }
  }
  unset($field_name, $field_info);
  // End of field comparison loop.

  return $fields_changed;
}
pwaterz
источник
Пожалуйста, объясните, как этот код отвечает на исходный вопрос (только размещение некоторого кода не соответствует правилам здесь).
Pierre.Vriens