Tổng quát phát hiện các trường đã thay đổi trong một hình thức tùy chỉnh trước khi lưu một nút


12

Tôi đang thêm các trường nhất định từ một loại nội dung vào một biểu mẫu tùy chỉnh bằng cách sử dụng field_attach_form (). Khi biểu mẫu được gửi, tôi đang xử lý các trường đó bằng cách gọi trường_attach_form_validate () và field_attach_submit () từ #validate và #submit gọi lại.

Tại thời điểm đó, tôi muốn so sánh đối tượng nút được gửi, đã chuẩn bị với nút ban đầu và chỉ bận tâm đến nút_save () nếu bất kỳ trường nào đã thay đổi. Do đó, tôi bắt đầu bằng cách tải nút gốc bằng cách sử dụng entity_load_unchanged().

Thật không may, các mảng trường trong đối tượng nút ban đầu không khớp với các mảng trường trong đối tượng nút đã chuẩn bị đang chờ lưu, ngay cả khi không có thay đổi nào được thực hiện cho các trường, do đó, đơn giản là "$ old_field == $ new_field "So sánh là không thể. Ví dụ: một trường văn bản đơn giản xuất hiện như thế này trong bản gốc:

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

Trong khi đó trong nút chuẩn bị nó xuất hiện như thế này.

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

Bạn có thể nghĩ chỉ so sánh khóa 'giá trị', nhưng sau đó bạn chạy vào các trường được tạo thành từ các yếu tố khác không có khóa 'giá trị'. Ví dụ: chúng ta hãy xem trường địa chỉ không có khóa 'giá trị' có các khóa trong cả các nút cũ và các nút được chuẩn bị không có đối tác.

Nút cũ

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

Nút chuẩn bị

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

Đối với các trường trống, vẫn có một sự khác biệt khác.

Nút cũ

$old_node->field_text = array();

Nút chuẩn bị

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

Tôi có thể so sánh tổng quát giá trị cũ và mới của bất kỳ trường nào để phát hiện xem nó có thay đổi hay không?
Đây chỉ là một điều không thể?


Tôi nghĩ rằng bạn có thể chơi với _field_invoke()hoặc một cái gì đó liên quan để chuẩn bị cấu trúc trường đầy đủ từ nút "đã chuẩn bị", kết xuất cả hai trường và chỉ cần so sánh các chuỗi HTML này. Chỉ là một ý tưởng.
kalabro

@kalabro Vâng, đó chắc chắn là con đường để đi, tôi không thể cảm thấy điều đó khá tệ cho hiệu suất - để làm cho nó chung chung, bạn cần phải tải từng bit thông tin trường bằng cách sử dụng biểu mẫu. Hoặc tôi đoán bạn có thể viết một truy vấn tổng hợp để lấy dữ liệu, nhưng sau đó các móc quan trọng có thể không kích hoạt. Về mặt khái niệm có vẻ như có thể, nhưng tôi nghĩ việc triển khai sẽ khá phức tạp
Clive

@kalabro Tôi không hiểu ý tưởng này lắm. Bạn có thể đăng một số mã giả để trình bày cách chuẩn bị cấu trúc trường và sau đó kết xuất nó như bạn mô tả không?
morbiD

Câu trả lời:


9

Điều này, cuối cùng, nên hoạt động như một giải pháp chung. Cảm ơn Clive và morbiD cho tất cả các đầu vào.

Truyền cả hai phiên bản của nút cho chức năng sau. Nó sẽ:

  1. Kéo tất cả các trường có thể chỉnh sửa của loại nội dung được phát hiện và các cột có thể chỉnh sửa của chúng (nghĩa là các mục có thể xuất hiện trên biểu mẫu tùy chỉnh) từ cơ sở dữ liệu trong một truy vấn duy nhất.

  2. Bỏ qua các trường và cột hoàn toàn trống trong cả hai phiên bản.

  3. Hãy coi một trường có số lượng giá trị khác nhau giữa hai phiên bản là một thay đổi.

  4. Lặp lại qua mọi trường, giá trị và cột và so sánh hai phiên bản.

  5. So sánh các mục không giống hệt nhau (! =) Nếu chúng là số và giống hệt nhau (! ==) nếu chúng là bất kỳ thứ gì khác.

  6. Ngay lập tức trả về TRUE trên thay đổi đầu tiên mà nó phát hiện (vì một thay đổi là đủ để biết chúng ta cần phải lưu lại nút).

  7. Trả về SAI nếu không có thay đổi nào được phát hiện sau khi tất cả các giá trị được so sánh.

  8. So sánh đệ quy các bộ sưu tập trường bằng cách tải chúng và lược đồ của chúng và truyền kết quả cho chính nó. Điều này thậm chí cho phép nó so sánh các bộ sưu tập trường lồng nhau. Mã KHÔNG nên có bất kỳ sự phụ thuộc nào vào mô đun Bộ sưu tập Trường.

Hãy cho tôi biết nếu có thêm lỗi hoặc lỗi chính tả trong mã này.

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

Đôi khi bạn quan tâm đến việc biết lĩnh vực nào đã thay đổi. Để biết điều đó, bạn có thể sử dụng phiên bản chức năng này:

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

Đôi khi bạn có thể muốn làm cho nó thay đổi các trường nhất định của một nút không làm cho dấu thời gian "đã thay đổi" của nút đó được cập nhật. Điều này có thể được thực hiện như sau:

/**
 * 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 (30/07/2013) Thắt chặt hỗ trợ thu gom hiện trường. Đã thêm hỗ trợ cho các trường có nhiều giá trị.

EDIT (2015/07/31) Added phiên bản của chức năng mà lợi nhuận các lĩnh vực đã thay đổi, và trường hợp ví dụ sử dụng.


Điều này thật tuyệt, tôi cảm thấy như thế này nên có trong một số loại mô-đun api cho các nhà phát triển sử dụng.
Jelle

3

Đây là một cách tiếp cận khác, đơn giản hơn, tránh các so sánh giá trị phía máy chủ phức tạp và sẽ hoạt động với mọi hình thức:

  1. Sử dụng jQuery để phát hiện nếu các giá trị biểu mẫu đã thay đổi
  2. Đặt giá trị phần tử ẩn để cho biết biểu mẫu đã thay đổi.
  3. Kiểm tra phía máy chủ giá trị phần tử ẩn và xử lý theo yêu cầu.

Bạn có thể sử dụng một plugin dạng bẩn của jQuery như https://github.com/codingance/jquery.AreYouSure

Mặc dù những người khác cho phép bạn nghe biểu mẫu thay đổi / trạng thái bẩn cũng sẽ hoạt động.

Thêm một người nghe để đặt giá trị của một phần tử biểu mẫu ẩn:

Đặt phần tử biểu mẫu ẩn thành giá trị mặc định là 'đã thay đổi' để lưu theo mặc định cho những người dùng bị tắt javascript (~ 2%).

ví dụ:

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

Sau đó, bạn có thể kiểm tra giá trị của phần tử ẩn

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

trong mẫu của bạn xác nhận / gửi trình xử lý.


2
Giải pháp tốt, mặc dù rõ ràng có một số người dùng không có js. Ngoài ra, hãy xem Drupal.behaviors.formUpdated trong tệp misc / form.js của lõi drupal. Một điều cần lưu ý là với cách thức mà một số biên tập viên wysiwyg và các mô-đun drupal của họ hoạt động, việc phát hiện một giá trị thay đổi không phải lúc nào cũng đơn giản như mong muốn.
rooby

Có, đặt giá trị mặc định là 'đã thay đổi' cho phần tử ẩn sẽ lưu theo mặc định cho một vài người dùng mà không bật js - tỷ lệ nhỏ. Lưu ý thú vị liên quan đến Drupal.behaviors.formUpdatedcó lẽ có val()thể được liên kết với điều đó mặc dù có vẻ như nó sẽ kích hoạt mà không có giá trị thực sự thay đổi (ví dụ bao gồm sự kiện nhấp chuột), trong khi các plugin chuyên dụng tốt hơn trong việc phát hiện các giá trị biểu mẫu thay đổi thực tế.
David Thomas

0

Tôi không chắc là nó hoàn hảo, nhưng tại sao không làm theo cách khác, bằng cách so sánh các biểu mẫu thay vì các đối tượng nút ?

Tôi không chắc chắn nếu bạn nghiêm túc trong một hình thức nút, nhưng dù sao bạn cũng có thể kết xuất biểu mẫu với nút cũ và nút mới của bạn:

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

So sánh các hình thức của bạn ...

Tôi hy vọng đó là một ca khúc hay ... cho tôi biết.


Tôi đã xem xét drupal_get_form () nhưng tôi không nhận ra bạn có thể truyền nút $ cho nó như một tham số thứ 2. Tuy nhiên, tôi vừa kiểm tra mã ví dụ của bạn ở trên và thật không may, trong khi các cấu trúc mảng được trả về là như nhau, thì các giá trị thì không. Có một cái nhìn tại array_diff_assoc đệ quy này () cho các trường địa chỉ Tôi đang thử nghiệm với: i.imgur.com/LUDPu1R.jpg
Morbid

Tôi thấy mảng_diff_assoc, nhưng bạn có thời gian để đưa ra dpm của cả hai drupal_get_form không? Có thể có một cách xung quanh này.
Gregory Kapustin

0

Đây là một phương thức sử dụng hook_node_presave (nút $). Nó chỉ là một mockup, nếu bạn nghĩ rằng nó giúp, kiểm tra nó và cải thiện nó theo nhu cầu của bạn!

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

Tôi giả sử rằng, đối với mỗi giá trị trường, các thể hiện được xác định trong nút $ phải được xác định và bằng nhau trong $ node_b Before. Tôi không quan tâm đến các trường của giá trị trường nằm trong $ node_b Before và không nằm trong nút $, tôi cho rằng chúng giữ nguyên.


Có lẽ tôi đang thiếu một cái gì đó, nhưng không hook_node_presave () ngụ ý node_save () đã được gọi? Chúng tôi đang cố gắng tránh gọi node_save () nếu không có trường nào bị thay đổi.
morbiD

Đúng, hook này được gọi bên trong node_save (). Nhưng bạn vẫn có thể hủy lưu, bằng cách gọi drupal_goto () bên trong mymodule_node_presave ().
dxvargas

2
@hiphip Đó thực sự không phải là một ý tưởng hay, bạn sẽ để nút lưu ở trạng thái không nhất quán nếu bạn chuyển hướng ở giữa nó
Clive

0

Đây chỉ là một số mã tôi đã ghép lại với nhau. Tất cả tín dụng phải vào @eclecto để thực hiện tất cả các công việc chân. Đây chỉ là một biến thể (tương tự chưa được kiểm tra) lấy trực tiếp các đối tượng nút, giảm DB một chút và đảm nhận việc đàm phán ngôn ngữ.

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
Bạn đã cho tôi suy nghĩ cùng dòng với phiên bản mới của tôi. Tôi thậm chí bao gồm kiểm tra độ tỉnh táo của nút.
Eric N

0

Câu trả lời được cung cấp là rất tốt và nó đã giúp tôi, nhưng có một số điều tôi phải sửa.

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

Trong foreach()vòng lặp, tôi đã phải thay đổi từ $new_fieldthành $old_field. Tôi không biết đây là phiên bản mới của Drupal hay chỉ mã của tôi (có thể là do mã khác ở nơi khác), nhưng tôi không có quyền truy cập $new_field['entity'].


Tôi vừa thử nghiệm hàm _fields_changed () trên bản cài đặt Drupal 7.41 mới và lưu một nút với trường_collection mang lại cho tôi $ old_field và $ new_field này . Có vẻ như tôi có thể đang gọi _fields_changed () với các tham số $ old_entity và $ new_entity theo cách sai (hoặc bạn đã vô tình hoán đổi tên biến trong mã của mình ở đâu đó).
morbiD

0

Cảm ơn cho bài viết, thực sự tiết kiệm cho tôi rất nhiều thời gian. Tôi đã sửa một loạt các cảnh báo và thông báo rằng chức năng đã xuất ra:

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

Vui lòng giải thích cách mã này trả lời câu hỏi ban đầu (chỉ đăng một số mã không tuân thủ các quy tắc quanh đây).
Pierre.Vriens
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.