การตรวจจับฟิลด์ที่ถูกเปลี่ยนแปลงโดยทั่วไปในรูปแบบที่กำหนดเองก่อนที่จะบันทึกโหนด


12

ฉันเพิ่มเขตข้อมูลบางอย่างจากประเภทเนื้อหาลงในแบบฟอร์มที่กำหนดเองโดยใช้ field_attach_form () เมื่อส่งแบบฟอร์มฉันกำลังประมวลผลฟิลด์เหล่านั้นโดยการเรียก field_attach_form_validate () และ field_attach_submit () จาก #validate และ #submit callbacks

ณ จุดนั้นฉันต้องการเปรียบเทียบออบเจกต์ post-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' แต่คุณจะพบกับฟิลด์ที่ประกอบด้วยองค์ประกอบอื่น ๆ ซึ่งไม่มีคีย์ '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 ใช่นั่นเป็นวิธีที่แน่นอนฉันไม่สามารถรู้สึกได้เลยว่ามันจะค่อนข้างแย่สำหรับการแสดง - เพื่อให้เป็นเรื่องปกติคุณจะต้องโหลดข้อมูลภาคสนามทุกบิตโดยใช้การส่งแบบฟอร์ม หรือฉันเดาว่าคุณสามารถเขียนแบบสอบถามแบบรวมเพื่อรับข้อมูล แต่ตะขอสำคัญอาจไม่ทำงาน ดูเหมือนว่าจะเป็นไปได้ในทางแนวคิด แต่ฉันคิดว่าการนำไปปฏิบัติจะค่อนข้างซับซ้อน
ไคลฟ์

@kalabro ฉันไม่เข้าใจความคิดนี้มากนัก คุณสามารถโพสต์รหัสจำลองบางส่วนเพื่อสาธิตวิธีเตรียมโครงสร้างเขตข้อมูลแล้วแสดงผลตามที่คุณอธิบายได้หรือไม่
โรค

คำตอบ:


9

ในที่สุดนี้ควรใช้เป็นวิธีการแก้ปัญหาทั่วไป ขอบคุณ Clive และ morbiD สำหรับอินพุตทั้งหมด

ส่งผ่านโหนดทั้งสองเวอร์ชันไปยังฟังก์ชันต่อไปนี้ มันจะ:

  1. ดึงเขตข้อมูลที่สามารถแก้ไขได้ของเนื้อหาที่ตรวจพบทั้งหมดและคอลัมน์ที่แก้ไขได้ (เช่นรายการที่อาจปรากฏบนฟอร์มที่กำหนดเอง) จากฐานข้อมูลในแบบสอบถามเดียว

  2. ละเว้นฟิลด์และคอลัมน์ที่ว่างเปล่าในทั้งสองเวอร์ชัน

  3. ปฏิบัติกับเขตข้อมูลที่มีค่าแตกต่างกันระหว่างสองเวอร์ชันเป็นการเปลี่ยนแปลง

  4. วนซ้ำทุกฟิลด์ค่าและคอลัมน์และเปรียบเทียบทั้งสองเวอร์ชัน

  5. เปรียบเทียบรายการที่ไม่เหมือนกัน (! =) หากเป็นรายการที่เป็นตัวเลขและเหมือนกัน (! ==) หากเป็นรายการอื่น

  6. ส่งกลับ TRUE ทันทีในการเปลี่ยนแปลงครั้งแรกที่ตรวจพบ (เนื่องจากการเปลี่ยนแปลงเพียงครั้งเดียวก็เพียงพอที่จะรู้ว่าเราจำเป็นต้องบันทึกโหนดใหม่)

  7. ส่งคืน FALSE หากตรวจไม่พบการเปลี่ยนแปลงหลังจากเปรียบเทียบค่าทั้งหมด

  8. เปรียบเทียบคอลเลกชันฟิลด์ซ้ำโดยการโหลดและสคีมาของพวกเขาและส่งผลลัพธ์ไปที่ตัว SHOULD นี้อนุญาตให้เปรียบเทียบคอลเลกชันฟิลด์ที่ซ้อนกันได้ รหัสไม่ควรมีการพึ่งพาใด ๆ ในโมดูลการเก็บรวบรวมสนาม

แจ้งให้เราทราบหากมีข้อบกพร่องหรือการพิมพ์ผิดในรหัสนี้

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

แก้ไข (7/30/2556)สนับสนุนการรวบรวมฟิลด์อย่างเข้มงวด เพิ่มการรองรับฟิลด์ที่มีหลายค่า

แก้ไข (2015/07/31)เพิ่มรุ่นของฟังก์ชั่นซึ่งผลตอบแทนที่สาขาที่มีการเปลี่ยนแปลงและการใช้งานตัวอย่างเช่นกรณี


นี่เป็นสิ่งที่ยอดเยี่ยมฉันรู้สึกว่าควรจะอยู่ในโมดูล api บางประเภทเพื่อให้นักพัฒนาใช้
Jelle

3

นี่เป็นอีกวิธีที่ง่ายกว่าและหลีกเลี่ยงการเปรียบเทียบค่าฝั่งเซิร์ฟเวอร์ที่ซับซ้อนและจะทำงานกับรูปแบบใดก็ได้:

  1. ใช้ jQuery เพื่อตรวจสอบว่ามีการเปลี่ยนแปลงค่าของแบบฟอร์มหรือไม่
  2. ตั้งค่าองค์ประกอบที่ซ่อนอยู่เพื่อระบุว่ามีการเปลี่ยนแปลงรูปแบบ
  3. ตรวจสอบด้านเซิร์ฟเวอร์และองค์ประกอบค่าที่ซ่อนอยู่ตามต้องการ

คุณสามารถใช้ปลั๊กอินรูปแบบสกปรก jQuery เช่นhttps://github.com/codedance/jquery.AreYouSure

แม้ว่าคนอื่น ๆ ที่ช่วยให้คุณฟังแบบฟอร์มการเปลี่ยนสถานะ / สกปรกก็จะทำงาน

เพิ่มฟังเพื่อตั้งค่าขององค์ประกอบรูปแบบที่ซ่อนอยู่:

ตั้งองค์ประกอบองค์ประกอบที่ซ่อนอยู่เป็นค่าเริ่มต้นของ 'เปลี่ยน' เพื่อบันทึกโดยค่าเริ่มต้นสำหรับผู้ใช้ที่ปิดการใช้งานจาวาสคริปต์ (~ 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 core สิ่งที่ควรทราบอีกประการหนึ่งคือวิธีที่บรรณาธิการบรรณาธิการ wysiwyg และโมดูล drupal ทำงานการตรวจสอบค่าที่เปลี่ยนแปลงนั้นไม่ได้ตรงไปตรงมาเท่าที่ควร
rooby

ใช่การตั้งค่าเริ่มต้นเป็น 'เปลี่ยน' สำหรับองค์ประกอบที่ซ่อนอยู่จะบันทึกตามค่าเริ่มต้นสำหรับผู้ใช้ไม่กี่คนที่ไม่ได้เปิดใช้ js - เปอร์เซ็นต์ขนาดเล็ก หมายเหตุที่น่าสนใจเกี่ยวกับDrupal.behaviors.formUpdatedอาจval()จะเชื่อมโยงกับที่แม้ว่ามันจะดูเหมือนว่ามันจะก่อให้เกิดโดยไม่ต้องมีมูลค่าการเปลี่ยนแปลงจริง (เช่นรวมถึงการคลิกเหตุการณ์) ในขณะที่ปลั๊กอินเฉพาะจะดีกว่าที่ตรวจจับค่าแบบฟอร์มการเปลี่ยนแปลงจริง
David Thomas

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 หรือไม่ อาจมีวิธีแก้ไขปัญหานี้
Gregory Kapustin

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 ฉันคิดว่ามันคงเหมือนเดิม


บางทีฉันหายไปบางอย่าง แต่ไม่ hook_node_presave () แปลว่า node_save () ถูกเรียกใช่ไหม เราพยายามหลีกเลี่ยงการเรียก node_save () หากไม่มีการเปลี่ยนแปลงฟิลด์
morbiD

ทรูเบ็ดนี้ถูกเรียกว่าข้างใน node_save () แต่คุณยังสามารถยกเลิกการบันทึกได้โดยโทรไปที่ drupal_goto () ภายใน mymodule_node_presave ()
dxvargas

2
@hiphip ไม่ใช่ความคิดที่ดีจริงๆคุณจะปล่อยให้โหนดบันทึกในสถานะที่ไม่สอดคล้องกันหากคุณเปลี่ยนเส้นทางในระหว่างนั้น
Clive

0

นี่เป็นเพียงรหัสบางอย่างที่ฉันเดินด้วยกัน เครดิตทั้งหมดต้องไปที่@ecoñoสำหรับการทำงานที่ได้ผล นี่เป็นเพียงการเปลี่ยนแปลง (ที่ยังไม่ได้ทดสอบในทำนองเดียวกัน) ซึ่งจะนำวัตถุโหนดโดยตรงลดฐานข้อมูลเข้ามาเล็กน้อยและดูแลการเจรจาทางภาษา

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']ได้


ฉันเพียงแค่ทดสอบ _fields_changed () ฟังก์ชันในสด Drupal 7.41 การติดตั้งและการประหยัดโหนดกับ field_collection จะช่วยให้ฉันนี้$ old_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;
}

โปรดอธิบายว่ารหัสนี้ตอบคำถามเดิมอย่างไร (การโพสต์รหัสบางอย่างไม่สอดคล้องกับกฎที่นี่)
Pierre.Vriens
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.