Skip to main content
Version: Next

Upgrade Notes

The following steps are necessary when updating to newer versions.

Upgrade to 2026.4.0​

Save and Delete Hooks Check Only the Affected IDs​

The data object save and delete hooks, the "Deliver directly on save" check and the cleanup of outdated rows in the exported file no longer load the IDs of the whole class. They query the configured listing restricted to the IDs concerned (id IN (...)), so the cost no longer grows with the number of objects of the class. ExporterInterface and getRelevantItemIds() are unchanged and still return the full list for bulk exports. Behaviour that differs:

  • RelevantItemListEvent is dispatched for the checks of single items as well, once per 1000 IDs. The listing the listeners get is not restricted to the IDs yet; the restriction is added after the listeners ran. A listener must not rely on the listing holding all items of the class. A listener that sets a limit or an offset keeps the previous behaviour (the full list is loaded and checked).
  • For save hook types other than queue, the queue items of the configuration are now deleted only for the saved relevant IDs, not for all relevant IDs of the class on any save.
  • AbstractExporter has two new public methods, filterRelevantItemIds(array $ids) and filterLegalItemIds(array $ids), and DataObjectSubscriber five new protected static methods (getConfigurations(), filterRelevantItemIds(), deleteQueueItems(), saveQueueItem(), queueDeletedElement()). A subclass that declares a method of the same name needs to rename it. Exporters that do not extend AbstractExporter are checked against their full getRelevantItemIds() list as before.

Flysystem Storages Are Injected​

The bundle's services no longer look up their storages with Pimcore\Tool\Storage::get(); it remains only as a fallback for subclasses constructed without the new argument (see below). The export storages pimcore.dataHubFileExport.storage and pimcore.dataHubFileExport.temp.storage are injected into the services that use them:

  • The exporter types (CSV, XML, JSON, based on Exporter\Type\AbstractExporter) get the temporary storage as a new optional last constructor argument ?FilesystemOperator $tmpStorage = null. Exporter\File::getFileStorage() returns it (types that only implement ExporterTypeInterface still use the Storage::get() lookup there). The storage is kept in a private property, so custom exporter types are free to declare members of their own.
  • The Download transmitter gets the download storage as a new optional last constructor argument ?FilesystemOperator $downloadStorage = null.

Both arguments are optional: a subclass that is registered without them keeps working and falls back to the Pimcore\Tool\Storage lookup as before. If your subclass overrides the constructor, add the argument and pass it to parent::__construct(); custom service definitions should set $tmpStorage / $downloadStorage as the bundle's services.yml does. DownloadController and ConfigurationEventSubscriber are @internal and now take the download storage as constructor argument.

generateAssetData() Deprecated​

AbstractExporter::generateAssetData() and File::generateAssetData() are deprecated and will be removed in 2027.1. Since the output-formatter refactoring in 2026.1 they are no longer called by the bundle: asset relations are formatted by the services tagged pimcore.datahub.fileExport.output_formatter. A custom exporter that overrides generateAssetData() still runs, but its override has had no effect on the exported data since 2026.1. Calling the method now triggers a deprecation.

Migrate such an override to an output formatter. Implement Pimcore\Bundle\DataHubFileExportBundle\Exporter\OutputFormatter\OutputFormatterInterface for the column type whose representation you customised (image, hotspotimage, imageGallery or video) and tag the service:

<?php

namespace App\DataHubFileExport\OutputFormatter;

use Pimcore\Bundle\DataHubFileExportBundle\Exporter\OutputFormatter\OutputFormatterInterface;

class MyImageFormatter implements OutputFormatterInterface
{
public function getType(): string
{
return 'image';
}

public function format(string $value, string $configName, array $config): mixed
{
// $value is the raw grid cell value of the column (the frontend path of the asset), $config is the
// export configuration, so the schema's thumbnail settings are available as
// $config['schema']['thumbnailImage']. Return the value that should be written to the file.
}
}
App\DataHubFileExport\OutputFormatter\MyImageFormatter:
tags:
- { name: "pimcore.datahub.fileExport.output_formatter", priority: -1 }

Formatters are indexed by getType() and the last one registered for a type wins. The shipped formatters use the default priority 0, and services of the same priority are iterated in container definition order, where bundle services come after application services. The negative priority makes your formatter come last so that it replaces the shipped one. See Customize and Extend.

Frontend-Aware Asset URLs​

All exported asset URLs are now built with the frontend-aware core API, so that pimcore.assets.frontend_prefixes is applied and the pimcore.frontend.path.asset / pimcore.frontend.path.asset.image.thumbnail / pimcore.frontend.path.asset.video.thumbnail frontend events are dispatched (CDN listeners). Previously this was only the case for image originals; hotspot images, video originals and thumbnails bypassed the prefixes.

The shape of the emitted URL changes for these values:

Exported valueBeforeNow
Hotspot image (original)raw storage path, no prefixfrontend_prefixes.source + path, pimcore.frontend.path.asset dispatched
Video (original)raw storage path, no prefixfrontend_prefixes.source + path, pimcore.frontend.path.asset dispatched
Image thumbnailTool::getHostUrl() + pathprefix-aware path, no host
Hotspot image thumbnailpath without any prefixfrontend_prefixes.thumbnail (or thumbnail_deferred) + path
Video thumbnailpath relative to the asset's folder, no prefix, not URL-encodedfrontend_prefixes.thumbnail + URL-encoded full path, pimcore.frontend.path.asset.video.thumbnail dispatched

Video thumbnails are built with Asset\Video::getFrontendThumbnail(), which applies frontend_prefixes.thumbnail also outside of a frontend request. Asset\Video::getThumbnail() does that only inside a frontend request, so it is not used for exports.

Image thumbnails no longer get a host prepended. Tool::getHostUrl() depends on the current request, which does not exist in a cron/CLI/messenger export: it defaulted the scheme to http even for HTTPS-only sites and returned an empty host when no domain was configured in Settings -> System -> Website, so the very same configuration produced different URLs depending on how the export was triggered. Asset originals never used the host either, and the core derives the scheme from the current request only, so these variants now behave the same way.

If the consumer of the exported file needs absolute URLs, configure absolute frontend prefixes (the documented Pimcore way to serve assets from a CDN) instead of relying on the exporter to guess a host:

pimcore:
assets:
frontend_prefixes:
source: 'https://cdn.example.com'
thumbnail: 'https://cdn.example.com'
thumbnail_deferred: 'https://cdn.example.com'

Alternatively, a listener on pimcore.frontend.path.asset / pimcore.frontend.path.asset.image.thumbnail can rewrite the paths — those events are now dispatched for every exported image and original asset path.

Upgrade to 2026.2.5​

Frontend Build Ships as a Packaged Archive​

The compiled Studio frontend is no longer committed as an expanded src/Resources/public/studio/build/ directory. It now ships as a single archive (build-dist/build-<id>.zip) that is extracted into src/Resources/public/studio/build/ automatically during cache warmup.

The already-required pimcore/studio-ui-bundle ^2026.2.6 provides the archive extraction, so no dependency change is needed.

Note: Read-only filesystem deployments must run bin/console cache:warmup (or cache:clear) during the build/deploy phase while the bundle directory (usually under vendor/) is still writable. Standard Pimcore deployments already do this. When assets:install runs in copy mode, run cache:warmup before it, otherwise no frontend assets are copied. If the filesystem becomes read-only before the first warmup, the bundle fails with BuildArchiveNotWritableException because there is no build to serve.

Upgrade to 2026.1.0​

PHP / Dependency Updates​

  • PHP minimum requirement raised to 8.4. PHP 8.5 is now supported.
  • Dropped support for PHP 8.3 and Symfony v6.
  • Removed the pimcore/admin-ui-classic-bundle dependency.
  • Removed Doctrine DBAL enum mapping support.

Removed ExtJS / Admin Classic UI​

  • Removed all ExtJS-based frontend JavaScript files (src/Resources/public/js/), including the full configuration UI, execution UI, log tab, transmitter configuration, workspace editor, and startup script. The configuration UI is now a Pimcore Studio plugin.
  • Removed StudioRouteLoader: Studio routes are now registered directly via routing.yml using attribute-based routing.
  • PimcoreDataHubFileExportBundle no longer implements PimcoreBundleAdminClassicInterface and no longer uses BundleAdminClassicTrait.
  • PimcoreDataHubFileExportBundle::registerDependentBundles() no longer registers PimcoreAdminBundle.
  • PimcoreDataHubFileExportBundle is not final, because other bundles extend it (for example data-hub-productsup).

New Features​

  • The CSV delimiter character is now configurable per export configuration. The default value remains ,.
  • Transmitters now access the exported file and its storage through the ExporterInterface instead of directly via the ExporterTypeInterface. This decoupling enables post-export file manipulation (e.g. bundling assets into a zip archive).

BC Breaks / Configuration Changes​

  • The Symfony Messenger transport DSN parameter has been renamed from %pimcore.messenger.transport_dsn% to %pimcore.messenger.transport_dsn_prefix%. Update your config.yml or environment variables accordingly.
  • TransmitterInterface and AbstractTransmitter no longer depend on ExporterTypeInterface. The methods setExporterType()/getExporterType() have been replaced by setExporter(ExporterInterface)/getExporter(). Custom transmitters must be updated to use the new ExporterInterface to access the file and storage.
  • ExporterInterface has two new methods: getFile(): string and getFileStorage(): FilesystemOperator. Custom exporter implementations must implement these methods.

Removed StudioFile Class: Merged into AbstractExporter​

  • Pimcore\Bundle\DataHubFileExportBundle\Exporter\StudioFile has been removed. All Studio GridService-based data extraction logic has been moved into AbstractExporter. Custom classes that extended StudioFile must now extend File instead.
  • The service ID pimcore.datahub.fileExport.exporter.studioFile has been renamed to pimcore.datahub.fileExport.exporter.file. The Helper::STUDIO_EXPORTER_SERVICE constant has been updated accordingly. Update any service configuration or code that references the old service ID.

Constructor Signature Change in AbstractExporter​

  • AbstractExporter::__construct() now requires two additional parameters: GridServiceInterface $gridService and OutputFormatterLoaderInterface $formatterLoader, inserted before the $exporterTypes array parameter. The new signature is:
    public function __construct(
    LocaleServiceInterface $localeService,
    EventDispatcherInterface $eventDispatcher,
    RouterInterface $router,
    AssetRelationFixer $assetRelationFixer,
    LoggerInterface $applicationLogger,
    GridServiceInterface $gridService, // NEW
    OutputFormatterLoaderInterface $formatterLoader, // NEW
    array $exporterTypes = [],
    array $transmitter = [],
    )
    Custom exporters that extend AbstractExporter or File and override the constructor must update their signatures to include these parameters and pass them to parent::__construct().

Data Extraction Now Uses Studio GridService Exclusively​

  • AbstractExporter no longer calls DataObject\Service::getCsvDataForObject(). All data extraction is now performed through the Studio GridServiceInterface::getGridDataForElement().
  • The legacy grid config format (schema.config.columns with helperDefinitions) is no longer supported by the base exporter. Configurations must use the Studio column format (schema.columns).
  • getGridConfigData() now returns array{columns: array, language: ?string} instead of the previous array{fields: array, helperDefinitions: array, language: ?string}. Code that relied on the fields or helperDefinitions keys must be updated.
  • getFieldMapping() now derives headers from column['label'] ?? column['key'] instead of DataObject\Service::getCsvDataForObject().
  • getItemData() now has an explicit return type of array|bool.
  • The protected method fixAssetRelations() has been removed and replaced by the private method fixStudioAssetRelations(), which delegates to registered OutputFormatter instances. Custom exporters that overrode fixAssetRelations() must migrate to the OutputFormatter system instead.
  • The protected method mapLabelToOperatorId() is no longer called from getItemData().

Logger Interface Change​

  • AbstractExporter::$applicationLogger, AbstractExporter::__construct() and AbstractTransmitter::setApplicationLogger() now type-hint against Psr\Log\LoggerInterface instead of Pimcore\Log\ApplicationLogger. Custom exporter and transmitter classes that override these must update their type hints accordingly.
  • Exporter\Type\AbstractExporter::__construct() constructor parameter $applicationLogger changed from ApplicationLogger to LoggerInterface.
  • The ApplicationLogger namespace was updated from Pimcore\Log\ApplicationLogger to Pimcore\Bundle\ApplicationLoggerBundle\ApplicationLogger. Update your imports if you referenced the old namespace directly.

Classes Marked as Final / Internal​

The following classes are now marked @internal and/or final. If you have extended or relied on these classes, you must refactor your implementation:

  • CronExecutionCommand: now final and @internal
  • ExportFileCommand: now final and @internal
  • ExportExecutionDispatcher: now final and @internal
  • ExportExecutionHandler: now final and @internal
  • ExportExecutionMessage: now final readonly and @internal
  • ConfigurationEventSubscriber: now final and @internal
  • CronExecutionService: now final and @internal
  • ExporterServiceCollector: now final and @internal
  • Installer: now final and @internal
  • ResetConfigEvent: now final; properties changed from protected to private
  • ValidateConfigEvent: now final; property changed from protected to private
  • DataObjectSubscriber: now @internal (not final, as it is extended by data-hub-productsup)

Return Type Changes​

  • Installer::getLastMigrationVersionClassName() return type changed from ?string to string (non-nullable).

Upgrade to 3.4.0​

The Pimcore Studio integration replaces the tree-based schema definition with the pipeline-based column format that Pimcore Studio grids use.

  • In 3.4.x, configurations created in the Admin Classic UI kept executing unchanged.
  • A configuration has to be migrated to the pipeline-based format before it can be edited in Pimcore Studio. Pimcore Studio guides you through the migration when you open such a configuration.
warning

The read-only execution support for legacy tree-based schemas was removed in 2026.1.0, earlier than the 2027.1.0 originally announced. AbstractExporter::getGridConfigData() now reads schema.columns only, and the shipped defaultExporter (pimcore.datahub.fileExport.exporter.file) is that same Studio-based exporter. A configuration that was never migrated fails at export time with No schema columns defined for config: "<name>". Migrate every configuration in Pimcore Studio while still on 3.4.x, before upgrading to 2026.1.0.

Upgrade to 3.0.0​

  • File name configuration has been changed. Extract the date/time format options into the new "date format for filename" field. Additionally, the format options have to be adjusted to the Carbon format. For example, %Y-%m-%d_%H-%i-%s has to be changed to Y-m-d_H-i-s. Further documentation on Carbon format options is available here.
  • Change return type to bool of exportData in ExporterTypeInterface and AbstractExporter

Upgrade to 2.2.0​

  • The folder where the temporary file and the output file will be generated can now be configured in the flysystem configuration.

Upgrade to 2.1.0​

  • Make sure all running exports are done prior to upgrading to version 2.1.0.

Upgrade to 2.0.0​

  • Make sure all running exports are done prior to upgrading to version 2.0.0.

Upgrade to 1.1.0​

  • Execute all migrations of the bundle.

Upgrade to Pimcore X​

  • Update to latest (allowed) bundle version in Pimcore 6.9 and execute all migrations.
  • Then update to Pimcore X.