Skip to main content
Version: 2026.2

Data Providers

A data provider is a service implementing DataProviderInterface. Components (for example conditions) that implement DataProviderDependentInterface declare the data providers they depend on, which triggers those providers to load their data before the component runs.

A data provider doesn't return its value directly; it sets it on the VisitorInfo instance instead. As a best practice, the shipped data providers expose their storage key as a constant, used to store and retrieve the data from the VisitorInfo. For example, the GeoIp data provider defines the GeoIp::PROVIDER_KEY constant used when storing and retrieving its data.

Shipped Data Providers

The bundle registers the following data providers by default (config/pimcore/default.yaml):

Provider keyClass
devicePimcore\Bundle\PersonalizationBundle\Targeting\DataProvider\Device
geoipPimcore\Bundle\PersonalizationBundle\Targeting\DataProvider\GeoIp
geolocationPimcore\Bundle\PersonalizationBundle\Targeting\DataProvider\GeoLocation
targeting_storagePimcore\Bundle\PersonalizationBundle\Targeting\DataProvider\TargetingStorage
visited_pages_counterPimcore\Bundle\PersonalizationBundle\Targeting\DataProvider\VisitedPagesCounter

device parses the user-agent string with the DeviceDetector library and backs the Hardware Platform and Operating System conditions. geoip and geolocation back the Country and Geo Point conditions.

Implementing a Data Provider

A data provider is a class implementing DataProviderInterface, registered as a service. A data provider can do anything, but the shipped data providers follow this pattern:

  • They store their information under a storage key exposed as a constant.
  • They always set their content key. If they can't resolve any data (for example, GeoIP can't resolve a location), they set null.
  • Before loading data, they check whether an entry already exists for their own storage key and abort if it does.

As an example, assume the DateTime used in the TimeOfTheDay condition (as implemented in the Conditions chapter) is more complex than a simple new DateTime(), for example because the date comes from a third party or involves calculation logic. Instead of creating it inside the condition, which doesn't have access to services, move it to a reusable DateTime data provider that stores the current DateTime on the VisitorInfo.

<?php

// src/Targeting/DataProvider/DateTime.php

namespace App\Targeting\DataProvider;

use Pimcore\Bundle\PersonalizationBundle\Targeting\DataProvider\DataProviderInterface;
use Pimcore\Bundle\PersonalizationBundle\Targeting\Model\VisitorInfo;

class DateTime implements DataProviderInterface
{
const PROVIDER_KEY = 'datetime';

public function load(VisitorInfo $visitorInfo): void
{
if ($visitorInfo->has(self::PROVIDER_KEY)) {
// abort if there already is data for this provider
return;
}

// assume creating the date is more complex (e.g. involves other services
// which are injected via DI)
$visitorInfo->set(self::PROVIDER_KEY, new \DateTimeImmutable());
}
}

Next, register your new data provider as a service:

services:
_defaults:
autowire: true
autoconfigure: true
public: false

App\Targeting\DataProvider\DateTime: ~

Register the provider with the targeting engine under its provider key:

pimcore_personalization:
targeting:
data_providers:
datetime: App\Targeting\DataProvider\DateTime

Consuming a Data Provider

To consume a data provider, implement DataProviderDependentInterface in your component and list the data providers it needs. As an example, update the TimeOfTheDay condition to fetch the current DateTime from the new provider:

<?php

// src/Targeting/Condition/TimeOfTheDay.php

namespace App\Targeting\Condition;

use App\Targeting\DataProvider\DateTime;
use Pimcore\Bundle\PersonalizationBundle\Targeting\Condition\AbstractVariableCondition;
use Pimcore\Bundle\PersonalizationBundle\Targeting\DataProviderDependentInterface;
use Pimcore\Bundle\PersonalizationBundle\Targeting\Model\VisitorInfo;

class TimeOfTheDay extends AbstractVariableCondition implements DataProviderDependentInterface
{
// ...

public function getDataProviderKeys(): array
{
return [DateTime::PROVIDER_KEY];
}

public function match(VisitorInfo $visitorInfo): bool
{
$dateTime = $visitorInfo->get(DateTime::PROVIDER_KEY);
if (!$dateTime) {
// provider did not provide a valid date - nothing to match against
return false;
}

$hour = (int)$dateTime->format('H');

if ($hour >= $this->hour) {
$this->setMatchedVariable('hour', $hour);

return true;
}

return false;
}
}

Instead of creating a new DateTime instance, the condition now expects one on the DateTime::PROVIDER_KEY storage of the VisitorInfo. The targeting engine takes care of loading every provider a component depends on before matching starts.

DataProviderDependentInterface isn't limited to conditions: action handlers and other data providers can use it too, so a data provider can depend on another data provider's data.