OpenCart Data Layer Implementation | OpsBlu Docs

OpenCart Data Layer Implementation

Build a comprehensive data layer for Google Tag Manager integration with OpenCart ecommerce tracking

Overview

The data layer is a JavaScript object that stores structured data about:

  • Page context (type, category, product)
  • User information (logged in status, customer ID)
  • Product data (IDs, names, prices, categories)
  • Transaction details (order ID, revenue, items)
  • Custom events and interactions

Benefits:

  • Tag independence: Change tags without modifying data layer
  • Consistent data: Single source of truth for all tags
  • Debugging: Easier to troubleshoot tracking issues
  • Flexibility: Add new tags without code changes

Data Layer Structure

Base Implementation

Initialize the data layer before GTM loads:

File: catalog/view/theme/[your-theme]/template/common/header.twig

<head>
<script>
// Initialize dataLayer before GTM
window.dataLayer = window.dataLayer || [];

// Push base page data
dataLayer.push({
    'pageType': '{{ page_type|default('other') }}',
    'pageName': '{{ page_name|default(heading_title) }}',
    'language': '{{ language }}',
    'currency': '{{ currency }}',
    'customerId': '{{ customer_id|default('') }}',
    'customerGroup': '{{ customer_group|default('Guest') }}',
    'customerEmail': '{{ customer_email_hash|default('') }}'
});
</script>

<!-- GTM Container Code -->
<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','GTM-XXXXXXX');</script>
<!-- End GTM -->
</head>

Controller-Side Data Preparation

File: catalog/controller/common/header.php

Add to index() method:

public function index() {
    // ... existing code ...

    // Determine page type
    $data['page_type'] = $this->getPageType();
    $data['page_name'] = $this->document->getTitle();

    // Customer data
    if ($this->customer->isLogged()) {
        $data['customer_id'] = $this->customer->getId();
        $data['customer_group'] = $this->customer->getGroupId();
        $data['customer_email_hash'] = hash('sha256', strtolower($this->customer->getEmail()));
    } else {
        $data['customer_id'] = '';
        $data['customer_group'] = 'Guest';
        $data['customer_email_hash'] = '';
    }

    // Store info
    $data['language'] = $this->session->data['language'];
    $data['currency'] = $this->session->data['currency'];

    // ... rest of existing code ...

    return $this->load->view('common/header', $data);
}

private function getPageType() {
    // Detect page type from route
    $route = isset($this->request->get['route']) ? $this->request->get['route'] : 'common/home';

    if ($route == 'common/home') {
        return 'home';
    } elseif (strpos($route, 'product/product') !== false) {
        return 'product';
    } elseif (strpos($route, 'product/category') !== false) {
        return 'category';
    } elseif (strpos($route, 'product/search') !== false) {
        return 'search';
    } elseif (strpos($route, 'checkout/cart') !== false) {
        return 'cart';
    } elseif (strpos($route, 'checkout/checkout') !== false) {
        return 'checkout';
    } elseif (strpos($route, 'checkout/success') !== false) {
        return 'purchase';
    } elseif (strpos($route, 'account/') !== false) {
        return 'account';
    } else {
        return 'other';
    }
}

Page-Specific Data Layers

Home Page

File: catalog/controller/common/home.php

public function index() {
    // ... existing code ...

    // Data layer for homepage
    $data['ecommerce_data'] = array(
        'pageType' => 'home',
        'impressions' => array()
    );

    // Get featured products if available
    if (isset($data['products'])) {
        foreach ($data['products'] as $index => $product) {
            $data['ecommerce_data']['impressions'][] = array(
                'id' => $product['product_id'],
                'name' => $product['name'],
                'price' => $this->currency->format($product['price'], $this->session->data['currency'], '', false),
                'list' => 'Homepage Featured',
                'position' => $index + 1
            );
        }
    }

    // ... rest of code ...
}

File: catalog/view/theme/[your-theme]/template/common/home.twig

{% if ecommerce_data %}
<script>
dataLayer.push({
    'event': 'productImpressions',
    'ecommerce': {
        'currencyCode': '{{ currency }}',
        'impressions': {{ ecommerce_data.impressions|json_encode|raw }}
    }
});
</script>
{% endif %}

Category Page

File: catalog/controller/product/category.php

public function index() {
    // ... existing code after products are loaded ...

    // Data layer for category page
    $data['ecommerce_data'] = array(
        'pageType' => 'category',
        'categoryName' => $this->document->getTitle(),
        'categoryId' => $category_id,
        'impressions' => array()
    );

    foreach ($data['products'] as $index => $product) {
        $data['ecommerce_data']['impressions'][] = array(
            'id' => $product['product_id'],
            'name' => $product['name'],
            'price' => $this->currency->format($product['price'], $this->session->data['currency'], '', false),
            'category' => $this->document->getTitle(),
            'list' => 'Category: ' . $this->document->getTitle(),
            'position' => $index + 1
        );
    }

    // ... rest of code ...
}

File: catalog/view/theme/[your-theme]/template/product/category.twig

{% if ecommerce_data %}
<script>
dataLayer.push({
    'event': 'productImpressions',
    'ecommerce': {
        'currencyCode': '{{ currency }}',
        'impressions': {{ ecommerce_data.impressions|json_encode|raw }}
    }
});

// Track product clicks
$(document).on('click', '.product-thumb a', function() {
    var productIndex = $(this).closest('.product-thumb').index();
    var impressions = {{ ecommerce_data.impressions|json_encode|raw }};

    if (impressions[productIndex]) {
        dataLayer.push({
            'event': 'productClick',
            'ecommerce': {
                'click': {
                    'actionField': {'list': '{{ ecommerce_data.categoryName }}'},
                    'products': [impressions[productIndex]]
                }
            }
        });
    }
});
</script>
{% endif %}

Product Detail Page

File: catalog/controller/product/product.php

public function index() {
    // ... existing code after product_info is loaded ...

    if ($product_info) {
        // Get category
        $categories = $this->model_catalog_product->getCategories($product_id);
        $category_name = '';

        if ($categories) {
            $this->load->model('catalog/category');
            $category_info = $this->model_catalog_category->getCategory($categories[0]['category_id']);
            $category_name = $category_info ? $category_info['name'] : '';
        }

        // Get brand/manufacturer
        $manufacturer_name = '';
        if ($product_info['manufacturer_id']) {
            $manufacturer_info = $this->model_catalog_product->getManufacturer($product_info['manufacturer_id']);
            $manufacturer_name = $manufacturer_info ? $manufacturer_info['name'] : '';
        }

        // Format price
        $price = $this->currency->format($product_info['price'], $this->session->data['currency'], '', false);

        $data['ecommerce_data'] = array(
            'pageType' => 'product',
            'product' => array(
                'id' => $product_info['product_id'],
                'name' => $product_info['name'],
                'price' => $price,
                'brand' => $manufacturer_name,
                'category' => $category_name,
                'variant' => '',  // Add option selection if needed
                'quantity' => 1
            )
        );
    }

    // ... rest of code ...
}

File: catalog/view/theme/[your-theme]/template/product/product.twig

{% if ecommerce_data %}
<script>
// Product detail view
dataLayer.push({
    'event': 'productDetail',
    'ecommerce': {
        'currencyCode': '{{ currency }}',
        'detail': {
            'products': [{{ ecommerce_data.product|json_encode|raw }}]
        }
    }
});

// Add to cart event
$('#button-cart').on('click', function() {
    var quantity = parseInt($('input[name="quantity"]').val()) || 1;
    var product = {{ ecommerce_data.product|json_encode|raw }};
    product.quantity = quantity;

    dataLayer.push({
        'event': 'addToCart',
        'ecommerce': {
            'currencyCode': '{{ currency }}',
            'add': {
                'products': [product]
            }
        }
    });
});
</script>
{% endif %}

Cart Page

File: catalog/controller/checkout/cart.php

public function index() {
    // ... existing code after cart products are loaded ...

    $data['ecommerce_data'] = array(
        'pageType' => 'cart',
        'products' => array(),
        'cartTotal' => 0
    );

    foreach ($this->cart->getProducts() as $product) {
        $price = $this->currency->format($product['price'], $this->session->data['currency'], '', false);

        $data['ecommerce_data']['products'][] = array(
            'id' => $product['product_id'],
            'name' => $product['name'],
            'price' => $price,
            'quantity' => $product['quantity']
        );

        $data['ecommerce_data']['cartTotal'] += $price * $product['quantity'];
    }

    // ... rest of code ...
}

File: catalog/view/theme/[your-theme]/template/checkout/cart.twig

{% if ecommerce_data %}
<script>
// Cart page view
dataLayer.push({
    'event': 'cartView',
    'ecommerce': {
        'currencyCode': '{{ currency }}',
        'cartValue': {{ ecommerce_data.cartTotal }},
        'products': {{ ecommerce_data.products|json_encode|raw }}
    }
});

// Remove from cart
$(document).on('click', '.btn-danger[onclick*="cart.remove"]', function() {
    var productId = $(this).attr('onclick').match(/cart\.remove\('(\d+)'\)/)[1];
    var products = {{ ecommerce_data.products|json_encode|raw }};
    var removedProduct = products.find(p => p.id == productId);

    if (removedProduct) {
        dataLayer.push({
            'event': 'removeFromCart',
            'ecommerce': {
                'remove': {
                    'products': [removedProduct]
                }
            }
        });
    }
});
</script>
{% endif %}

Checkout Page

File: catalog/controller/checkout/checkout.php

public function index() {
    // ... existing code ...

    // Get cart products for data layer
    $data['ecommerce_data'] = array(
        'pageType' => 'checkout',
        'step' => 1,
        'products' => array(),
        'checkoutValue' => 0
    );

    foreach ($this->cart->getProducts() as $product) {
        $price = $this->currency->format($product['price'], $this->session->data['currency'], '', false);

        $data['ecommerce_data']['products'][] = array(
            'id' => $product['product_id'],
            'name' => $product['name'],
            'price' => $price,
            'quantity' => $product['quantity']
        );

        $data['ecommerce_data']['checkoutValue'] += $price * $product['quantity'];
    }

    // ... rest of code ...
}

File: catalog/view/theme/[your-theme]/template/checkout/checkout.twig

{% if ecommerce_data %}
<script>
// Checkout initiation
dataLayer.push({
    'event': 'checkout',
    'ecommerce': {
        'currencyCode': '{{ currency }}',
        'checkout': {
            'actionField': {'step': {{ ecommerce_data.step }}},
            'products': {{ ecommerce_data.products|json_encode|raw }}
        }
    }
});

// Track checkout steps
$(document).on('click', '#button-payment-address', function() {
    dataLayer.push({
        'event': 'checkoutStep',
        'ecommerce': {
            'checkout': {
                'actionField': {'step': 2, 'option': 'Payment Address'}
            }
        }
    });
});

$(document).on('click', '#button-shipping-address', function() {
    dataLayer.push({
        'event': 'checkoutStep',
        'ecommerce': {
            'checkout': {
                'actionField': {'step': 3, 'option': 'Shipping Address'}
            }
        }
    });
});

$(document).on('click', '#button-shipping-method', function() {
    var shippingMethod = $('input[name="shipping_method"]:checked').val();

    dataLayer.push({
        'event': 'checkoutStep',
        'ecommerce': {
            'checkout': {
                'actionField': {'step': 4, 'option': shippingMethod}
            }
        }
    });
});

$(document).on('click', '#button-payment-method', function() {
    var paymentMethod = $('input[name="payment_method"]:checked').val();

    dataLayer.push({
        'event': 'checkoutStep',
        'ecommerce': {
            'checkout': {
                'actionField': {'step': 5, 'option': paymentMethod}
            }
        }
    });
});
</script>
{% endif %}

Purchase Confirmation

File: catalog/controller/checkout/success.php

public function index() {
    // ... existing code ...

    $order_id = isset($this->session->data['order_id']) ? $this->session->data['order_id'] : 0;

    if ($order_id && !isset($this->session->data['gtm_tracked_' . $order_id])) {
        $this->load->model('checkout/order');
        $order_info = $this->model_checkout_order->getOrder($order_id);

        if ($order_info) {
            $order_products = $this->model_checkout_order->getOrderProducts($order_id);

            $products = array();

            foreach ($order_products as $product) {
                $price = $this->currency->format($product['price'], $order_info['currency_code'], '', false);

                $products[] = array(
                    'id' => $product['product_id'],
                    'name' => $product['name'],
                    'price' => $price,
                    'quantity' => $product['quantity'],
                    'category' => '',  // Add if available
                    'variant' => ''    // Add if options available
                );
            }

            // Calculate tax and shipping
            $tax = 0;
            $shipping = 0;

            $order_totals = $this->model_checkout_order->getOrderTotals($order_id);

            foreach ($order_totals as $total) {
                if ($total['code'] == 'tax') {
                    $tax += $this->currency->format($total['value'], $order_info['currency_code'], '', false);
                } elseif ($total['code'] == 'shipping') {
                    $shipping = $this->currency->format($total['value'], $order_info['currency_code'], '', false);
                }
            }

            $data['ecommerce_data'] = array(
                'transactionId' => $order_id,
                'affiliation' => $this->config->get('config_name'),
                'revenue' => $this->currency->format($order_info['total'], $order_info['currency_code'], '', false),
                'tax' => $tax,
                'shipping' => $shipping,
                'currency' => $order_info['currency_code'],
                'products' => $products
            );

            // Mark as tracked
            $this->session->data['gtm_tracked_' . $order_id] = true;
        }

        // Clear order_id from session
        unset($this->session->data['order_id']);
    }

    // ... rest of code ...
}

File: catalog/view/theme/[your-theme]/template/common/success.twig

{% if ecommerce_data %}
<script>
// Purchase event
dataLayer.push({
    'event': 'purchase',
    'ecommerce': {
        'purchase': {
            'actionField': {
                'id': '{{ ecommerce_data.transactionId }}',
                'affiliation': '{{ ecommerce_data.affiliation }}',
                'revenue': {{ ecommerce_data.revenue }},
                'tax': {{ ecommerce_data.tax }},
                'shipping': {{ ecommerce_data.shipping }}
            },
            'products': {{ ecommerce_data.products|json_encode|raw }}
        }
    }
});
</script>
{% endif %}

Custom Events

User Interactions

Newsletter Signup:

<script>
$(document).on('submit', '#newsletter-form', function() {
    dataLayer.push({
        'event': 'newsletterSignup',
        'formLocation': 'footer',
        'emailProvided': true
    });
});
</script>

Contact Form:

<script>
$(document).on('submit', '#contact-form', function() {
    dataLayer.push({
        'event': 'contactFormSubmit',
        'formType': 'contact',
        'enquiryType': $('select[name="enquiry"]').val()
    });
});
</script>

Product Reviews:

<script>
$(document).on('submit', '#form-review', function() {
    var rating = $('input[name="rating"]:checked').val();

    dataLayer.push({
        'event': 'productReview',
        'productId': '{{ product_id }}',
        'productName': '{{ product_name }}',
        'rating': rating
    });
});
</script>

Search Tracking

<script>
$(document).on('submit', '#search input[name="search"]').closest('form'), function() {
    var searchTerm = $(this).find('input[name="search"]').val();

    dataLayer.push({
        'event': 'siteSearch',
        'searchTerm': searchTerm
    });
});
</script>

Enhanced Data Layer with User Properties

File: catalog/view/theme/[your-theme]/template/common/header.twig

<script>
window.dataLayer = window.dataLayer || [];

dataLayer.push({
    // Page data
    'pageType': '{{ page_type }}',
    'pageTitle': '{{ page_name }}',
    'pageUrl': window.location.href,
    'pageReferrer': document.referrer,

    // Store data
    'storeName': '{{ config_name }}',
    'storeId': '{{ config_store_id }}',
    'language': '{{ language }}',
    'currency': '{{ currency }}',

    // User data
    'userId': '{{ customer_id|default('') }}',
    'userType': '{{ customer_id ? "logged_in" : "guest" }}',
    'customerGroup': '{{ customer_group|default('Guest') }}',

    // Session data
    'sessionId': '{{ session_id|default('') }}',

    // Cart data
    'cartValue': {{ cart_total|default(0) }},
    'cartItemCount': {{ cart_item_count|default(0) }}
});
</script>

Debugging Data Layer

Console Inspection

// View entire dataLayer
console.table(dataLayer);

// View latest push
console.log(dataLayer[dataLayer.length - 1]);

// Filter specific events
dataLayer.filter(item => item.event === 'addToCart');

GTM Preview Mode

  1. In GTM, click Preview
  2. Enter your store URL
  3. Browse your store
  4. Check Data Layer tab in debugger
  5. Verify variables are populated correctly

Data Layer Monitor Extension

Install dataLayer Inspector+

Shows real-time dataLayer pushes in browser DevTools.

Best Practices

1. Consistent Naming

Use camelCase for all variables:

{
    'productId': '123',      // Good
    'product_id': '123',     // Avoid
    'ProductId': '123'       // Avoid
}

2. Data Types

Ensure correct data types:

{
    'productId': '123',          // String
    'productPrice': 29.99,       // Number (not "29.99")
    'inStock': true,             // Boolean (not "true")
    'categories': ['cat1', 'cat2'] // Array
}

3. Clear Before Push

For ecommerce events, clear previous data:

dataLayer.push({
    'ecommerce': null  // Clear previous ecommerce object
});

dataLayer.push({
    'event': 'addToCart',
    'ecommerce': {
        // Fresh data
    }
});

4. Avoid PII

Never send personally identifiable information:

// Bad
'customerEmail': 'user@example.com'

// Good
'customerEmail': hash('user@example.com')  // Hashed

Next Steps

Additional Resources