Détection générique des champs modifiés dans un formulaire personnalisé avant d'enregistrer un nœud

12

J'ajoute certains champs d'un type de contenu dans un formulaire personnalisé en utilisant field_attach_form (). Lorsque le formulaire est soumis, je traite ces champs en appelant field_attach_form_validate () et field_attach_submit () à partir des rappels #validate et #submit.

À ce stade, je veux comparer l'objet de nœud préparé post-soumission au nœud d'origine et ne le dérange que pour node_save () si l'un des champs a changé. Par conséquent, je commence par charger le nœud d'origine à l'aide de entity_load_unchanged().

Malheureusement, les tableaux de champs dans l'objet nœud d'origine ne correspondent pas aux tableaux de champs dans l'objet nœud préparé qui attend d'être enregistré, même si aucune modification n'a été apportée aux champs, donc un simple "$ old_field == $ new_field "la comparaison est impossible. Par exemple, un simple champ de texte apparaît comme ceci dans l'original:

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

Alors que dans le nœud préparé, cela apparaît comme ceci.

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

Vous pourriez penser à comparer simplement la clé «valeur», mais vous rencontrez ensuite des champs composés d'autres éléments qui n'ont pas de clé «valeur». Par exemple, regardons un champ d'adresse où il n'y a pas de clé «valeur» et il y a des clés dans les nœuds anciens et préparés qui n'ont pas d'homologues.

Ancien noeud

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

Noeud préparé

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

Pour les champs vides, il existe encore un autre écart.

Ancien noeud

$old_node->field_text = array();

Noeud préparé

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

Puis-je comparer de manière générique l'ancienne et la nouvelle valeur d'un champ pour détecter s'il a changé ou non?
Est-ce juste une impossibilité?

morbide
la source
Je pense que vous pouvez jouer avec _field_invoke()ou quelque chose de similaire pour préparer la structure complète des champs à partir du nœud "préparé", rendre les deux champs et simplement comparer ces chaînes HTML. Juste une idée.
kalabro
@kalabro Oui, c'est certainement la voie à suivre, je ne peux pas m'empêcher de penser que ce serait assez mauvais pour les performances - pour le rendre générique, vous devez charger chaque bit d'informations sur le champ individuellement en utilisant le formulaire de soumission. Ou je suppose que vous pouvez écrire une requête agrégée pour obtenir les données, mais les crochets importants peuvent ne pas se déclencher. Conceptuellement, cela semble possible, mais je pense qu'une mise en œuvre serait assez compliquée
Clive
@kalabro Je ne comprends pas très bien cette idée. Pourriez-vous publier un pseudocode pour montrer comment préparer la structure du champ, puis la restituer comme vous l'avez décrit?
morbiD

Réponses:

9

Cela devrait enfin fonctionner comme une solution générique. Merci à Clive et morbiD pour toutes les contributions.

Passez les deux versions du nœud à la fonction suivante. Ce sera:

  1. Tirez tous les champs modifiables du type de contenu détecté et leurs colonnes modifiables (c'est-à-dire les éléments qui pourraient éventuellement apparaître sur le formulaire personnalisé) de la base de données dans une seule requête.

  2. Ignorez les champs et les colonnes qui sont complètement vides dans les deux versions.

  3. Traitez un champ qui a un nombre différent de valeurs entre les deux versions comme une modification.

  4. Parcourez chaque champ, valeur et colonne et comparez les deux versions.

  5. Comparez les éléments de manière non identique (! =) S'ils sont numériques et identiquement (! ==) s'ils sont autre chose.

  6. Renvoyez immédiatement TRUE lors du premier changement qu'il détecte (puisqu'un changement suffit pour savoir que nous devons réenregistrer le nœud).

  7. Retourne FALSE si aucun changement n'est détecté après que toutes les valeurs ont été comparées.

  8. Comparez récursivement les collections de champs en les chargeant et leur schéma et en transmettant les résultats à lui-même. Cela DEVRAIT même lui permettre de comparer les collections de champs imbriqués. Le code ne doit PAS avoir de dépendance sur le module Field Collection.

Faites-moi savoir s'il y a plus de bogues ou de fautes de frappe dans ce code.

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

Parfois, vous souhaitez savoir quels champs ont changé. Pour le savoir, vous pouvez utiliser cette version de la fonction:

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

Parfois, vous souhaiterez peut-être faire en sorte que la modification de certains champs d'un nœud n'entraîne pas la mise à jour de l'horodatage "modifié" de ce nœud. Cela pourrait être mis en œuvre comme suit:

/**
 * 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) Renforcement du support de collecte sur le terrain. Ajout de la prise en charge des champs avec plusieurs valeurs.

EDIT (31/07/2015) Version ajoutée de la fonction qui retourne quels champs ont changé et exemple d'utilisation.

Eric N
la source
C'est super, je pense que cela devrait être dans une sorte de module api pour les développeurs.
Jelle
3

Voici une autre approche, plus simple, qui évite les comparaisons de valeurs côté serveur complexes et fonctionnerait avec n'importe quelle forme:

  1. Utilisez jQuery pour détecter si les valeurs du formulaire ont changé
  2. Définissez une valeur d'élément masqué pour indiquer que le formulaire a changé.
  3. Vérifiez le côté serveur de la valeur de l'élément caché et procédez comme requis.

Vous pouvez utiliser un plugin de formulaire sale jQuery tel que https://github.com/codedance/jquery.AreYouSure

Bien que d'autres qui vous permettent d'écouter le formulaire modifié / statut sale fonctionneraient également.

Ajoutez un écouteur pour définir la valeur d'un élément de formulaire masqué:

Définissez l'élément de formulaire masqué sur une valeur par défaut «modifié» pour enregistrer par défaut pour les utilisateurs avec Javascript désactivé (~ 2%).

par exemple:

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

Vous pouvez ensuite vérifier la valeur de l'élément caché

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

dans votre formulaire, validez / soumettez les gestionnaires.

David Thomas
la source
2
Belle solution, bien qu'il y ait évidemment des utilisateurs sans js. Consultez également Drupal.behaviors.formUpdated dans le fichier misc / form.js du noyau drupal. Une autre chose à noter est qu'avec la façon dont certains éditeurs wysiwyg et leurs modules drupal fonctionnent, la détection d'une valeur modifiée n'est pas toujours aussi simple qu'elle devrait l'être.
rooby
Oui, la définition d'une valeur par défaut «modifié» pour l'élément caché permettrait d'économiser par défaut pour ces quelques utilisateurs sans js activé - petit pourcentage. Une note intéressante concernant Drupal.behaviors.formUpdatedpeut-être le val()pourrait être liée à cela, même s'il semble qu'elle se déclenchera sans que la valeur ne change réellement (par exemple, inclut l'événement de clic), tandis que les plugins dédiés sont mieux à même de détecter les valeurs de formulaire modifiées.
David Thomas
0

Je ne suis pas sûr que ce soit parfait, mais pourquoi ne pas le prendre dans l'autre sens, en comparant les formes au lieu des objets nœuds ?

Je ne suis pas sûr que vous soyez si vous êtes strictement dans une forme de nœud, mais de toute façon vous pouvez rendre le formulaire avec votre ancien nœud et votre nouveau nœud:

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

Comparez vos formulaires ...

J'espère que c'est une bonne piste ... faites le moi savoir.

Gregory Kapustin
la source
J'avais déjà examiné drupal_get_form () mais je ne savais pas que vous pouviez lui passer $ node comme 2ème paramètre. Cependant, je viens de tester votre exemple de code ci-dessus et malheureusement, bien que les structures de tableau renvoyées soient les mêmes, les valeurs ne le sont pas. Jetez un œil à ce tableau récursif array_diff_assoc () pour le champ d'adresse que je teste avec: i.imgur.com/LUDPu1R.jpg
morbiD
Je vois que array_diff_assoc, mais auriez-vous le temps de donner le dpm des deux drupal_get_form? Il pourrait y avoir un moyen de contourner cela.
Gregory Kapustin
0

Voici une méthode utilisant hook_node_presave ($ node). Ce n'est qu'une maquette, si vous pensez que cela aide, testez-la et améliorez-la selon vos besoins!

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

Je suppose que, pour chaque valeur de champ, les instances définies dans $ node doivent être définies et égales dans $ node_before. Je ne me soucie pas des champs de valeur de champ qui sont dans $ node_before et ne sont pas dans $ node, je suppose qu'ils restent les mêmes.

dxvargas
la source
Peut-être que je manque quelque chose, mais hook_node_presave () n'implique-t-il pas que node_save () a été appelé? Nous essayons d'éviter d'appeler node_save () si aucun champ n'a été modifié.
morbiD
Vrai, ce hook est appelé à l'intérieur de node_save (). Mais vous pouvez toujours annuler l'enregistrement, en appelant drupal_goto () dans mymodule_node_presave ().
dxvargas
2
@hiphip Ce n'est vraiment pas une bonne idée, vous laisserez le nœud sauvegarder dans un état incohérent si vous redirigez au milieu
Clive
0

Ce n'est qu'un code que j'ai bricolé. Tout crédit doit aller à @eclecto pour avoir fait tout le travail des jambes. Il s'agit simplement d'une variante (similaire non testée) qui prend directement les objets du nœud, réduit un peu les hits de la base de données et s'occupe de la négociation du langage.

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;
}
Clive
la source
1
Vous m'avez fait penser dans le même sens avec ma nouvelle version. J'ai même inclus le contrôle d'intégrité du type de nœud.
Eric N
0

La réponse fournie est excellente et cela m'a aidé, mais il y a quelque chose que j'ai dû corriger.

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

Dans la foreach()boucle, j'ai dû changer de $new_fieldà $old_field. Je ne sais pas s'il s'agit d'une nouvelle version de Drupal ou seulement de mon code (peut-être dû à un autre code ailleurs), mais je n'y ai pas accès $new_field['entity'].

Doud
la source
Je viens de tester la fonction _fields_changed () sur une nouvelle installation de Drupal 7.41 et l'enregistrement d'un nœud avec un field_collection me donne ce $ old_field et $ new_field . Il me semble que vous pourriez appeler _fields_changed () avec les paramètres $ old_entity et $ new_entity dans le mauvais sens (ou vous avez accidentellement échangé quelque part les noms de variables dans votre code).
morbiD
0

Merci pour le message, ça m'a vraiment fait gagner beaucoup de temps. J'ai corrigé un tas d'avertissements et d'avis que la fonction sortait:

/*
 * 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
la source
Veuillez expliquer comment ce code répond à la question d'origine (publier uniquement du code n'est pas conforme aux règles ici).
Pierre.Vriens