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 Laravel Package

widop/google-analytics

A PHP client for querying Google Analytics data via the Core Reporting API, with OAuth2 authentication and simple requests for metrics, dimensions, filters, and segments from your analytics profiles.

View on GitHub
Deep Wiki
Context7

Getting Started

What it is. A thin, framework-agnostic PHP wrapper around the Google Analytics Core Reporting API (v3) that handles OAuth2 service-account (certificate) authentication, so you can run reports from cron jobs, dashboards, and back-office tools without a browser or a refresh-token dance.

Install and orient.

composer require widop/google-analytics

Read the README first, then src/. Four classes carry the whole package: Client (credentials/auth), GoogleAnalytics (transport, query()), Query (fluent request builder), and Response (payload accessors). The tests are the de-facto documentation for supported parameter names.

One-time Google setup — do this before writing code:

  1. Google Cloud project → enable the "Analytics API".
  2. Create a service account and download its key (historically P12 — see Gotchas).
  3. Copy the service-account email ([email protected]) and add it as a user on the GA view with Read & Analyze.

First use case — top 10 countries, last 30 days:

use Widop\GoogleAnalytics\Client;
use Widop\GoogleAnalytics\GoogleAnalytics;
use Widop\GoogleAnalytics\Query;

$client    = new Client($clientId, $serviceAccountEmail, '/path/to/key.p12');
$analytics = new GoogleAnalytics($client);

$query = (new Query())
    ->setIds('ga:12345678')                 // view ID, not property ID
    ->setStartDate(new \DateTime('-30 days'))
    ->setEndDate(new \DateTime('yesterday'))
    ->setMetrics(['ga:sessions'])
    ->setDimensions(['ga:country'])
    ->setSorts(['-ga:sessions'])
    ->setMaxResults(10);

foreach ($analytics->query($query)->getRows() as $row) {
    printf("%s: %d\n", $row[0], $row[1]);
}

If this returns rows, everything downstream (key, scope, permissions, view ID) is correct. Prove that path end-to-end before building any abstraction on top.

Implementation Patterns

Bind one service, configure per environment. Don't scatter new Client(...) across the codebase. A single factory bound to config (client ID, service-account email, key path, view ID, default segment/filters) gives you one place to swap credentials per environment and one seam to mock in tests.

Layer reusable query builders. Most reporting code differs only in metrics, dimensions, and date range. Start from a baseQuery() that sets ids, date bounds, and sampling level, then specialize:

$q = $this->baseQuery($from, $to)
    ->setMetrics(['ga:pageviews', 'ga:uniquePageviews'])
    ->setDimensions(['ga:pagePath'])
    ->setFilters('ga:pagePath=~/blog/')
    ->setMaxResults(500);

Normalize rows once, immediately. Response mirrors the JSON payload: rows are positional arrays in the order of dimensions then metrics. Convert to associative arrays using the column headers at the boundary and pass only those inward; never let positional indexes leak into templates or exports.

Paginate explicitly. The API caps max-results; walk start-index until you've collected totalResults, or set a deliberate cap for dashboards. There's no batching here — N reports means N round trips, which makes caching mandatory rather than optional.

Cache by query hash. Key on a hash of the serialized query parameters with a TTL matching your reporting cadence (hourly for ops, daily for marketing). Per-project quotas are generous for one dashboard and trivially exhausted by per-user report rendering.

Classify failures at one choke point. 401 = key/scope; 403 = service account missing from the view (by far the most common); 400 = invalid metric/dimension combination or malformed filter; 429 = quota. Always log the raw query string — for this library it's the only artifact you need to reproduce a bug.

Test at the GoogleAnalytics seam. Return canned Response objects from a stub and unit-test your mapping, filtering, and caching logic. Keep one integration test against a real view, marked/skipped by default.

Isolate for migration. Treat this as a legacy-UA adapter. Put it behind your own repository interface so a later swap to the GA4 Data API (google/analytics-data) is a one-class change rather than a rewrite of every caller.

Gotchas and Tips

  • This targets Universal Analytics (v3). GA standard properties stopped processing hits in July 2023 and the v3 APIs were shut down. Expect errors or empty payloads for standard properties. Verify against your specific property before committing to a feature — this is the single biggest reason to pick or reject the package.
  • Certificate vs. JSON key. The library was written around P12 service-account keys (password usually notasecret). Google now issues JSON keys. If the constructor asks for a path plus password, either convert the key or patch the auth layer; don't quietly hand it a JSON file.
  • ga:VIEW_ID, not property ID. The ids parameter takes the numeric view (profile) ID — not the UA property ID, not the GA4 property ID. Mixing these up is the second-most-common failure after permissions.
  • Valid auth ≠ working API. The service-account email must be added as a user on that specific view. Symptom: a 403 despite a perfectly good token.
  • Scope. Request https://www.googleapis.com/auth/analytics.readonly. Anything broader will still authenticate and should fail review.
  • Syntax quirks. Descending sorts use a - prefix (-ga:sessions); filters use GA's mini-language (; = AND, , = OR, =~ = regex). Don't hand-roll URL encoding if the client already does it.
  • Dates are inclusive and supplied as \DateTime; interpretation follows the view's timezone, so "yesterday" shifts across regions.
  • Check sampling flags before charting. Prefer raising the sampling level over silently plotting sampled numbers.
  • Maintenance risk. 27 stars, zero dependents, and an elderly dependency footprint. Pin versions, confirm PHP 8 compatibility, and expect composer conflicts if the app already ships modern google/apiclient. Budget for a fork — the extension points are just subclassing Query/Response or replacing Client, so forking is cheap.
  • Debugging. Log the outbound query parameters and the raw response before any massaging; the library is too small to offer rich diagnostics, but every failure here is visible in those two artifacts.
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.
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel
draftsmaninc/draftsman
square-bit/laravel-volition
himito/laravel-mailman
htinlynn/genius-helper
maco-studios/hephaestus