Letting Drupal know about new/updated/deleted external entities

Created on 30 June 2025, 4 days ago

I was wondering how everyone deals with letting Drupal know about new, updated and deleted external entities.

Out of the box (AFAICT?), quite common hook implementations such as hook_entity_presave(), hook_entity_insert(), hook_entity_update() and hook_entity_delete() are not automatically invoked. A lot of core functionality and modules listen to these hooks (cache invalidations, Search API, ...).

This issue could serve as a place to share experiences, perhaps provide at least some code than assist in this, ...

💬 Support request
Status

Active

Version

3.0

Component

Code

Created by

🇧🇪Belgium rp7

Live updates comments and jobs are added and updated live.
Sign in to follow issues

Comments & Activities

  • Issue created by @rp7
  • 🇧🇪Belgium rp7

    Here's my experience on a project we're working on.

    We have an external system that notifies us (through a custom API endpoint) about changes on our external content. Once we receive a message (operation, type, ID), after some trial and error we ended up with the following code to invoke entity lifecycle hooks:

    if ($action === 'delete') {
      $entity::preDelete($entity_storage, [$entity]);
      $this->moduleHandler->invokeAll($entity->getEntityTypeId() . '_predelete', [$entity]);
      $this->moduleHandler->invokeAll('entity_predelete', [$entity]);
      $entity::postDelete($entity_storage, [$entity]);
    }
    else {
      $entity->original = clone $entity;
      $entity->preSave($entity_storage);
      $this->moduleHandler->invokeAll($entity->getEntityTypeId() . '_presave', [$entity]);
      $this->moduleHandler->invokeAll('entity_presave', [$entity]);
      $entity->postSave($entity_storage);
    }
    
    $hook = $action === 'create' ? 'insert' : $action;
    $this->moduleHandler->invokeAll($entity->getEntityTypeId() . '_' . $hook, [$entity]);
    $this->moduleHandler->invokeAll('entity_' . $hook, [$entity]);
    

    This has worked more or less OK for quite a while. It does have some gaps, however. Thinking about the entity translation-specific hooks, for example.

  • 🇧🇪Belgium rp7

    After some thinking, I think the most proper way is to be able to call $external_entity->save() or $external_entity->delete(). This comes very close (if not completely) to Drupal's default way of working with entities - but doing this could (if your external entities are not marked as read-only) send a write to the external API - which is undesired in this scenario (since we only want to call it to make Drupal do its thing on the new/updated/deleted entity).

    I was wondering if it's a good idea to introduce something so that we can (temporarily) mark external entities on which save/delete operations are performed, to not push through to the external API. See patch attached. It's based on a similar functionality Drupal core (setSyncing and isSyncing).

    With this patch, the code above could be changed to:

    $entity->skipExternalStorageMutation();
    $entity->save();
    $entity->skipExternalStorageMutation(FALSE);
    

    and

    $entity->skipExternalStorageMutation();
    $entity->delete();
    $entity->skipExternalStorageMutation(FALSE);
    

    Any thoughts? How is everyone else tackling this problem space?

  • 🇫🇷France guignonv Montpellier

    I'm not there yet but I'll keep an eye on this issue. ;)

Production build 0.71.5 2024