fluffydiscord/google-analytics-bundle
A Symfony bundle for integrating Google Analytics into your application, providing configuration and helpers to add tracking code and monitor page views and user activity from within your Symfony project.
Start by confirming how the package is wired into your layout, since v2.2.0 changes the data layer path from a fetch wrapper to a Stimulus controller.
assets/src/data_layer_controller.jsassets/src/analytics_events.js<head>
{{ google_analytics_head() }}
</head>
This now renders the dataLayer bootstrap with a data-controller attribute.config/*.php:
'stimulus_controller_name' => 'data-layer',
Only change this if you already use that identifier for another controller. The rendered data-controller value must match the registered Stimulus controller name.import DataLayerController from './../../vendor/.../data_layer_controller'
application.register('data-layer', DataLayerController)
Use the identifier configured in stimulus_controller_name.render:started, reads the events collected by that response, and pushes them into the data layer. Inspect window.dataLayer in the browser console to confirm the payloads.If you previously called the documented fetch wrapper for your own AJAX requests, that path is replaced. For custom requests, call pushAnalyticsEvents(response) from analytics_events.js after the response resolves.
The primary day-to-day path is now automatic. data_layer_controller.js hooks every Live Component’s render:started event and pushes the events returned in that response. In practice:
dataLayer.A typical Live Component action remains unchanged:
public function addToCart()
{
Cart::add($this->product);
$this->dispatchBrowserEvent('cart-updated');
// Analytics events are collected by the package's Live Component integration.
}
Then verify in the browser:
window.dataLayer
For shop-side AJAX calls that do not go through a Live Component, use the exposed helper:
import { pushAnalyticsEvents } from './../../vendor/.../analytics_events'
const response = await fetch('/cart/add', {
method: 'POST',
headers: { 'X-Requested-With': 'XMLHttpRequest' },
})
await pushAnalyticsEvents(response)
The helper expects the same response shape used by the Live Component bridge. Confirm the exact contract in assets/src/analytics_events.js; typically this means a JSON response containing whatever event payloads the server collected.
Every pushed payload is also dispatched as an analytics:event CustomEvent. This is useful when you need to forward events to another system or add client-side enrichment:
window.addEventListener('analytics:event', (event) => {
// event.detail contains the pushed payload
console.log('Analytics event:', event.detail)
// Example: forward to a non-GA endpoint
navigator.sendBeacon('/analytics/collect', JSON.stringify(event.detail))
})
This is also a good place to add consent checks, debug logging, or transformation for downstream tools.
The package bootstraps dataLayer through google_analytics_head(). If you use Google Tag Manager, ensure your GTM snippet runs after the package bootstrap but still reads from the same dataLayer array. Avoid declaring a second independent dataLayer unless you intentionally isolate it.
A practical order in Blade:
<head>
{{ google_analytics_head() }}
{{-- GTM or analytics.js snippet --}}
@include('partials.gtm')
</head>
If your application already registers a controller named data-layer, change the package config rather than renaming your own controller:
// config/analytics.php (or the package config file)
'stimulus_controller_name' => 'shop-data-layer',
Then make sure the same identifier is registered:
application.register('shop-data-layer', DataLayerController)
Clear config cache after changing it:
php artisan config:clear
fetch wrapper. Any code, Blade partial, or documentation snippet still calling the old fetch wrapper must move to pushAnalyticsEvents(response) for custom AJAX calls. Live Component tracking is now handled by the Stimulus controller instead.google_analytics_head() renders the data-controller attribute, but if the controller is missing from your Stimulus app, nothing will push. Check the browser console for “controller not found” style errors.stimulus_controller_name does not match the registered Stimulus identifier, the data layer bootstrap may render but no bridge will run. Keep the config and application.register(...) value in sync.dataLayer, inspect the Live Component response and the server-side event collection first.pushAnalyticsEvents(response) for a request that is already handled by the Live Component bridge, you will push the same payload twice. Use the manual helper only for non-Live-Component AJAX calls.analytics:event for debugging. The CustomEvent is the easiest place to confirm the payload shape before it reaches GTM or GA. Add a temporary listener and log event.detail.google_analytics_head() before your analytics/GTM snippet, and avoid moving it into a deferred bundle unless you also defer all consumers.stimulus_controller_name, run php artisan config:clear (and config:cache in production if that is your deployment practice).assets/src. If you publish or compile package assets, confirm the compiled controller is included in your build and allowed by your Content Security Policy.analytics:event forwarding or your GTM triggers. The package pushes to dataLayer; whether those events become hits depends on your tag configuration and consent settings.render:started can fire for multiple components. If you see more events than expected, verify whether nested component renders are also collecting and pushing payloads.window.dataLayer contains the expected event. For custom AJAX, assert that pushAnalyticsEvents was called and that the corresponding analytics:event was dispatched.How can I help you explore Laravel packages today?