A data provider is a service implementing the DataProviderInterface
.
Components (e.g. conditions) which implement the DataProviderDependentInterface
can define a set of data providers they depend on, triggering the data provider to load its data before the component
is used.
A data provider does not directly return its value, but is expected to set it on the VisitorInfo
instance instead. As
best practice, the core data providers expose their storage key as constant. This constant is used to store and retrieve
the data from the VisitorInfo
storage. As example: the GeoIP
data provider defines the GeoIP::PROVIDER_KEY
constant which is used when storing and retrieving the data.
A data provider is simply a class implementing the DataProviderInterface
which is registered as service. Basically a
data provider can do anything, however the core data providers do the following:
- They store their information on a storage key which is exposed as constant
- They always set their content key. If no data can be resolved (e.g. GeoIP is unable to resolve a location),
null
is set. - Before loading data, core providers check if there already is an entry for the own storage key and abort loading if that is the case.
As an example let's assume the DateTime
used in the TimeOfTheDay
condition (as implemented on the Conditions
page) is more complex than a simple new DateTime()
, i.e. because the date is fetched from a third party or involves
calculation logic. Instead of creating it inside the condition which does not have access to services we move it to a
reusable DateTime
data provider which 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 service:
services:
_defaults:
autowire: true
autoconfigure: true
public: false
App\Targeting\DataProvider\DateTime: ~
And register the provider to the targeting engine with its provider key:
pimcore_personalization:
targeting:
data_providers:
datetime: App\Targeting\DataProvider\DateTime
To consume a data provider, implement the DataProviderDependentInterface
in your components and specify a list of data
providers to use. As an example, let's update the TimeOfTheDay
condition to fetch the current DateTime
from our 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;
}
}
As you can see, instead of creating a new DateTime
instance, the condition now expects an instance on the DateTime::PROVIDER_KEY
storage on the VisitorInfo
. The targeting engine takes care of loading every provider the condition depends on before
starting to match.
The DataProviderDependentInterface
can not only be used from conditions, but also from action handlers and other
data providers (a data provider can depend on another data providers' data).