Skip to content

Instantly share code, notes, and snippets.

@grayside
Created January 27, 2017 23:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save grayside/a7b8aba74ccf36ff984b0b9499b3a188 to your computer and use it in GitHub Desktop.
Save grayside/a7b8aba74ccf36ff984b0b9499b3a188 to your computer and use it in GitHub Desktop.
Finding Backreferences and relevant backreferencing fields.
<?php
namespace Drupal\back_reference;
use Drupal\field\Entity\FieldConfig;
use Drupal\Core\Entity\Query\QueryFactory;
use Drupal\Core\Entity\EntityTypeManager;
use Drupal\Core\Entity\EntityFieldManager;
/**
* Identifies backreferences from entities to the specified node.
*/
class BackReferenceFinder {
/**
* The entity query factory service.
*
* @var \Drupal\Core\Entity\Query\QueryFactory
*/
protected $entityQuery;
/**
* Entity Type Manager service.
*
* @var \Drupal\Core\Entity\EntityTypeManager
*/
protected $entityTypeManager;
/**
* Entity Field Manager service.
*
* @var \Drupal\Core\Entity\EntityFieldManager
*/
protected $entityFieldManager;
/**
* Array of all fields that can reference the given entity/bundle.
*
* @var array
*/
protected $backReferenceFields = [];
/**
* Stores an array of entity IDs that are backreferencing.
*
* @var array
*/
protected $backReferenceEntityIds = [];
/**
* Constructs a \Drupal\back_reference\BackReferenceFinder
*
* @param \Drupal\Core\Entity\Query\QueryFactory $entity_query
* Entity Field Query.
* @param \Drupal\Core\Entity\EntityTypeManager $entity_type_manager
* Entity Type Manager.
* @param \Drupal\Core\Entity\EntityFieldManager $entity_field_manager
* Entity Field Manager.
*/
public function __construct(QueryFactory $entity_query, EntityTypeManager $entity_type_manager, EntityFieldManager $entity_field_manager) {
$this->entityQuery = $entity_query;
$this->entityTypeManager = $entity_type_manager;
$this->entityFieldManager = $entity_field_manager;
}
/**
* Load all entities that reference the entity of the given identifier.
*
* This method does not perform any checks on the targeted entity to verify
* the applicability of the field, as those are assumed to have been handled
* as part of deriving a confirmed FieldConfig object.
*
* @param \Drupal\field\Entity\FieldConfig $field
* The field definition from which we derive query conditions.
* @param string $target_id
* The entity identifier the reference field should target.
*
* @return \Drupal\Core\Entity\EntityInterface[]
* Array of Entities that reference our specified target ID.
*
* @todo Expand for workflow/workspace if and when those elements are added.
*/
public function loadReferencingEntities(FieldConfig $field, $target_id) {
$cid = $field->getTargetEntityTypeId() . $field->getName() . $target_id;
if (!isset($this->backReferenceEntityIds[$cid])) {
$query = $this->entityQuery->get($field->getTargetEntityTypeId(), 'AND')
->condition($field->getName(), $target_id)
->condition('status', 1)
->addTag('node_access');
$this->backReferenceFields[$cid] = $query->execute();
}
$ids = $this->backReferenceFields[$cid];
return $this->entityTypeManager->getStorage($field->getTargetEntityTypeId())
->loadMultiple($ids);
}
/**
* Retrieves every reference field which can point at the current entity.
*
* @param string $entity_type_id
* The machine name identifier for the entity type.
* @param string $entity_bundle_id
* The machine name identifier for the entity bundle. Defaults to NULL.
*
* @return \Drupal\field\Entity\FieldConfig[]
* Array of all applicable fields keyed by field name.
*
* @todo Determine if we need specific fields instead of all fields. If so,
* add a array $mask = [] parameter as whitelist.
*/
public function referencingFields($entity_type_id, $entity_bundle_id) {
$cid = $entity_type_id . $entity_bundle_id;
if (isset($this->backReferenceFields[$cid])) {
return $this->backReferenceFields[$cid];
}
$entity_reference_fields = $this->entityFieldManager->getFieldMapByFieldType('entity_reference');
$fields = [];
foreach ($entity_reference_fields as $entity_type_field_is_on => $field_info) {
foreach ($field_info as $field_name => $field_data) {
foreach ($field_data['bundles'] as $entity_bundle_field_is_on) {
/* @var \Drupal\field\Entity\FieldConfig */
$field = FieldConfig::loadByName($entity_type_field_is_on, $entity_bundle_field_is_on, $field_name);
// Check to see if the field is applicable to our entity and check for
// references if so.
if ($field && static::referenceFieldAppliesToEntity($field, $entity_type_id, $entity_bundle_id)) {
$fields[$field_name] = $field;
}
}
}
}
$this->backReferenceFields[$entity_type_id . $entity_bundle_id] = $fields;
return $this->backReferenceFields[$cid];
}
/**
* Identifies if supplied field is applicable to the given entity.
*
* @param \Drupal\field\Entity\FieldConfig $field
* The field configuration we are testing.
* @param string $entity_type_id
* The machine name identifier for the entity type.
* @param string $entity_bundle_id
* The machine name identifier for the entity bundle. Defaults to NULL.
*
* @return bool
* TRUE if the field is applicable, FALSE otherwise.
*/
public static function referenceFieldAppliesToEntity(FieldConfig $field, $entity_type_id, $entity_bundle_id = NULL) {
$entity_type_targeted_by_field = $field->getSetting('target_type');
$field_handler = $field->getSetting('handler_settings');
return $entity_type_targeted_by_field == $entity_type_id &&
isset($field_handler['target_bundles']) &&
isset($field_handler['target_bundles'][$entity_bundle_id]);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment