Skip to main content
Version: 2024.3

Extending Search Index

The portal engine is powered by OpenSearch via Generic Data Index bundle for search, listings and filters. Therefore, it's needed to store the data from Pimcore elements into data index indices.

Take a look at Pimcore Generic Data Index Bundle documentation for an introduction on how to index the data into OpenSearch and keep it up to date. There you will find also more information about the structure of the indices.

Extending Search Index via Events

The regular index update process stores a defined set of standard data types in the data index which makes it possible to find, filter, sort and list them in the portal engine.

It is possible to extend the index with custom attributes if needed. For this purpose the following events exist. You will find code examples at the end of this section.

UpdateIndexDataEvent

This event can be used to store additional fields in the search index. Please refer to the Pimcore Generic Data Index Bundle documentation for detailed information and examples.

ExtractMappingEvent

With this event it's possible to define the mapping of the additional custom fields. Please refer to the Pimcore Generic Data Index Bundle documentation for detailed information and examples.

FilterableFieldsEvent

Now as the mapping is defined and the data is indexed, it is possible to create a filter on this data. The Pimcore\Bundle\PortalEngineBundle\Event\Search\FilterableFieldsEvent can be used to make the new fields appear in the filter field selection of the data pool config document.

SortableFieldsEvent

The Pimcore\Bundle\PortalEngineBundle\Event\Search\SortableFieldsEvent works quite the same way as the FilterableFieldsEvent but defines fields which can appear in the list of sortable fields.

ListableFieldsEvent

The last event is the Pimcore\Bundle\PortalEngineBundle\Event\Search\ListableFieldsEvent. This can be used if you would like to display the additional fields in the list view of the data pool listings.

Example 1: Assets

The following example creates an EventSubscriber which uses custom "fileSizeSelection" field to make it listable, filterable and sortable. For the information on how to add a custom field to the data index, please refer to the Pimcore Generic Data Index Bundle documentation

<?php

namespace AppBundle\EventListener;

use Pimcore\Bundle\PortalEngineBundle\Event\Search\FilterableFieldsEvent;
use Pimcore\Bundle\PortalEngineBundle\Event\Search\ListableFieldsEvent;
use Pimcore\Bundle\PortalEngineBundle\Event\Search\SortableFieldsEvent;
use Pimcore\Bundle\PortalEngineBundle\Model\Configuration\DataPool\AssetConfig;
use Pimcore\Bundle\PortalEngineBundle\Model\Configuration\DataPool\DataPoolConfigInterface;
use Pimcore\Bundle\PortalEngineBundle\Model\Configuration\DataPool\Field\FilterableField;
use Pimcore\Bundle\PortalEngineBundle\Model\Configuration\DataPool\Field\ListableField;
use Pimcore\Bundle\PortalEngineBundle\Model\Configuration\DataPool\Field\SortableField;
use Pimcore\Bundle\PortalEngineBundle\Service\SearchIndex\Asset\FieldDefinitionAdapter\DefaultAdapter;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class PortalEngineFileSizeIndexSubscriber implements EventSubscriberInterface
{

public static function getSubscribedEvents()
{
return [
FilterableFieldsEvent::class => 'onGetFilterableFields',
SortableFieldsEvent::class => 'onGetSortableFields',
ListableFieldsEvent::class => 'onGetListableFields',
];
}

public function __construct(protected DefaultAdapter $defaultAdapter) {}

public function onGetFilterableFields(FilterableFieldsEvent $event)
{
// should be visible for asset pools only
if(!$event->getDataPoolConfig() instanceof AssetConfig) {
return;
}

// This array contains all filterable fields. Therefore, it would be possible to remove fields too if needed.
$filterableFields = $event->getFilterableFields();

$filterableFields[] = (new FilterableField())
# technical name (will be used for GET parameter and translation key)
->setName('fileSizeSelection')
# nice name for the select list
->setTitle('File Size Selection')
# full path of the field within the data index
->setPath('custom_fields.fileSizeSelection')

# optionally set field definition adapter (e.g. use default adapter to translate labels in filter options)
->setFieldDefinitionAdapter($this->defaultAdapter);

$event->setFilterableFields($filterableFields);
}

public function onGetSortableFields(SortableFieldsEvent $event)
{
// should be visible for asset pools only
if(!$event->getDataPoolConfig() instanceof AssetConfig) {
return;
}

// This array contains all sortable fields. Therefore, it would be possible to remove fields too if needed.
$sortableFields = $event->getSortableFields();

$sortableFields[] = (new SortableField())
# technical name (will be used for GET parameter and translation key)
->setName('fileSizeSelection')
# nice name
->setTitle('File Size Selection')
# full path of the field within the data index
->setPath('custom_fields.fileSizeSelection');

$event->setSortableFields($sortableFields);
}

public function onGetListableFields(ListableFieldsEvent $event)
{
// should be visible for asset pools only
if(!$event->getDataPoolConfig() instanceof AssetConfig) {
return;
}

// This array contains all listable fields. Therefore, it would be possible to remove fields too if needed.
$listableFields = $event->getListableFields();

$listableFields[] = (new ListableField())
# technical name (will be used for GET parameter and translation key)
->setName('fileSizeSelection')
# nice name
->setTitle('File Size Selection')
# full path of the field within the data index
->setPath('custom_fields.fileSizeSelection');

$event->setListableFields($listableFields);
}
}


# service definition

services:
_defaults:
autowire: true

AppBundle\EventListener\PortalEngineFileSizeIndexSubscriber:
tags:
- { name: kernel.event_subscriber }

Example 2: Data Objects

In this example a custom "User Owner" field is used. Subscriber is going to make it listable, filterable and sortable. For the information on how to add a custom field to the data index, please refer to the Pimcore Generic Data Index Bundle documentation

<?php

namespace AppBundle\EventListener;

use Pimcore\Bundle\PortalEngineBundle\Event\Search\FilterableFieldsEvent;
use Pimcore\Bundle\PortalEngineBundle\Event\Search\ListableFieldsEvent;
use Pimcore\Bundle\PortalEngineBundle\Event\Search\SortableFieldsEvent;
use Pimcore\Bundle\PortalEngineBundle\Model\Configuration\DataPool\DataObjectConfig;
use Pimcore\Bundle\PortalEngineBundle\Model\Configuration\DataPool\DataPoolConfigInterface;
use Pimcore\Bundle\PortalEngineBundle\Model\Configuration\DataPool\Field\FilterableField;
use Pimcore\Bundle\PortalEngineBundle\Model\Configuration\DataPool\Field\ListableField;
use Pimcore\Bundle\PortalEngineBundle\Model\Configuration\DataPool\Field\SortableField;
use Pimcore\Bundle\PortalEngineBundle\Service\SearchIndex\DataObject\FieldDefinitionAdapter\DefaultAdapter;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;

class PortalEngineCarOwnerSubscriber implements EventSubscriberInterface
{
public static function getSubscribedEvents()
{
return [
FilterableFieldsEvent::class => 'onGetFilterableFields',
SortableFieldsEvent::class => 'onGetSortableFields',
ListableFieldsEvent::class => 'onGetListableFields',
];
}

public function __construct(protected DefaultAdapter $defaultAdapter) {}

public function onGetFilterableFields(FilterableFieldsEvent $event)
{
// should be visible for car data pools only
if(!$this->isCarDataPool($event->getDataPoolConfig())) {
return;
}

// This array contains all filterable fields. Therefore, it would be possible to remove fields too if needed.
$filterableFields = $event->getFilterableFields();

$filterableFields[] = (new FilterableField())
->setName('userOwner')
->setTitle('User Owner')
->setPath('custom_fields.userOwner')

# optionally set field definition adapter (e.g. use default adapter to translate labels in filter options)
->setFieldDefinitionAdapter($this->defaultAdapter);
;

$event->setFilterableFields($filterableFields);
}

public function onGetSortableFields(SortableFieldsEvent $event)
{
// should be visible for car data pools only
if(!$this->isCarDataPool($event->getDataPoolConfig())) {
return;
}

// This array contains all sortable fields. Therefore, it would be possible to remove fields too if needed.
$sortableFields = $event->getSortableFields();

$sortableFields[] = (new SortableField())
->setName('owner')
->setTitle('User Owner')
->setPath('custom_fields.userOwner');

$event->setSortableFields($sortableFields);
}

public function onGetListableFields(ListableFieldsEvent $event)
{
// should be visible for car data pools only
if(!$this->isCarDataPool($event->getDataPoolConfig())) {
return;
}

// This array contains all listable fields. Therefore, it would be possible to remove fields too if needed.
$listableFields = $event->getListableFields();

$listableFields[] = (new ListableField())
->setName('owner')
->setTitle('User Owner')
->setPath('custom_fields.userOwner');

$event->setListableFields($listableFields);
}

protected function isCarDataPool(DataPoolConfigInterface $dataPoolConfig): bool
{
return $dataPoolConfig instanceof DataObjectConfig && $dataPoolConfig->getDataObjectClass() === 'CAR';
}
}

Update index mapping and data

Call the following console command as soon as the event subscriber is set up in the symfony container configuration.

./bin/console generic-data-index:update:index -r

Configure the new field in data pool configuration document

As soon as all these steps are finished the new field should appear in the select lists of the data pool configuration document. Configure the new field there and the new options should appear in the frontend.