Problem/Motivation

Entity reference field items are not removed when the referenced entity is deleted.

E.g.

  1. Create taxonomy term with tid 123 "trees".
  2. Create node "pine" with nid 456 tagged with "trees".
  3. Delete the "trees" term. Nid 456 will still be related to tid 123 even though there is no longer such a term.

Mostly this isn't a problem. Some examples where it can cause problems:

  • Search facets with an incorrect count of results.
  • Cryptic error messages when editing entities that contain collapsed Paragraphs.
  • Null fields in GraphQL results (which is invalid GraphQL).
  • Acquia's Cohesion product (see "Why is my Cohesion layout table so large" which points to this issue)

What can I do until a patch has been created?

Why doesn't a fix for this already exist in core?

  • There are scenarios where dangling references shouldn't be automatically cleaned up (if you need a human, or some other system to take action).
  • It's a performance challenge. There may be tens of thousands (or more) dangling references to clean up.

Proposed resolution

In interactive scenarios - like the entity delete confirm form - ask the editor if they want to delete the orphaned references:

  • immediately (default)
  • in the background or
  • don't remove the references at all (not recommended)

In a non-interactive environment (PHP API, REST API) always use the second option.

Remaining tasks

  • Write code
  • ...
  • Profit

User interface changes

New radio group on the entity delete confirmation form (and bulk operations confirm form too?).

API changes

TBD

Data model changes

None

Comments

blazey created an issue. See original summary.

blazey’s picture

Issue summary: View changes
blazey’s picture

Issue summary: View changes
blazey’s picture

Issue summary: View changes

Version: 8.6.x-dev » 8.7.x-dev

Drupal 8.6.0-alpha1 will be released the week of July 16, 2018, which means new developments and disruptive changes should now be targeted against the 8.7.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

Version: 8.7.x-dev » 8.8.x-dev

Drupal 8.7.0-alpha1 will be released the week of March 11, 2019, which means new developments and disruptive changes should now be targeted against the 8.8.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

wim leers’s picture

Issue tags: +data integrity
hchonov’s picture

+1 on such a feature.

The option should be available only if enabled by a setting.

However the user should be aware what exactly is being deleted and we would need to preset a list (probably with checkboxes to allow opting out of some deletions).

Please note that even the owner of the entity is behind an entity reference field. Therefore we need to enable this per field - this is something we do in one of our projects already.

However the list might get long in cases of long and nested list of references - for example entity type A references 10 entities of entity type B, which references another 20 entities of entity type C and so on ...

Version: 8.8.x-dev » 8.9.x-dev

Drupal 8.8.0-alpha1 will be released the week of October 14th, 2019, which means new developments and disruptive changes should now be targeted against the 8.9.x-dev branch. (Any changes to 8.9.x will also be committed to 9.0.x in preparation for Drupal 9’s release, but some changes like significant feature additions will be deferred to 9.1.x.). For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

Version: 8.9.x-dev » 9.1.x-dev

Drupal 8.9.0-beta1 was released on March 20, 2020. 8.9.x is the final, long-term support (LTS) minor release of Drupal 8, which means new developments and disruptive changes should now be targeted against the 9.1.x-dev branch. For more information see the Drupal 8 and 9 minor version schedule and the Allowed changes during the Drupal 8 and 9 release cycles.

chi’s picture

Version: 9.1.x-dev » 8.9.x-dev
Category: Task » Bug report

Entity reference field items are not removed when the referenced entity is deleted.

It's more like a bug. Isn't it?

dino.amino’s picture

It's more like a bug. Isn't it?

Yep. A real hot mess. I don't think most people realize how out of control it becomes. We certainly didn't when we started our Drupal project.

3 years, 400 authors and 8,000 nodes later we have a crisis on our hands.

If we only knew then what we know now ... thanks Acquia /s

mrpauldriver’s picture

#12 Yes I have just become aware of this 'hot mess'. I worry this could lead to problems down the road.

dalin’s picture

Title: Remove orphaned references when an entity is deleted » Remove orphaned (dangling) entity references when an entity is deleted
Issue summary: View changes

Reading through the comment history, it looks like a couple people are confused about this is about.

Note that we're not talking about going the other direction (If you delete a entity, also delete the entities that are referenced by this entity).

I'm cleaning up the description to clarify.

kreatil’s picture

+1

A possible solution would be to scan all field entries of the type "entity reference" (How to select by field type?). If a field contains the entity id of the entity (to be) deleted, the entry is removed.

bojan.m’s picture

I also had the same problem with this thing but only with referenced custom entities. My solution was that, like kreatIL said, to go through all entities which has entity reference field and update them. This is my solution and it works fine for me. I am not sure why we do not have something like this already in core because it is really problematic thing when you are working (create/update/delete) entities programmatically.

/**
 * Implements hook_entity_delete().
 */
function hook_entity_delete(EntityInterface $entity) {

  $field_map = \Drupal::service('entity_field.manager')->getFieldMapByFieldType('entity_reference');

  foreach ($field_map as $entity_type => $item) {

    foreach ($item as $field_name => $item2) {

      foreach ($item2['bundles'] as $bundle) {

        $bundle_fields = \Drupal::getContainer()->get('entity_field.manager')->getFieldDefinitions($entity_type, $bundle);
        $field_definition = $bundle_fields[$field_name];

        if ($field_definition->getItemDefinition()->getSetting('target_type') == $entity->getEntityType()->id()) {

          $parent_entities = \Drupal::entityTypeManager()->getStorage($entity_type)->loadByProperties([$field_name => $entity->id()]);

          foreach ($parent_entities as $parent_entity) {
            foreach ($parent_entity->get($field_name) as $delta => $field_item) {
              if ($field_item->target_id == $entity->id()) {
                $parent_entity->get($field_name)->removeItem($delta);
                $parent_entity->save();
                break;
              }
            }
          }

        }

      }

    }

  }

}
dalin’s picture

Issue summary: View changes

@bojan.m

I am not sure why we do not have something like this already in core

I updated the issue description to describe why:

Why doesn't a fix for this already exist in core?

  • There are scenarios where dangling references shouldn't be automatically cleaned up (if you need a human, or some other system to take action).
  • It's a performance challenge. There may be tens of thousands (or more) dangling references to clean up.
mdupont’s picture

Version: 8.9.x-dev » 9.3.x-dev

Still relevant for 9.x :-)

Also there is already a basic example of reference cleanup in core in node_user_predelete() which deletes nodes which have as author the user being deleted.

bojan.m’s picture

@dalin

Why doesn't a fix for this already exist in core?

  • There are scenarios where dangling references shouldn't be automatically cleaned up (if you need a human, or some other system to take action).
  • It's a performance challenge. There may be tens of thousands (or more) dangling references to clean up.

Well, I do not see this performance challenge as big issue because it can be managed by queues like Revisions that are no longer used.
Something like "Entity Reference Revisions Orphan Purger" from Entity Reference Revisions module where you do not update all entities in one request but you put them in queue.

dalin’s picture

@bojan.m

I agree, I'm just stating why it doesn't already exist.

bojan.m’s picture

We had some free time to create module that will solve this problem.

Entity reference purger

Feel free to test it and use it.. if you have something on your mind please write us issue.
Thanks.

Version: 9.3.x-dev » 9.4.x-dev

Drupal 9.3.0-rc1 was released on November 26, 2021, which means new developments and disruptive changes should now be targeted for the 9.4.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

Version: 9.4.x-dev » 9.5.x-dev

Drupal 9.4.0-alpha1 was released on May 6, 2022, which means new developments and disruptive changes should now be targeted for the 9.5.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

taggartj’s picture

Yes in my case it was JSON API having a node resource with a (Required) dynamic_entity_reference field of type node
if you delete the referenced node = fire and brimstone and serious cryptic error.

leading to you doubting core , JSON API and your own sanity

  // no this is not the issue its still thinking the node exists :( 
  $relatable_resource_types = $resource_type->getRelatableResourceTypesByField($resource_type->getPublicName($field->getName()));
  assert(!empty($relatable_resource_types));

Version: 9.5.x-dev » 10.1.x-dev

Drupal 9.5.0-beta2 and Drupal 10.0.0-beta2 were released on September 29, 2022, which means new developments and disruptive changes should now be targeted for the 10.1.x-dev branch. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

dalin’s picture

Issue summary: View changes
dalin’s picture

Issue summary: View changes

Version: 10.1.x-dev » 11.x-dev

Drupal core is moving towards using a “main” branch. As an interim step, a new 11.x branch has been opened, as Drupal.org infrastructure cannot currently fully support a branch named main. New developments and disruptive changes should now be targeted for the 11.x branch, which currently accepts only minor-version allowed changes. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

xurizaemon’s picture

Issue summary: View changes

Acquia docs link to this issue as the cause of surprisingly large DB tables belonging to their Cohesion system.. On a site I looked at there were 63K nodes, 75K node revisions, and the table cohesion_layout_field_revision was 630MB in size. (This was larger than the entire Drupal codebase and the remainder of the site DB put together.)

Adding Acquia's Cohesion tables to the "examples of this problem" list in ID.

joachim’s picture

I made a contrib module that takes care of deleting references: https://www.drupal.org/project/entity_reference_delete

anybody’s picture

In addition to #30:

I agree, references need a functionality like databases have to track references and remove orphans and it should be in core. Still I understand the complexities ;)

kreatil’s picture

#31: +1
We have been using Entity Reference Purger in production for over a year. The module addresses exactly the issue described in the original post. In our usecase, the number of references to be deleted is typically in the triple digits, without any significant performance issues.

Version: 11.x-dev » main

Drupal core is now using the main branch as the primary development branch. New developments and disruptive changes should now be targeted to the main branch.

Read more in the announcement.