eg-mohamed/laravel-ga4-events

A Laravel package to push events to Google Tag Manager dataLayer from PHP, Livewire, and JavaScript.

Maintainers

Package info

github.com/EG-Mohamed/Laravel-GTM-Events

Homepage

pkg:composer/eg-mohamed/laravel-ga4-events

Fund package maintenance!

MohamedSaid

Statistics

Installs: 16

Dependents: 0

Suggesters: 0

Stars: 0

Open Issues: 0

1.6.2 2026-03-31 13:12 UTC

This package is auto-updated.

Last update: 2026-03-31 13:14:47 UTC


README

Latest Version on Packagist GitHub Tests Action Status GitHub Code Style Action Status Total Downloads

Push events to Google Tag Manager's dataLayer from Laravel, Livewire, and plain JavaScript through one unified bridge. The package injects the GTM snippet, validates payloads, and provides browser-side debug output through console.log.

Features

  • Push events to window.dataLayer for GTM to handle
  • Auto-injects the GTM container snippet
  • Client-side payload validation before pushing
  • Works from JavaScript, DOM custom events, and Livewire
  • Blade component and directive for easy layout injection
  • Console debug output

Compatibility

  • PHP: ^8.3
  • Laravel: 12.x, 13.x

Installation

composer require eg-mohamed/laravel-ga4-events

Publish the config:

php artisan vendor:publish --tag="ga4-events-config"

Quick Start

  1. Set your GTM container ID in .env:
GTM_CONTAINER_ID=GTM-XXXXXXX
GTM_EVENTS_ENABLED=true
  1. Inject the bridge once in your layout (before </body>):
<x-ga4-events />

Legacy directive also works:

@ga4Events
  1. Push events from JavaScript:
window.GTMEvents.track('purchase_started', {
    currency: 'USD',
    value: 99.95,
    item_count: 2,
})

GTM receives {event: 'purchase_started', currency: 'USD', value: 99.95, item_count: 2} in dataLayer.

Livewire Usage

$this->dispatch('gtm-event', name: 'profile_updated', params: ['section' => 'security']);

The bridge normalizes wrapped Livewire payload shapes automatically.

JavaScript API

track(name, params = {})

window.GTMEvents.track('add_to_cart', { product_id: 15, value: 150 })

dispatch(payload, source = 'manual')

window.GTMEvents.dispatch({ name: 'search', params: { term: 'sneakers' } })

DOM custom event

window.dispatchEvent(new CustomEvent('gtm:event', {
    detail: { name: 'checkout_step', params: { step: 2 } },
}))

config

console.log(window.GTMEvents.config)

Payload Structure

{
    "name": "event_name",
    "params": {
        "key": "value"
    }
}

Validation rules:

  • name must be a non-empty string matching allowed_name_pattern
  • name must not exceed max_event_name_length
  • params must be an object
  • param keys validated for length
  • param values support string, number, boolean, null, or nested objects
  • string values truncated to max_param_value_length

Configuration

Published at config/ga4-events.php:

return [
    'enabled' => (bool) env('GTM_EVENTS_ENABLED', true),
    'container_id' => env('GTM_CONTAINER_ID'),
    'inject_gtm_script' => (bool) env('GTM_EVENTS_INJECT_SCRIPT', true),
    'event_bus_name' => env('GTM_EVENTS_EVENT_BUS_NAME', 'gtm:event'),
    'livewire_event_name' => env('GTM_EVENTS_LIVEWIRE_EVENT_NAME', 'gtm-event'),
    'global_js_object' => env('GTM_EVENTS_GLOBAL_JS_OBJECT', 'GTMEvents'),
    'debug' => (bool) env('GTM_EVENTS_DEBUG', false),
    'strict_validation' => (bool) env('GTM_EVENTS_STRICT_VALIDATION', false),
    'drop_invalid_events' => (bool) env('GTM_EVENTS_DROP_INVALID_EVENTS', true),
    'max_event_name_length' => (int) env('GTM_EVENTS_MAX_EVENT_NAME_LENGTH', 40),
    'max_params' => (int) env('GTM_EVENTS_MAX_PARAMS', 25),
    'max_param_key_length' => (int) env('GTM_EVENTS_MAX_PARAM_KEY_LENGTH', 40),
    'max_param_value_length' => (int) env('GTM_EVENTS_MAX_PARAM_VALUE_LENGTH', 100),
    'max_param_nesting' => (int) env('GTM_EVENTS_MAX_PARAM_NESTING', 4),
    'allowed_name_pattern' => env('GTM_EVENTS_ALLOWED_NAME_PATTERN', '/^[a-zA-Z][a-zA-Z0-9_]*$/'),
    'console_prefix' => env('GTM_EVENTS_CONSOLE_PREFIX', '[GTM Events]'),
];

Debug Mode

GTM_EVENTS_DEBUG=true

Example console output:

[GTM Events] [INFO] GTM bridge initialized.
[GTM Events] [INFO] Event pushed to dataLayer from api.
[GTM Events] [ERROR] Invalid GTM payload from livewire.

Artisan Command

php artisan gtm-events:check

Validates that the package is enabled and the container ID is set.

Skip Auto Injection

If you load the GTM snippet yourself:

GTM_EVENTS_INJECT_SCRIPT=false

The bridge still pushes events to window.dataLayer.

PHP-side Validation

use MohamedSaid\LaravelGa4Events\Facades\LaravelGa4Events;

$result = LaravelGa4Events::track('purchase_completed', ['value' => 120]);

// $result['valid']  — bool
// $result['errors'] — array of strings
// $result['name']   — sanitized name
// $result['params'] — sanitized params

Changelog

Please see CHANGELOG for information about changes.

Contributing

Please see CONTRIBUTING for contribution details.

Security

Please review our security policy for reporting vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.