Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Google Analytics Bundle Laravel Package

swis/google-analytics-bundle

A Symfony bundle for integrating Google Analytics into your application. Configure your tracking ID and render the analytics JavaScript snippet from templates, making it easy to add page-view tracking without manually editing layouts.

View on GitHub
Deep Wiki
Context7

Getting Started

Read the source before you install it — it's small enough to audit in an afternoon, and it will tell you which version of Google Analytics it actually targets. The description markets it as a tracking-snippet injector, but the bundle also exposes the Reporting API v3 via google/apiclient and Google_Service_Analytics. Those are two very different things, and the second one only works against Universal Analytics (UA) properties. UA stopped processing hits on 1 July 2023, and the v3 API only serves UA. Confirm in the GA admin whether your property is UA or GA4 before writing a line of integration code — if it's GA4, this bundle's reporting half is dead weight no matter how you configure it.

Assuming you have a legacy UA view or an archived property you still need to read from, the minimal path in a Symfony host (and a Laravel developer should treat this as a reading exercise, not an install) looks like:

  1. Check the dependency constraints first. Open the bundle's composer.json. Most bundles of this vintage declare something like symfony/symfony: ^2.3 || ^3.0 and an old google/apiclient. If you're on Symfony 6/7 or PHP 8.x, expect Composer to resolve but runtime to fail. Verify constraints before assuming anything works.

  2. Register the bundle. Legacy apps: new Swis\GoogleAnalyticsBundle\SwisGoogleAnalyticsBundle() in AppKernel::registerBundles(). Symfony 4+: add to config/bundles.php.

  3. Configure credentials in config/packages/swis_google_analytics.yaml. You need either an OAuth2 client (id/secret/redirect) for user-consented access, or a service-account email plus a .p12/JSON key for server-to-server reads. For any backend reporting or cron job, choose the service-account path — OAuth consent flows inside a scheduled job are a maintenance trap.

  4. Grant the service account access in the GA admin UI. Add the [email protected] email as a user on the account or view with "Read & Analyze". A 403 on your first query is almost always this missing grant, not a code bug.

  5. First use case: a console command that pulls sessions and pageviews for one view ID over the last 30 days. That validates the whole chain — credentials, scopes, view ID, and the API version — before you wire anything into a dashboard. The call shape is roughly:

$analytics = $container->get('swis_google_analytics.api');
$results = $analytics->query($viewId, $startDate, $endDate, ['ga:sessions', 'ga:pageviews']);

Use bin/console debug:container to find the exact service ID and constructor signature; do not assume the ID from the docs, they drift.

For a Laravel app, the correct "getting started" is to stop. This is a Symfony bundle. Laravel does not boot Bundle classes; there is no registerBundles(), no Symfony config tree, no Kernel. A composer require will succeed and the package will sit inert. Treat it as a reference implementation, then implement the equivalent in a Blade partial (for the snippet) or an AnalyticsTracker/AnalyticsReporter interface backed by the GA4 Data API SDK (for reporting).

Implementation Patterns

Pattern 1 — Snippet injection via a Blade partial (Laravel-native replacement). The bundle's tracking-snippet half maps to a Blade component or partial in Laravel. Put the measurement ID in config/services.php or a dedicated config/analytics.php, expose it via config(), and render a <x-analytics /> component inside your layout. This gives you the same "centralized config, no hand-editing views" ergonomics the bundle advertises, with zero external dependency. Add a @production check and a consent guard while you're in there — the bundle predates consent mode and won't model it.

// config/analytics.php
return [
    'ga4_measurement_id' => env('GA4_MEASUREMENT_ID'),
];
{{-- resources/views/components/analytics.blade.php --}}
@production
@if(config('analytics.ga4_measurement_id'))
<script async src="https://www.googletagmanager.com/gtag/js?id={{ config('analytics.ga4_measurement_id') }}"></script>
<script>
  window.dataLayer = window.dataLayer || [];
  function gtag(){dataLayer.push(arguments);}
  gtag('js', new Date());
  gtag('consent', 'default', { analytics_storage: 'denied' });
  gtag('config', '{{ config('analytics.ga4_measurement_id') }}');
</script>
@endif
@endproduction

Pattern 2 — Server-side reporting behind an interface (Laravel-native replacement for the bundle's report half). Do not call the reporting client directly from controllers or jobs. Define an application-owned contract and bind it in a service provider. This is the single most important pattern: it makes the vendor choice reversible in one commit.

interface AnalyticsReporter
{
    public function sessions(string $propertyId, Carbon $from, Carbon $to): int;
}

final class Ga4Reporter implements AnalyticsReporter
{
    public function __construct(private BetaAnalyticsDataClient $client) {}
    // ...
}
// App\Providers\AnalyticsServiceProvider
$this->app->bind(AnalyticsReporter::class, Ga4Reporter::class);

Pattern 3 — If you're actually on legacy Symfony, wrap the bundle's service, don't inject it everywhere. Pull swis_google_analytics.api from the container once, adapt it to your own interface, and let the rest of the codebase depend on the interface. That way, when you migrate off UA (you will), you swap one adapter class instead of chasing service IDs through the codebase.

Pattern 4 — Server-side event emission via the GA4 Measurement Protocol. This is what most teams actually need and it is 20 lines with Laravel's HTTP client. Explicitly set timeouts, retries, and a consent check before dispatch; the bundle offers none of this.

Http::withQueryParameters(['measurement_id' => $id, 'api_secret' => $secret])
    ->timeout(3)
    ->retry(2, 200)
    ->post('https://www.google-analytics.com/mp/collect', [
        'client_id' => $clientId,
        'events' => [['name' => 'purchase', 'params' => $params]],
    ]);

Workflow tip — feature-flag the rollout. Run the new adapter in parallel behind a flag and compare event delivery / report output against whatever the legacy path produced. Since the bundle is unmaintained, you have no community validation to lean on; your own diff is the evidence.

Gotchas and Tips

UA is over; the v3 API is a dead end. The biggest gotcha is silent obsolescence. Google_Service_Analytics and analyticsreporting.googleapis.com only serve Universal Analytics properties. If your property is GA4, the bundle will return empty results or deprecation errors with no obvious failure signal. Always confirm property type in the GA admin before blaming your code.

Container bridging is the integration gotcha nobody warns you about. Laravel will not register a Symfony bundle. Symptoms range from "no service found" to Class not found on SwisGoogleAnalyticsBundle. A successful composer require is not evidence of integration — it's evidence of autoloading.

Service-account 403s are almost always a permissions issue, not a credential issue. If queries fail immediately, check that the service account email has been added as a GA user with "Read & Analyze". Second most common: wrong view ID. Third: the API not enabled on the Google Cloud project the key belongs to.

OAuth in cron jobs is a trap. Consent flows need a browser redirect and stored refresh tokens. For any backend read, use a service account with a JSON key. Store the key outside the repo (e.g., storage/app/analytics/service-account.json) and reference the path via env — never commit .p12 or JSON keys.

Consent mode and GDPR are not modeled. The bundle predates consent mode (gtag('consent', 'default', {...})) and cookie banners. If you're in scope for GDPR/CCPA, you cannot ship the snippet it produces without modification. That's a compliance issue, not a preference — build your own partial so consent is explicit.

View IDs are not property IDs. The bundle wants a UA view ID (the ga:XXXXXXXXX format). GA4 uses a numeric property ID in the Data API. Confusing the two produces "not found" errors that look like auth failures.

Do not extend it in place. With 1 star, 0 dependents, no visible release cadence, and PHP/Symfony constraints that likely block modern stacks, any fix you need is a fix you'll own. If you must use it, fork it, pin the commit, put it behind your own interface, and add a removal date to your tech-debt register.

Extension points to know if you're auditing or forking: the bundle's Twig template (analytics-snippet rendering — override by copying into your app's templates), the config tree under swis_google_analytics, and the swis_google_analytics.api service. That's roughly the whole API surface; anything else you add is your code.

Debugging order for "no data": (1) confirm property is UA not GA4; (2) confirm service account added to the view; (3) confirm view/property ID format; (4) run a minimal query with bin/console and dump the raw response; (5) only then suspect the bundle.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
store-doc/laravel-storage-driver
plus-it/laravel-translation-manager
diephp/perhaps
diephp/telegraph
osmaviation/spreadsheet
microsoft/fluentui-system-icons
mmccook/php-json-canonicalization-scheme
middlewares/firewall
qubiqx/montapacking
nyoncode/laravel-package-console
pnscripts/filament-broadquill-lite
pnscripts/laravel-sentfold-lite
vleap/mx-sdk-laravel-internal
twidpay/utils
htetmyathlaing/scout-elasticsearch-driver
artisan-toolbox/front-awesome
ls/client-assistant
users-au/laravel-client
cgv/localized-resource
noith/settings