Ibexa DXP (formerly eZ Platform, originally eZ Publish) is a Symfony-based enterprise content management platform. It uses Twig templating, Symfony service containers, and a powerful content model with Content Types and SiteAccess configurations for multi-site management. Analytics integration follows Symfony conventions with Ibexa-specific content awareness.
Integration Architecture
Ibexa DXP provides four integration paths:
- Twig Templates -- Edit your site's
pagelayout.html.twig(the base layout template) to add tracking scripts. Located in your site bundle attemplates/themes/yourtheme/pagelayout.html.twig. - Symfony Bundles -- Install analytics bundles via Composer. Ibexa runs on Symfony, so any Symfony-compatible bundle works.
- SiteAccess Configuration -- Ibexa's SiteAccess system allows different configurations per site/language. Set different GTM container IDs per SiteAccess in
config/packages/ibexa.yaml. - Content Type Mapping -- Ibexa's Content Type system provides structured data that maps directly to analytics dimensions (content type identifier, section, location depth).
Available Integrations
Analytics Platforms
- Twig layout template injection
- GTM-based GA4 with content-type data layer (recommended)
- Ibexa Personalization integration for segment-aware tracking
Tag Management
- Base Twig layout injection
- SiteAccess-specific container IDs
- Symfony bundle for admin-configurable GTM
Marketing Pixels
- Via GTM container (recommended)
- Twig template head injection
Twig Layout Integration with Data Layer
Edit your base layout template to add GTM and a content-aware data layer:
{# templates/themes/yourtheme/pagelayout.html.twig #}
<!DOCTYPE html>
<html lang="{{ app.request.locale }}">
<head>
<!-- Google Tag Manager -->
<script>(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;
j.src='https://www.googletagmanager.com/gtm.js?id='+i+dl;
f.parentNode.insertBefore(j,f);})(window,document,'script','dataLayer',
'{{ ibexa_config_resolver_parameter("gtm_container_id", "app") }}');</script>
{% if content is defined %}
<script>
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'contentTypeIdentifier': '{{ content.contentInfo.contentType.identifier }}',
'contentId': '{{ content.id }}',
'contentName': '{{ ez_content_name(content)|e("js") }}',
'locationId': '{{ location.id }}',
'sectionName': '{{ content.contentInfo.section.name|default("")|e("js") }}',
'locationDepth': '{{ location.depth }}',
'language': '{{ content.contentInfo.mainLanguageCode }}',
'siteAccess': '{{ ezpublish.siteaccess.name }}'
});
</script>
{% endif %}
{% block head %}{% endblock %}
</head>
<body>
<noscript><iframe src="https://www.googletagmanager.com/ns.html?id={{ ibexa_config_resolver_parameter('gtm_container_id', 'app') }}"
height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
{% block body %}{% endblock %}
</body>
</html>
SiteAccess-Specific Configuration
Configure different GTM containers per SiteAccess in your Ibexa configuration:
# config/packages/ibexa.yaml
ibexa:
system:
site_en:
params:
gtm_container_id: 'GTM-EN001'
site_de:
params:
gtm_container_id: 'GTM-DE001'
site_fr:
params:
gtm_container_id: 'GTM-FR001'
Access these in Twig with {{ ibexa_config_resolver_parameter('gtm_container_id', 'app') }}. The resolver automatically picks the correct value based on the active SiteAccess.
Platform Limitations
HTTP cache (Varnish/Fastly). Ibexa DXP uses HTTP cache (Varnish or Fastly) aggressively. Pages served from cache have identical HTML, including data layer values. User-specific data layer variables (logged-in state, user segment) must be populated client-side via JavaScript, not server-side in Twig.
Content Type changes. Modifying Content Type identifiers in the Ibexa admin breaks data layer mappings. Use the Content Type identifier (machine name) rather than the display name in your data layer to minimize the impact of admin changes.
Ibexa Personalization overlap. Ibexa's built-in Personalization engine tracks user behavior independently. Running both Ibexa Personalization and GA4 creates parallel data streams. Coordinate event definitions to avoid duplicate tracking.
Symfony profiler in dev. The Symfony web profiler toolbar injects JavaScript in dev mode that can interfere with tracking. Test analytics in production or staging environments.
Bundle compatibility. Not all Symfony bundles are compatible with Ibexa's version constraints. Verify Symfony version requirements before installing analytics bundles.
Performance Considerations
- Twig compilation. Template expressions compile to PHP on first render. Data layer Twig code has no measurable runtime cost after compilation. Clear the Symfony cache (
bin/console cache:clear) after template changes. - Content repository queries. Accessing
content.contentInfo.section.nametriggers a repository API call. For high-traffic pages, ensure Ibexa's Persistence Cache (Redis/Memcached) is configured to avoid redundant database queries. - Multi-site overhead. Each SiteAccess can have a different GTM container. If using many SiteAccesses with unique containers, each container loads independently -- this is by design but increases the number of distinct GTM containers to manage.
Recommended Integration Priority
- Add GTM to
pagelayout.html.twig-- Use SiteAccess-aware config for container IDs - Build content-type-aware data layer -- Map Ibexa Content Types, sections, and location depth to analytics dimensions
- Configure GA4 via GTM -- Use content type identifiers for GA4 content groups
- Add Meta Pixel via GTM -- Standard engagement tracking with consent mode for GDPR
Next Steps
For general integration concepts, see the integrations overview.