Skip to main content
Version: 2026.2

Custom Strategies

Most parts of the import process are pluggable. Each one follows the same pattern: implement a PHP interface, register the implementation as a tagged Symfony service, and add a matching settings form to Pimcore Studio.

Extension Points

Extension pointService tagInterfaceSelectable in Studio
Data sourcepimcore.datahub.data_importer.loaderDataSource\Loader\DataLoaderInterfaceYes
File formatpimcore.datahub.data_importer.interpreterDataSource\Interpreter\InterpreterInterfaceYes
Transformation operatorpimcore.datahub.data_importer.operatorMapping\Operator\OperatorInterfaceYes
Data targetpimcore.datahub.data_importer.data_targetMapping\DataTarget\DataTargetInterfaceYes
Element loading strategypimcore.datahub.data_importer.resolver.loadResolver\Load\LoadStrategyInterfaceYes
Element location strategypimcore.datahub.data_importer.resolver.locationResolver\Location\LocationStrategyInterfaceYes
Element publishing strategypimcore.datahub.data_importer.resolver.publishResolver\Publish\PublishStrategyInterfaceYes
Element factorypimcore.datahub.data_importer.resolver.factoryResolver\Factory\FactoryInterfaceNo
Cleanup strategypimcore.datahub.data_importer.cleanupCleanup\CleanupStrategyInterfaceNo

All interfaces live under the Pimcore\Bundle\DataImporterBundle namespace.

The last two rows are backend-only. The Cleanup Strategy select in the processing settings offers the shipped delete and unpublish options from a hard-coded list, and the element factory is not exposed in the configuration panel at all. A custom implementation of either is loaded correctly at import time, but there is no Studio registry to register it into, so it cannot be picked in the panel. Reaching it requires writing the type into the stored configuration through another route, or replacing the corresponding Studio component in your own plugin.

1. Implement the PHP Class

Implement the interface of the extension point. Several extension points ship an abstract base class that already covers the repetitive parts, for example AbstractInterpreter for file formats and AbstractOperator for operators. Extending an existing implementation works too: the Many-to-Many Relation data target extends Direct, and the SQL file format extends the JSON one.

Study the shipped implementations before writing your own. They are the reference for the contract each interface expects.

2. Register the Service

Register the class as a Symfony service and tag it. The tag name selects the extension point. The tag type is the identifier of your implementation: it has to be unique, it is stored in the saved import configuration, and it links the PHP class to its Studio settings form.

services:
Pimcore\Bundle\DataImporterBundle\DataSource\Loader\HttpLoader:
tags:
- { name: "pimcore.datahub.data_importer.loader", type: "http" }

At this point the import runs with your implementation, but nobody can select it in Pimcore Studio yet.

3. Add the Studio Settings Form

This step applies to the extension points marked selectable above. The configuration panel is a Pimcore Studio plugin. Those extension points have a registry in the Studio dependency injection container, and each selectable option is a dynamic type registered into it.

Create a dynamic type that extends the abstract class of the extension point. Its id has to match the type of the service tag, label is a translation key, and renderSettings() returns the settings form:

import React from 'react'
import { injectable } from '@pimcore/studio-ui-bundle/app'
import { DynamicTypeLoaderAbstract } from '.../dynamic-type-loader-abstract'
import { AssetLoaderSettings } from './asset-loader-settings'

@injectable()
export class DynamicTypeLoaderAsset extends DynamicTypeLoaderAbstract {
readonly id = 'asset'
readonly label = 'data-importer.loader.asset'

renderSettings (_configName: string): React.JSX.Element | null {
return <AssetLoaderSettings />
}
}

Bind the type in your plugin and register it with the matching registry. The registries are resolved from the container by these service identifiers:

Extension pointRegistry service identifier
Data sourceDataImporter/DynamicTypes/Loader/Registry
File formatDataImporter/DynamicTypes/Interpreter/Registry
Transformation operatorDataImporter/DynamicTypes/Transformer/Registry
Data targetDataImporter/DynamicTypes/DataTarget/Registry
Resolver strategiesDataImporter/DynamicTypes/Resolver/Registry
const loaderRegistry = container.get<DynamicTypeLoaderRegistry>('DataImporter/DynamicTypes/Loader/Registry')
loaderRegistry.registerDynamicType(container.get('DataImporter/DynamicTypes/Loader/Asset'))

The order of registration determines the order of the options in the UI.

For the plugin scaffolding itself, see the Studio UI Bundle extension documentation.

note

An operator that needs no settings has no form to render. The bundle registers those through a shared helper rather than a dedicated class per operator.