To allow nested
Facet searches, based on referenced entity's titles ($target->field_title->value
),
in our Drupal 8 site we defined a custom-field with a processor
(which create and sets our my_titles_field
field);
Place the MyCustomProcessor.php
file in modules/custom/my_module/src/Plugin/search_api/processor/
directory;
With contents like below:
<?php
namespace Drupal\my_module\Plugin\search_api\processor;
use Drupal\Core\Entity\EntityInterface;
use Drupal\search_api\Datasource\DatasourceInterface;
use Drupal\search_api\IndexInterface;
use Drupal\search_api\Item\Field;
use Drupal\search_api\Item\ItemInterface;
use Drupal\search_api\Plugin\PluginFormTrait;
use Drupal\search_api\Processor\ProcessorPluginBase;
use Drupal\search_api\Processor\ProcessorProperty;
/**
* Allows finding by titles associated (with field_references entities).
*
* @SearchApiProcessor(
* id = "my_custom_processor",
* label = @Translation("My Custom Processor"),
* description = @Translation("Allows filtering by entity-reference's titles."),
* stages = {
* "add_properties" = 0,
* },
* locked = true,
* hidden = true,
* )
*/
class MyCustomProcessor extends ProcessorPluginBase {
use PluginFormTrait;
const ENTITY = 'node';
const DATASOURCE = 'entity:' . self::ENTITY;
const FIELD_NAME = 'my_titles_field';
/**
* {@inheritdoc}
*/
public function getPropertyDefinitions(DatasourceInterface $datasource = NULL) {
$properties = [];
// Ensures our field is in General section, else we would check:
// ```
// if ($dataSource && $dataSource->getEntityTypeId() == self::ENTITY) {
// ```
if (!$datasource) {
$property = new ProcessorProperty([
'label' => $this->t('Entity-title'),
'description' => $this->t('Attached Entity-ref title list'),
'type' => 'object',
'is_list' => FALSE,
'hidden' => FALSE,
'processor_id' => $this->getPluginId(),
]);
$properties[self::FIELD_NAME] = $property;
}
return $properties;
}
/**
* {@inheritdoc}
*/
public function addFieldValues(ItemInterface $item) {
// Loads actual entity (which $item represents).
$entity = $item->getOriginalObject()->getValue();
if (!($entity instanceof EntityInterface)) {
return;
}
// Generate searchable JSON object (that is returned to Search-API callers
// and which we can edit as we like to fit our needs).
$json = [];
$references = $entity->get('field_references');
foreach ($references->referencedEntities() as $key => $target) {
$json[$target->id()] = [
'value' => $target->field_title->value,
];
}
// At last, assign our custom filter-criteria value as an index field.
$fields = $item->getFields(FALSE);
$fields = $this->getFieldsHelper()
->filterForPropertyPath($fields, NULL, self::FIELD_NAME);
foreach ($fields as $field) {
$field->addValue($json);
}
}
/**
* {@inheritdoc}
*/
public function preIndexSave() {
// 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);
// }
// }
}
}
Note that the field needs to added to your index
by clicking Add fields
in
/admin/config/search/search-api/index/__my_index__/fields
route manually.
For Drupal-7 See also Cross-entity searches