2

On my user accounts, I have a text field, field_nickname. Users can change this at any time.

When they do change it, however, when the user account is saved I want to copy the old value for field_nickname to a text field accepting unlimited values, field_nicknamehistory.

In Drupal 7, you could do this in Rules by making a rule "on user update" and accessing the old value through the tokens in the rule interface. However, Rules isn't here yet for Drupal 8, so I'm trying to do this in code.

I tried to do this with hook_user_presave() but I don't know how to get the old value to compare it against the new value:

function mymodule_user_presave(Drupal\user\UserInterface $entity) {
  // How do I get $previousnickname?
  if ($entity->get("field_nickname") !== $previousnickname) {
    $entity->get('field_nicknamehistory')->appendItem($entity->get($previousnickname));
  }

I looked through the API and found onChange(), but I don't see how that would help me here.

  • EntityAdapter is a data type class; it can be used to create entity's properties, but you cannot used it for what you want to achieve. – kiamlaluno Feb 5 '16 at 10:19
  • I have updated my answer. It seems that what content_translation_entity_presave() does is a little excessive. – kiamlaluno Feb 5 '16 at 17:20
3

The Content translation module has code you could use, right in its hook_entity_presave() implementation (i.e. content_translation_entity_presave()).

if (!isset($entity->original)) {
  $entity->original = entity_load_unchanged($entity->entityType(), $entity->id());
}

In $entity->original you then find all the values that entity object had before being changed, as the documentation for entity_load_unchanged() says:

Loads the unchanged, i.e. not modified, entity from the database.

Unlike entity_load() this function ensures the entity is directly loaded from the database, thus bypassing any static cache. In particular, this function is useful to determine changes by comparing the entity being saved to the stored entity.

If you want to write code that will need less changes when passing to Drupal 9, you can use the following one, which replaces the call to entity_load_unchanged() (marked deprecated) with that function's code.

if (!isset($entity->original)) {
  $entity->original = \Drupal::entityManager()->getStorage($entity->entityType())->loadUnchanged($entity->id());
}

Also image_entity_presave() uses $entity->original, but it doesn't load it if it has not been set.

$original_uuid = isset($entity->original) ? $entity->original->getSetting('default_image')['uuid'] : NULL;

Probably, you could simplify the code.

if (!isset($entity->original)) {
  // Get the old value of the field.
}

Looking at the method calling hook_entity_presave(), EntityStorageBase::doPreSave(), the latter seems a more correct code.

  $id = $entity->id();

  // Track the original ID.
  if ($entity->getOriginalId() !== NULL) {
    $id = $entity->getOriginalId();
  }

  // Track if this entity exists already.
  $id_exists = $this->has($id, $entity);

  // Omissis

  // Load the original entity, if any.
  if ($id_exists && !isset($entity->original)) {
    $entity->original = $this->loadUnchanged($id);
  }

  // Allow code to run before saving.
  $entity->preSave($this);
  $this->invokeHook('presave', $entity);

EntityStorageBase::doPreSave() already sets $entity->original for entities that aren't new; the code used from content_translation_entity_presave() seems excessive.

  • I don't know why content_translation_entity_presave() does that. For an update, $entity->original should always exist. And for a new entity, it can't since there is no original entity yet. So instead of that, just make sure with !$entity->isNew() that you are indeed updating. – Berdir Feb 5 '16 at 16:07
  • There are just two implementations of hook_entity_presave(); the other one doesn't set $entity->original. Considering what EntityStorageBase::doPreSave() does, I would say it's enough to to use if (isset($entity->original)) { /* */ }. I added a note about that. – kiamlaluno Feb 5 '16 at 17:18
0

Based on @kiamlaluno's answer, here is the code that worked for me (note that it adds a timestamp when recording the change to the field):

function mymodule_user_presave(Drupal\user\UserInterface $entity) {
  if (!$entity->isNew() && ($entity->get('field_nickname')->value !== ($entity->original->get('field_nickname')->value))) {
    $record = $entity->original->get('field_nickname')->value.' '.date('Y-m-d H:i:s');
    $entity->get('field_nicknamehistory')->appendItem($record);
  }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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