google/analytics-data
Idiomatic PHP client for the Google Analytics Data API (GA4). Query reports, dimensions/metrics, audience exports, and more via REST or gRPC. Install with Composer and authenticate with Google Cloud credentials to start making requests.
## Getting Started
### Minimal Setup
1. **Installation**:
```bash
composer require google/analytics-data
Ensure your project uses PHP 8.0+ (recommended: 8.1+).
Authentication:
Use Google’s authentication guide to set up credentials (e.g., service account JSON key). Store the path securely (e.g., Laravel .env):
GOOGLE_APPLICATION_CREDENTIALS=/path/to/your/service-account.json
First Use Case:
Fetch basic metrics for a GA4 property (replace properties/{PROPERTY_ID}):
use Google\Analytics\Data\V1beta\BetaAnalyticsDataClient;
use Google\Analytics\Data\V1beta\RunReportRequest;
use Google\Analytics\Data\V1beta\Dimension;
use Google\Analytics\Data\V1beta\Metric;
$client = new BetaAnalyticsDataClient();
$request = (new RunReportRequest())
->setProperty('properties/12345678')
->setDimensions([new Dimension()->setName('country')])
->setMetrics([new Metric()->setName('activeUsers')]);
$response = $client->runReport($request);
BetaAnalyticsDataClient (v1beta, recommended for new projects)AlphaAnalyticsDataClient (v1alpha, legacy—avoid unless maintaining old code)runReport(): Standard reporting.runPivotReport(): Pivot tables.runAsyncQuery(): Background processing.SheetExportAudienceList (removed in v1alpha) is no longer available in v1beta.Use FilterExpression for granular data extraction:
use Google\Analytics\Data\V1beta\FilterExpression;
use Google\Analytics\Data\V1beta\SimpleComparisonFilter;
$filter = (new FilterExpression())
->setFilters([
(new SimpleComparisonFilter())
->setDimensionName('country')
->setOperator('EQUALS')
->setValue('United States')
]);
$request->setFilter($filter);
Define time spans with DateRange:
use Google\Analytics\Data\V1beta\DateRange;
$request->setDateRanges([
(new DateRange())
->setStartDate('7daysAgo')
->setEndDate('today')
]);
Offload heavy queries to Google’s servers:
$asyncRequest = (new RunAsyncQueryRequest())
->setProperty('properties/12345678')
->setQuery('SELECT * FROM events WHERE event_name = "purchase"');
$task = $client->runAsyncQuery($asyncRequest);
$result = $client->getReportTask($task->getName());
Service Provider:
// app/Providers/AnalyticsServiceProvider.php
public function register()
{
$this->app->singleton(BetaAnalyticsDataClient::class, function ($app) {
return new BetaAnalyticsDataClient();
});
}
Facade (Optional):
// app/Facades/Analytics.php
public static function runReport(array $params)
{
return app(BetaAnalyticsDataClient::class)->runReport($params);
}
Usage in Controllers:
use App\Facades\Analytics;
$data = Analytics::runReport([
'property' => 'properties/12345678',
'dimensions' => ['country'],
'metrics' => ['activeUsers'],
]);
Wrap API calls in try-catch blocks:
try {
$response = $client->runReport($request);
} catch (ApiException $e) {
Log::error('Analytics API Error: ' . $e->getMessage());
return response()->json(['error' => 'Failed to fetch data'], 500);
}
Use nextPageToken for large datasets:
$response = $client->runReport($request);
$nextPageToken = $response->getNextPageToken();
while ($nextPageToken) {
$request->setPageToken($nextPageToken);
$response = $client->runReport($request);
$nextPageToken = $response->getNextPageToken();
}
Explicitly set sampling levels:
$request->setSamplingLevel('HIGH_PRECISION');
Reference custom definitions by name:
$request->setDimensions([new Dimension()->setName('customEvent:custom_dimension')]);
Authentication Issues:
Invalid credentials or 403 Forbidden.GOOGLE_APPLICATION_CREDENTIALS points to a valid JSON key with analytics.readonly (or .data) scope. Verify the service account has access to the GA4 property.Deprecated Methods:
AlphaAnalyticsDataClient (v1alpha). Use BetaAnalyticsDataClient (v1beta) for new projects.SheetExportAudienceList is no longer available in any version. Ensure your code doesn’t rely on this deprecated feature.Quota Limits:
quotaStatus in responses:
$quota = $response->getMetadata()->getQuotaStatus();
if ($quota->getConsumed() >= $quota->getLimit()) {
// Handle quota exhaustion
}
Date Range Formatting:
YYYY-MM-DD) or relative terms (7daysAgo, today). Invalid formats return empty results.Async Task Timeouts:
GetReportTask may take minutes to hours. Implement exponential backoff:
$retryDelay = 1000; // ms
while (!$result->getCompleted()) {
sleep($retryDelay / 1000);
$retryDelay *= 2;
$result = $client->getReportTask($task->getName());
}
Enable gRPC Logging: Set environment variable to debug gRPC calls:
GRPC_VERBOSITY=DEBUG
Validate Requests:
Use serializeToJsonString() to inspect request payloads:
$requestJson = $request->serializeToJsonString();
Log::debug('Analytics Request:', ['payload' => $requestJson]);
Common Errors:
INVALID_ARGUMENT: Check dimension/metric names (case-sensitive, e.g., country vs Country).RESOURCE_EXHAUSTED: Reduce date range or sampling level.UNAVAILABLE: Retry with exponential backoff (Google’s backend issues).Custom Response Mappers: Transform raw responses into Laravel collections:
$rows = collect($response->getRows())
->map(function ($row) {
return [
'dimension' => $row->getDimensionValues()[0]->getValue(),
'metric' => $row->getMetricValues()[0]->getValue(),
];
});
Caching Layer: Cache frequent reports (e.g., daily metrics) using Laravel’s cache:
$cacheKey = 'analytics_report_' . md5($request->serializeToJsonString());
return Cache::remember($cacheKey, now()->addHours(1), function () use ($client, $request) {
return $client->runReport($request);
});
Webhook Integration:
Use AudienceExport for real-time data pushes (if applicable to your use case):
$export = (new CreateAudienceExportRequest())
->setName('properties/12345678/audienceExports/export1')
->setDestination('bigquery://project/dataset/table')
->setAudienceSegment('segments/123');
$client->createAudienceExport($export);
Testing: Use mock
How can I help you explore Laravel packages today?