0

I have two fields (field_contributing_author, field_attorneys more details on the code block below, they are commented there) that I'm interested, one is a taxonomy and the other an entity reference. What I'm trying to achieve is to add both in the index of the search API so that when someone is using the search and the search term matches either the title of the entity reference or the name of the term the search would return the linked content title of the node that referenced them.

There are two parts on my setup, the search API configuration and the View responsible for making the request.

I tried various combinations of configuring either with no results, especially the search API, I was able to find and add the related fields into the search index from there, but they didn't work, cause they return the id of the referenced node instead of the id of the referee. So, I thought in this very specific situation it may not be possible to be done with just configuration. So, I wrote this processor below, however it never fires the addFieldValues function, and I don't understand why.

Is this the way forward or am I completely lost? How you guys would go about solving this problem?

<?php

namespace Drupal\hs_author_processor\Plugin\search_api\processor;

use Drupal\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Entity\EntityInterface;
use Drupal\Core\Entity\EntityTypeManagerInterface;
use Drupal\search_api\Datasource\DatasourceInterface;
use Drupal\search_api\IndexInterface;
use Drupal\search_api\Item\Field;
use Drupal\search_api\Item\FieldInterface;
use Drupal\search_api\Item\ItemInterface;
use Drupal\search_api\Plugin\PluginFormTrait;
use Drupal\search_api\Processor\EntityProcessorProperty;
use Drupal\search_api\Processor\ProcessorPluginBase;
use Drupal\search_api\Processor\ProcessorProperty;
use Drupal\search_api\SearchApiException;
use Drupal\search_api\Utility\FieldsHelperInterface;
use Drupal\search_api\Utility\Utility;

/**
 * Allows finding by titles associated (with field_references entities).
 *
 * @SearchApiProcessor(
 *   id = "hs_author_processor",
 *   label = @Translation("HS Author Processor"),
 *   description = @Translation("Allows filtering by entity-reference's authors."),
 *   stages = {
 *     "add_properties" = 20,
 *   },
 *
 *
 * )
 */
class HsAuthorProcessor extends ProcessorPluginBase
{

  use PluginFormTrait;

  const ENTITY = 'taxonomy_term';
  const DATASOURCE = 'entity:' . self::ENTITY;
  const FIELD_NAME = 'field_contributing_author';

  // field_contributing_author :: Entity reference - Reference type: Taxonomy term - Vocabulary: Contributing author
  // field_attorneys :: Entity reference - Reference type: Content - Content type: People

  /**
   * The entity type manager.
   *
   * @var EntityTypeManagerInterface|null
   */
  protected ?EntityTypeManagerInterface $entityTypeManager;

  /**
   * The fields' helper.
   *
   * @var FieldsHelperInterface|null
   */
  protected $fieldsHelper;

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface|\Symfony\Component\DependencyInjection\ContainerInterface $container, array $configuration, $plugin_id, $plugin_definition) {
    /** @var static $processor */
    $processor = parent::create($container, $configuration, $plugin_id, $plugin_definition);

    $processor->setEntityTypeManager($container->get('entity_type.manager'));
    $processor->setFieldsHelper($container->get('search_api.fields_helper'));

    return $processor;
  }

  /**
   * Retrieves the entity type manager.
   *
   * @return EntityTypeManagerInterface
   *   The entity type manager.
   */
  public function getEntityTypeManager(): EntityTypeManagerInterface
  {
    return $this->entityTypeManager ?: \Drupal::entityTypeManager();
  }

  /**
   * Sets the entity type manager.
   *
   * @param EntityTypeManagerInterface $entity_type_manager
   *   The new entity type manager.
   *
   * @return $this
   */
  public function setEntityTypeManager(EntityTypeManagerInterface $entity_type_manager): static
  {
    $this->entityTypeManager = $entity_type_manager;
    return $this;
  }

  /**
   * Retrieves the fields helper.
   *
   * @return FieldsHelperInterface
   *   The fields' helper.
   */
  public function getFieldsHelper(): FieldsHelperInterface
  {
    return $this->fieldsHelper ?: \Drupal::service('search_api.fields_helper');
  }

  /**
   * Sets the fields helper.
   *
   * @param FieldsHelperInterface $fields_helper
   *   The new fields' helper.
   *
   * @return $this
   */
  public function setFieldsHelper(FieldsHelperInterface $fields_helper): static
  {
    $this->fieldsHelper = $fields_helper;
    return $this;
  }

  /**
   * {@inheritdoc}
   */
  public static function supportsIndex(IndexInterface $index): bool
  {
    foreach ($index->getDatasources() as $datasource) {
      $entity_type_id = $datasource->getEntityTypeId();
      
      if ($entity_type_id && $entity_type_id !== 'taxonomy_term') {
        return TRUE;
      }
    }
    return FALSE;
  }

  /**
   * {@inheritdoc}
   */
  public function getPropertyDefinitions(DatasourceInterface $datasource = NULL): array
  {
    $properties = [];

    if (!$datasource) {
      $definition = [
        'label' => $this->t('Entity-title'),
        'description' => $this->t('Attached Entity-ref title list'),
        'type' => 'entity:taxonomy_term',
        'is_list' => FALSE,
        'hidden' => FALSE,
        'processor_id' => $this->getPluginId(),
      ];
      $properties[self::FIELD_NAME] = new EntityProcessorProperty($definition);
      $properties[self::FIELD_NAME]->setEntityTypeId('node');

      return $properties;
    }

    return $properties;
  }

  /**
   * {@inheritdoc}
   * @throws SearchApiException
   */
  public function addFieldValues(ItemInterface $item): void
  {
    \Drupal::logger('hs_author_processor')->debug('addFieldValues has fired.');

    $entity = $item->getOriginalObject()->getValue();

    if (!($entity instanceof EntityInterface)) {
      return;
    }

    /** @var FieldInterface[][] $to_extract */
    $to_extract = [];
    foreach ($item->getFields() as $field) {
      if (!$field->getDatasourceId()) {
        continue;
      }
      $property_path = $field->getPropertyPath();
      [$direct, $nested] = Utility::splitPropertyPath($property_path, FALSE);
      if ($direct === self::FIELD_NAME) {
        $to_extract[$nested][] = $field;
      }
    }

    if (!$to_extract) {
      return;
    }

    $taxonomy = $this->getEntityTypeManager()
      ->getStorage('taxonomy_term')
      ->load($entity->id());
    if (!$taxonomy) {
      return;
    }
    $this->getFieldsHelper()
      ->extractFields($taxonomy->getTypedData(), $to_extract, $item->getLanguage());
  }

  /**
   * {@inheritdoc}
   */
  public function preIndexSave() {
    \Drupal::logger('hs_author_processor')->debug('preIndexSave has fired.');

    // For General fields:
    $this->ensureField(null, self::FIELD_NAME);

    // For content this would look like:
    foreach ($this->index->getDatasources() as $sourceId => $dataSource) {
      if ($dataSource && $dataSource->getEntityTypeId() == self::ENTITY) {
        $this->ensureField($sourceId, self::FIELD_NAME);
      }
    }
  }
}
2
  • The Aggregate Field type should work for this case.
    – Kevin
    Commented Apr 23 at 13:00
  • @Kevin This is also something I tried, but I was getting the field as a result, but not a content that had the search term in this field. If it makes sense what I mean.
    – Alimba
    Commented Apr 23 at 13:09

0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Browse other questions tagged or ask your own question.