Created
November 3, 2020 21:28
-
-
Save GuyPaddock/69ce592e2b58862da852afc5d7563c4a to your computer and use it in GitHub Desktop.
NestedEntityReference utility class for safely getting access to ER fields
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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