Data Layer Implementation | OpsBlu Docs

Data Layer Implementation

Implement comprehensive data layer for OSCommerce ecommerce tracking through GTM

Overview

The data layer is a JavaScript object that holds information about your store, products, and customer interactions. GTM reads this data to fire tags with the correct information.

Benefits:

  • Centralized data management
  • Consistent tracking across platforms
  • Easier debugging
  • No hard-coded values in tags

Data Layer Structure

window.dataLayer = window.dataLayer || [];
dataLayer.push({
  'event': 'eventName',        // Trigger identifier
  'key': 'value',              // Data parameters
  'nestedObject': {            // Structured data
    'property': 'value'
  }
});

Base Data Layer Implementation

Initialize the data layer before GTM loads.

File: catalog/includes/header.php

Add BEFORE GTM code:

<?php
// Determine page type
$page_script = basename($_SERVER['PHP_SELF'], '.php');

// Map OSCommerce pages to page types
$page_type_map = array(
  'index' => 'home',
  'product_info' => 'product',
  'shopping_cart' => 'cart',
  'checkout_shipping' => 'checkout',
  'checkout_payment' => 'checkout',
  'checkout_confirmation' => 'checkout',
  'checkout_success' => 'purchase',
  'advanced_search_result' => 'search'
);

$page_type = isset($page_type_map[$page_script]) ? $page_type_map[$page_script] : 'other';

// Get user status
$user_status = tep_session_is_registered('customer_id') ? 'logged_in' : 'guest';
$user_id = tep_session_is_registered('customer_id') ? $customer_id : null;

// Get currency
$currency = isset($_SESSION['currency']) ? $_SESSION['currency'] : DEFAULT_CURRENCY;
?>

<script>
window.dataLayer = window.dataLayer || [];
dataLayer.push({
  'pageType': '<?php echo $page_type; ?>',
  'pageName': '<?php echo addslashes($page_script); ?>',
  'userStatus': '<?php echo $user_status; ?>',
  'currency': '<?php echo $currency; ?>'
  <?php if ($user_id): ?>
  ,'userId': '<?php echo $user_id; ?>'
  ,'customerId': '<?php echo $user_id; ?>'
  <?php endif; ?>
});
</script>

<!-- GTM code follows here -->

Product Page Data Layer

File: catalog/product_info.php

Add after product information is loaded:

<?php
// After $product_info is retrieved
if (tep_not_null($product_info)) {
  // Get category
  $category_query = tep_db_query("
    SELECT cd.categories_name, cd.categories_id
    FROM " . TABLE_CATEGORIES_DESCRIPTION . " cd
    INNER JOIN " . TABLE_PRODUCTS_TO_CATEGORIES . " p2c ON cd.categories_id = p2c.categories_id
    WHERE p2c.products_id = '" . (int)$product_info['products_id'] . "'
    AND cd.language_id = '" . (int)$languages_id . "'
    LIMIT 1
  ");
  $category = tep_db_fetch_array($category_query);

  // Get manufacturer/brand
  $manufacturer_query = tep_db_query("
    SELECT manufacturers_name
    FROM " . TABLE_MANUFACTURERS . " m
    INNER JOIN " . TABLE_PRODUCTS . " p ON m.manufacturers_id = p.manufacturers_id
    WHERE p.products_id = '" . (int)$product_info['products_id'] . "'
  ");
  $manufacturer = tep_db_fetch_array($manufacturer_query);

  // Calculate price
  $special_price = tep_get_products_special_price($product_info['products_id']);
  $final_price = $special_price ? $special_price : $product_info['products_price'];
?>

<script>
dataLayer.push({
  'event': 'productView',
  'ecommerce': {
    'detail': {
      'products': [{
        'id': '<?php echo $product_info['products_id']; ?>',
        'name': '<?php echo addslashes($product_info['products_name']); ?>',
        'price': <?php echo $final_price; ?>,
        'brand': '<?php echo $manufacturer ? addslashes($manufacturer['manufacturers_name']) : ''; ?>',
        'category': '<?php echo $category ? addslashes($category['categories_name']) : ''; ?>',
        'variant': ''
        <?php if ($special_price): ?>
        ,'discount': <?php echo $product_info['products_price'] - $special_price; ?>
        <?php endif; ?>
      }]
    }
  }
});
</script>
<?php
}
?>

Add to Cart Data Layer

File: catalog/product_info.php

Add JavaScript to track add to cart action:

<script>
// Add to cart tracking
document.querySelector('form[name="cart_quantity"]').addEventListener('submit', function(e) {
  var productId = '<?php echo $product_info['products_id']; ?>';
  var productName = '<?php echo addslashes($product_info['products_name']); ?>';
  var productPrice = <?php echo $final_price; ?>;
  var quantity = parseInt(document.querySelector('input[name="cart_quantity"]').value) || 1;

  dataLayer.push({
    'event': 'addToCart',
    'ecommerce': {
      'currencyCode': '<?php echo $currency; ?>',
      'add': {
        'products': [{
          'id': productId,
          'name': productName,
          'price': productPrice,
          'brand': '<?php echo $manufacturer ? addslashes($manufacturer['manufacturers_name']) : ''; ?>',
          'category': '<?php echo $category ? addslashes($category['categories_name']) : ''; ?>',
          'quantity': quantity
        }]
      }
    }
  });
});
</script>

Remove from Cart Data Layer

File: catalog/shopping_cart.php

Track cart removals:

<?php
// Detect removal action
if (isset($_GET['action']) && $_GET['action'] == 'remove_product') {
  $products_id = tep_get_prid($_GET['products_id']);

  // Get product info before removal
  if (isset($_SESSION['cart']->contents[$products_id])) {
    $product_query = tep_db_query("
      SELECT pd.products_name, p.products_price
      FROM " . TABLE_PRODUCTS . " p
      INNER JOIN " . TABLE_PRODUCTS_DESCRIPTION . " pd ON p.products_id = pd.products_id
      WHERE p.products_id = '" . (int)$products_id . "'
      AND pd.language_id = '" . (int)$languages_id . "'
    ");
    $product = tep_db_fetch_array($product_query);
    $quantity = $_SESSION['cart']->contents[$products_id]['qty'];
?>
<script>
dataLayer.push({
  'event': 'removeFromCart',
  'ecommerce': {
    'remove': {
      'products': [{
        'id': '<?php echo $products_id; ?>',
        'name': '<?php echo addslashes($product['products_name']); ?>',
        'price': <?php echo $product['products_price']; ?>,
        'quantity': <?php echo $quantity; ?>
      }]
    }
  }
});
</script>
<?php
  }
}
?>

Cart View Data Layer

File: catalog/shopping_cart.php

Push cart contents to data layer:

<?php
// After cart is loaded
if (isset($_SESSION['cart']) && $_SESSION['cart']->count_contents() > 0) {
  $products = $_SESSION['cart']->get_products();
  $cart_items = array();

  foreach ($products as $product) {
    $cart_items[] = sprintf(
      '{
        "id": "%s",
        "name": "%s",
        "price": %s,
        "quantity": %d
      }',
      $product['id'],
      addslashes($product['name']),
      $product['final_price'],
      $product['quantity']
    );
  }
?>
<script>
dataLayer.push({
  'event': 'cartView',
  'ecommerce': {
    'currencyCode': '<?php echo $_SESSION['currency']; ?>',
    'cart': {
      'products': [<?php echo implode(',', $cart_items); ?>]
    }
  }
});
</script>
<?php
}
?>

Checkout Initiation Data Layer

File: catalog/checkout_shipping.php

<?php
// On first checkout page
if (isset($_SESSION['cart']) && $_SESSION['cart']->count_contents() > 0) {
  $products = $_SESSION['cart']->get_products();
  $checkout_items = array();

  foreach ($products as $product) {
    $checkout_items[] = sprintf(
      '{
        "id": "%s",
        "name": "%s",
        "price": %s,
        "quantity": %d
      }',
      $product['id'],
      addslashes($product['name']),
      $product['final_price'],
      $product['quantity']
    );
  }
?>
<script>
dataLayer.push({
  'event': 'checkout',
  'ecommerce': {
    'checkout': {
      'actionField': {
        'step': 1,
        'option': 'Shipping Information'
      },
      'products': [<?php echo implode(',', $checkout_items); ?>]
    }
  }
});
</script>
<?php
}
?>

Checkout Step Tracking

File: catalog/checkout_payment.php

<script>
dataLayer.push({
  'event': 'checkoutStep',
  'ecommerce': {
    'checkout': {
      'actionField': {
        'step': 2,
        'option': 'Payment Information'
      }
    }
  }
});
</script>

File: catalog/checkout_confirmation.php

<script>
dataLayer.push({
  'event': 'checkoutStep',
  'ecommerce': {
    'checkout': {
      'actionField': {
        'step': 3,
        'option': 'Order Confirmation'
      }
    }
  }
});
</script>

Purchase Data Layer

File: catalog/checkout_process.php

Store transaction in session after order creation:

<?php
// After order is inserted ($insert_id contains order_id)
if ($insert_id) {
  // Get order details
  $order_query = tep_db_query("SELECT * FROM " . TABLE_ORDERS . " WHERE orders_id = '" . (int)$insert_id . "'");
  $order = tep_db_fetch_array($order_query);

  // Get order products
  $products_query = tep_db_query("SELECT * FROM " . TABLE_ORDERS_PRODUCTS . " WHERE orders_id = '" . (int)$insert_id . "'");
  $order_products = array();

  while ($product = tep_db_fetch_array($products_query)) {
    $order_products[] = array(
      'id' => $product['products_id'],
      'name' => $product['products_name'],
      'price' => $product['final_price'],
      'quantity' => $product['products_quantity']
    );
  }

  // Get order totals
  $totals_query = tep_db_query("SELECT * FROM " . TABLE_ORDERS_TOTAL . " WHERE orders_id = '" . (int)$insert_id . "'");
  $tax = 0;
  $shipping = 0;
  $discount = 0;

  while ($total = tep_db_fetch_array($totals_query)) {
    if ($total['class'] == 'ot_tax') $tax = $total['value'];
    if ($total['class'] == 'ot_shipping') $shipping = $total['value'];
    if ($total['class'] == 'ot_coupon') $discount = abs($total['value']);
  }

  // Store in session for success page
  $_SESSION['gtm_purchase_data'] = array(
    'transactionId' => $insert_id,
    'transactionTotal' => $order['order_total'],
    'transactionTax' => $tax,
    'transactionShipping' => $shipping,
    'transactionDiscount' => $discount,
    'currency' => $order['currency'],
    'products' => $order_products
  );
}
?>

File: catalog/checkout_success.php

Push to data layer on success page:

<?php
// Retrieve purchase data from session
if (isset($_SESSION['gtm_purchase_data'])) {
  $purchase = $_SESSION['gtm_purchase_data'];
  $products_json = json_encode($purchase['products']);
?>
<script>
dataLayer.push({
  'event': 'purchase',
  'ecommerce': {
    'purchase': {
      'actionField': {
        'id': '<?php echo $purchase['transactionId']; ?>',
        'revenue': <?php echo $purchase['transactionTotal']; ?>,
        'tax': <?php echo $purchase['transactionTax']; ?>,
        'shipping': <?php echo $purchase['transactionShipping']; ?>
        <?php if ($purchase['transactionDiscount'] > 0): ?>
        ,'coupon': 'DISCOUNT'
        <?php endif; ?>
      },
      'products': <?php echo $products_json; ?>
    }
  }
});
</script>
<?php
  // Clear to prevent duplicate tracking
  unset($_SESSION['gtm_purchase_data']);
}
?>

Product List Impressions

File: catalog/index.php or category pages

<?php
// After product listing is loaded
if (isset($listing) && is_array($listing)) {
  $impressions = array();

  foreach ($listing as $index => $product) {
    $impressions[] = sprintf(
      '{
        "id": "%s",
        "name": "%s",
        "price": %s,
        "list": "Category - %s",
        "position": %d
      }',
      $product['products_id'],
      addslashes($product['products_name']),
      $product['products_price'],
      addslashes($category_name),
      $index + 1
    );
  }
?>
<script>
dataLayer.push({
  'event': 'productImpressions',
  'ecommerce': {
    'impressions': [<?php echo implode(',', $impressions); ?>]
  }
});
</script>
<?php
}
?>

Product Click Tracking

Add click tracking to product links:

<a href="product_info.php?products_id=<?php echo $product['products_id']; ?>" echo $product['products_id']; ?>', '<?php echo addslashes($product['products_name']); ?>', '<?php echo $category_name; ?>', <?php echo $index; ?>)">
  <?php echo $product['products_name']; ?>
</a>

<script>
function trackProductClick(id, name, category, position) {
  dataLayer.push({
    'event': 'productClick',
    'ecommerce': {
      'click': {
        'actionField': {'list': 'Category - ' + category},
        'products': [{
          'id': id,
          'name': name,
          'position': position
        }]
      }
    }
  });
}
</script>

Custom Events

Newsletter Signup

File: catalog/newsletter.php

<?php
if ($subscription_successful) {
?>
<script>
dataLayer.push({
  'event': 'newsletterSignup',
  'eventCategory': 'Email',
  'eventAction': 'Newsletter Signup',
  'eventLabel': 'Footer Form'
});
</script>
<?php
}
?>

Search Events

File: catalog/advanced_search_result.php

<?php
if (isset($_GET['keywords'])) {
  $search_term = tep_output_string($_GET['keywords']);
  $result_count = tep_db_num_rows($listing_query);
?>
<script>
dataLayer.push({
  'event': 'search',
  'searchTerm': '<?php echo addslashes($search_term); ?>',
  'searchResults': <?php echo $result_count; ?>
});
</script>
<?php
}
?>

User Login

File: catalog/login.php

<?php
if (tep_session_is_registered('customer_id')) {
?>
<script>
dataLayer.push({
  'event': 'login',
  'userId': '<?php echo $customer_id; ?>',
  'loginMethod': 'Email'
});
</script>
<?php
}
?>

GTM Configuration for Data Layer Events

Create Triggers in GTM

Trigger 1: Add to Cart

  1. Triggers > New
  2. Custom Event
  3. Event name: addToCart
  4. This trigger fires on: All Custom Events
  5. Save

Trigger 2: Purchase

  1. Triggers > New
  2. Custom Event
  3. Event name: purchase
  4. Save

Create Variables in GTM

Variable 1: Transaction ID

  1. Variables > New
  2. Data Layer Variable
  3. Data Layer Variable Name: ecommerce.purchase.actionField.id
  4. Name: DLV - Transaction ID
  5. Save

Variable 2: Transaction Revenue

  1. Variables > New
  2. Data Layer Variable
  3. Data Layer Variable Name: ecommerce.purchase.actionField.revenue
  4. Name: DLV - Transaction Revenue
  5. Save

Create Tags Using Data Layer

GA4 Purchase Event Tag

  1. Tags > New
  2. Google Analytics: GA4 Event
  3. Configuration Tag: Your GA4 Config
  4. Event Name: purchase
  5. Event Parameters:
    • transaction_id: \{\{DLV - Transaction ID\}\}
    • value: \{\{DLV - Transaction Revenue\}\}
    • currency: \{\{DLV - Currency\}\}
  6. Triggering: purchase event
  7. Save

Debug Data Layer

Console Monitoring

<script>
// Monitor all dataLayer pushes
(function() {
  var originalPush = dataLayer.push;
  dataLayer.push = function() {
    console.log('dataLayer.push:', arguments[0]);
    return originalPush.apply(dataLayer, arguments);
  };
})();
</script>

View Current Data Layer

// In browser console
console.table(dataLayer);

// View specific event
dataLayer.filter(item => item.event === 'purchase');

Best Practices

1. Initialize Before GTM

Always initialize dataLayer before GTM container loads.

2. Use Consistent Event Names

// GOOD - Consistent naming
'event': 'addToCart'
'event': 'removeFromCart'
'event': 'productView'

// BAD - Inconsistent
'event': 'add_to_cart'
'event': 'removeCart'
'event': 'ViewProduct'

3. Clear Ecommerce Object

Clear previous ecommerce data before pushing new:

dataLayer.push({
  'ecommerce': null  // Clear previous ecommerce
});
dataLayer.push({
  'event': 'addToCart',
  'ecommerce': { /* new data */ }
});

4. Validate Data Types

// Ensure correct types
'id': '123',              // String
'price': 29.99,           // Number
'quantity': 1,            // Number
'revenue': 149.95         // Number

Testing Checklist

  • dataLayer initializes before GTM
  • Product views push correct data
  • Add to cart events fire
  • Remove from cart tracked
  • Checkout steps tracked in sequence
  • Purchase data complete and accurate
  • User ID populated for logged-in users
  • Currency code is correct
  • No JavaScript errors
  • GTM Preview shows events

Next Steps

Additional Resources