Skip to content

Instantly share code, notes, and snippets.

@GuyPaddock
Created November 3, 2020 21:28
Show Gist options
  • Save GuyPaddock/69ce592e2b58862da852afc5d7563c4a to your computer and use it in GitHub Desktop.
Save GuyPaddock/69ce592e2b58862da852afc5d7563c4a to your computer and use it in GitHub Desktop.
NestedEntityReference utility class for safely getting access to ER fields
<?php
namespace Drupal\my_utiilities\Utility;
use Drupal\Core\Entity\ContentEntityInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\Plugin\DataType\EntityAdapter;
use Drupal\Core\Field\EntityReferenceFieldItemListInterface;
use Drupal\Core\Field\FieldItemInterface;
use Drupal\Core\TypedData\DataReferenceInterface;
use Drupal\Core\TypedData\Exception\MissingDataException;
/**
* Utility class for working with entity reference fields.
*
* @ingroup utility
*/
class NestedEntityReference {
/**
* Gets an entity referenced by a single-delta entity reference field.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The entity containing the entity reference field.
* @param string $field_name
* The machine name of the field for which an entity reference is desired.
* @param string|null $target_type_assertion
* An optional, fully-qualified class name that the target entity reference
* will be checked against using an assertion. This allows for type checking
* in development and test environments.
*
* @return \Drupal\Core\Entity\EntityInterface|null
* Either the target entity; or NULL if the field is either empty or points
* to a non-existent entity.
*
* @throws \InvalidArgumentException
* If the specified entity reference field refers to more than a single
* target entity.
*/
public static function getTargetEntity(ContentEntityInterface $entity,
string $field_name,
string $target_type_assertion = NULL): ?EntityInterface {
$target_entities =
self::getTargetEntities($entity, $field_name, $target_type_assertion);
$target_count = count($target_entities);
if ($target_count > 1) {
throw new RuntimeException(
sprintf(
'The current value of the target entity reference field ("%s") refers to more than one target entity (counted "%d" references).',
$field_name,
$target_count
)
);
}
return $target_entities[0] ?? NULL;
}
/**
* Gets all entities referenced by all deltas of an entity reference field.
*
* @param \Drupal\Core\Entity\ContentEntityInterface $entity
* The entity containing the entity reference field.
* @param string $field_name
* The machine name of the field for which an entity reference is desired.
* @param string|null $target_type_assertion
* An optional, fully-qualified class name that the target entity reference
* will be checked against using an assertion. This allows for type checking
* in development and test environments.
*
* @return \Drupal\Core\Entity\EntityInterface[]
* The target entities; may be empty if the field is either empty or points
* to only non-existent entities.
*/
public static function getTargetEntities(ContentEntityInterface $entity,
string $field_name,
string $target_type_assertion = NULL): array {
$reference_field_values = $entity->get($field_name);
assert($reference_field_values instanceof EntityReferenceFieldItemListInterface);
$target_entities = [];
foreach ($reference_field_values as $reference_item) {
try {
assert($reference_item instanceof FieldItemInterface);
$entity_reference = $reference_item->get('entity');
assert($entity_reference instanceof DataReferenceInterface);
$entity_adapter = $entity_reference->getTarget();
assert($entity_adapter instanceof EntityAdapter);
if (!$entity_adapter->isEmpty()) {
$target_entity = $entity_adapter->getValue();
assert($target_entity instanceof EntityInterface);
if ($target_type_assertion != NULL) {
assert(is_a($target_entity, $target_type_assertion));
}
$target_entities[] = $target_entity;
}
}
catch (MissingDataException $ex) {
// Bad data structure -- should not happen under normal circumstances.
throw new RuntimeException(
sprintf(
'Failed to retrieve target entity from entity reference field "%s" of entity type "%s" (ID #%s): %s',
$field_name,
$entity->getEntityTypeId(),
$entity->id(),
$ex->getMessage()
),
0,
$ex
);
}
}
return $target_entities;
}
/**
* Private constructor for singleton static utility class.
*/
private function __construct() {
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment