CS-Cart Analytics: Smarty Templates, Hook System, | OpsBlu Docs

CS-Cart Analytics: Smarty Templates, Hook System,

Implement analytics on CS-Cart stores. Covers Smarty template script injection, PHP hook-based data layers, multi-vendor marketplace tracking, and GA4...

Analytics Architecture on CS-Cart

CS-Cart is a self-hosted PHP ecommerce platform that uses Smarty templates for rendering and a hook system for extending functionality. Analytics tracking integrates through three layers:

  • Smarty templates (.tpl files) in the theme directory control where scripts are injected. The index.tpl wrapper and block templates provide global and page-type-specific injection points
  • PHP hooks fire at key points in the request lifecycle (dispatch_before_display, checkout_place_order) and let add-ons prepare analytics data server-side before templates render
  • CS-Cart add-ons package tracking integrations as installable modules. The Google Analytics add-on ships built-in, but custom add-ons provide more control over data layer structure and event timing
  • Multi-vendor mode (CS-Cart Multi-Vendor) adds vendor-level data to orders, products, and categories that can be pushed to the data layer for marketplace analytics

CS-Cart uses a controller-based routing system where the dispatch parameter determines which page renders. The dispatch value (products.view, checkout.checkout, orders.details) maps directly to the analytics page type.


Installing Tracking Scripts

Via Smarty Templates

Add GTM to the main layout template:

{* design/themes/your_theme/templates/common/mainbox.tpl *}
{* Add before the closing </head> tag *}
<script>(function(w,d,s,l,i){lbrace}w[l]=w[l]||[];w[l].push({lbrace}'gtm.start':
new Date().getTime(),event:'gtm.js'{rbrace});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);
{rbrace})(window,document,'script','dataLayer','GTM-XXXXXX');</script>

Note: Smarty uses { and } as delimiters, so JavaScript curly braces must be escaped with {lbrace} and {rbrace}, or wrapped in {literal}...{/literal} tags:

{literal}
<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-XXXXXX');</script>
{/literal}

Via CS-Cart Add-on

Create a custom add-on for cleaner analytics integration:

// app/addons/my_analytics/func.php
function fn_my_analytics_dispatch_before_display() {
    $dispatch = $_REQUEST['dispatch'] ?? 'index.index';
    $page_type = explode('.', $dispatch)[0];

    Tygh\Registry::set('runtime.my_analytics.page_type', $page_type);
    Tygh\Registry::set('runtime.my_analytics.dispatch', $dispatch);
}
<!-- app/addons/my_analytics/addon.xml -->
<addon scheme="3.0">
    <id>my_analytics</id>
    <name>Analytics Integration</name>
    <version>1.0</version>
    <priority>100</priority>
</addon>

Data Layer Setup

Build the data layer from CS-Cart's Smarty template variables. Product, category, and cart data is available in the template context:

Product Page

{* design/themes/your_theme/templates/views/products/view.tpl *}
{if $product}
{literal}
<script>
  window.dataLayer = window.dataLayer || [];
  dataLayer.push({
    'event': 'view_item',
    'ecommerce': {
      'items': [{
        'item_id': '{/literal}{$product.product_code}{literal}',
        'item_name': '{/literal}{$product.product|escape:"javascript"}{literal}',
        'price': {/literal}{$product.price}{literal},
        'item_brand': '{/literal}{$product.header_features.brand.value|default:""}{literal}',
        'item_category': '{/literal}{$product.main_category_name|escape:"javascript"|default:""}{literal}'
      }]
    }
  });
</script>
{/literal}
{/if}

Category Page

{* design/themes/your_theme/templates/views/categories/view.tpl *}
{if $category}
{literal}
<script>
  window.dataLayer = window.dataLayer || [];
  var items = [];
  {/literal}
  {foreach from=$products item=product name=products}
    items.push({lbrace}
      'item_id': '{$product.product_code}',
      'item_name': '{$product.product|escape:"javascript"}',
      'price': {$product.price},
      'item_list_name': '{$category.category|escape:"javascript"}',
      'index': {$smarty.foreach.products.index}
    {rbrace});
  {/foreach}
  {literal}
  dataLayer.push({
    'event': 'view_item_list',
    'ecommerce': {
      'item_list_name': '{/literal}{$category.category|escape:"javascript"}{literal}',
      'items': items
    }
  });
</script>
{/literal}
{/if}

Ecommerce Event Tracking

Add to Cart

CS-Cart uses AJAX for add-to-cart actions. Hook into the cart update via a PHP hook:

// app/addons/my_analytics/func.php
function fn_my_analytics_add_to_cart(&$cart, $product_data, $auth) {
    foreach ($product_data as $key => $item) {
        $product = fn_get_product_data($item['product_id'], $auth);
        Tygh\Registry::set('runtime.analytics_events.' . $key, [
            'event' => 'add_to_cart',
            'item_id' => $product['product_code'],
            'item_name' => $product['product'],
            'price' => $product['price'],
            'quantity' => $item['amount'],
        ]);
    }
}

Then output queued events in the template:

{* In mainbox.tpl or a hook template *}
{if $runtime.analytics_events}
{literal}<script>
  window.dataLayer = window.dataLayer || [];
{/literal}
{foreach from=$runtime.analytics_events item=evt}
  dataLayer.push({lbrace}
    'event': '{$evt.event}',
    'ecommerce': {lbrace}
      'items': [{lbrace}
        'item_id': '{$evt.item_id}',
        'item_name': '{$evt.item_name|escape:"javascript"}',
        'price': {$evt.price},
        'quantity': {$evt.quantity}
      {rbrace}]
    {rbrace}
  {rbrace});
{/foreach}
{literal}</script>{/literal}
{/if}

Purchase Event

Track completed orders on the order confirmation page:

// Hook: checkout_place_order (in func.php)
function fn_my_analytics_checkout_place_order(&$cart, &$auth, $action, &$order_id) {
    if ($order_id) {
        $order = fn_get_order_info($order_id);
        $items = [];
        foreach ($order['products'] as $p) {
            $items[] = [
                'item_id' => $p['product_code'],
                'item_name' => $p['product'],
                'price' => $p['price'],
                'quantity' => $p['amount'],
            ];
        }

        $_SESSION['analytics_purchase'] = [
            'transaction_id' => $order_id,
            'value' => $order['total'],
            'currency' => $order['secondary_currency'] ?? 'USD',
            'tax' => $order['tax_subtotal'],
            'shipping' => $order['shipping_cost'],
            'items' => $items,
        ];
    }
}

Multi-Vendor Analytics

CS-Cart Multi-Vendor adds vendor data to every product and order. Include vendor attribution in the data layer for marketplace analytics:

{if $product.company_id}
{literal}<script>
  window.dataLayer = window.dataLayer || [];
  dataLayer.push({
    'vendor_id': '{/literal}{$product.company_id}{literal}',
    'vendor_name': '{/literal}{$product.company_name|escape:"javascript"}{literal}'
  });
</script>{/literal}
{/if}

For order confirmation, loop through order items and include vendor data per item to track marketplace commission and vendor performance:

// In the purchase event handler
foreach ($order['products'] as $p) {
    $items[] = [
        'item_id' => $p['product_code'],
        'item_name' => $p['product'],
        'price' => $p['price'],
        'quantity' => $p['amount'],
        'item_brand' => fn_get_company_name($p['company_id']), // vendor name
    ];
}

Common Errors

Error Cause Fix
Smarty delimiters break JavaScript { and } parsed as Smarty tags Wrap JS in {literal}...{/literal} or use {lbrace}/{rbrace}
Product data empty in template Accessing variables outside the correct template scope Check which dispatch renders the template; product data is only in products.view
Add-on hooks not firing Hook function name does not follow naming convention Function must be fn_{addon_id}_{hook_name} exactly
Purchase event fires twice Order confirmation page reloaded or back button pressed Store a processed flag in the session and check before pushing
Multi-vendor data missing Standard CS-Cart edition does not include vendor fields Vendor data is only available in CS-Cart Multi-Vendor edition
Cached pages show stale data layer CS-Cart full-page cache serves old template output Disable caching on analytics-critical pages or use client-side API calls
Theme update overwrites templates Custom templates in the theme directory replaced Use template hooks or add-on templates that override without modifying core files
AJAX cart updates not tracked Server-side hook fires but page does not refresh to show the script Use JavaScript to listen for AJAX cart responses and push events client-side

Performance Considerations

  • Smarty compilation: Smarty compiles templates to PHP on first load. Adding analytics code to heavily nested templates increases compilation time. Keep analytics scripts in the main layout template
  • Full-page cache: CS-Cart's built-in caching serves pre-rendered HTML. Data layer values baked into templates remain static until cache invalidation. Use no_cache Smarty tags for dynamic analytics data
  • Add-on loading order: CS-Cart loads add-ons by priority value. Set your analytics add-on to a high priority number (100+) so it runs after all product and cart data is prepared
  • AJAX optimization: CS-Cart uses extensive AJAX for cart, wishlist, and comparison features. Track these interactions with JavaScript event listeners on the client side rather than server-side hooks that require page reloads
  • Database queries: Avoid heavy database queries in analytics hooks. Use data already available in the $cart or $product objects rather than calling fn_get_product_data() for every event