When targeting is enabled, a snippet of JavaScript in injected into every response. This snippet contains some information
needed by the frontend regarding the application state (e.g. if debug mode is enabled) and includes the script
pimcore/static6/js/frontend/targeting.js
which handles the frontend behaviour of the targeting engine (e.g. exposes an API to set the visitor ID). The whole targeting
logic is handled inside the window._ptg
object.
The frontend code provides a couple of extension points which are documented on this page.
The targeting.js
exposes a method to set a visitor ID programmatically. By doing so, the visitor ID will be set and stored
in the visitor ID cookie which is delivered to the backend.
_ptg.api.setVisitorId('my-custom-visitor-id');
The code which is injected into the response is generated by the TargetingCodeGenerator
which fires a TargetingEvents::TARGETING_CODE
event. This event can be used to influence data and the used template which is used to render the code snippet:
<?php
// src/EventListener/TargetingCodeListener.php
namespace App\EventListener;
use Pimcore\Bundle\PersonalizationBundle\Event\Targeting\TargetingCodeEvent;
use Pimcore\Bundle\PersonalizationBundle\Event\TargetingEvents;
use Pimcore\Bundle\PersonalizationBundle\Targeting\Code\TargetingCodeGenerator;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
class TargetingCodeListener implements EventSubscriberInterface
{
public static function getSubscribedEvents(): array
{
return [
TargetingEvents::TARGETING_CODE => 'onTargetingCode',
];
}
public function onTargetingCode(TargetingCodeEvent $event): void
{
// add code to a code block (see TargetingCodeGenerator and the default
// template for a list of blocks and their location)
$event
->getBlock(TargetingCodeGenerator::BLOCK_BEFORE_SCRIPT)
->append([
'console.log("Custom targeting code");'
]);
// completely override the rendered template
$event->setTemplate('@App/Targeting/targetingCode.html.twig');
}
}
The listener above sets a custom template which can either extend the core one or define a completely custom output:
{# templates/Targeting/targetingCode.html.twig #}
{% extends '@PimcorePersonalization/Targeting/targetingCode.html.twig' %}
{% block beforeScriptTag %}
{{ parent() }}
<script type="text/javascript">
console.log('Custom targeting template');
</script>
{% endblock %}
Some conditions or data providers might need data from the frontend. To make this possible, a backend implementation can
call $visitorInfo->addFrontendDataProvider()
during the matching process to inform the frontend that it needs data from
a specific provider. These frontend data providers need to be implemented and registered to the frontend JS and are expected
to deliver their data to the backend in some way (e.g. by storing data in a cookie or by sending it through an async request).
Currently, this feature is only sparsely used, but the GeoLocation
data provider which can read the visitor location from browser geolocation data informs the frontend that it needs data
from the geolocation
frontend data provider. This information is added to the browser response, triggering the targeting.js
to execute this frontend data provider. The data provider in turn stores its data as cookie which is consumed by the (backend)
data provider on the next request.
Simply register your frontend data provider on the window._ptg.dataProviders
object before targeting.js
is loaded. As
example a simple provider which does nothing more than logging the current user agent to the console:
// /public/js/targeting/frontend.js
(function () {
window._ptg = window._ptg || {};
window._ptg.dataProviders = window._ptg.dataProviders || {};
window._ptg.dataProviders.userAgent = function() {
console.log('The current user agent is ' + navigator.userAgent);
};
}());
Taking the example listener above, we use a custom code snippet template to load our script:
{# templates/Targeting/targetingCode.html.twig #}
{% block targetingScript %}
<script type="text/javascript" src="{{ asset('bundles/app/js/targeting/frontend.js') }}"></script>
{{ parent() }}
{% endblock %}
To actually execute the provider, add the following call somewhere in your matching process (e.g. while loading data from a data provider):
$visitorInfo->addFrontendDataProvider('userAgent');