Google Tag Manager
Nahtlose GTM-Integration mit GA4-Events für Shopware 6
Überblick
BronnGoogleTagManager integriert Google Tag Manager und Google Consent Mode v2 nahtlos in Shopware 6. Das Plugin liefert sechs GA4-Enhanced-E-Commerce-Events (page_view, view_item, view_item_list, add_to_cart, begin_checkout, purchase), Hard-Blocking via Consent Mode v2 und eine automatische Brücke zum Shopware-Standard-Cookie-Banner.
Was das Plugin in Version 1.2.2 mitbringt:
Keine Datenbankmigrationen, kein Admin-Modul, keine Twig-Block-Overrides am Layout. Konfiguration ausschließlich über die Shopware-Plugin-Einstellungen.
Aktuelle Version: 1.2.2
Kompatibel mit Shopware 6.7+.
Systemvoraussetzungen
Installation
Empfohlener Weg (per ZIP-Upload im Admin):
BronnGoogleTagManager-1.2.2.zip herunterladenKein bin/build-storefront.sh, kein bin/build-administration.sh, kein theme:compile von der CLI erforderlich — das Plugin nutzt Inline-Tracking + Response-Subscriber und ist damit unmittelbar einsatzbereit.
Update von einer früheren Version:
Neues ZIP über die bestehende Installation hochladen, im Admin auf Aktualisieren klicken, anschließend Cache leeren. Snippets werden inkrementell ergänzt — vorhandene Übersetzungen bleiben erhalten.
Konfiguration
Die Plugin-Konfiguration liegt unter Erweiterungen > Meine Erweiterungen > Bronn Google Tag Manager > Konfiguration. Alle Schalter sind Sales-Channel-spezifisch.
Allgemein:
Event-Schalter (alle Standard `true`):
GA4 Enhanced-E-Commerce-Events
Alle Events werden GA4-konform über den dataLayer mit ecommerce-Objekt ausgelöst:
| Event | Trigger | Daten |
|---|---|---|
| page_view | Jeder Seitenaufruf | Standard-GA4 |
| view_item_list | Listing/Suche/Cross-Selling/CMS-Listing | currency, items[] mit item_id, item_name, item_brand, item_list_id, item_list_name, index, price |
| view_item | Produktdetailseite | currency, value, items[] mit item_id, item_name, item_brand, item_category, item_category2, item_variant, price, quantity (zusätzlich item_list_* falls aus Listing kommend) |
| add_to_cart | Klick auf Warenkorb-Button (Inline-Submit-Handler) | currency, value, items[] mit item_id, item_name, item_brand, item_category, item_category2, item_variant, price, quantity (zusätzlich item_list_* falls aus Listing kommend) |
| begin_checkout | Bestätigungsseite (Checkout-Start) | currency, value, items[] mit allen Cart-Positionen |
| purchase | Bestellabschlussseite | transaction_id, value, currency, items[] mit allen Produktpositionen |
Alle Geldwerte werden defensiv per number_format(2, '.', '') ausgegeben — kein Tausendertrenner-Parse-Error im Inline-Block, auch wenn ein Drittanbieter-Decorator die unitPrice manipuliert.
Click-Source-Tracking:
Klickt der Nutzer eine Produktkarte aus einem Listing an, schreibt das Plugin {item_id, item_list_id, item_list_name, index, ts} in sessionStorage['bronn_gtm_last_list']. Beim nachfolgenden view_item/add_to_cart werden diese Werte automatisch ans Item gehängt — bis 30 Minuten nach dem Listing-Klick.
Google Consent Mode v2
Bei aktivem enableConsentMode (Standard: an) injiziert das Plugin direkt vor dem GTM-Container-Snippet einen Consent-Default-Block mit allen Werten auf denied:
gtag('consent', 'default', {
ad_storage: 'denied',
ad_user_data: 'denied',
ad_personalization: 'denied',
analytics_storage: 'denied',
functionality_storage: 'denied',
personalization_storage: 'denied',
security_storage: 'granted',
wait_for_update: 500
});
gtag('set', 'ads_data_redaction', true);
gtag('set', 'url_passthrough', true);GTM sieht im ersten Consent Initialization - All Pages-Trigger garantiert die denied-Werte. Damit wird Hard-Blocking zuverlässig: keine Pixel-Tags und keine Pings vor User-Zustimmung.
Cookie-Banner-Bridge:
Der Shopware-Standard-Cookie-Banner ruft kein gtag('consent', 'update', ...) auf. Das Plugin schließt diese Lücke mit drei parallelen Akzeptanz-Triggern:
| Trigger | Wann | Selektor / Event |
|---|---|---|
| Klick-Delegation | Erstbesucher klickt "Akzeptieren" | .js-cookie-permission-button, .js-cookie-accept-all-button, [data-cookie-accept-all], .js-offcanvas-cookie-submit |
| Shopware-Event-Bus | Cookie-Configuration-Offcanvas "Speichern" | $emitter.subscribe('CookieConfiguration/Update', ...) |
| Returning-User-Detection | Page-Load wenn cookie-preference(s)-Cookie schon gesetzt | Cookie-Inhalt-Auswertung |
Bei Akzeptanz ruft die Bridge gtag('consent', 'update', {alle 6 Storage-Werte granted}) auf und pusht zusätzlich ein bronn_gtm_consent_granted-Event in den dataLayer (nutzbar als GTM-Trigger). Idempotent über window.__bronnGtmConsentGranted.
Technische Architektur
KernelResponseSubscriber (src/Subscriber/KernelResponseSubscriber.php):
Lauscht auf Symfony\Component\HttpKernel\KernelEvents::RESPONSE. Bei jedem Storefront-HTML-Response (Main-Request, Content-Type text/html, Sales-Channel-Context vorhanden) injiziert der Subscriber per preg_replace:
Dieser Ansatz ist theme-resistent: Selbst Custom-Themes, die base_head und base_head_meta_charset ohne {{ parent() }} überschreiben, können den Subscriber nicht umgehen. Der Subscriber arbeitet nach dem Twig-Rendering direkt am fertigen HTML-Output.
StorefrontSubscriber (src/Subscriber/StorefrontSubscriber.php):
Reagiert auf StorefrontRenderEvent und stellt Sales-Channel-spezifische Template-Variablen bereit (bronnGtmContainerId, bronnGtmTrackPageView, bronnGtmTrackViewItem, bronnGtmTrackViewItemList, bronnGtmTrackAddToCart, bronnGtmTrackBeginCheckout, bronnGtmTrackPurchase, bronnGtmEnableConsentMode).
Page-Twig-Overrides (für Inline-Events):
Die Page-Twig-Overrides werden vom Theme in der Praxis selten überschrieben — sie sind unkritisch. Globale Inject-Snippets (Head/Body) laufen über den Subscriber.
JS-Plugins (src/Resources/app/storefront/src/plugin/):
gtm-add-to-cart.plugin.js und gtm-list-click.plugin.js sind als optionaler Bonus enthalten — sie laufen nur, wenn bin/build-storefront.sh auf dem Server gelaufen ist. Wenn das Inline-Tracking bereits gegriffen hat, treten die JS-Plugins über die bronnGtmInlineTracked-Markierung zurück; kein Doppel-Tracking.
Snippets:
Kein CSRF-Token, keine eigenen Datenbanktabellen, keine Migrationen, kein Admin-Modul.
DSGVO & Datenschutz
Das Plugin selbst setzt keine eigenen Cookies. Mit aktiviertem Google Consent Mode v2 und der Cookie-Banner-Bridge (beides Standard ab v1.2.2) ist DSGVO-konformes Hard-Blocking out-of-the-box gewährleistet:
ad_storage, ad_user_data, ad_personalization, analytics_storage, functionality_storage und personalization_storage auf denied gesetzt — GTM blockiert ALLE Pixel-Tags.ads_data_redaction: true und url_passthrough: true schicken zusätzlich anonymisierte Pings, falls einzelne Tags trotzdem feuern.gtag('consent', 'update', granted) auf — erst danach feuert GTM die Marketing-Tags.cookie-preference-Cookie bekommen automatisch beim Page-Load den granted-Status.Die Verantwortung für die korrekte Konfiguration der Tags im GTM-Container (z.B. "Wait for Consent" pro Tag) liegt weiterhin beim Shop-Betreiber. Das Plugin liefert die Infrastruktur — die GTM-Tag-Logik bleibt im GTM.
Kompatibilität & Theme-Verhalten
Fehlerbehebung
Im DataLayer kommt nichts an:
Im Browser DevTools-Console eingeben: window.dataLayer — sollte mindestens gtm.start und page_view zeigen. Wenn leer: Plugin nicht aktiv oder Container-ID nicht gesetzt.
add_to_cart triggert nicht:
window.dataLayer.filter(e => e.event === 'add_to_cart') — sollte das Event enthalten.Consent-Mode bleibt auf denied trotz Cookie-Akzeptanz:
google_tag_data.ics.entries — wenn default: false und update: undefined, dann ist die Bridge nicht durchgelaufen.window.__bronnGtmConsentGranted === true nach Akzeptanz-Klick — wenn nicht, hat der Cookie-Banner-Klick nicht den erwarteten Selektor getroffen.gtag('consent', 'update', {...})-Aufruf aus dem Banner-Akzeptanz-Handler triggern.Kategorien sind im view_item leer:
Das Plugin nutzt einen Cascading-Fallback (seoCategory → aktive Navigation → categories-Collection). Wenn alle drei leer sind, hat das Produkt weder eine SEO-Kategorie noch eine aktive Navigation — item_category bleibt korrekt leer (das ist GA4-konform).