You are here

public function ContentEntity::getAffectedItemsForEntityChange in Search API 8

Identifies items affected by a change to a referenced entity.

A "change" in this context means an entity getting updated or deleted. (It won't get called for entities being inserted, as new entities cannot already have references pointing to them.)

This method usually doesn't have to return the specified entity itself, even if it is part of this datasource. This method should instead only be used to detect items that are indirectly affected by this change.

For instance, if an index contains nodes, and nodes can contain tags (which are taxonomy term references), and the search index contains the name of the tags as one of its fields, then a change of a term name should result in all nodes being reindexed that contain that term as a tag. So, the item IDs of those nodes should be returned by this method (in case this datasource contains them).

This method will only be called if this datasource plugin returns TRUE in canContainEntityReferences().

Parameters

\Drupal\Core\Entity\EntityInterface $entity: The entity that just got changed.

array[] $foreign_entity_relationship_map: Map of known entity relationships that exist in the index. Its structure is identical to the return value of the \Drupal\search_api\Utility\TrackingHelper::getForeignEntityRelationsMap() method.

\Drupal\Core\Entity\EntityInterface|null $original_entity: (optional) The original entity before the change. If this argument is NULL, it means the entity got deleted.

Return value

string[] Array of item IDs that are affected by the changes between $entity and $original_entity entities.

Overrides DatasourcePluginBase::getAffectedItemsForEntityChange

See also

\Drupal\search_api\Datasource\DatasourceInterface::canContainEntityReferences()

File

src/Plugin/search_api/datasource/ContentEntity.php, line 1094

Class

ContentEntity
Represents a datasource which exposes the content entities.

Namespace

Drupal\search_api\Plugin\search_api\datasource

Code

public function getAffectedItemsForEntityChange(EntityInterface $entity, array $foreign_entity_relationship_map, EntityInterface $original_entity = NULL) : array {
  if (!$entity instanceof ContentEntityInterface) {
    return [];
  }
  $ids_to_reindex = [];
  $path_separator = IndexInterface::PROPERTY_PATH_SEPARATOR;
  foreach ($foreign_entity_relationship_map as $relation_info) {

    // Ignore relationships belonging to other datasources.
    if (!empty($relation_info['datasource']) && $relation_info['datasource'] !== $this
      ->getPluginId()) {
      continue;
    }

    // Check whether entity type and (if specified) bundles match the entity.
    if ($relation_info['entity_type'] !== $entity
      ->getEntityTypeId()) {
      continue;
    }
    if (!empty($relation_info['bundles']) && !in_array($entity
      ->bundle(), $relation_info['bundles'])) {
      continue;
    }

    // Maybe this entity belongs to a bundle that does not have this field
    // attached. Hence we have this check to ensure the field is present on
    // this particular entity.
    if (!$entity
      ->hasField($relation_info['field_name'])) {
      continue;
    }
    $items = $entity
      ->get($relation_info['field_name']);

    // We trigger re-indexing if either it is a removed entity or the
    // entity has changed its field value (in case it's an update).
    if (!$original_entity || !$items
      ->equals($original_entity
      ->get($relation_info['field_name']))) {
      $query = $this->entityTypeManager
        ->getStorage($this
        ->getEntityTypeId())
        ->getQuery();
      $query
        ->accessCheck(FALSE);

      // Luckily, to translate from property path to the entity query
      // condition syntax, all we have to do is replace the property path
      // separator with the entity query path separator (a dot) and that's it.
      $property_path = $relation_info['property_path_to_foreign_entity'];
      $property_path = str_replace($path_separator, '.', $property_path);
      $query
        ->condition($property_path, $entity
        ->id());
      try {
        $entity_ids = array_values($query
          ->execute());
      } catch (\Throwable $e) {

        // We don't want to catch all PHP \Error objects thrown, but just the
        // ones caused by #2893747.
        if (!$e instanceof \Exception && (get_class($e) !== \Error::class || $e
          ->getMessage() !== 'Call to a member function getColumns() on bool')) {
          throw $e;
        }
        $vars = [
          '%index' => $this->index
            ->label(),
          '%entity_type' => $entity
            ->getEntityType()
            ->getLabel(),
          '@entity_id' => $entity
            ->id(),
        ];
        try {
          $link = $entity
            ->toLink($this
            ->t('Go to changed %entity_type with ID "@entity_id"', $vars))
            ->toString()
            ->getGeneratedLink();
        } catch (\Throwable $e) {

          // Ignore any errors here, it's not that important that the log
          // message contains a link.
          $link = NULL;
        }
        $this
          ->logException($e, '%type while attempting to find indexed entities referencing changed %entity_type with ID "@entity_id" for index %index: @message in %function (line %line of %file).', $vars, RfcLogLevel::ERROR, $link);
        continue;
      }
      foreach ($entity_ids as $entity_id) {
        foreach ($this
          ->getLanguages() as $language) {
          $ids_to_reindex["{$entity_id}:{$language->getId()}"] = 1;
        }
      }
    }
  }
  return array_keys($ids_to_reindex);
}