Needs work
Project:
Drupal core
Version:
main
Component:
entity system
Priority:
Normal
Category:
Bug report
Assigned:
Unassigned
Reporter:
Created:
11 Nov 2016 at 19:12 UTC
Updated:
21 Aug 2026 at 09:12 UTC
Jump to comment: Most recent
EntityReferenceFieldItemList::referencedEntities in its current implementation will collect all the target ids from the field items and then collect the referenced entity objects by calling loadMultiple, but this is faulty in the following use cases:
resetCache is called after the entity objects have been initialized on some of the field items.EntityReferenceFieldItemList::referencedEntities should first collect the entities which are already set on the field items and load the remaining ones through loadMultiple.
None.
None.
None.
Start within a Git clone of the project using the version control instructions.
Or, if you do not have SSH keys set up on git.drupalcode.org:
Comments
Comment #2
hchonovThe same actually applies for the entity_reference_revisions module where the function is 1:1 copy of its parent just there loadRevision is being called instead of loadMultiple.
Comment #3
jibranThis applies to DER as well.
Comment #14
quietone commentedThis issue was the random bugsmash bug to triage a few days ago, adding tag.
It looks like this is waiting on a decision. I'll see if I can ping @hchonov in Slack.
What can be done to make a decision here?
Comment #15
jibran@hchonov is the one who purposed the solutions so we need @berdir, @amateescu, or @catch's opinion on the issue.
Comment #18
dieterholvoet commentedDrupal\Core\TypedData\Plugin\DataType\ItemList::getValue()doesn't have an$include_computedparameter, so that option is off the table.I'll create a MR implementing the first option.
Comment #19
dieterholvoet commentedComment #21
dieterholvoet commentedComment #22
smustgrave commentedThere are 2 options it appears in the issue summary proposed solution. Can it be highlighted which was chosen?
Also could use a test case showing the issue.
Comment #23
dieterholvoet commentedComment #25
dieterholvoet commentedIt seems like the current implementation of the MR causes a performance problem.
$item->entity !== NULLtriggersDataReferenceBase::getValue(), which triggersEntityReference::getTarget(), which triggersEntityStorageInterface::load(). This means that every entity is now loaded separately. I'll look for a different solution.Comment #26
dieterholvoet commentedI found a fix. In a custom API endpoint with a bunch of
referencedEntities()calls this makes a huge performance difference: -5.62 s (-75%) and -2 219 database queries. Hopefully anyone using a patch from this issue sees this.Comment #27
berdirYes, that's the problem with accessing the property, it auto-initializes, so that condition is always true if it can be loaded.
I don't think your change is correct either. getValue() will never return computed values, so the result is the same as HEAD.
I don't think it is possible at all to do this, because even accessing the EntityReference property directly with get('entity') there simply is no API to detect whether it has been set or not. There is no public method to access $this->target and \Drupal\Core\Entity\Plugin\DataType\EntityReference::getTarget() always loads it if not set, that's the same as ->entity.
That's why \Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceFormatterBase::prepareView + \Drupal\Core\Field\Plugin\Field\FieldFormatter\EntityReferenceFormatterBase::getEntitiesToView() use the magic _loaded property to track that.
So to change this, we'd first need to extend the underlying entity reference property to add a method to check if the refernce is loaded or not.
Comment #28
dieterholvoet commentedNo, that does seem to work. Because
EntityReferenceItem::getValue()looks like this:$this->entityalso triggersEntityReference::getTarget(), but sinceisset($this->target)it doesn't trigger a new entity load and returns the existing entity object instead.I think that means that the previous approach would have also worked, if we had just replaced
$item->entity !== NULLwith$item->hasNewEntity().Comment #29
dieterholvoet commentedBy the way, I just tested this with code like this:
$typescontained the newly created, unsaved entity.Comment #30
berdirIt works for *new* entities, but it doesn't work for existing, changed entities, which is what this issue is trying to fix. \Drupal\Core\Field\EntityReferenceFieldItemList::referencedEntities() in HEAD already accounts for this, because it falls back to that if target_id is null. That's consistent with hasNewEntity(), which also explicitly checks for target_id === NULL, so it doesn't matter whether that's checked first or not.
Your code will also work in HEAD. What is the bug that you are trying to fix?
We can not solve this and keep multiloading without exposing something like isTargetLoaded() on the reference property.