EntityInterface defines a referencedEntities() method :

  /**
   * Returns a list of entities referenced by this entity.
   *
   * @return \Drupal\Core\Entity\EntityInterface[]
   *   An array of entities.
   */
  public function referencedEntities();

- The exact semantics are a bit fuzzy ("referenced by" - in what way ?)

- It was introduced for cache tags two years ago in #1605290: Enable entity render caching with cache tag support, but it is currently not called anywhere in Core anymore.

- The basic implementation in Entity just returns [], and no config entity ever overrides that

- The basic implementation for ContentEntities loops through all properties of all deltas of all fields to look for values that are DataType\EntityReference :

foreach ($field_items as $field_item) {
        // Loop over all properties of a field item.
        foreach ($field_item->getProperties(TRUE) as $property) {
          if ($property instanceof EntityReference && $entity = $property->getValue()) {
            $referenced_entities[] = $entity;
          }
       }
    }

That is widely inefficient (single-loads all entities).

- Pretty much all reference-like field types in core are now subclasses of EntityReference fields, which now have a more efficient method :

foreach ($this->getFields() as $field_items) {
      // If the field type extends entity_reference, take advantage of the
      // multiple-load capabilities
      if ($field_items instanceof EntityReferenceFieldItemListInterface) {
        $referenced_entities = array_merge($referenced_entities, $field_items->referencedEntities());
      }
    }

The difference being that it wouldn't catch "properties that happen to hold a entity in a random field type that is not EntityReference". If we still want those (not sure, since the semantics of the method is not too clear :-)) we could add an "else { current HEAD code}" after that.

But do we really still need that Entity::referencedEntities() method to begin with ?

Support from Acquia helps fund testing for Drupal Acquia logo

Comments

yched’s picture

Status: Active » Needs review
FileSize
1.89 KB

Patch tries to improve the performance of the existing code.

Still not sure we really need that method to begin with :-)

amateescu’s picture

I think we should just remove the method, it's slow without this optimization, unused, probably untested and it doesn't serve its initial purpose anymore. We need to ask a core committer about this :)

yched’s picture

FWIW, the original implementation of referencedEntities() in #1605290: Enable entity render caching with cache tag support only took EntityReference field type into account.

Then it was rewritten as part of #2004244: Move entity revision, content translation, validation and field methods to ContentEntityInterface (comment #108 over there (/cc @Berdir :-)), to account for "any property that contains an entity, whatever the field type".

Status: Needs review » Needs work

The last submitted patch, 1: 2471801_ContentEntityBase_referencedEntities-1.patch, failed testing.

larowlan’s picture

Fwiw default content module uses this in export and entity pilot does too.

effulgentsia’s picture

Given #5, I think we should keep EntityInterface::referencedEntities(), but make it work correctly, efficiently, and add test coverage.

The basic implementation in Entity just returns [], and no config entity ever overrides that

Now that config has a 'dependencies' property with 'content' and 'config' subkeys, we should be able to implement ConfigEntityBase::referencedEntities() as returning those, right?

  1. +++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
    @@ -976,11 +978,20 @@ public function referencedEntities() {
         foreach ($this->getFields() as $field_items) {
    +      // If the field type extends entity_reference, take advantage of the
    +      // multiple-load capabilities
    +      if ($field_items instanceof EntityReferenceFieldItemListInterface) {
    +        $referenced_entities = array_merge($referenced_entities, $field_items->referencedEntities());
    +      }
    

    +1 to the increase in efficiency here. But is it still inefficient to call getFields() if most fields aren't entity reference fields? Should we loop on getFieldDefinitions() instead and only instantiate the fields that are references?

  2. +++ b/core/lib/Drupal/Core/Entity/ContentEntityBase.php
    @@ -976,11 +978,20 @@ public function referencedEntities() {
    +      else {
    +        // Loop over all properties of all items.
    +        // @todo Would be more performant to start by checking the property definitions
    +        // and only iterate on items for properties whose definition say they hold entities ?
    +        foreach ($field_items as $field_item) {
    +          foreach ($field_item->getProperties(TRUE) as $property) {
    +            if ($property instanceof EntityReference && $entity = $property->getValue()) {
    +              $referenced_entities[] = $entity;
    +            }
               }
             }
           }
    

    I'm not clear on the use case of a non-ER field holding an ER property, but if we can clarify what a legitimate use case of that is, then yes, it makes sense to have this fallback, and +1 to implementing the @todo.

yched’s picture

I'm fine with keeping it. But given we have no actual use case in core, it's a bit hard to define the exact behavior we want here :-)

repeating @effulgentsia's question in #6 :

- For ConfigEntities,

Now that config has a 'dependencies' property with 'content' and 'config' subkeys, we should be able to implement ConfigEntityBase::referencedEntities() as returning those ?

Do we want "a config dependency counts as a reference" ? Maybe, dunno - ConfigEntities don't really have a notion of "reference" :-)

- For ContentEntities,

I'm not clear on the use case of a non-ER field holding an ER property, but if we can clarify what a legitimate use case of that is, then yes, it makes sense to have this fallback

Right, no clue either :-)

@larowlan, what do you think ?

larowlan’s picture

The current behavior is adequate. We should just document that it only considers ER items.

effulgentsia’s picture

Maybe, dunno - ConfigEntities don't really have a notion of "reference"

As an example, let's consider core.entity_form_display.comment.comment.default.yml, which has the following lines that are relevant for this issue:

dependencies:
  config:
    - comment.type.comment
    - field.field.comment.comment.comment_body
targetEntityType: comment
bundle: comment
content:
  comment_body:
    settings:
      rows: 5

IMO, the config entities listed as the dependencies are equivalent to references. This config references comment.type.comment via the targetEntityType and bundle properties, and it references field.field.comment.comment.comment_body via the targetEntityType, bundle, and content.comment_body properties. For config, I see dependencies and references as equivalent, because:

  • why depend on something that you don't reference?
  • if config references something that it doesn't list as a dependency, a deployment would break referential integrity.

Another interesting example though is block.block.bartik_account_menu.yml, the relevant lines of which are:

plugin: 'system_menu_block:account'
dependencies:
  config:
    - system.menu.account

Here, we depend on system.menu.account though don't hold a direct reference to it. Instead we reference the system menu block plugin, and that plugin references the menu. But I think it's still desirable for it to be returned by referencedEntities(), because logically, the block entity is referencing the menu entity: that it's doing it via a plugin as an intermediary is an implementation detail that I don't think the caller of $block_entity->referencedEntities() would care about.

Version: 8.0.x-dev » 8.1.x-dev

Drupal 8.0.6 was released on April 6 and is the final bugfix release for the Drupal 8.0.x series. Drupal 8.0.x will not receive any further development aside from security fixes. Drupal 8.1.0-rc1 is now available and sites should prepare to update to 8.1.0.

Bug reports should be targeted against the 8.1.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.2.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.1.x-dev » 8.2.x-dev

Drupal 8.1.9 was released on September 7 and is the final bugfix release for the Drupal 8.1.x series. Drupal 8.1.x will not receive any further development aside from security fixes. Drupal 8.2.0-rc1 is now available and sites should prepare to upgrade to 8.2.0.

Bug reports should be targeted against the 8.2.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.3.x-dev branch. For more information see the Drupal 8 minor version schedule and the Allowed changes during the Drupal 8 release cycle.

tstoeckler’s picture

Just stumbled upon this and wanted to point everyone to #2824097: Deep serialization for content entities to make an exact snapshot of an entity object's structure based on its current state as that will in some form need to collect the referenced entities and might make use of that method.

Version: 8.2.x-dev » 8.3.x-dev

Drupal 8.2.6 was released on February 1, 2017 and is the final full bugfix release for the Drupal 8.2.x series. Drupal 8.2.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.3.0 on April 5, 2017. (Drupal 8.3.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.3.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.4.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.3.x-dev » 8.4.x-dev

Drupal 8.3.6 was released on August 2, 2017 and is the final full bugfix release for the Drupal 8.3.x series. Drupal 8.3.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.4.0 on October 4, 2017. (Drupal 8.4.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.4.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.5.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.4.x-dev » 8.5.x-dev

Drupal 8.4.4 was released on January 3, 2018 and is the final full bugfix release for the Drupal 8.4.x series. Drupal 8.4.x will not receive any further development aside from critical and security fixes. Sites should prepare to update to 8.5.0 on March 7, 2018. (Drupal 8.5.0-alpha1 is available for testing.)

Bug reports should be targeted against the 8.5.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.6.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.5.x-dev » 8.6.x-dev

Drupal 8.5.6 was released on August 1, 2018 and is the final bugfix release for the Drupal 8.5.x series. Drupal 8.5.x will not receive any further development aside from security fixes. Sites should prepare to update to 8.6.0 on September 5, 2018. (Drupal 8.6.0-rc1 is available for testing.)

Bug reports should be targeted against the 8.6.x-dev branch from now on, and new development or disruptive changes should 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.6.x-dev » 8.8.x-dev

Drupal 8.6.x will not receive any further development aside from security fixes. Bug reports should be targeted against the 8.8.x-dev branch from now on, and new development or disruptive changes should be targeted against the 8.9.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.

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

Drupal 8.8.7 was released on June 3, 2020 and is the final full bugfix release for the Drupal 8.8.x series. Drupal 8.8.x will not receive any further development aside from security fixes. Sites should prepare to update to Drupal 8.9.0 or Drupal 9.0.0 for ongoing support.

Bug reports should be targeted against the 8.9.x-dev branch from now on, and new development or disruptive changes should 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.

geek-merlin’s picture

The referenced issue might change the assumptions of the IS.

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

Drupal 8 is end-of-life as of November 17, 2021. There will not be further changes made to Drupal 8. Bugfixes are now made to the 9.3.x and higher branches only. For more information see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.

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

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

Drupal 9.3.15 was released on June 1st, 2022 and is the final full bugfix release for the Drupal 9.3.x series. Drupal 9.3.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.4.x-dev branch from now on, and new development or disruptive changes should 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.

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

Drupal 9.4.9 was released on December 7, 2022 and is the final full bugfix release for the Drupal 9.4.x series. Drupal 9.4.x will not receive any further development aside from security fixes. Drupal 9 bug reports should be targeted for the 9.5.x-dev branch from now on, and new development or disruptive changes should 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.

Version: 9.5.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. For more information, see the Drupal core minor version schedule and the Allowed changes during the Drupal core release cycle.