Skip to content

Tracking with PHP API

You can interact directly with the service using the PHP API for advanced tracking usage.

Advanced usage – direct interaction with the service

EventMapper method

The recommended method, providing full control over event tracking, is EventMapper method. It allows to interact directly with the service, supporting advanced use cases not covered by default implementation.

Check the following example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
<php>
use Ibexa\Contracts\ConnectorRaptor\Tracking\EventMapperInterface;
use Ibexa\Contracts\ConnectorRaptor\Tracking\ServerSideTrackingDispatcherInterface;
use Ibexa\Contracts\ConnectorRaptor\Tracking\EventType;

class MyCustomService
{
    public function __construct(
        private readonly EventMapperInterface $eventMapper,
        private ServerSideTrackingDispatcherInterface $trackingDispatcher,
    ) {}

    public function trackProductView(ProductInterface $product, string $url): void
    {
        // Map product to VisitEventData automatically
        $eventData = $this->eventMapper->map(EventType::VISIT, $product);

        // Send tracking event
        $this->trackingDispatcher->dispatch($eventData);
    }
}
</php>

Manual EventData creation

Manual creation of EventData allows precise control over the events sent to the service. It enables to define custom event parameters, track specific user interactions, and tailor data collection to advanced use cases.

Check the following example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<php>
use Ibexa\Contracts\ConnectorRaptor\Tracking\Event\VisitEventData;

$eventData = new VisitEventData(
    productId: $product->getCode(),
    productName: $product->getName(),
    categoryPath: '25#Electronics;26#Smartphones',  // Build manually
        currency: 'USD',
    itemPrice: '999.99'
);

$this->trackingDispatcher->dispatch($eventData);
</php>

Example - event subscriber

If you need to track events automatically based on application events, you can use Event Subscriber. It reacts to specific events in the application and triggers tracking logic without the need to add it manually in templates.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
<php>
use Ibexa\Contracts\ConnectorRaptor\Tracking\EventMapperInterface;
use Ibexa\Contracts\ConnectorRaptor\Tracking\ServerSideTrackingDispatcherInterface;
use Ibexa\Contracts\ConnectorRaptor\Tracking\EventType;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\HttpKernel\Event\ResponseEvent;
use Symfony\Component\HttpKernel\KernelEvents;

class ProductViewTrackingSubscriber implements EventSubscriberInterface
{
    public function __construct(
        private readonly EventMapperInterface $eventMapper,
        private ServerSideTrackingDispatcherInterface $trackingDispatcher,
    ) {}

    public static function getSubscribedEvents(): array
    {
        return [KernelEvents::RESPONSE => ['onResponse', -10]];
    }

    public function onResponse(ResponseEvent $event): void
    {
        if (!$event->isMainRequest()) {
            return;
        }

        $request = $event->getRequest();

        // Example: track only if request has specific attribute
        $product = $request->attributes->get('product');
        if (!$product) {
            return;
        }

        $eventData = $this->eventMapper->map(EventType::VISIT, $product);
        $this->trackingDispatcher->dispatch($eventData);
    }
}
</php>

Tracking events

The following events are supported and can be triggered from Twig templates:

Product visit event

This event tracks product page visits by users. It's the most common e-commerce tracking event used to capture product views for analytics, recommendation models, and user behavior processing.

Required data:

  • Product object - defines the product being tracked. It implements ProductInterface so the system can read its information (for example, ID, price, category).

Example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
# templates/product/view.html.twig #}
{% extends 'base.html.twig' %}

{% block content %}
    <div class="product-details">
        <h1>{{ product.name }}</h1>
        <p>{{ product.description }}</p>
        <div class="price">{{ product.price }}</div>
    </div>

    {# Track product visit #}
    {{ ibexa_tracking_track_event('visit', product) }}
{% endblock %}

Content visit event

This event tracks content page visits by users. It can used to check content views for analytics, personalization, and user behavior tracking.

  • Content object - defines the content being tracked.

Basket event

This event tracks when a product is added to the shopping basket.

It catches user interest and helps with conversion tracking and product recommendations.

Required data:

  • Product object - defines the product being added to the basket.
  • Context array with basket information - provides optional data about the basket, like quantity or basket ID, to provide context for the event.

Example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
# templates/cart/add_confirmation.html.twig #}
{% extends 'base.html.twig' %}

{% block content %}
    <div class="cart-notification">
        <p>Product "{{ product.name }}" has been added to your cart!</p>
        <p>Quantity: {{ addedQuantity }}</p>
    </div>

    {# Build basket content string: "SKU:quantity;SKU:quantity" #}
    {% set basketContent = [] %}
    {% for item in basket.items %}
        {% set basketContent = basketContent|merge([item.product.code ~ ':' ~ item.quantity]) %}
    {% endfor %}

    {# Track basket addition #}
    {% set basketContext = {
        'basketContent': basketContent|join(';'),
        'basketId': basket.id,
        'quantity': addedQuantity
    } %}

    {{ ibexa_tracking_track_event('basket', product, basketContext) }}

    <a href="{{ path('cart_view') }}">View Cart</a>
{% endblock %}

Simplified example with Twig filter:

1
2
3
4
5
6
7
8
{# If you have a custom Twig filter to format basket content #}
{% set basketContext = {
    'basketContent': basket|format_basket_content,  {# Returns "SKU-1:2;SKU-2:1;SKU-3:5" #}
    'basketId': basket.id,
    'quantity': addedQuantity
} %}

{{ ibexa_tracking_track_event('basket', product, basketContext) }}

Simplified example with Twig filter:

1
2
3
4
5
6
7
8
{# If you have a custom Twig filter to format basket content #}
{% set basketContext = {
    'basketContent': basket|format_basket_content,  {# Returns "SKU-1:2;SKU-2:1;SKU-3:5" #}
    'basketId': basket.id,
    'quantity': addedQuantity
} %}

{{ ibexa_tracking_track_event('basket', product, basketContext) }}

Complex integration

For more complex integrations, the Ibexa Design Engine can be used to override parts or entire templates that render the tracking script.

Template Description Example project path
@ibexadesign/ibexa/tracking/script.html.twig Responsible for creating the window.raptor object and handling consent. Loads tracking only if consent is given and listens for the enableTracking event. templates/themes/standard/ibexa/tracking/script.html.twig
@ibexadesign/ibexa/tracking/script.js.twig Handles the loading of the tracking JavaScript. templates/themes/standard/ibexa/tracking/script.js.twig

Available variables are:

  • customer_id - type: string, Raptor account ID used for tracking
  • script_url - type: string, URL of the tracking script, by default //deliver.raptorstatic.com/script/raptor-3.0.min.js, configurable through ibexa.connector.raptor.tracking_script.url Symfony Dependency Injection container parameter (not SiteAccess-aware)
  • has_consented - type: boolean, indicates whether the user has given consent, default value: false (unless explicitly passed as function argument)
  • debug - type: boolean, kernel.debug Symfony dependency injection container parameter, typically true in development environments and false in production

The default template defines a Twig block that includes script.js.twig. When extending the template, this block can be overridden to customize the script’s behavior.

You can override the default templates, either individually or both at the same time.

Extension

It's possible to extend script.html.twig by combining the Ibexa Design Engine with standard Symfony template reference in templates/themes/standard/ibexa/tracking/script.html.twig:

1
2
3
4
{% extends '@IbexaConnectorRaptor/themes/standard/ibexa/tracking/script.html.twig' %}
{% block ibexa_tracking_script %}
    console.log('My custom tracking script, but relying on loadTracking function.');
{% endblock %}

In most cases, the preferred approach is to do the opposite:

1
2
3
4
5
<script type="text/javascript">
    if (myCustomConsentIsGiven) {
           {{ include('@ibexadesign/ibexa/tracking/script.js.twig', {'customer_id': customer_id}) }}
    }
</script>

Example custom integration

Example custom integration with TermsFeed:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
``` html
<!-- Cookie Consent by TermsFeed https://www.TermsFeed.com -->
<script type="text/javascript" src="https://www.termsfeed.com/public/cookie-consent/4.2.0/cookie-consent.js" charset="UTF-8"></script>
<script type="text/javascript" charset="UTF-8">
    document.addEventListener('DOMContentLoaded', function () {
        cookieconsent.run({
            "notice_banner_type": "simple",
            "consent_type": "implied",
            "palette": "dark",
            "language": "en",
            "page_load_consent_levels": ["strictly-necessary"],
            "notice_banner_reject_button_hide": false,
            "preferences_center_close_button_hide": false,
            "page_refresh_confirmation_buttons": false,
            "website_name": "Ibexa Storefront",
            "callbacks": {
                "scripts_specific_loaded": (level) => {
                    switch(level) {
                        case 'tracking':
                            document.dispatchEvent(new CustomEvent('enableTracking'));
                            break;
                    }
                }
            },
            "callbacks_force": true
        });
    });
</script>
<noscript>Free cookie consent management tool by <a href="https://www.termsfeed.com/">TermsFeed</a></noscript>
<!-- End Cookie Consent by TermsFeed https://www.TermsFeed.com -->

```