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 `` | GTM container script + dataLayer pushes (page view, view_item, purchase…) | -| `{{ gtm_body() }}` | right after `` | GTM `