Skip to main content
Version: 2025.4

Customize and Extend

There are multiple ways for extending the file export adapter.

It is possible to add additional file types and transmitters (see below) and there are a couple of events that allow to further customize the export.

In addition to that, it is also possible to completely customize the whole exporter by providing a custom exporter service. This gives you full control over the whole export process and allows things like adding custom query conditions for the workspaces etc.

Additional Transmitters

Transmitters are responsible for delivering the file to its desired destination (local directory, remote location, etc.). If you want to add further transmitters just provide a PHP service class like this:

<?php
namespace AppBundle\DataHubFileExport\Exporter\Transmitter;

class MyCustomTransmitter extends \Pimcore\Bundle\DataHubFileExportBundle\Exporter\Transmitter\AbstractTransmitter {

public function execute()
{
$exportType = $this->getExporterType();

$config = $exportType->getExporter()->getDataHubConfiguration()->getConfiguration();
$transmitterConfig = $config['deliveryDestination']['transmitter_myCustomTransmitter'];
var_dump($transmitterConfig); exit;
//push it somewhere
}
}

... and add it as a Service with the tag pimcore.datahub.fileExport.exporter.transmitter

AppBundle\DataSyndicator\Exporter\Transmitter\MyCustomTransmitter:
shared: false
tags:
- {
name: "pimcore.datahub.fileExport.exporter.transmitter",
type: "myCustomTransmitter",
}

For the classic admin UI (ExtJS), provide a fieldset for the custom configuration fields of your transmitter like follows. If you registered the PHP Service with the type myCustomTransmitter, your id has to be transmitter_myCustomTransmitter (type prefixed with transmitter_). Please make sure that the id naming convention is correct.

pimcoreDataHubFileExportBundlePlugin.transmitter.myCustomTransmitter =
function () {
let id = "transmitter_myCustomTransmitter";
return {
xtype: "fieldset",
title: t(
"plugin_pimcore_datahub_delivery_destination_transmitter_myCustomTransmitter"
),
width: 600,
itemId: id,
defaultType: "textfield",
hidden: false,
items: [
{
fieldLabel: t(
"plugin_pimcore_datahub_delivery_destination_directory"
),
name: id + ".directory",
width: 560,
value: this.getValue(
"deliveryDestination." + id + ".directory"
),
},
],
};
};

If you don't need further configuration, you can just define an empty function, so the transmitter shows up in the Delivery type dropdown:

pimcoreDataHubFileExportBundlePlugin.transmitter.myCustomTransmitter =
function () {};

Please make sure with bundle priorities, that your Bundle is loaded after the DataHub File Export, otherwise you will get a Javascript error because the variable doesn't exist.

The ExtJS fieldset above only covers the classic admin UI. At this point the export pipeline runs your transmitter, but nobody can select it in Pimcore Studio yet.

Making the Transmitter Selectable in Pimcore Studio

The Delivery Type dropdown and the settings form shown below it are driven by a registry in the Studio dependency injection container. Each entry is a dynamic type; the six built-in transmitters are registered the same way. Add yours from your own Pimcore Studio plugin (for the plugin scaffolding itself, see the Studio UI Bundle extension documentation).

The File Export plugin exposes its SDK as the module federation remote pimcore_datahubfileexport_bundle. Add it to the remotes of your plugin's rsbuild.config.ts next to the Studio UI remote:

remotes: {
'@pimcore/studio-ui-bundle': createDynamicRemote('pimcore_studio_ui_bundle'),
'@pimcore/data-hub-file-export': createDynamicRemote('pimcore_datahubfileexport_bundle'),
},

Create a dynamic type that extends DynamicTypeTransmitterAbstract. Its id has to match the type of the service tag, because the settings are stored under deliveryDestination.transmitter_<id>, which is where the PHP transmitter reads them from. label is a translation key, and renderSettings() returns the form fields, each nested under ['deliveryDestination', 'transmitter_<id>', ...]. Override getDefaultValues() for settings a new configuration should start with:

import React from 'react'
import { Form, Input } from '@pimcore/studio-ui-bundle/components'
import { injectable, useTranslation } from '@pimcore/studio-ui-bundle/app'
import { DynamicTypeTransmitterAbstract, type TransmitterSettingsProps } from '@pimcore/data-hub-file-export'

const MyCustomTransmitterSettings = ({ isWriteable }: TransmitterSettingsProps): React.JSX.Element => {
const { t } = useTranslation()

return (
<Form.Item
label={ t('app.transmitter.my-custom.endpoint') }
name={ ['deliveryDestination', 'transmitter_myCustomTransmitter', 'endpoint'] }
>
<Input disabled={ !isWriteable } />
</Form.Item>
)
}

@injectable()
export class DynamicTypeTransmitterMyCustom extends DynamicTypeTransmitterAbstract {
readonly id = 'myCustomTransmitter'
readonly label = 'app.transmitter.my-custom'

renderSettings (props: TransmitterSettingsProps): React.JSX.Element {
return <MyCustomTransmitterSettings { ...props } />
}

getDefaultValues (): Record<string, unknown> {
return { endpoint: '' }
}
}

Bind the type in your plugin's onInit and register it with the transmitter registry in a module registered during onStartup. The registry is resolved from the container by the service identifier DataHubFileExport/DynamicTypes/Transmitter/Registry, available as localServiceIds['DataHubFileExport/DynamicTypes/Transmitter/Registry'] from the SDK:

import { type IAbstractPlugin } from '@pimcore/studio-ui-bundle'
import { container } from '@pimcore/studio-ui-bundle/app'
import { type DynamicTypeTransmitterRegistry, localServiceIds } from '@pimcore/data-hub-file-export'
import { DynamicTypeTransmitterMyCustom } from './dynamic-type-transmitter-my-custom'

export const MyPlugin: IAbstractPlugin = {
name: 'my-plugin',

onInit: ({ container: pluginContainer }): void => {
pluginContainer.bind('MyPlugin/DynamicTypes/Transmitter/MyCustom').to(DynamicTypeTransmitterMyCustom).inSingletonScope()
},

onStartup: ({ moduleSystem }): void => {
moduleSystem.registerModule({
onInit: (): void => {
const transmitterRegistry = container.get<DynamicTypeTransmitterRegistry>(
localServiceIds['DataHubFileExport/DynamicTypes/Transmitter/Registry']
)

transmitterRegistry.registerDynamicType(container.get('MyPlugin/DynamicTypes/Transmitter/MyCustom'))
}
})
}
}

The order of registration determines the order of the options in the dropdown. A configuration whose delivery type has no registered dynamic type keeps its stored settings and shows the raw type instead of a settings form.

note

The SDK is consumed at runtime through module federation; no npm package with type declarations is published for it yet. Until there is one, declare the module in your plugin (for example in a types/data-hub-file-export.d.ts) with the members you use, or type the imports loosely.

Additional Exporter Types

To add additional exporter types (besides CSV, XML and JSON) just have a look at the existing ones. You can create custom exporter types by just adding additional services add extend the Pimcore\Bundle\DataHubFileExportBundle\Exporter\Type\AbstractExporter class and register them with the tag pimcore.datahub.fileExport.exporter.type.

AppBundle\Exporter\Type\JSON:
shared: false
tags:
- { name: "pimcore.datahub.fileExport.exporter.type", type: "MY_JSON" }

Customize built in Exporter Types/Transmitters

To customize the built in exporter/transmitter you can override the existing services. Please take a look at Service definition of the bundle on how they are defined.

If you want to override the XML exporter type, you could do it like this:

Create your service class e.g:

<?php

namespace AppBundle\DataSyndicator\Exporter\Type;

class XML extends \Pimcore\Bundle\DataHubFileExportBundle\Exporter\Type\XML
{

protected $itemKey = 'myCustomItemKey';

protected function writeData($data)
{
return parent::writeData($data);
}

protected function getExistingData(): array
{
return parent::getExistingData();
}
}

... and override the service definition in your services.yml with:

Pimcore\Bundle\DataHubFileExportBundle\Exporter\Type\XML:
class: AppBundle\DataSyndicator\Exporter\Type\XML
shared: false
tags:
- { name: "pimcore.datahub.fileExport.exporter.type", type: "XML" }

This would just change the XML node item key from item to myCustomItemKey.

Extending the JSON Exporter

An example of extending the JSON exporter to nest the data under a key, such that an object is top level instead of an array:

class JSONNested extends \Pimcore\Bundle\DataHubFileExportBundle\Exporter\Type\JSON
{
protected $itemKey = 'items';

protected function writeData($data)
{
return parent::writeData([$this->itemKey => $data]);
}

protected function extractData($data)
{
return $data[$this->itemKey];
}
}

which takes advantage of the protected method extractData.

Custom exporter services

The exporter service implements and controls the whole export process. The default bundle configuration for the file exporter looks like this. So you can override custom parts if you need it.

pimcore_data_hub_file_export:
defaultExporter: "pimcore.datahub.fileExport.exporter.file"

By default, the pimcore.datahub.fileExport.exporter.file service (see settings above) is used to execute the export. You can change the exporter globally (settings above) or per export configuration.

To create a custom exporter service you have to register the class as a public service.

Extending the Studio Exporter

To customize data extraction while keeping the Studio GridService integration, extend StudioFile instead of File:

<?php

namespace App\DataHubFileExport\Exporter;

use Pimcore\Bundle\DataHubFileExportBundle\Exporter\StudioFile;
use Pimcore\Model\DataObject;

class MyStudioFile extends StudioFile
{
public function getItemData(DataObject\Concrete $object): array|bool
{
$data = parent::getItemData($object);

if ($data === false) {
return false;
}

// Add a computed field
$data['Full Name'] = ($data['First Name'] ?? '') . ' ' . ($data['Last Name'] ?? '');

return $data;
}
}

Register it as a service:

app.datahub.my-studio-exporter:
class: App\DataHubFileExport\Exporter\MyStudioFile
public: true
shared: false
tags:
- { name: "pimcore.datahub.fileExport.exporter" }

Then set app.datahub.my-studio-exporter as the custom exporter service in the DataHub configuration, or change the default exporter globally.

Change download date

This option allows you to control whether the filename reflects the download event or the file creation event.

pimcore_data_hub_file_export:
useDownloadDateForFileName: false

If useDownloadDateForFileName is set to true, the exported file's name will include the date and time when the file is downloaded. If set to false, the file name will use the date and time when the file was originally generated.

Sample

Definition
app.datahub.my-exporter:
class: AppBundle\DataSyndicator\Exporter\File
public: true
shared: false
tags:
- { name: "pimcore.datahub.fileExport.exporter" }

Please make sure it is public, not shared and tagged as in the sample above!

Implementation
<?php

namespace AppBundle\DataSyndicator\Exporter;

class File extends \Pimcore\Bundle\DataHubFileExportBundle\Exporter\File {

public function execute($data)
{
//var_dump($this->getDataHubConfiguration()->getName());
//do some custom stuff instead of calling the parent
return parent::execute($data);
}
}