Skip to main content
Version: 2024.4

Action Handlers

After a targeting rule matched it executes one or more actions as configured in the admin UI. These actions are actually executed by action handlers, which are services implementing the ActionHandlerInterface.

As with conditions, an action handler consists of 2 parts:

Implementing an Action Handler

As example, we'll implement a simple Log action handler which will log each matched rule with a configured log level. The action handler is a normal service which can depend on other services (as we do with the logger here).

You can't rely on a visitor ID being set or a rule being passed. The visitor ID might be inexistent on the first request to the site and action handlers might be executed from outside the matching engine without passing a rule. Always check those values before using them!

<?php

// src/Targeting/ActionHandler/Log.php

namespace App\Targeting\ActionHandler;

use Pimcore\Bundle\PersonalizationBundle\Model\Tool\Targeting\Rule;
use Pimcore\Bundle\PersonalizationBundle\Targeting\ActionHandler\ActionHandlerInterface;
use Pimcore\Bundle\PersonalizationBundle\Targeting\Model\VisitorInfo;
use Psr\Log\LoggerInterface;

class Log implements ActionHandlerInterface
{
private LoggerInterface $logger;

public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}

public function apply(VisitorInfo $visitorInfo, array $action, Rule $rule = null): void
{
$level = $action['level'] ?? 'info';

$this->logger->log(
$level,
'Matched target rule {ruleName} for visitor ID {visitorID} with config {config}',
[
'visitorID' => $visitorInfo->hasVisitorId() ? $visitorInfo->getVisitorId() : '(no visitor ID)',
'ruleName' => $rule ? $rule->getName() : '(no rule)',
'config' => json_encode($action)
]
);
}
}

Next, register your action handler as service. Note: we're using autowiring here to make sure the logger is automatically injected. If you don't use autowiring, enhance the service definition accordingly.

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

App\Targeting\ActionHandler\Log: ~

As last step, register the action handler to the targeting engine. Make sure the identifier is unique to your provider as you'll need to use it when implementing the admin UI JS part.

pimcore_personalization:
targeting:
action_handlers:
log: App\Targeting\ActionHandler\Log

Postponed Actions

If your action handler needs to apply data in a later stage of the request/response cycle it can register an action on the VisitorInfo which can be consumed later. Currently only the response action scope is defined which is executed in the onKernelResponse event, but more action scopes might be added in the future.

Have a look at the CodeSnippet action handler as example. It registers an action via $visitorInfo->addAction() and implements the ResponseTransformingActionHandlerInterface::transformResponse() which is called by the targeting engine for every action registered with the response scope.

Admin UI

To make your action handler appear in the admin UI, you need to create and register a JS class defining the admin interface for your action. Create a class extending pimcore.settings.targeting.action.abstract and register it to the system by calling pimcore.settings.targeting.actions.register().

Have a look at this bundle's actions and the Customer Management Framework for examples.

Start by adding a new JS file implementing the admin UI panel for your action:

// public/js/targeting/actions.js

(function () {
'use strict';

pimcore.bundle.personalization.settings.actions.register(
'log',
Class.create(pimcore.bundle.personalization.settings.action.abstract, {
getName: function () {
return 'Log';
},

getPanel: function (panel, data) {
var id = Ext.id();

return new Ext.form.FormPanel({
id: id,
forceLayout: true,
style: 'margin: 10px 0 0 0',
bodyStyle: 'padding: 10px 30px 10px 30px; min-height:40px;',
tbar: pimcore.bundle.personalization.settings.actions.getTopBar(this, id, panel),
items: [
{
name: 'level',
fieldLabel: 'Level',
xtype: 'combo',
store: [
['debug', 'Debug'],
['info', 'Info'],
['notice', 'Notice'],
['warning', 'Warning'],
['error', 'Error'],
['critical', 'Critical'],
['alert', 'Alert'],
['emergency', 'Emergency']
],
mode: 'local',
width: 300,
value: ('undefined' !== typeof data.level) ? data.level : 'info',
editable: false,
triggerAction: 'all'
},
{
xtype: 'hidden',
name: 'type',
value: 'log'
}
]
});
}
})
);
}());

As soon as you configured Pimcore to load the newly created file you should see your new action in the list of available actions:

Log ActionLog ActionLog Action