Secara umum mendeteksi bidang yang diubah dalam bentuk khusus sebelum menyimpan node


12

Saya menambahkan bidang tertentu dari jenis konten ke formulir kustom menggunakan field_attach_form (). Ketika formulir dikirimkan saya sedang memproses bidang-bidang tersebut dengan memanggil field_attach_form_validate () dan field_attach_submit () dari #validate dan #submit callbacks.

Pada titik itu, saya ingin membandingkan post-submit, objek node yang disiapkan dengan node asli dan hanya repot-repot untuk node_save () jika ada bidang yang berubah. Oleh karena itu, saya mulai dengan memuat simpul asli menggunakan entity_load_unchanged().

Sayangnya, array bidang dalam objek simpul asli tidak cocok dengan array bidang dalam objek simpul yang disiapkan yang sedang menunggu untuk disimpan, bahkan jika tidak ada perubahan yang dilakukan pada bidang, jadi sederhana "$ old_field == $ new_field "perbandingan itu tidak mungkin. Misalnya, bidang teks sederhana muncul seperti ini di aslinya:

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

Sedangkan pada node yang disiapkan tampak seperti ini.

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

Anda mungkin berpikir untuk hanya membandingkan kunci 'nilai', tetapi kemudian Anda lari ke bidang yang terdiri dari elemen lain yang tidak memiliki kunci 'nilai'. Sebagai contoh, mari kita lihat bidang alamat di mana tidak ada kunci 'nilai' dan ada kunci di kedua node lama dan siap yang tidak memiliki rekan.

Node lama

$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 yang disiapkan

$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',
);

Untuk bidang kosong, masih ada perbedaan.

Node lama

$old_node->field_text = array();

Node yang disiapkan

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

Dapatkah saya secara umum membandingkan nilai lama dan baru bidang apa saja untuk mendeteksi apakah sudah berubah atau tidak?
Apakah ini hanya ketidakmungkinan?


Saya pikir Anda dapat bermain dengan _field_invoke()atau sesuatu yang terkait untuk menyiapkan struktur bidang penuh dari "disiapkan" simpul, membuat kedua bidang dan hanya membandingkan string HTML ini. Hanya sebuah ide.
kalabro

@ kalabro Ya itu pasti cara yang harus ditempuh, saya tidak dapat menahan perasaan bahwa itu akan sangat buruk untuk kinerja - untuk membuatnya generik Anda harus memuat setiap bit info bidang secara individual menggunakan pengiriman formulir. Atau saya kira Anda bisa menulis kueri teragregasi untuk mendapatkan data, tetapi kait penting mungkin tidak menyala. Secara konseptual kelihatannya mungkin, tetapi saya pikir implementasi akan sangat rumit
Clive

@ kalabro Saya tidak begitu mengerti ide ini. Bisakah Anda memposting beberapa kodesemu untuk mendemonstrasikan cara menyiapkan struktur bidang dan kemudian merendernya seperti yang Anda jelaskan?
morbiD

Jawaban:


9

Ini, pada akhirnya, harus berfungsi sebagai solusi umum. Terima kasih kepada Clive dan morbiD untuk semua masukan.

Lewati kedua versi node ke fungsi berikut. Itu akan:

  1. Tarik semua bidang yang dapat diedit tipe konten yang terdeteksi dan kolom yang dapat diedit (yaitu item yang mungkin muncul pada formulir khusus) dari database dalam satu permintaan.

  2. Abaikan bidang dan kolom yang benar-benar kosong di kedua versi.

  3. Perlakukan bidang yang memiliki jumlah nilai yang berbeda antara dua versi sebagai perubahan.

  4. Ulangi setiap bidang, nilai, dan kolom dan bandingkan dua versi.

  5. Bandingkan item secara non-identik (! =) Jika item berupa angka dan identik (! ==) jika item lain.

  6. Segera kembalikan TRUE pada perubahan pertama yang dideteksinya (karena satu perubahan sudah cukup untuk mengetahui kita perlu menyimpan kembali node).

  7. Kembalikan FALSE jika tidak ada perubahan yang terdeteksi setelah semua nilai dibandingkan.

  8. Secara rekursif membandingkan koleksi lapangan dengan memuatnya dan skema mereka dan meneruskan hasilnya sendiri. HARUS ini bahkan memungkinkan untuk membandingkan koleksi bidang bersarang. Kode harus TIDAK memiliki ketergantungan pada modul Koleksi Lapangan.

Beri tahu saya jika ada bug atau kesalahan ketik lagi dalam kode ini.

/*
 * 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;
}

Terkadang Anda tertarik mengetahui bidang mana yang telah berubah. Untuk mengetahuinya, Anda dapat menggunakan versi fungsi ini:

/*
 * 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;
}

Kadang-kadang Anda mungkin ingin membuatnya sehingga mengubah bidang tertentu dari suatu simpul tidak menyebabkan timestamp "perubahan" simpul tersebut diperbarui. Ini dapat diimplementasikan seperti berikut:

/**
 * 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;
  }
}

EDIT (7/30/2013) Dukungan pengumpulan lapangan diperketat . Dukungan tambahan untuk bidang dengan beberapa nilai.

EDIT (7/31/2015) Menambahkan versi fungsi yang mengembalikan bidang mana yang telah berubah, dan contoh use case.


Ini hebat, saya merasa seperti ini harus ada dalam semacam modul api untuk digunakan pengembang.
Jelle

3

Berikut ini pendekatan lain, lebih sederhana, yang menghindari perbandingan nilai sisi server yang kompleks, dan akan bekerja dengan segala bentuk:

  1. Gunakan jQuery untuk mendeteksi jika nilai formulir telah berubah
  2. Tetapkan nilai elemen tersembunyi untuk menunjukkan bahwa formulir telah berubah.
  3. Periksa sisi server nilai elemen tersembunyi dan proses sesuai kebutuhan.

Anda bisa menggunakan plugin form jQuery dirty seperti https://github.com/codedance/jquery.AreYouSure

Meskipun orang lain yang membiarkan Anda mendengarkan formulir berubah / status kotor juga akan berfungsi.

Tambahkan pendengar untuk menetapkan nilai elemen formulir tersembunyi:

Setel elemen formulir tersembunyi ke nilai default 'diubah' untuk menyimpan secara default untuk para pengguna dengan javascript dinonaktifkan (~ 2%).

misalnya:

// 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('');
      }
    }
 });

Anda kemudian dapat memeriksa nilai elemen tersembunyi

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

dalam formulir Anda validasi / kirim penangan.


2
Solusi yang bagus, meskipun jelas ada beberapa pengguna tanpa js. Juga, periksa Drupal.behaviors.formUpdated dalam file misc / form.js dari inti drupal. Hal lain yang perlu diperhatikan adalah bahwa dengan cara beberapa editor wysiwyg dan modul drupal mereka bekerja, mendeteksi nilai yang berubah tidak selalu semudah yang seharusnya.
rooby

Ya, menetapkan nilai default 'diubah' untuk elemen tersembunyi akan menyimpan secara default untuk beberapa pengguna tanpa mengaktifkan js - persentase kecil. Catatan menarik tentang Drupal.behaviors.formUpdatedmungkin val()dapat dikaitkan dengan itu meskipun sepertinya itu akan memicu tanpa nilai yang benar-benar berubah (mis. Termasuk acara klik), sedangkan plugin khusus lebih baik dalam mendeteksi nilai formulir yang sebenarnya diubah.
David Thomas

0

Saya tidak yakin itu sempurna, tetapi mengapa tidak mengambil sebaliknya, dengan membandingkan bentuk bukan objek simpul ?

Saya tidak yakin Anda jika Anda benar-benar dalam bentuk simpul, tetapi Anda bisa membuat formulir dengan simpul lama dan simpul baru Anda:

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);

Bandingkan formulir Anda ...

Saya harap ini lagu yang bagus ... beri tahu saya.


Saya sudah melihat ke drupal_get_form () tapi saya tidak sadar Anda bisa melewatkan $ node sebagai parameter ke-2. Namun, saya baru saja menguji kode contoh Anda di atas dan sayangnya, sementara struktur array yang dikembalikan adalah sama, nilainya tidak. Lihat array_diff_assoc () rekursif ini untuk bidang alamat yang saya uji dengan: i.imgur.com/LUDPu1R.jpg
morbiD

Saya melihat array_diff_assoc itu, tetapi apakah Anda punya waktu untuk memberikan dpm dari kedua drupal_get_form? Mungkin ada jalan keluarnya.
Gregory Kapustin

0

Berikut adalah metode menggunakan hook_node_presave ($ node). Itu hanya mockup, jika menurut Anda itu membantu, ujilah dan tingkatkan sesuai kebutuhan Anda!

  /**
   * 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);
  }

Saya mengira bahwa, untuk setiap nilai bidang, instance yang didefinisikan dalam $ node harus didefinisikan dan sama dengan $ node_before. Saya tidak peduli untuk bidang nilai bidang yang ada di $ node_before dan tidak dalam $ node, saya kira mereka tetap sama.


Mungkin saya melewatkan sesuatu, tetapi bukankah hook_node_presave () menyiratkan node_save () telah dipanggil? Kami berusaha menghindari panggilan node_save () jika tidak ada bidang yang diubah.
morbiD

Benar, pengait ini disebut di dalam node_save (). Tetapi Anda masih dapat membatalkan penyimpanan, dengan memanggil drupal_goto () di dalam mymodule_node_presave ().
dxvargas

2
@hiphip Itu benar-benar bukan ide yang baik, Anda akan meninggalkan node save dalam keadaan tidak konsisten jika Anda mengarahkan ulang di tengahnya
Clive

0

Ini hanya beberapa kode yang saya buat bersama. Semua kredit harus masuk ke @eclecto untuk melakukan semua pekerjaan kaki. Ini hanya variasi (yang belum diuji) yang mengambil objek node secara langsung, mengurangi hit DB sedikit dan menangani negosiasi bahasa.

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
Anda membuat saya berpikir sejalan dengan versi baru saya. Saya bahkan menyertakan cek sanity tipe simpul.
Eric N

0

Jawaban yang diberikan sangat bagus dan itu membantu saya, tetapi ada sesuatu yang harus saya koreksi.

// 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);
}

Dalam foreach()lingkaran, saya harus mengubah dari $new_fieldke $old_field. Saya tidak tahu apakah ini versi Drupal baru atau hanya kode saya (mungkin karena kode lain di tempat lain), tetapi saya tidak memiliki akses ke $new_field['entity'].


Saya baru saja menguji fungsi _fields_changed () pada instalasi Drupal 7.41 baru dan menyimpan sebuah node dengan field_collection memberi saya $ old_field dan $ new_field ini . Bagi saya sepertinya Anda memanggil _fields_changed () dengan parameter $ old_entity dan $ new_entity dengan cara yang salah (atau Anda secara tidak sengaja mengganti nama variabel dalam kode Anda di suatu tempat).
morbiD

0

Terima kasih untuk postingnya, benar-benar menyelamatkan saya banyak waktu. Saya memperbaiki banyak peringatan dan pemberitahuan bahwa fungsi menghasilkan:

/*
 * 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;
}

Tolong jelaskan bagaimana kode ini menjawab pertanyaan asli (hanya memposting beberapa kode yang tidak sesuai dengan aturan di sini).
Pierre.Vriens
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.