You are here

protected function TrackingHelper::getForeignEntityRelationsMap in Search API 8

Analyzes the index fields and constructs a map of entity references.

This map tries to record all ways that entities' values are indirectly indexed by the given index. (That is, what items' indexed contents might be affected by a given entity being updated or deleted.)

Parameters

\Drupal\search_api\IndexInterface $index: The index for which to create the map.

Return value

array[] A (numerically keyed) array of foreign relationship mappings. Each sub-array represents a single known relationship. Such sub-arrays will have the following structure:

  • datasource: (string) The ID of the datasource which contains this relationship.
  • entity_type: (string) The entity type that is referenced from the index.
  • bundles: (string[]) An optional array of particular entity bundles that are referred to from the index. An empty array here means that the index refers to all the bundles.
  • property_path_to_foreign_entity: (string) Property path where the index refers to this entity.
  • field_name: (string) Name of the field on the referenced entity that is indexed in the search index.
1 call to TrackingHelper::getForeignEntityRelationsMap()
TrackingHelper::trackReferencedEntityUpdate in src/Utility/TrackingHelper.php
Reacts to an entity being updated or deleted.

File

src/Utility/TrackingHelper.php, line 158

Class

TrackingHelper
Provides datasource-independent item change tracking functionality.

Namespace

Drupal\search_api\Utility

Code

protected function getForeignEntityRelationsMap(IndexInterface $index) : array {
  $cid = "search_api:{$index->id()}:foreign_entities_relations_map";
  $cache = $this->cache
    ->get($cid);
  if ($cache) {
    return $cache->data;
  }
  $cacheability = new CacheableMetadata();
  $cacheability
    ->addCacheableDependency($index);
  $data = [];
  foreach ($index
    ->getFields() as $field) {
    try {
      $datasource = $field
        ->getDatasource();
    } catch (SearchApiException $e) {
      continue;
    }
    if (!$datasource) {
      continue;
    }
    $relation_info = [
      'datasource' => $datasource
        ->getPluginId(),
      'entity_type' => NULL,
      'bundles' => NULL,
      'property_path_to_foreign_entity' => NULL,
    ];
    $seen_path_chunks = [];
    $property_definitions = $datasource
      ->getPropertyDefinitions();
    $field_property = Utility::splitPropertyPath($field
      ->getPropertyPath(), FALSE);
    for (; $field_property[0]; $field_property = Utility::splitPropertyPath($field_property[1], FALSE)) {
      $property_definition = $this->fieldsHelper
        ->retrieveNestedProperty($property_definitions, $field_property[0]);
      if (!$property_definition) {

        // Seems like we could not map it from the property path to some Typed
        // Data definition. In the absence of a better alternative, let's
        // simply disregard this field.
        break;
      }
      $seen_path_chunks[] = $field_property[0];
      if ($property_definition instanceof FieldItemDataDefinitionInterface && $property_definition
        ->getFieldDefinition()
        ->isComputed()) {

        // We cannot really deal with computed fields since we have no
        // knowledge about their internal logic. Thus we cannot process
        // this field any further.
        break;
      }
      if ($relation_info['entity_type'] && $property_definition instanceof FieldItemDataDefinitionInterface) {

        // Parent is an entity. Hence this level is fields of the entity.
        $cacheability
          ->addCacheableDependency($property_definition
          ->getFieldDefinition());
        $data[] = $relation_info + [
          'field_name' => $property_definition
            ->getFieldDefinition()
            ->getName(),
        ];
      }
      $entity_reference = $this
        ->isEntityReferenceDataDefinition($property_definition, $cacheability);
      if ($entity_reference) {

        // Unfortunately, the nested "entity" property for entity reference
        // fields comes without a bundles restriction, so we need to copy the
        // bundles information from the level above (on the field itself), if
        // any.
        if ($relation_info['entity_type'] === $entity_reference['entity_type'] && empty($entity_reference['bundles']) && !empty($relation_info['bundles']) && $field_property[0] === 'entity') {
          $entity_reference['bundles'] = $relation_info['bundles'];
        }
        $relation_info = $entity_reference;
        $relation_info['property_path_to_foreign_entity'] = implode(IndexInterface::PROPERTY_PATH_SEPARATOR, $seen_path_chunks);
        $relation_info['datasource'] = $datasource
          ->getPluginId();
      }
      if ($property_definition instanceof ComplexDataDefinitionInterface) {
        $property_definitions = $this->fieldsHelper
          ->getNestedProperties($property_definition);
      }
      else {

        // This item no longer has "nested" properties in its Typed Data
        // definition. Thus we cannot examine it any further than the current
        // point.
        break;
      }
    }
  }

  // Let other modules alter this information, potentially adding more
  // relationships.
  $event = new MappingForeignRelationshipsEvent($index, $data, $cacheability);
  $this->eventDispatcher
    ->dispatch(SearchApiEvents::MAPPING_FOREIGN_RELATIONSHIPS, $event);
  $this->cache
    ->set($cid, $data, $cacheability
    ->getCacheMaxAge(), $cacheability
    ->getCacheTags());
  return $data;
}