diff --git a/GoogleTagManager.php b/GoogleTagManager.php
index 05dc9c3..9fd903a 100755
--- a/GoogleTagManager.php
+++ b/GoogleTagManager.php
@@ -27,6 +27,7 @@ class GoogleTagManager extends BaseModule
public const GOOGLE_TAG_VIEW_ITEM = 'google_tag_view_item';
public const GOOGLE_TAG_VIEW_LIST_ITEM = 'google_tag_view_list_item';
public const GOOGLE_TAG_TRIGGER_LOGIN = 'google_tag_trigger_login';
+ public const GOOGLE_TAG_PURCHASE = 'google_tag_purchase';
/**
* @throws PropelException
diff --git a/Hook/Theme/GoogleTagManagerThemeHook.php b/Hook/Theme/GoogleTagManagerThemeHook.php
new file mode 100644
index 0000000..8db032b
--- /dev/null
+++ b/Hook/Theme/GoogleTagManagerThemeHook.php
@@ -0,0 +1,55 @@
+config->getContainerId();
+
+ if( empty($containerId) ) {
+ return '';
+ }
+
+ $productId = $parameters['product']['id'] ?? null;
+ return match ($hookName) {
+ // The dataLayer pushes must come before the container script loads gtm.js.
+ 'layout.head.bottom' => $this->dataLayerProvider->renderHead()
+ .$this->twig->render('@GoogleTagManagerModule/theme-hook/script.html.twig', [
+ 'containerId' => $containerId,
+ ]),
+ 'layout.body.top' => $this->twig->render('@GoogleTagManagerModule/theme-hook/noscript.html.twig', [
+ 'containerId' => $containerId
+ ]),
+ 'layout.body.bottom' => $this->dataLayerProvider->renderJsInit(),
+ 'product.bottom' => $productId ? $this->dataLayerProvider->trackProduct($productId) : '',
+ default => '',
+ };
+ }
+}
diff --git a/Listener/GoogleTagListener.php b/Listener/GoogleTagListener.php
index fcf8d24..a906d04 100644
--- a/Listener/GoogleTagListener.php
+++ b/Listener/GoogleTagListener.php
@@ -11,6 +11,7 @@
use Thelia\Core\Event\Customer\CustomerCreateOrUpdateEvent;
use Thelia\Core\Event\Customer\CustomerLoginEvent;
use Thelia\Core\Event\Loop\LoopExtendsParseResultsEvent;
+use Thelia\Core\Event\Order\OrderEvent;
use Thelia\Core\Event\TheliaEvents;
use Thelia\Model\CurrencyQuery;
use Thelia\Model\Lang;
@@ -32,6 +33,8 @@ public static function getSubscribedEvents(): array
GoogleTagManager::GOOGLE_TAG_VIEW_ITEM => ['getViewItem', 128],
TheliaEvents::CUSTOMER_LOGIN => ['triggerLoginEvent', 128],
TheliaEvents::CUSTOMER_CREATEACCOUNT => ['triggerRegisterEvent', 128],
+ // Runs after the core Order::create (priority 128) which sets the placed order.
+ TheliaEvents::ORDER_PAY => ['trackPurchase', 64],
TheliaEvents::getLoopExtendsEvent(
TheliaEvents::LOOP_EXTENDS_PARSE_RESULTS,
'product'
@@ -39,6 +42,28 @@ public static function getSubscribedEvents(): array
];
}
+ /**
+ * Stores the placed order id in session, consumed by DataLayerProvider::renderHead
+ * on the confirmation page to push the purchase/payment/shipping dataLayer events.
+ * The Flexy checkout confirmation page carries no order_id in the request.
+ */
+ public function trackPurchase(OrderEvent $event): void
+ {
+ $request = $this->requestStack->getCurrentRequest();
+
+ // Tracking must never break the payment flow: ORDER_PAY can be dispatched outside a
+ // web context (CLI, payment callback), where RequestStack::getSession() would throw.
+ // getPlacedOrder() is safe here — Order::create either sets it or throws at priority 128.
+ if (null === $request || !$request->hasSession()) {
+ return;
+ }
+
+ $request->getSession()->set(
+ GoogleTagManager::GOOGLE_TAG_PURCHASE,
+ $event->getPlacedOrder()->getId()
+ );
+ }
+
/**
* @throws \JsonException|PropelException
*/
diff --git a/Readme.md b/Readme.md
index f4abe0f..2e664fc 100755
--- a/Readme.md
+++ b/Readme.md
@@ -7,53 +7,73 @@ This module is made to use the Google Tag Manager / Google Analytics 4.
### Composer
```
-composer require thelia/google-tag-manager-module:~2.1.0
+composer require thelia/google-tag-manager-module:^4.0
```
## Usage
You need to configure the id from your Google tag manager account in the thelia administration panel.\
-It should look like ```GTM-XXXX```.
+It should look like ```GTM-XXXX```. Nothing is rendered while this id is empty.
-This module renders its scripts through Twig functions you place in your front theme.
+The module injects its scripts through **theme hooks**: as long as your front theme declares the
+hook points below, there is nothing to add to your templates.
-| Function | Where | What it outputs |
-|----------|-------|-----------------|
-| `{{ gtm_head() }}` | in `