modules/quanthub_indicator/src/QuanthubIndicatorContentEntityTrackingManager.php (170 lines of code) (raw):

<?php namespace Drupal\quanthub_indicator; use Drupal\Core\Database\Connection; use Drupal\Core\Entity\ContentEntityInterface; use Drupal\Core\Entity\EntityInterface; use Drupal\Core\Entity\EntityTypeManagerInterface; use Drupal\Core\Language\LanguageManagerInterface; use Drupal\quanthub_sdmx_sync\QuanthubSdmxClient; use Drupal\search_api\Plugin\search_api\datasource\ContentEntityTrackingManager; use Drupal\search_api\Task\TaskManagerInterface; /** * Quanthub indicator tracking manager, added logic for tracking indicator CT. */ class QuanthubIndicatorContentEntityTrackingManager extends ContentEntityTrackingManager { /** * The SDMX client. * * @var \Drupal\quanthub_sdmx_sync\QuanthubSdmxClient */ protected $sdmxClient; /** * The database connection service. * * @var \Drupal\Core\Database\Connection */ protected $database; /** * Constructs a new class instance. * * @param \Drupal\Core\Entity\EntityTypeManagerInterface $entityTypeManager * The entity type manager. * @param \Drupal\Core\Language\LanguageManagerInterface $languageManager * The language manager. * @param \Drupal\search_api\Task\TaskManagerInterface $taskManager * The task manager. * @param \Drupal\quanthub_sdmx_sync\QuanthubSdmxClient $sdmxClient * The SDMX client. * @param \Drupal\Core\Database\Connection $database * The database service. */ public function __construct( EntityTypeManagerInterface $entityTypeManager, LanguageManagerInterface $languageManager, TaskManagerInterface $taskManager, QuanthubSdmxClient $sdmxClient, Connection $database, ) { $this->entityTypeManager = $entityTypeManager; $this->languageManager = $languageManager; $this->taskManager = $taskManager; $this->sdmxClient = $sdmxClient; $this->database = $database; } /** * Queues an entity for indexing. * * If "Index items immediately" is enabled for the index, the entity will be * indexed right at the end of the page request. * * When calling this method with an existing entity * (@code $new = FALSE @endcode), changes in the existing translations will * only be recognized if an appropriate @code $entity->original @endcode value * is set. * * @param \Drupal\Core\Entity\ContentEntityInterface $entity * The content entity to be indexed. * @param bool $new * (optional) TRUE if this is a new entity, FALSE if it already existed (and * should already be known to the tracker). */ public function trackEntityChange(ContentEntityInterface $entity, bool $new = FALSE) { // Check if the entity is a content entity. if (!empty($entity->search_api_skip_tracking)) { return; } $indexes = $this->getIndexesForEntity($entity); if (!$indexes) { return; } // Compare old and new languages for the entity to identify inserted, // updated and deleted translations (and, therefore, search items). $entity_id = $entity->id(); $new_translations = array_keys($entity->getTranslationLanguages()); $old_translations = []; if (!$new) { // In case we don't have the original, fall back to the current entity, // and assume no new translations were added. $original = $entity->original ?? $entity; $old_translations = array_keys($original->getTranslationLanguages()); } $deleted_translations = array_diff($old_translations, $new_translations); $inserted_translations = array_diff($new_translations, $old_translations); $updated_translations = array_diff($new_translations, $inserted_translations); $datasource_id = 'entity:' . $entity->getEntityTypeid(); $get_ids = function (string $langcode) use ($entity_id): string { return $entity_id . ':' . $langcode; }; $inserted_ids = array_map($get_ids, $inserted_translations); $updated_ids = array_map($get_ids, $updated_translations); $deleted_ids = array_map($get_ids, $deleted_translations); $indicators = []; if ($entity->getType() == 'indicator') { $indicators = $this->getIndicatorsToIndex($entity); } foreach ($indexes as $index) { if ($inserted_ids) { $filtered_item_ids = static::filterValidItemids($index, $datasource_id, $inserted_ids); if ($filtered_item_ids) { if ($entity->getType() == 'indicator') { if (!empty($indicators)) { foreach ($inserted_translations as $inserted_translation) { if ($entity->hasTranslation($inserted_translation)) { $filtered_indicator_id = []; foreach ($indicators as $indicator) { $filtered_indicator_id[] = $this->prepareSearchApiEntityKey($entity->getTranslation($inserted_translation)) . '_indicator_' . $indicator['id']; } if (!empty($filtered_indicator_id)) { $index->trackItemsInserted($datasource_id, $filtered_indicator_id); } } } } } else { $index->trackItemsInserted($datasource_id, $filtered_item_ids); } } } if ($updated_ids) { $filtered_item_ids = static::filterValidItemids($index, $datasource_id, $updated_ids); if ($filtered_item_ids && !empty($updated_translations)) { if ($entity->getType() == 'indicator') { foreach ($updated_translations as $updated_translation) { if ($entity->hasTranslation($updated_translation)) { $indicators_tracker_ids = $this->getIndicatorsTrackerIds($entity, $updated_translation); $existed_sdmx_indicators = []; foreach ($indicators as $indicator) { $existed_sdmx_indicators[] = $this->prepareSearchApiEntityKey($entity, $updated_translation) . '_indicator_' . $indicator['id']; } // Diff between existed indicator. // if someone disappear we need to remove. $indicators_to_delete = array_diff($indicators_tracker_ids, $existed_sdmx_indicators); if (!empty($indicators_to_delete)) { $index->trackItemsDeleted($datasource_id, $indicators_to_delete); } // All new indicators should be inserted. $indicators_to_insert = array_diff($existed_sdmx_indicators, $indicators_tracker_ids); if (!empty($indicators_to_insert)) { $index->trackItemsInserted($datasource_id, $indicators_to_insert); } // Existed indicators should be updated. $indicators_to_update = array_intersect($indicators_tracker_ids, $existed_sdmx_indicators); if (!empty($indicators_to_update)) { $index->trackItemsUpdated($datasource_id, $indicators_to_update); } } } } else { $index->trackItemsUpdated($datasource_id, $filtered_item_ids); } } } if ($deleted_ids) { $filtered_item_ids = static::filterValidItemids($index, $datasource_id, $deleted_ids); if ($filtered_item_ids) { if ($entity->getType() == 'indicator') { foreach ($deleted_translations as $deleted_translation) { // Get items related to this indicator. // There could be difference between saved indicators and // news list from sdmx. // So just remove all that we have in tracker db table. $indicators_tracker_ids = $this->getIndicatorsTrackerIds($entity, $deleted_translation); $index->trackItemsDeleted($datasource_id, $indicators_tracker_ids); } } else { $index->trackItemsDeleted($datasource_id, $filtered_item_ids); } } } } } /** * Implements hook_entity_delete(). * * Deletes all entries for this entity from the tracking table for each index * that tracks this entity type. * * By setting the $entity->search_api_skip_tracking property to a true-like * value before this hook is invoked, you can prevent this behavior and make * the Search API ignore this deletion. (Note that this might lead to stale * data in the tracking table or on the server, since the item will not * removed from there (if it has been added before).) * * @param \Drupal\Core\Entity\EntityInterface $entity * The deleted entity. * * @see search_api_entity_delete() */ public function entityDelete(EntityInterface $entity) { // Check if the entity is a content entity. if (!($entity instanceof ContentEntityInterface) || !empty($entity->search_api_skip_tracking)) { return; } $indexes = $this->getIndexesForEntity($entity); if (!$indexes) { return; } $indicators_ids = []; if ($entity->getType() == 'indicator') { $indicators_ids = $this->getIndicatorsTrackerIds($entity); $datasource_id = 'entity:' . $entity->getEntityTypeId(); foreach ($indexes as $index) { $index->trackItemsDeleted($datasource_id, $indicators_ids); } } } /** * Get indicators ids in tracker db table search item. */ public function getIndicatorsTrackerIds(EntityInterface $indicator_entity, $langcode = NULL) { $indicator_entity_key = $this->prepareSearchApiEntityKey($indicator_entity, $langcode); return $this->database ->select('search_api_item', 'si') ->fields('si', ['item_id']) ->condition('si.item_id', $indicator_entity_key . '%', 'LIKE') ->execute() ->fetchCol(); } /** * Get Dataset indicators from SDMX. * * @param \Drupal\Core\Entity\ContentEntityInterface $entity * The indicator entity. * * @return array|null * Return null or array of dataset indicators. */ private function getIndicatorsToIndex(ContentEntityInterface $entity) { $dataset_urn = $entity ->field_dataset ->first() ->entity ->field_quanthub_urn ->getString(); $dimension_id = $entity->field_indicator_parameter->getString(); return $this->sdmxClient->datasetIndicators($dataset_urn, $dimension_id); } /** * Prepare key for search_api_item db table. */ private function prepareSearchApiEntityKey($entity, $langcode = NULL) { if (!$langcode) { $langcode = $entity->language()->getId(); } return 'entity:' . $entity->getEntityTypeId() . '/' . $entity->id() . ':' . $langcode; } }