diff --git a/modules/field/modules/field_sql_storage/field_sql_storage.module b/modules/field/modules/field_sql_storage/field_sql_storage.module index 842893a..63c0f1d 100644 --- a/modules/field/modules/field_sql_storage/field_sql_storage.module +++ b/modules/field/modules/field_sql_storage/field_sql_storage.module @@ -435,6 +435,80 @@ function field_sql_storage_field_storage_load($entity_type, $entities, $age, $fi } /** + * Callback for array_filter(). + */ +function _field_sql_storage_write_compare_filter_callback($value) { + return null !== $value && '' !== $value; +} + +/** + * Cleanup field values for later values comparison. + * + * @param array $array + * Field values to cleanup. + * + * @return array + * Filtered array. + */ +function _field_sql_storage_write_compare_filter($field, $array) { + foreach ($array as $language => $items) { + if (empty($items)) { + unset($array[$language]); + } + else { + foreach ($items as $delta => $item) { + // This should not happen but it happens that some modules are + // sometime giving invalid data to the field API. + if (!is_array($item)) { + continue; + } + // Let's start by pruning empty values and non storable values. + $array[$language][$delta] = array_filter(array_intersect_key($item, $field['columns']), '_field_sql_storage_write_compare_filter_callback'); + // Ordering is important because for widget elements and loaded + // columns from database order might differ and give false + // positives on field value change, especially with complex + // fields such as image fields. + ksort($array[$language][$delta]); + } + } + } + return $array; +} + +/** + * Compare a single field value for both entities and tell us if it changed. + * + * @param array $field + * Loaded field structure. + * @param stdClass $entity1 + * First entity to compare. + * @param stdClass $entity2 + * Second entity to compare. + * + * @return boolean + * True if field value changed, false otherwise. + */ +function _field_sql_storage_write_compare($field, $entity1, $entity2) { + $field_name = $field['field_name']; + if (empty($entity1->$field_name) && empty($entity2->$field_name)) { + // Both are empty we can safely assume that it did not change. + return false; + } + if (!isset($entity1->$field_name) || !isset($entity2->$field_name)) { + // One of them is missing but not the other the value changed. + return true; + } + // We need to proceed to deep array comparison, problem is that we + // can't do it naively: in most cases the field values come from + // the edit form, and some Form API widget values that are not + // field columns may stale in the structure items. We need to clean + // up both original and new field values before comparison. + $items1 = _field_sql_storage_write_compare_filter($field, (array)$entity1->$field_name); + $items2 = _field_sql_storage_write_compare_filter($field, (array)$entity2->$field_name); + return $items1 !== $items2; +} + +/** * Implements hook_field_storage_write(). */ function field_sql_storage_field_storage_write($entity_type, $entity, $op, $fields) { @@ -443,8 +517,27 @@ function field_sql_storage_field_storage_write($entity_type, $entity, $op, $fiel $vid = $id; } + // Check if the given entity is a new revision or not. In case of a new + // revision creation, we cannot skip any field else the new revision would + // be empty. + if (!empty($vid) && !empty($entity->original)) { + list(, $original_vid) = entity_extract_ids($entity_type, $entity->original); + if (null === $original_vid) { + $original_vid = $id; + } + $is_new_revision = $original_vid != $vid; + } + else { + $is_new_revision = false; + } + foreach ($fields as $field_id) { $field = field_info_field_by_id($field_id); + + if (!$is_new_revision && !empty($entity->original) && !_field_sql_storage_write_compare($field, $entity, $entity->original)) { + continue; + } + $field_name = $field['field_name']; $table_name = _field_sql_storage_tablename($field); $revision_name = _field_sql_storage_revision_tablename($field);