7

I've seen a variety of approaches to access entity values in the theming layer. The code below is just a simple example using magic methods. It assumes all values are set - if any part of that returns a NULL, then in my experience errors ensue.

$value = $node->field_paragraph
    ->entity->field_media
    ->entity->field_file
    ->entity->field_text->value;

There are methods like ->hasField and ->count() to check things, and i have a variety in my themes. I can get the job done but sometimes it feels ugly. I'm interested the coding patterns that devs use to safely and elegantly drill through entity references.

1
  • 1
    There is a contrib module called reference_map. It's not very user friendly, but it was written expressly for this issue. Commented Jun 19, 2019 at 16:28

3 Answers 3

7

Suggestion, create a domain object that has the logic.

Normally these entities represent something that fits your business-domain.

E.g in your instance, the node might be an event.

So you might model a domain object called EventWrapper.

<?php

namespace Drupal\my_domain;
use Drupal\node\NodeInterface;
use Drupal\media\MediaInterface;
use Drupal\file\FileInterface;

class EventWrapper {
  protected $node;
  public static function fromNode(NodeInterface $node): EventWrapper {
    $instance = new static();
    $instance->node = $node;
    return $instance;
  }
  public function getMedia() : ?MediaInterface {
    if ($this->node->hasField('field_media') && !$this->node->get('field_media')->isEmpty()) {
      return $this->node->field_media->entity;
    }
    return NULL;
  }
  public function getMediaImage() : ?FileInterface {
    if (($media = this->getMedia()) && $media->hasField('field_file') && !$media->get('field_file')->isEmpty()) {
      return $media->field_file->entity;
    }
    return NULL;
  }
  public function getImageCaption(): ?string {
    if (($file = this->getMediaImage()) && $file->hasField('field_text') && !$file->get('field_text')->isEmpty()) {
      return $file->field_text->value;
    }
    return NULL;
  }
}

Then in your code:

<?php

$image_caption = EventWrapper::fromNode($node)->getImageCaption();
6

Although normally you render paragraphs recursively, you can retrieve a fixed structure non-recursively by recreating it with foreach loops:

foreach ($node->field_paragraph->referencedEntities() as $paragraph) {
  foreach ($paragraph->field_media->referencedEntities() as $media) {
    ...
  }
}

This avoids accessing empty fields and is able to process multi-value fields.

0
1

To expand on the great answer by 4k4 which shows a pattern we use widely but doesn't cover the existence of the field, you could create a simple class patterned after larowlan's great answer:

class EntityReferenceItemListHelper {
   protected $fieldItemList;
   static function create(?EntityReferenceFieldItemListInterface $fieldItemList) {
     $instance = new static;
     $instance->fieldItemList = $fieldItemList;
   }
   public function referencedEntities(): array {
     return $this->fieldItemList ? $this->fieldItemList->referencedEntities() : [];
   }
}

and then foreach (EntityReferenceItemListHelper::create($paragraph->field_media)->referencedEntities() as $media) {

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.