5

I am trying to figure out an efficient way to get the fields names/values that have been updated. It's kind of similar to what the diff module does to calculate changed on a node but not as detailed. I just want to see what fields have been updated when a user saves an entity. For example a user may not change the title but could change the body. I would like to build an api function returns that. something like this pseudo code:

// possibly on presave or form submit. 
function my_module_entity_presave() {
    // Return only the fields values that are not similar to the original node: 
    $modified = $entity->getModifiedFields();
}

and the method could be something like this psudo code:

public function getModifiedFields() {
  $original = $this->originalFieldsValues();
  $new = $this->newFieldValues();
  // compare original with new
  // finally return only modified fields name values.
  $modified = ['title' => 'new value', 'body' => 'new value' ...];
  return $modified 
}
4
  • I think this is the API function you are looking for hook_ENTITY_TYPE_presave
    – 4uk4
    Commented Jan 4, 2019 at 17:53
  • @4k4 thanks, yes I am aware of the hooks but not if there is an existing way to check what have changed.
    – awm
    Commented Jan 4, 2019 at 18:03
  • 2
    I think array_keys(DiffArray::diffAssocRecursive($node->toArray(), $node->original->toArray()) may give you what you want.
    – mpdonadio
    Commented Jan 5, 2019 at 18:50
  • 1
    @mpdonadio worked like a charm. Add it as an answer so I can accept it.
    – awm
    Commented Jan 8, 2019 at 19:44

3 Answers 3

7

When you are in the presave hook, the updated $node object will have $node->original, which is the node before things were changed.

Nodes also have a $node->toArray(), method that will flatten the object into a multidimensional associative array.

So, this

array_keys(DiffArray::diffAssocRecursive($node->toArray(), $node->original->toArray())

should

  1. Take the $node and $node->original and convert them into arrays.
  2. Run the two arrays through one of the Drupal helper classes that will generate differences in multidimensional associative arrays.
  3. Return the top level keys. Because of the ways nodes are built, this will be all of the changed property and field names.
1
  • I just had to adjust this a bit for my purposes. By default the field values are not present on the node or original. So after computing the difference, if you need to know the field values that changed and their difference you need compute them as well. Looks like a good candidate for a recursive function .. thanks
    – awm
    Commented Jan 8, 2019 at 20:29
2

Here's an excerpt from a method I use to determine if an entity has changed. I adapted it a little bit to return the names for you. You may need to adapt it more for your specific needs.

mymodule > src > GaryFunctions.php

<?php

/**
 * File for holding helper functions user by Gary
 */


namespace Drupal\mymodule;

use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\FieldableEntityInterface;
use Drupal\Core\Entity\Entity\EntityViewDisplay;

class GaryFunctions {

  /**
   * Check if entity field content has changed
   * @param  EntityInterface $entity     The entity being saved
   * @return array                    A list of changed field names
   */
  public function entityHasChanged(EntityInterface $entity) {
    $changed_fields = [];
    if (!$entity->original) {
      return $changed_fields;
    }
    $field_names = $this->getFieldList($entity->bundle(), $entity->getEntityTypeId());
    foreach($field_names as $key => $field_name) {
      if($entity->hasField($field_name) && $field_name != 'field_comments' && !$entity->get($field_name)->equals($entity->original->get($field_name))){
        $changed_fields[] = $field_name;
        // $entity->get($field_name)->getValue();
      }
    }
     return $changed_fields;
  }


  /**
   * Get list of field names from bundle
   * @param  string $bundle Bundle name
   * @return array         Array of field names
   */
  public function getFieldList($bundle, $entity_type_id) {
    $fields_by_weight = [];
    $bundle_fields = \Drupal::entityTypeManager()
      ->getStorage('entity_view_display')
      ->load($entity_type_id . '.' . $bundle . '.' . 'default')
      ->getComponents();

    foreach ($bundle_fields as $name => $options) {
      $fields_by_weight[] = $name;
    }
    return $fields_by_weight;
  }

}

mymodule > mymodule.module

<?php

use Drupal\Core\Entity\EntityInterface;
use Drupal\mymodule\GaryFunctions;

function mymodule_entity_presave(EntityInterface $entity) {
  $helper = new GaryFunctions;

  switch ($entity->bundle()){

    case 'projects':
      $changed_fields = $helper->entityHasChanged($entity);
      if (!empty($changed_fields)) {
        print_r($changed_fields);
      } else {
        print 'didnt change';
      }
    break;

    default:
    break;
  }

}
0

You can use $node->original, it does give access to the original node values, so lets assume that the field you desire check is foo_field :

function myModule_node_presave($node) {
  if ($node->getType() == 'my_content_type') {
    if ($node->original->foo_field != $node->foo_field) {
      // Do what you want here.
    }
  }
}
1
  • 1
    I don't know the field name. I want all the fields that have changed. But I understand what you mean and that's my current approach it to iterate over all of the known fields.
    – awm
    Commented Jan 4, 2019 at 18:57

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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