Skip to content

Commit a508ae5

Browse files
authored
Rewrote part of GA4 to support add/remove quantity from cart and cart_updates (#3377)
1 parent 9e17d42 commit a508ae5

File tree

4 files changed

+127
-95
lines changed

4 files changed

+127
-95
lines changed

app/code/core/Mage/GoogleAnalytics/Block/Ga.php

Lines changed: 55 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -131,67 +131,57 @@ protected function _getEnhancedEcommerceDataForAnalytics4()
131131
$request = $this->getRequest();
132132
$moduleName = $request->getModuleName();
133133
$controllerName = $request->getControllerName();
134+
$helper = Mage::helper('googleanalytics');
134135

135136
/**
136137
* This event signifies that an item was removed from a cart.
137138
*
138139
* @link https://developers.google.com/tag-platform/gtagjs/reference/events#remove_from_cart
139140
*/
140-
$removedProducts = Mage::getSingleton('core/session')->getRemovedProductsCart();
141+
$removedProducts = Mage::getSingleton('core/session')->getRemovedProductsForAnalytics();
141142
if ($removedProducts) {
142143
foreach ($removedProducts as $removedProduct) {
143-
$_removedProduct = Mage::getModel('catalog/product')->load($removedProduct);
144144
$eventData = [];
145145
$eventData['currency'] = Mage::app()->getStore()->getCurrentCurrencyCode();
146-
$eventData['value'] = number_format($_removedProduct->getFinalPrice(), 2, '.', '');
146+
$eventData['value'] = $helper->formatPrice($removedProduct['price'] * $removedProduct['qty']);
147147
$eventData['items'] = [];
148148
$_item = [
149-
'item_id' => $_removedProduct->getSku(),
150-
'item_name' => $_removedProduct->getName(),
151-
'price' => number_format($_removedProduct->getFinalPrice(), 2, '.', ''),
149+
'item_id' => $removedProduct['sku'],
150+
'item_name' => $removedProduct['name'],
151+
'price' => $helper->formatPrice($removedProduct['price']),
152+
'quantity' => (int) $removedProduct['qty'],
153+
'item_brand' => $removedProduct['manufacturer'],
154+
'item_category' => $removedProduct['category'],
152155
];
153-
if ($_removedProduct->getAttributeText('manufacturer')) {
154-
$_item['item_brand'] = $_removedProduct->getAttributeText('manufacturer');
155-
}
156-
$itemCategory = Mage::helper('googleanalytics')->getLastCategoryName($_removedProduct);
157-
if ($itemCategory) {
158-
$_item['item_category'] = $itemCategory;
159-
}
160-
array_push($eventData['items'], $_item);
156+
$eventData['items'][] = $_item;
161157
$result[] = "gtag('event', 'remove_from_cart', " . json_encode($eventData, JSON_THROW_ON_ERROR) . ");";
162158
}
163-
Mage::getSingleton('core/session')->unsRemovedProductsCart();
159+
Mage::getSingleton('core/session')->unsRemovedProductsForAnalytics();
164160
}
165161

166162
/**
167163
* This event signifies that an item was added to a cart for purchase.
168164
*
169165
* @link https://developers.google.com/tag-platform/gtagjs/reference/events#add_to_cart
170166
*/
171-
$addedProducts = Mage::getSingleton('core/session')->getAddedProductsCart();
167+
$addedProducts = Mage::getSingleton('core/session')->getAddedProductsForAnalytics();
172168
if ($addedProducts) {
173-
foreach ($addedProducts as $addedProduct) {
174-
$_addedProduct = Mage::getModel('catalog/product')->load($addedProduct);
169+
foreach ($addedProducts as $_addedProduct) {
175170
$eventData = [];
176171
$eventData['currency'] = Mage::app()->getStore()->getCurrentCurrencyCode();
177-
$eventData['value'] = number_format($_addedProduct->getFinalPrice(), 2, '.', '');
172+
$eventData['value'] = $helper->formatPrice($_addedProduct['price'] * $_addedProduct['qty']);
178173
$eventData['items'] = [];
179174
$_item = [
180-
'item_id' => $_addedProduct->getSku(),
181-
'item_name' => $_addedProduct->getName(),
182-
'price' => number_format($_addedProduct->getFinalPrice(), 2, '.', ''),
175+
'item_id' => $_addedProduct['sku'],
176+
'item_name' => $_addedProduct['name'],
177+
'price' => $helper->formatPrice($_addedProduct['price']),
178+
'quantity' => (int) $_addedProduct['qty'],
179+
'item_brand' => $_addedProduct['manufacturer'],
180+
'item_category' => $_addedProduct['category'],
183181
];
184-
if ($_addedProduct->getAttributeText('manufacturer')) {
185-
$_item['item_brand'] = $_addedProduct->getAttributeText('manufacturer');
186-
}
187-
188-
$itemCategory = Mage::helper('googleanalytics')->getLastCategoryName($_addedProduct);
189-
if ($itemCategory) {
190-
$_item['item_category'] = $itemCategory;
191-
}
192-
array_push($eventData['items'], $_item);
182+
$eventData['items'][] = $_item;
193183
$result[] = "gtag('event', 'add_to_cart', " . json_encode($eventData, JSON_THROW_ON_ERROR) . ");";
194-
Mage::getSingleton('core/session')->unsAddedProductsCart();
184+
Mage::getSingleton('core/session')->unsAddedProductsForAnalytics();
195185
}
196186
}
197187

@@ -205,14 +195,14 @@ protected function _getEnhancedEcommerceDataForAnalytics4()
205195
$category = Mage::registry('current_category') ? Mage::registry('current_category')->getName() : false;
206196
$eventData = [];
207197
$eventData['currency'] = Mage::app()->getStore()->getCurrentCurrencyCode();
208-
$eventData['value'] = number_format($productViewed->getFinalPrice(), 2, '.', '');
198+
$eventData['value'] = $helper->formatPrice($productViewed->getFinalPrice());
209199
$eventData['items'] = [];
210200
$_item = [
211201
'item_id' => $productViewed->getSku(),
212202
'item_name' => $productViewed->getName(),
213203
'list_name' => 'Product Detail Page',
214204
'item_category' => $category,
215-
'price' => number_format($productViewed->getFinalPrice(), 2, '.', ''),
205+
'price' => $helper->formatPrice($productViewed->getFinalPrice()),
216206
];
217207
if ($productViewed->getAttributeText('manufacturer')) {
218208
$_item['item_brand'] = $productViewed->getAttributeText('manufacturer');
@@ -251,7 +241,7 @@ protected function _getEnhancedEcommerceDataForAnalytics4()
251241
'item_id' => $productViewed->getSku(),
252242
'index' => $index,
253243
'item_name' => $productViewed->getName(),
254-
'price' => number_format($productViewed->getFinalPrice(), 2, '.', ''),
244+
'price' => $helper->formatPrice($productViewed->getFinalPrice()),
255245
];
256246
if ($productViewed->getAttributeText('manufacturer')) {
257247
$_item['item_brand'] = $productViewed->getAttributeText('manufacturer');
@@ -263,7 +253,7 @@ protected function _getEnhancedEcommerceDataForAnalytics4()
263253
$index++;
264254
$eventData['value'] += $productViewed->getFinalPrice();
265255
}
266-
$eventData['value'] = number_format($eventData['value'], 2, '.', '');
256+
$eventData['value'] = $helper->formatPrice($eventData['value']);
267257
$result[] = "gtag('event', 'view_item_list', " . json_encode($eventData, JSON_THROW_ON_ERROR) . ");";
268258
}
269259

@@ -273,32 +263,34 @@ protected function _getEnhancedEcommerceDataForAnalytics4()
273263
* @link https://developers.google.com/tag-platform/gtagjs/reference/events#view_cart
274264
*/
275265
elseif ($moduleName == 'checkout' && $controllerName == 'cart') {
276-
$productCollection = Mage::getSingleton('checkout/session')->getQuote()->getAllVisibleItems();
266+
$productCollection = Mage::getSingleton('checkout/session')->getQuote()->getAllItems();
277267
$eventData = [];
278268
$eventData['currency'] = Mage::app()->getStore()->getCurrentCurrencyCode();
279269
$eventData['value'] = 0.00;
280270
$eventData['items'] = [];
281271

282272
foreach ($productCollection as $productInCart) {
283-
$_product = Mage::getModel('catalog/product')->load($productInCart->getProductId());
273+
if ($productInCart->getParentItem()) {
274+
continue;
275+
}
276+
$_product = $productInCart->getProduct();
284277
$_item = [
285278
'item_id' => $_product->getSku(),
286279
'item_name' => $_product->getName(),
287-
'price' => number_format($_product->getFinalPrice(), 2, '.', ''),
280+
'price' => $helper->formatPrice($_product->getFinalPrice()),
288281
'quantity' => (int) $productInCart->getQty(),
289282
];
290283
if ($_product->getAttributeText('manufacturer')) {
291284
$_item['item_brand'] = $_product->getAttributeText('manufacturer');
292285
}
293-
294-
$itemCategory = Mage::helper('googleanalytics')->getLastCategoryName($_product);
286+
$itemCategory = $helper->getLastCategoryName($_product);
295287
if ($itemCategory) {
296288
$_item['item_category'] = $itemCategory;
297289
}
298290
array_push($eventData['items'], $_item);
299-
$eventData['value'] += $_product->getFinalPrice();
291+
$eventData['value'] += $_product->getFinalPrice() * $productInCart->getQty();
300292
}
301-
$eventData['value'] = number_format($eventData['value'], 2, '.', '');
293+
$eventData['value'] = $helper->formatPrice($eventData['value']);
302294
$result[] = "gtag('event', 'view_cart', " . json_encode($eventData, JSON_THROW_ON_ERROR) . ");";
303295
}
304296

@@ -308,32 +300,34 @@ protected function _getEnhancedEcommerceDataForAnalytics4()
308300
* @link https://developers.google.com/tag-platform/gtagjs/reference/events#begin_checkout
309301
*/
310302
elseif ($moduleName == static::CHECKOUT_MODULE_NAME && $controllerName == static::CHECKOUT_CONTROLLER_NAME) {
311-
$productCollection = Mage::getSingleton('checkout/session')->getQuote()->getAllVisibleItems();
303+
$productCollection = Mage::getSingleton('checkout/session')->getQuote()->getAllItems();
312304
if ($productCollection) {
313305
$eventData = [];
314306
$eventData['currency'] = Mage::app()->getStore()->getCurrentCurrencyCode();
315307
$eventData['value'] = 0.00;
316308
$eventData['items'] = [];
317309
foreach ($productCollection as $productInCart) {
318-
$_product = Mage::getModel('catalog/product')->load($productInCart->getProductId());
310+
if ($productInCart->getParentItem()) {
311+
continue;
312+
}
313+
$_product = $productInCart->getProduct();
319314
$_item = [
320315
'item_id' => $_product->getSku(),
321316
'item_name' => $_product->getName(),
322-
'price' => number_format($_product->getFinalPrice(), 2, '.', ''),
317+
'price' => $helper->formatPrice($_product->getFinalPrice()),
323318
'quantity' => (int) $productInCart->getQty(),
324319
];
325320
if ($_product->getAttributeText('manufacturer')) {
326321
$_item['item_brand'] = $_product->getAttributeText('manufacturer');
327322
}
328-
329-
$itemCategory = Mage::helper('googleanalytics')->getLastCategoryName($_product);
323+
$itemCategory = $helper->getLastCategoryName($_product);
330324
if ($itemCategory) {
331325
$_item['item_category'] = $itemCategory;
332326
}
333327
array_push($eventData['items'], $_item);
334328
$eventData['value'] += $_product->getFinalPrice();
335329
}
336-
$eventData['value'] = number_format($eventData['value'], 2, '.', '');
330+
$eventData['value'] = $helper->formatPrice($eventData['value']);
337331
$result[] = "gtag('event', 'begin_checkout', " . json_encode($eventData, JSON_THROW_ON_ERROR) . ");";
338332
}
339333
}
@@ -352,28 +346,30 @@ protected function _getEnhancedEcommerceDataForAnalytics4()
352346
$orderData = [
353347
'currency' => $order->getBaseCurrencyCode(),
354348
'transaction_id' => $order->getIncrementId(),
355-
'value' => number_format($order->getBaseGrandTotal(), 2, '.', ''),
356-
'coupon' => strtoupper($order->getCouponCode()),
357-
'shipping' => number_format($order->getBaseShippingAmount(), 2, '.', ''),
358-
'tax' => number_format($order->getBaseTaxAmount(), 2, '.', ''),
349+
'value' => $helper->formatPrice($order->getBaseGrandTotal()),
350+
'coupon' => strtoupper((string)$order->getCouponCode()),
351+
'shipping' => $helper->formatPrice($order->getBaseShippingAmount()),
352+
'tax' => $helper->formatPrice($order->getBaseTaxAmount()),
359353
'items' => []
360354
];
361355

362356
/** @var Mage_Sales_Model_Order_Item $item */
363-
foreach ($order->getAllVisibleItems() as $item) {
357+
foreach ($order->getAllItems() as $item) {
358+
if ($item->getParentItem()) {
359+
continue;
360+
}
361+
$_product = $item->getProduct();
364362
$_item = [
365363
'item_id' => $item->getSku(),
366364
'item_name' => $item->getName(),
367365
'quantity' => (int) $item->getQtyOrdered(),
368-
'price' => number_format($item->getBasePrice(), 2, '.', ''),
369-
'discount' => number_format($item->getBaseDiscountAmount(), 2, '.', '')
366+
'price' => $helper->formatPrice($item->getBasePrice()),
367+
'discount' => $helper->formatPrice($item->getBaseDiscountAmount())
370368
];
371-
$_product = Mage::getModel('catalog/product')->load($item->getProductId());
372369
if ($_product->getAttributeText('manufacturer')) {
373370
$_item['item_brand'] = $_product->getAttributeText('manufacturer');
374371
}
375-
376-
$itemCategory = Mage::helper('googleanalytics')->getLastCategoryName($_product);
372+
$itemCategory = $helper->getLastCategoryName($_product);
377373
if ($itemCategory) {
378374
$_item['item_category'] = $itemCategory;
379375
}

app/code/core/Mage/GoogleAnalytics/Helper/Data.php

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,4 +184,13 @@ public function getLastCategoryName($product): string
184184
}
185185
return '';
186186
}
187+
188+
/**
189+
* @param int|float|string $price
190+
* @return string
191+
*/
192+
public function formatPrice($price): string
193+
{
194+
return number_format($price, 2, '.', '');
195+
}
187196
}

app/code/core/Mage/GoogleAnalytics/Model/Observer.php

Lines changed: 53 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -39,38 +39,65 @@ public function setGoogleAnalyticsOnOrderSuccessPageView(Varien_Event_Observer $
3939
}
4040

4141
/**
42-
* Add 'removed item' from cart into session for GA4 block to render event on cart view
43-
*
42+
* Process items added or removed from cart for GA4 block to render event on cart view
4443
* @param Varien_Event_Observer $observer
44+
* @return void
4545
*/
46-
public function removeItemFromCartGoogleAnalytics(Varien_Event_Observer $observer)
46+
public function processItemsAddedOrRemovedFromCart(Varien_Event_Observer $observer): void
4747
{
48-
$productRemoved = $observer->getEvent()->getQuoteItem()->getProduct();
49-
if ($productRemoved) {
50-
$_removedProducts = Mage::getSingleton('core/session')->getRemovedProductsCart() ?: [];
51-
$_removedProducts[] = $productRemoved->getId();
52-
$_removedProducts = array_unique($_removedProducts);
53-
Mage::getSingleton('core/session')->setRemovedProductsCart($_removedProducts);
48+
/** @var Mage_Sales_Model_Quote_Item $item */
49+
$item = $observer->getEvent()->getItem();
50+
if ($item->getParentItem()) {
51+
return;
5452
}
55-
}
5653

57-
/**
58-
* Add 'added item' to cart into session for GA4 block to render event on cart view
59-
*
60-
* @param Varien_Event_Observer $observer
61-
*/
62-
public function addItemToCartGoogleAnalytics(Varien_Event_Observer $observer)
63-
{
64-
$productAdded = $observer->getEvent()->getQuoteItem()->getProduct();
65-
if ($productAdded) {
66-
// Fix double add to cart for configurable products, skip child product
67-
if ($productAdded->getParentProductId()) {
68-
return;
54+
// avoid to process the same quote_item more than once
55+
// this could happen in case of double save of the same quote_item
56+
$processedProductsRegistry = Mage::registry('processed_quote_items_for_analytics') ?? new ArrayObject();
57+
if ($processedProductsRegistry->offsetExists($item->getId())) {
58+
return;
59+
}
60+
$processedProductsRegistry[$item->getId()] = true;
61+
Mage::register('processed_quote_items_for_analytics', $processedProductsRegistry, true);
62+
63+
$addedQty = 0;
64+
$removedQty = 0;
65+
if ($item->isObjectNew()) {
66+
$addedQty = $item->getQty();
67+
} elseif ($item->isDeleted()) {
68+
$removedQty = $item->getQty();
69+
} elseif ($item->hasDataChanges()) {
70+
$newQty = $item->getQty();
71+
$oldQty = $item->getOrigData('qty');
72+
if ($newQty > $oldQty) {
73+
$addedQty = $newQty - $oldQty;
74+
} elseif ($newQty < $oldQty) {
75+
$removedQty = $oldQty - $newQty;
76+
}
77+
}
78+
79+
if ($addedQty || $removedQty) {
80+
$product = $item->getProduct();
81+
$dataForAnalytics = [
82+
'id' => $product->getId(),
83+
'sku' => $product->getSku(),
84+
'name' => $product->getName(),
85+
'qty' => $addedQty ?: $removedQty,
86+
'price' => $product->getFinalPrice(),
87+
'manufacturer' => $product->getAttributeText('manufacturer') ?: '',
88+
'category' => Mage::helper('googleanalytics')->getLastCategoryName($product)
89+
];
90+
91+
$session = Mage::getSingleton('core/session');
92+
if ($addedQty) {
93+
$addedProducts = $session->getAddedProductsForAnalytics() ?: [];
94+
$addedProducts[] = $dataForAnalytics;
95+
$session->setAddedProductsForAnalytics($addedProducts);
96+
} else {
97+
$removedProducts = $session->getRemovedProductsForAnalytics() ?: [];
98+
$removedProducts[] = $dataForAnalytics;
99+
$session->setRemovedProductsForAnalytics($removedProducts);
69100
}
70-
$_addedProducts = Mage::getSingleton('core/session')->getAddedProductsCart() ?: [];
71-
$_addedProducts[] = $productAdded->getParentItem() ? $productAdded->getParentItem()->getId() : $productAdded->getId();
72-
$_addedProducts = array_unique($_addedProducts);
73-
Mage::getSingleton('core/session')->setAddedProductsCart($_addedProducts);
74101
}
75102
}
76103
}

0 commit comments

Comments
 (0)