53

I have a node object with image field named field_image. When I do

$node
  ->get('field_image')
  ->first()
  ->getValue()

I get array with some properties instead of entity object. The array looks like this:

'target_id' => string '8' (length=1)
'alt' => string '' (length=0)
'title' => string '' (length=0)
'width' => string '587' (length=3)
'height' => string '458' (length=3)
'_loaded' => boolean true
  '_accessCacheability' => 
    object(Drupal\Core\Cache\CacheableMetadata)[1092]
      protected 'cacheContexts' => 
        array (size=0)
          ...
      protected 'cacheTags' => 
        array (size=0)
          ...
      protected 'cacheMaxAge' => int -1

Do I have to manullay load the file entity using target_id or there is a way to automatically get referenced entity object from instance of Drupal\image\Plugin\Field\FieldType\ImageItem?

Why the referenced entity is not accessible via getter? It could be lazy loaded if it's not available immediately.

3
  • 1
    $node->get('imagefield')->first()->getTarget() if there is only one item or $node->get('imagefield')->referencedEntities() ... actually that last one is all you need.
    – user21641
    Commented Jan 10, 2016 at 14:52
  • @IvanJaros Not exactly: PHP Fatal error: Call to undefined method Drupal\image\Plugin\Field\FieldType\ImageItem::getTarget(). However $node->get('imagefield')->referencedEntities() works thanks to the fact $node->get('imagefield') returns instance of EntityReferenceFieldItemList which implements referencedEntities() method. So your solution is only partial - it works for lists but not for single fields. Commented Jan 10, 2016 at 15:18
  • Yeah right, the getTarget is on entity reference data type, not field item.
    – user21641
    Commented Jan 10, 2016 at 15:23

6 Answers 6

111

The path to referenced entity is very long:

// $id = some node ID
// $field = field name for entity reference field
$node = Node::load($id);

/** @var \Drupal\Core\Field\Plugin\Field\FieldType\EntityReferenceItem $referenceItem */
$referenceItem = $node->get($field)->first();

/** @var \Drupal\Core\Entity\Plugin\DataType\EntityReference $entityReference */
$entityReference = $referenceItem->get('entity');

/** @var \Drupal\Core\Entity\Plugin\DataType\EntityAdapter $entityAdapter */
$entityAdapter = $entityReference->getTarget();

/** @var \Drupal\Core\Entity\EntityInterface $referencedEntity */
$referencedEntity = $entityAdapter->getValue();

// At this point $referencedEntity is the referenced entity object.

Of course one can still get it via one liner call, but still, it is cumbersome and I wonder why EntityReferenceItem does not provide method that would return the reference entity object.

$referencedEntity = $node
  ->get($field)
  ->first()
  ->get('entity')
  ->getTarget()
  ->getValue()
;

Note: if you have php 8, you can use the nullsafe operator:

$referencedEntity = $node
  ?->get($field)
  ?->first()
  ?->get('entity')
  ?->getTarget()
  ?->getValue()
;

It's also worth noting that it's quite easy to get array of all referenced entities:

$node->get($field)->referencedEntities();

It works because for entity reference fields $node->get($field) returns instance of EntityReferenceFieldItemList which implements referencedEntities() method.

1
  • Despite all the crap around it @ЕлинЙ. it's a move in the right direction. I look at it as an intermediary step towards something better. You're always free to contribute at early stages and improve things. It after all a joint effort. Commented Aug 31, 2021 at 14:23
43

You can use $node->field_image->entity, if you want the first value.

8
  • 14
    This is why magic methods are bad. No way to document this in code and no way to figure out this by looking at implemented interfaces. Commented Jan 10, 2016 at 20:33
  • 7
    It's hard to document in code maybe, but there is documentation: wizzlern.nl/sites/wizzlern.nl/files/artikel/…. Due to the dynamic structure, methods wouldn't really work better, you can't figure out which class you're working with exactly as everything is built together dynamically. It might be hard to find for the first time, but it's easy enough to remember once you did.
    – Berdir
    Commented Jan 10, 2016 at 21:17
  • 2
    There is drupal.org/project/agcobcau which will help with auto completion in IDEs
    – larowlan
    Commented Jan 10, 2016 at 23:03
  • 15
    @Berdir I do understand that, however this is still bad design. Look at this: $node->get('field_image')->entity returns File entity. However $node->get('field_image')->getEntity() returns parent Node entity! I do understand that magic methods might be usable, but they are PITA in larger projects. Why bother writing interfaces and oo code if we are encouraged to use magic anyway? You wrote it's easy enough to remember once you did - I disagree. Once you start doing this, you'll develop really bad habit that will make your code less readable and less maintainable. Commented Jan 11, 2016 at 8:44
  • 5
    Downvoted. I used to do it this way. However this will sometimes break if the entity references haven't been loaded before (the result will be an array with target_id instead of an instantiated entity). The method outlined by SiliconMind will properly handle all cases and will always return an instantiated entity class if the field is not empty. Commented Nov 20, 2017 at 15:09
24

For how to get an entity from a referenced field. Im agree with @SiliconMind about referencedEntities it return an array of entity objects keyed by field item deltas.

Just simple:

$node->get('field_name')->referencedEntities();

EntityReferenceFieldItemList::referencedEntities

1
  • This is indeed a very clear way to do this, and code is self explanatory. Until I read your anwer, I was doing $term_id = $productAttributeValue->get('field_product_attribute_statut')->getValue()[0]['target_id']; which works but is awful :-)
    – Kojo
    Commented May 14, 2020 at 14:39
13

What worked for me was:

$entity_id = $node->get('field_contact')->first()->getValue()['target_id'];

$entity = Node::load($entity_id)->toArray();

The other answers didn't produce the entity fields array I was expecting

2

$ENTITY->FIELD_MACHINE_NAME->entity returns the first item that referenced. but if you want to get value of the specified field in that entity use

$ENTITY->FIELD_MACHINE_NAME->entity->FIELDMACHINENAME_IN_REFERENCED_ENTITY->value

for example, if you want to get hospital_key of the hospital entity reference field on user entity. use

$user->field_hospital->entity->field_hospital_key->value
0

Here's a gist for a NestedEntityReference utility class I cooked up, modeled after the NestedArray utility class in Drupal core: https://gist.github.com/GuyPaddock/69ce592e2b58862da852afc5d7563c4a

This provides a way to access the targets of ER fields safely, even if the ER field is empty or refers to non-existent entities (e.g. if a target entity was deleted separately through an admin interface, such as in the case of https://www.drupal.org/project/entityreference/issues/2827177). As a bonus, this utility class bakes-in some development-time assertions so you can catch data structure/data issues early.

Example of usage (navigating from a node -> media entity -> URL to video file in the entity):

/** @var \Drupal\media\MediaInterface|null $video_media */
$video_media =
  NestedEntityReference::getTargetEntity(
    $my_node,
    'field_video',
    MediaInterface::class
  );

if ($video_media !== NULL) {
  /** @var \Drupal\file\FileInterface $video_file */
  $video_file =
    NestedEntityReference::getTargetEntity(
      $video_media,
      'field_file',
      FileInterface::class
    );

  $file_url = $video_file->createFileUrl()
}

Feedback on the Gist is welcome!

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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