TrackingHelper.php in Search API 8
File
src/Utility/TrackingHelper.php
View source
<?php
declare (strict_types=1);
namespace Drupal\search_api\Utility;
use Drupal\Component\Plugin\Exception\InvalidPluginDefinitionException;
use Drupal\Component\Plugin\Exception\PluginNotFoundException;
use Drupal\Core\Cache\CacheableMetadata;
use Drupal\Core\Cache\CacheBackendInterface;
use Drupal\Core\Cache\RefinableCacheableDependencyInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\Core\Entity\TypedData\EntityDataDefinitionInterface;
use Drupal\Core\Field\TypedData\FieldItemDataDefinitionInterface;
use Drupal\Core\Language\LanguageManagerInterface;
use Drupal\Core\TypedData\ComplexDataDefinitionInterface;
use Drupal\Core\TypedData\DataDefinitionInterface;
use Drupal\search_api\Event\MappingForeignRelationshipsEvent;
use Drupal\search_api\Event\SearchApiEvents;
use Drupal\search_api\IndexInterface;
use Drupal\search_api\SearchApiException;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
class TrackingHelper implements TrackingHelperInterface {
protected $entityTypeManager;
protected $languageManager;
protected $eventDispatcher;
protected $fieldsHelper;
protected $cache;
public function __construct(EntityTypeManagerInterface $entityTypeManager, LanguageManagerInterface $languageManager, EventDispatcherInterface $eventDispatcher, FieldsHelperInterface $fieldsHelper, CacheBackendInterface $cache) {
$this->languageManager = $languageManager;
$this->entityTypeManager = $entityTypeManager;
$this->eventDispatcher = $eventDispatcher;
$this->fieldsHelper = $fieldsHelper;
$this->cache = $cache;
}
public function trackReferencedEntityUpdate(EntityInterface $entity, bool $deleted = FALSE) {
$indexes = [];
try {
$indexes = $this->entityTypeManager
->getStorage('search_api_index')
->loadMultiple();
} catch (InvalidPluginDefinitionException $e) {
} catch (PluginNotFoundException $e) {
}
$original = $deleted ? NULL : $entity->original ?? NULL;
foreach ($indexes as $index) {
$map = NULL;
foreach ($index
->getDatasources() as $datasource_id => $datasource) {
if (!$datasource
->canContainEntityReferences()) {
continue;
}
if ($map === NULL) {
$map = $this
->getForeignEntityRelationsMap($index);
if (!$map) {
break 1;
}
}
$item_ids = $datasource
->getAffectedItemsForEntityChange($entity, $map, $original);
if (!empty($item_ids)) {
$index
->trackItemsUpdated($datasource_id, $item_ids);
}
}
}
}
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) {
break;
}
$seen_path_chunks[] = $field_property[0];
if ($property_definition instanceof FieldItemDataDefinitionInterface && $property_definition
->getFieldDefinition()
->isComputed()) {
break;
}
if ($relation_info['entity_type'] && $property_definition instanceof FieldItemDataDefinitionInterface) {
$cacheability
->addCacheableDependency($property_definition
->getFieldDefinition());
$data[] = $relation_info + [
'field_name' => $property_definition
->getFieldDefinition()
->getName(),
];
}
$entity_reference = $this
->isEntityReferenceDataDefinition($property_definition, $cacheability);
if ($entity_reference) {
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 {
break;
}
}
}
$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;
}
protected function isEntityReferenceDataDefinition(DataDefinitionInterface $property_definition, RefinableCacheableDependencyInterface $cacheability) : array {
$return = [];
if ($property_definition instanceof FieldItemDataDefinitionInterface && $property_definition
->getFieldDefinition()
->getType() === 'entity_reference') {
$field = $property_definition
->getFieldDefinition();
$cacheability
->addCacheableDependency($field);
$return['entity_type'] = $field
->getSetting('target_type');
$field_settings = $field
->getSetting('handler_settings');
$return['bundles'] = $field_settings['target_bundles'] ?? [];
}
elseif ($property_definition instanceof EntityDataDefinitionInterface) {
$return['entity_type'] = $property_definition
->getEntityTypeId();
$return['bundles'] = $property_definition
->getBundles() ?: [];
}
return $return;
}
}
Classes
Name |
Description |
TrackingHelper |
Provides datasource-independent item change tracking functionality. |