Skip to content

Raptor integration - feature branch#3121

Open
julitafalcondusza wants to merge 16 commits into5.0from
raptor_integration_feature_branch
Open

Raptor integration - feature branch#3121
julitafalcondusza wants to merge 16 commits into5.0from
raptor_integration_feature_branch

Conversation

@julitafalcondusza
Copy link
Copy Markdown
Contributor

Question Answer
JIRA Ticket
Versions 5.0
Edition all

Checklist

  • Text renders correctly
  • Text has been checked with vale
  • Description metadata is up to date
  • Redirects cover removed/moved pages
  • Code samples are working
  • PHP code samples have been fixed with PHP CS fixer
  • Added link to this PR in relevant JIRA ticket or code PR

Copy link
Copy Markdown
Contributor

@adriendupuis adriendupuis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PHP API page content a lot of non PHP examples to move to Twig functions' pages.
It misses more link to navigate through the pages.
I added some PHP API Reference links (preview: https://ez-systems-developer-documentation--3122.com.readthedocs.build/en/3122/api/php_api/php_api_reference/namespaces/ibexa-contracts-connectorraptor.html).

Copy link
Copy Markdown
Contributor

@adriendupuis adriendupuis left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Few links to fix to get the preview again.

You can merge #3123 so you'll also have the 3 PHP API Ref pages used.

* Add preview of linked PHP API Ref entries
* Fix few links
@julitafalcondusza julitafalcondusza marked this pull request as ready for review April 8, 2026 12:16
@sonarqubecloud
Copy link
Copy Markdown

sonarqubecloud bot commented Apr 9, 2026

Quality Gate Failed Quality Gate failed

Failed conditions
7 Security Hotspots
46.5% Duplication on New Code (required ≤ 3%)
C Reliability Rating on New Code (required ≥ A)

See analysis details on SonarQube Cloud

Catch issues before they fail your Quality Gate with our IDE extension SonarQube for IDE

@github-actions
Copy link
Copy Markdown

github-actions bot commented Apr 9, 2026

code_samples/ change report

Before (on target branch)After (in current PR)

code_samples/recommendations/EventData.php


code_samples/recommendations/EventData.php

docs/recommendations/raptor_integration/tracking_php_api.md@30:``` php
docs/recommendations/raptor_integration/tracking_php_api.md@31:[[= include_file('code_samples/recommendations/EventData.php') =]]
docs/recommendations/raptor_integration/tracking_php_api.md@32:```

001⫶<php>
002⫶use Ibexa\Contracts\ConnectorRaptor\Tracking\Event\VisitEventData;
003⫶
004⫶$eventData = new VisitEventData(
005⫶ productCode: $product->getCode(),
006⫶ productName: $product->getName(),
007⫶ categoryPath: '25#Electronics;26#Smartphones', // Build manually
008⫶ currency: 'USD',
009⫶ itemPrice: '999.99'
010⫶);
011⫶
012⫶$this->trackingDispatcher->dispatch($eventData);
013⫶</php>


code_samples/recommendations/EventMapper.php


code_samples/recommendations/EventMapper.php

docs/recommendations/raptor_integration/tracking_php_api.md@19:``` php
docs/recommendations/raptor_integration/tracking_php_api.md@20:[[= include_file('code_samples/recommendations/EventMapper.php') =]]
docs/recommendations/raptor_integration/tracking_php_api.md@21:```

001⫶<php>
002⫶use Ibexa\Contracts\ConnectorRaptor\Tracking\EventMapperInterface;
003⫶use Ibexa\Contracts\ConnectorRaptor\Tracking\ServerSideTrackingDispatcherInterface;
004⫶use Ibexa\Contracts\ConnectorRaptor\Tracking\EventType;
005⫶
006⫶class MyCustomService
007⫶{
008⫶ public function __construct(
009⫶ private readonly EventMapperInterface $eventMapper,
010⫶ private ServerSideTrackingDispatcherInterface $trackingDispatcher,
011⫶ ) {}
012⫶
013⫶ public function trackProductView(ProductInterface $product, string $url): void
014⫶ {
015⫶ // Map product to VisitEventData automatically
016⫶ $eventData = $this->eventMapper->map(EventType::VISIT, $product);
017⫶
018⫶ // Send tracking event
019⫶ $this->trackingDispatcher->dispatch($eventData);
020⫶ }
021⫶}
022⫶</php>


code_samples/recommendations/EventSubscriber.php


code_samples/recommendations/EventSubscriber.php

docs/recommendations/raptor_integration/tracking_php_api.md@47:``` php
docs/recommendations/raptor_integration/tracking_php_api.md@48:[[= include_file('code_samples/recommendations/EventSubscriber.php') =]]
docs/recommendations/raptor_integration/tracking_php_api.md@49:```

001⫶<php>
002⫶use Ibexa\Contracts\ConnectorRaptor\Tracking\EventMapperInterface;
003⫶use Ibexa\Contracts\ConnectorRaptor\Tracking\ServerSideTrackingDispatcherInterface;
004⫶use Ibexa\Contracts\ConnectorRaptor\Tracking\EventType;
005⫶use Symfony\Component\EventDispatcher\EventSubscriberInterface;
006⫶use Symfony\Component\HttpKernel\Event\ResponseEvent;
007⫶use Symfony\Component\HttpKernel\KernelEvents;
008⫶
009⫶class ProductViewTrackingSubscriber implements EventSubscriberInterface
010⫶{
011⫶ public function __construct(
012⫶ private readonly EventMapperInterface $eventMapper,
013⫶ private ServerSideTrackingDispatcherInterface $trackingDispatcher,
014⫶ ) {}
015⫶
016⫶ public static function getSubscribedEvents(): array
017⫶ {
018⫶ return [KernelEvents::RESPONSE => ['onResponse', -10]];
019⫶ }
020⫶
021⫶ public function onResponse(ResponseEvent $event): void
022⫶ {
023⫶ if (!$event->isMainRequest()) {
024⫶ return;
025⫶ }
026⫶
027⫶ $request = $event->getRequest();
028⫶
029⫶ // Example: track only if request has specific attribute
030⫶ $product = $request->attributes->get('product');
031⫶ if (null === $product) {
032⫶ return;
033⫶ }
034⫶
035⫶ $eventData = $this->eventMapper->map(EventType::VISIT, $product);
036⫶ $this->trackingDispatcher->dispatch($eventData);
037⫶ }
038⫶}
039⫶</php>


code_samples/recommendations/config/packages/ibexa_connector_raptor.yaml


code_samples/recommendations/config/packages/ibexa_connector_raptor.yaml

docs/recommendations/raptor_integration/connector_installation_configuration.md@23:``` yaml
docs/recommendations/raptor_integration/connector_installation_configuration.md@24:[[= include_file('code_samples/recommendations/config/packages/ibexa_connector_raptor.yaml', 1, 14) =]]
docs/recommendations/raptor_integration/connector_installation_configuration.md@25:```

001⫶ system:
002⫶ <scope>:
003⫶ connector_raptor:
004⫶ enabled: true
005⫶ customer_id: ~ # Required
006⫶ tracking_type: client # One of: "client" or "server"
007⫶
008⫶ # Raptor Recommendations API key
009⫶ recommendations_api_key: ~ # Required
010⫶
011⫶ # Raptor Recommendations API URL, optional, set by default
012⫶ recommendations_api_url: '%ibexa.connector.raptor.recommendations.api_url%'
013⫶ibexa_connector_raptor:

docs/recommendations/raptor_integration/connector_installation_configuration.md@71:``` yaml hl_lines="14-17"
docs/recommendations/raptor_integration/connector_installation_configuration.md@72:[[= include_file('code_samples/recommendations/config/packages/ibexa_connector_raptor.yaml') =]]
docs/recommendations/raptor_integration/connector_installation_configuration.md@73:```

001⫶ibexa:
002⫶ system:
003⫶ <scope>:
004⫶ connector_raptor:
005⫶ enabled: true
006⫶ customer_id: ~ # Required
007⫶ tracking_type: client # One of: "client" or "server"
008⫶
009⫶ # Raptor Recommendations API key
010⫶ recommendations_api_key: ~ # Required
011⫶
012⫶ # Raptor Recommendations API URL, optional, set by default
013⫶ recommendations_api_url: '%ibexa.connector.raptor.recommendations.api_url%'
014❇️ibexa_connector_raptor:
015❇️ # When enabled, tracking exceptions are thrown instead of being silently handled
016❇️ strict_exceptions: true


code_samples/recommendations/custom_integration.html


code_samples/recommendations/custom_integration.html

docs/recommendations/raptor_integration/tracking_functions.md@92:``` html
docs/recommendations/raptor_integration/tracking_functions.md@93:[[= include_file('code_samples/recommendations/custom_integration.html') =]]
docs/recommendations/raptor_integration/tracking_functions.md@94:```

001⫶``` html
002⫶<!-- Cookie Consent by TermsFeed https://www.TermsFeed.com -->
003⫶<script type="text/javascript" src="https://www.termsfeed.com/public/cookie-consent/4.2.0/cookie-consent.js" charset="UTF-8"></script>
004⫶<script type="text/javascript" charset="UTF-8">
005⫶ document.addEventListener('DOMContentLoaded', function () {
006⫶ cookieconsent.run({
007⫶ "notice_banner_type": "simple",
008⫶ "consent_type": "implied",
009⫶ "palette": "dark",
010⫶ "language": "en",
011⫶ "page_load_consent_levels": ["strictly-necessary"],
012⫶ "notice_banner_reject_button_hide": false,
013⫶ "preferences_center_close_button_hide": false,
014⫶ "page_refresh_confirmation_buttons": false,
015⫶ "website_name": "Ibexa Storefront",
016⫶ "callbacks": {
017⫶ "scripts_specific_loaded": (level) => {
018⫶ switch(level) {
019⫶ case 'tracking':
020⫶ document.dispatchEvent(new CustomEvent('enableTracking'));
021⫶ break;
022⫶ }
023⫶ }
024⫶ },
025⫶ "callbacks_force": true
026⫶ });
027⫶ });
028⫶</script>
029⫶<noscript>Free cookie consent management tool by <a href="https://www.termsfeed.com/">TermsFeed</a></noscript>
030⫶<!-- End Cookie Consent by TermsFeed https://www.TermsFeed.com -->
031⫶```


code_samples/recommendations/events/basket_event.html.twig


code_samples/recommendations/events/basket_event.html.twig

docs/templating/twig_function_reference/recommendations_twig_functions.md@121:``` html+twig
docs/templating/twig_function_reference/recommendations_twig_functions.md@122:[[= include_file('code_samples/recommendations/events/basket_event.html.twig') =]]
docs/templating/twig_function_reference/recommendations_twig_functions.md@123:```

001⫶{# templates/cart/add_confirmation.html.twig #}
002⫶{% extends 'base.html.twig' %}
003⫶
004⫶{% block content %}
005⫶ <div class="cart-notification">
006⫶ <p>Product "{{ product.name }}" has been added to your cart!</p>
007⫶ <p>Quantity: {{ addedQuantity }}</p>
008⫶ </div>
009⫶
010⫶ {# Build basket content string: "SKU:quantity;SKU:quantity" #}
011⫶ {% set basketContent = [] %}
012⫶ {% for item in basket.items %}
013⫶ {% set basketContent = basketContent|merge([item.product.code ~ ':' ~ item.quantity]) %}
014⫶ {% endfor %}
015⫶
016⫶ {# Track basket addition #}
017⫶ {% set basketContext = {
018⫶ 'basketContent': basketContent|join(';'),
019⫶ 'basketId': basket.id,
020⫶ 'quantity': addedQuantity
021⫶ } %}
022⫶
023⫶ {{ ibexa_tracking_track_event('basket', product, basketContext) }}
024⫶
025⫶ <a href="{{ path('cart_view') }}">View Cart</a>
026⫶{% endblock %}


code_samples/recommendations/events/basket_event_simplified.html.twig


code_samples/recommendations/events/basket_event_simplified.html.twig

docs/templating/twig_function_reference/recommendations_twig_functions.md@127:``` html+twig
docs/templating/twig_function_reference/recommendations_twig_functions.md@128:[[= include_file('code_samples/recommendations/events/basket_event_simplified.html.twig') =]]
docs/templating/twig_function_reference/recommendations_twig_functions.md@129:```

001⫶{# If you have a custom Twig filter to format basket content #}
002⫶{% set basketContext = {
003⫶ 'basketContent': basket|format_basket_content, {# Returns "SKU-1:2;SKU-2:1;SKU-3:5" #}
004⫶ 'basketId': basket.id,
005⫶ 'quantity': addedQuantity
006⫶} %}
007⫶
008⫶{{ ibexa_tracking_track_event('basket', product, basketContext) }}


code_samples/recommendations/events/content_visit_event.html.twig


code_samples/recommendations/events/content_visit_event.html.twig

docs/templating/twig_function_reference/recommendations_twig_functions.md@104:``` html+twig
docs/templating/twig_function_reference/recommendations_twig_functions.md@105:[[= include_file('code_samples/recommendations/events/content_visit_event.html.twig') =]]
docs/templating/twig_function_reference/recommendations_twig_functions.md@106:```

001⫶{# templates/bundles/IbexaCoreBundle/default/content/full.html.twig #}
002⫶{% extends '@!IbexaCore/default/content/full.html.twig' %}
003⫶
004⫶{% block content %}
005⫶ {{ parent() }}
006⫶ {{ ibexa_tracking_track_event('contentvisit', content) }}
007⫶{% endblock %}


code_samples/recommendations/events/product_visit_event.html.twig


code_samples/recommendations/events/product_visit_event.html.twig

docs/templating/twig_function_reference/recommendations_twig_functions.md@91:``` html+twig
docs/templating/twig_function_reference/recommendations_twig_functions.md@92:[[= include_file('code_samples/recommendations/events/product_visit_event.html.twig') =]]
docs/templating/twig_function_reference/recommendations_twig_functions.md@93:```

001⫶# templates/product/view.html.twig #}
002⫶{% extends 'base.html.twig' %}
003⫶
004⫶{% block content %}
005⫶ <div class="product-details">
006⫶ <h1>{{ product.name }}</h1>
007⫶ <p>{{ product.description }}</p>
008⫶ <div class="price">{{ product.price }}</div>
009⫶ </div>
010⫶
011⫶ {# Track product visit #}
012⫶ {{ ibexa_tracking_track_event('visit', product) }}
013⫶{% endblock %}


code_samples/recommendations/templates/themes/standard/ibexa/tracking/script.html.twig


code_samples/recommendations/templates/themes/standard/ibexa/tracking/script.html.twig

docs/recommendations/raptor_integration/tracking_functions.md@78:``` html+twig
docs/recommendations/raptor_integration/tracking_functions.md@79:[[= include_file('code_samples/recommendations/templates/themes/standard/ibexa/tracking/script.html.twig') =]]
docs/recommendations/raptor_integration/tracking_functions.md@80:```

001⫶{% extends '@IbexaConnectorRaptor/themes/standard/ibexa/tracking/script.html.twig' %}
002⫶{% block ibexa_tracking_script %}
003⫶ console.log('My custom tracking script, but relying on loadTracking function.');
004⫶{% endblock %}


code_samples/recommendations/templates/themes/standard/ibexa/tracking/script.js.twig


code_samples/recommendations/templates/themes/standard/ibexa/tracking/script.js.twig

docs/recommendations/raptor_integration/tracking_functions.md@84:``` html+twig
docs/recommendations/raptor_integration/tracking_functions.md@85:[[= include_file('code_samples/recommendations/templates/themes/standard/ibexa/tracking/script.js.twig') =]]
docs/recommendations/raptor_integration/tracking_functions.md@86:```

001⫶<script type="text/javascript">
002⫶ if (myCustomConsentIsGiven) {
003⫶ {{ include('@ibexadesign/ibexa/tracking/script.js.twig', {'customer_id': customer_id}) }}
004⫶ }
005⫶</script>


code_samples/recommendations/templates/themes/standard/pagelayout.html.twig


code_samples/recommendations/templates/themes/standard/pagelayout.html.twig

docs/recommendations/raptor_integration/tracking_functions.md@21:``` html+twig
docs/recommendations/raptor_integration/tracking_functions.md@22:[[= include_file('code_samples/recommendations/templates/themes/standard/pagelayout.html.twig') =]]
docs/recommendations/raptor_integration/tracking_functions.md@23:```

001⫶{# templates/pagelayout.html.twig #}
002⫶<!DOCTYPE html>
003⫶<html>
004⫶<head>
005⫶ {# ... other head content ... #}
006⫶
007⫶ {# Initialize Raptor tracking - must be called before any tracking events #}
008⫶ {{ ibexa_tracking_script() }}
009⫶</head>
010⫶<body>
011⫶ {# ... page content ... #}
012⫶</body>
013⫶</html>


code_samples/recommendations/templates/tracking/custom_visit.html.twig


code_samples/recommendations/templates/tracking/custom_visit.html.twig

docs/templating/twig_function_reference/recommendations_twig_functions.md@159:``` html+twig
docs/templating/twig_function_reference/recommendations_twig_functions.md@160:[[= include_file('code_samples/recommendations/templates/tracking/custom_visit.html.twig') =]]
docs/templating/twig_function_reference/recommendations_twig_functions.md@161:```

001⫶{{ ibexa_tracking_track_event(
002⫶ 'visit',
003⫶ product,
004⫶ {},
005⫶ '@App/tracking/custom_visit.html.twig'
006⫶) }}
007⫶```
008⫶
009⫶{# templates/tracking/custom_visit.html.twig #}
010⫶
011⫶ {#
012⫶ # Custom visit tracking template
013⫶ #
014⫶ # Available variables:
015⫶ # - parameters: array of Raptor tracking parameters (p1, p2, p3, etc.)
016⫶ # - debug: boolean flag to enable debug console messages
017⫶ #}
018⫶
019⫶ <script type="text/javascript">
020⫶ {% autoescape 'js' %}
021⫶ (function () {
022⫶ // Custom logic before tracking
023⫶ console.log('Custom visit tracking template');
024⫶ console.log('Tracking parameters:', {{ parameters|json_encode|raw }});
025⫶
026⫶ // Send the tracking event (REQUIRED for tracking to work)
027⫶ const event = 'trackEvent';
028⫶ const params = {{ parameters|json_encode|raw }};
029⫶ window.raptor.push(event, params);
030⫶
031⫶ // Custom logic after tracking
032⫶ {% if debug %}
033⫶ console.log('Visit event tracked successfully');
034⫶ {% endif %}
035⫶ })();
036⫶ {% endautoescape %}
037⫶ </script>

Download colorized diff

Comment on lines +1 to +13
<php>
use Ibexa\Contracts\ConnectorRaptor\Tracking\Event\VisitEventData;

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

$this->trackingDispatcher->dispatch($eventData);
</php>
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

<php> is not a correct markup for PHP code. Is this something related to ReadTheDocs embedding?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this is incorrect - these files should be just valid PHP codes for PHPStan to verify them.

See https://github.com/ibexa/documentation-developer/blob/5.0/code_samples/api/public_php_api/src/embedding_fields.php or https://github.com/ibexa/documentation-developer/blob/5.0/code_samples/api/commerce/src/Command/CartCommand.php for examples

This applies to all PHP samples

<p>Quantity: {{ addedQuantity }}</p>
</div>

{# Build basket content string: "SKU:quantity;SKU:quantity" #}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
{# Build basket content string: "SKU:quantity;SKU:quantity" #}
{# Build basket content string: "product-code:quantity;product-code:quantity" #}


# Configuration procedure

To configure the Raptor integration, follow a step-by-step procedure that allows you to activate the Raptor connector.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
To configure the Raptor integration, follow a step-by-step procedure that allows you to activate the Raptor connector.
To configure the Raptor integration, follow the step-by-step procedure described below.

{# 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,
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In case of our API, here we would usually use the Cart object?

I mean, we're calling it "cart" consistently in the docs and in the code:
https://doc.ibexa.co/en/5.0/commerce/cart/cart/

and IMHO the code samples should transition from cart (DXP terminlogy) to basket (Raptor terminilogy), showing how these two are connected.

@@ -0,0 +1,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" #}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usually we use product code instead of SKU

Comment on lines +1 to +13
<php>
use Ibexa\Contracts\ConnectorRaptor\Tracking\Event\VisitEventData;

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

$this->trackingDispatcher->dispatch($eventData);
</php>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, this is incorrect - these files should be just valid PHP codes for PHPStan to verify them.

See https://github.com/ibexa/documentation-developer/blob/5.0/code_samples/api/public_php_api/src/embedding_fields.php or https://github.com/ibexa/documentation-developer/blob/5.0/code_samples/api/commerce/src/Command/CartCommand.php for examples

This applies to all PHP samples

private ServerSideTrackingDispatcherInterface $trackingDispatcher,
) {}

public function trackProductView(ProductInterface $product, string $url): void
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one should fail on PHPStan, as ProductInterface is not defined - incorrect code sample


### Basket event

This event tracks when a product is added to the shopping basket.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
This event tracks when a product is added to the shopping basket.
This event tracks when a product is added to the [cart](cart.md).

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.
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
- **Context array with basket information** - provides optional data about the basket, like quantity or basket ID, to provide context for the event.
- **Context array with cart information** - provides optional data about the cart, like product quantity or cart identifier, to provide context for the event.

[[= include_file('code_samples/recommendations/events/basket_event.html.twig') =]]
```

Simplified example with Twig filter:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not a fan of this example, it's "here's how simple it could be if you write your custom code to make it simpler". If this is useful, would be nice to provide this out of the box - I believe Partners are aware that they can create their own helpers when needed.

I'd just remove this TBH


#### Tracking events

The following events are supported and can be triggered from Twig templates:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is itemclicked and buy not supported? They are not on this list

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants