Tell me more ×
Drupal Answers is a question and answer site for Drupal developers and administrators. It's 100% free, no registration required.

I have written a custom submit handler that gets called whenever a certain content type is edited.

I was wondering if there was a flag in $form_state that indicates whether the node was actually edited vs whether someone is just clicking the save button without actually changing anything. I am wondering because I would like to perform an action only if the node is edited. Thank you

function hook_form_alter(&$form, &$form_state, $form_id) {
  $form['#submit'][] = '_unpublish_new_product';
}

function _unpublish_new_product($form, &$form_state) {
   // is there a flag in $form_state that indicates if any fields were changed so I
   // could perform a conditional action??
}
share|improve this question

3 Answers

up vote 7 down vote accepted
+50

As far as I know, there isn't such flag, but you can use entity_load_unchanged() to get a copy of the entity before any change, and compare the field value with the one you get from the form.

The function is called, for example, from node_save(), which contain the following code.

// Load the stored entity, if any.
if (!empty($node->nid) && !isset($node->original)) {
  $node->original = entity_load_unchanged('node', $node->nid);
}

field_attach_presave('node', $node);
global $user;

// Determine if we will be inserting a new node.
if (!isset($node->is_new)) {
  $node->is_new = empty($node->nid);
}

// Set the timestamp fields.
if (empty($node->created)) {
  $node->created = REQUEST_TIME;
}
// The changed timestamp is always updated for bookkeeping purposes,
// for example: revisions, searching, etc.
$node->changed = REQUEST_TIME;

$node->timestamp = REQUEST_TIME;
$update_node = TRUE;

// Let modules modify the node before it is saved to the database.
module_invoke_all('node_presave', $node);
module_invoke_all('entity_presave', $node, 'node');

This means that the implementations of hook_field_attach_presave(), hook_node_presave(), and hook_entity_presave() will get the unchanged version of the node object in $node->original (or $entity->original); if you are writing code for one of these hooks, you don't need to call entity_load_unchanged() because node_save() already did it for you.

Similar code is present in comment_save(), taxonomy_term_save(), taxonomy_vocabulary_save(), and user_save().

if (!empty($comment->cid) && !isset($comment->original)) {
  $comment->original = entity_load_unchanged('comment', $comment->cid);
}

field_attach_presave('comment', $comment);

// Allow modules to alter the comment before saving.
module_invoke_all('comment_presave', $comment);
module_invoke_all('entity_presave', $comment, 'comment');
  if (!empty($term->tid) && !isset($term->original)) {
    $term->original = entity_load_unchanged('taxonomy_term', $term->tid);
  }

  field_attach_presave('taxonomy_term', $term);
  module_invoke_all('taxonomy_term_presave', $term);
  module_invoke_all('entity_presave', $term, 'taxonomy_term');
share|improve this answer

As far as I know there isn't such a flag. You will have to compare the values yourself I think. For node forms I made this first attempt to compare the node data. This is just a quick idea how a solution could be, maybe I am missing some edge cases where this isn't working, but in my tests with a simple default "page" node form it was working well. (I used the "Preview" button for reloading the form and testing.)

As Mohammad Ali Akbari says, hook_presave() might be a better place to compare the data, but the comparison of the flatted arrays should work there, too.

Attribution for the flatten function goes to Rob Peck.

/**
 * @file mymodule.module
 * Do stuff.
 */

/**
 * Implements hook_form_FORM_ID_alter().
 *
 * Proof of concept of checking for changes to a node in form submit.
 * Uses the default "page" content type.
 *
 * You need devel module to see an nice Krumo of the fields that have changed.
 */
function mymodule_form_page_node_form_alter(&$form, &$form_state, $form_id) {
  //Get uncached data of the current node from the db.
  //Cast the node object to an array so we can use the flatten function
  //and php array_diff functions.
  $node_array_db = (array) node_load($form['#node']->nid,NULL, TRUE);

  //Throw away the changed value as this is alwasy different.
  unset($node_array_db['changed']);

  //Cast the node object to an array so we can use the flatten function
  //and php aray_diff functions.
  $node_array_form = (array) $form_state['node'];

  //The $node object from the form holds some metadata. Throw away the fields
  //in the forms $node object/array that are not in the version from the db.
  $node_array_form = array_intersect_key($node_array_form,$node_array_db );

  //Flatten the nested array structure so we can use array_diff for comparing no
  //matter what the fields are structured.
  $result = array_diff(flatten($node_array_form), flatten($node_array_db));

  //Print all fields with differences in the field values.
  dpm($result);

  if (empty($result)) {
    dpm('No Changes');
    //No changes. Just "Save" was pressed.
  }
  else {
    dpm('There are different field values in this form than those in the database.');
    //Do something.
  }

}

/**
 * Function for flattening an array.
 *
 */
function flatten($arr, $base = "", $divider_char = "/") {
  $ret = array();

  if (is_array($arr)) {
    foreach ($arr as $k => $v) {
      if (is_array($v)) {
        $tmp_array = flatten($v, $base.$k.$divider_char, $divider_char);
        $ret = array_merge($ret, $tmp_array);
      }
      else {
        $ret[$base.$k] = $v;
      }
    }
  }
  return $ret;
}
share|improve this answer

do you can do your action inside hook_node_presave($node)? where you have $node and $node->original to compare

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.