Visitor Info
The VisitorInfo is the central data object passed to every part of the targeting system. The engine creates a new
VisitorInfo for every request and enriches it with data throughout the matching process. Starting from an empty
object, several components add data to it, so it ends up holding all relevant targeting data for the current
request.
Visitor ID
To identify returning visitors, the engine needs a unique identifier assigned to each visitor. This identifier,
internally called the visitor ID, is generated or set by the browser and stored in a cookie. When creating a
VisitorInfo, the targeting engine tries to load a visitor ID from the _pc_vis cookie.
By design, the frontend targeting.js script generates this ID: when a site with targeting enabled loads and no
visitor ID was previously set, it generates a random string and exposes a method to set it manually:
_ptg.api.setVisitorId('my-custom-visitor-id');
Upon setting a visitor ID, the script stores it in two places in the browser:
- a
_pc_viscookie - a
_ptg.userlocal storage entry, holding persistent data about the visitor (an activity log and a list of previous visitor IDs). If the visitor ID cookie is missing but the local storage entry has an ID, that ID is used to write a new cookie.
The visitor ID is used wherever a unique identifier is needed, for example to recognize returning visitors or to persist data for a specific visitor in a targeting storage.
Accessing the Visitor Info in Your Code
Similar to the security token in Symfony's security system, fetch the current VisitorInfo from the
VisitorInfoStorage. This storage is defined as a service: inject it into your own services, or use it as a
controller action argument when your controllers are registered as services. Declare VisitorInfoStorageInterface
as a dependency and Symfony's autowiring takes care of the rest.
As an example, a sample service working with the VisitorInfo:
<?php
namespace App\Targeting;
use Pimcore\Bundle\PersonalizationBundle\Targeting\VisitorInfoStorageInterface;
class MyService
{
private VisitorInfoStorageInterface $visitorInfoStorage;
public function __construct(VisitorInfoStorageInterface $visitorInfoStorage)
{
$this->visitorInfoStorage = $visitorInfoStorage;
}
public function getVisitorId(): ?string
{
// always check if there is a visitor info before trying to fetch it
if (!$this->visitorInfoStorage->hasVisitorInfo()) {
return null;
}
$visitorInfo = $this->visitorInfoStorage->getVisitorInfo();
return $visitorInfo->getVisitorId();
}
}
With autowiring enabled, defining the service is enough; the type hint against VisitorInfoStorageInterface lets
Symfony autowire it:
services:
_defaults:
autowire: true
autoconfigure: true
public: false
App\Targeting\MyService: ~
Without autowiring, wire the dependency manually instead:
services:
App\Targeting\MyService:
arguments:
$visitorInfoStorage: '@Pimcore\Bundle\PersonalizationBundle\Targeting\VisitorInfoStorageInterface'
If your controllers are defined as services, use argument injection:
<?php
namespace App\Controller;
use Pimcore\Bundle\PersonalizationBundle\Targeting\VisitorInfoStorageInterface;
use Symfony\Component\HttpFoundation\JsonResponse;
use Symfony\Component\Routing\Attribute\Route;
class VisitorInfoController
{
#[Route('/visitor-info')]
public function visitorInfoAction(VisitorInfoStorageInterface $visitorInfoStorage): JsonResponse
{
$data = [
'visitorId' => null
];
// always check if there is a visitor info before trying to fetch it
if ($visitorInfoStorage->hasVisitorInfo()) {
$visitorInfo = $visitorInfoStorage->getVisitorInfo();
$data['visitorId'] = $visitorInfo->getVisitorId();
}
return new JsonResponse($data);
}
}