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.