Using the Google Analytics API v3 with PHP: Fetching Data
In the first part of our series, we introduced the Google Analytics API, including the basic usage. In this part, we will continue creating our demo and see how we can extend it with more functionality.
Google Analytics API
Management API
As we discussed in the first part, the Management API is responsible for getting user accounts, properties, views… for our first example we will retrieve the list of accounts available for our authenticated user.
// app/src/GA_Service.php
public function accounts(){
if( !$this->isLoggedIn() ){
//login
}
$service = new Google_AnalyticsService($this->client);
$man_accounts = $service->management_accounts->listManagementAccounts();
$accounts = [];
foreach ($man_accounts['items'] as $account) {
$accounts[] = [ 'id' => $account['id'], 'name' => $account['name'] ];
}
return $accounts;
}//accounts
// app/controllers/HomeController.php
public function accounts(){
$accounts = $this->ga->accounts();
return $accounts;
}//accounts
// app/routes.php
Route::get('/accounts', 'HomeController@accounts');
Inside GA_Service::accounts
we create a new Google_AnalyticsService
with our authorized client and then query the API for the list of accounts.
In this case the result is an array, but the API also makes use of objects, we just need to specify that inside our GA_Service::init
function. In the following examples, I’m going to use array results.
$this->client->setUseObjects(true);
The listManagementAccounts
function returns an array containing:
{
kind: "analytics#accounts",
username: "me@mail.com",
totalResults: 3,
startIndex: 1,
itemsPerPage: 1000,
items: [
{
id: "4449308",
kind: "analytics#account",
selfLink: "https://www.googleapis.com/analytics/v3/management/accounts/4449308",
name: "me@mail.com",
permissions: {
effective: [
"COLLABORATE",
"EDIT",
"MANAGE_USERS",
"READ_AND_ANALYZE"
]
},
created: "2013-10-01T11:04:28.478Z",
updated: "2013-10-01T11:04:28.478Z",
childLink: {
type: "analytics#webproperties",
href: "https://www.googleapis.com/analytics/v3/management/accounts/4449308/webproperties"
}
}
]
}
Note that when you return an array as a response, Laravel automatically encodes the result as a JSON response and sends it to the browser.
The result contains information about the total results and some pagination info as well. The items
column contains the list of accounts with their IDs, permissions, etc., but we looped through items
to extract only the id
and name
from the accounts.
If you would like to have result pagination, you can always pass more options to listManagementAccount
:
$service->management_accounts->listManagementAccounts( [ 'max-results' => $max_results, 'start-index' => $start_index ] );
Let’s assume that we’re going to show our users their list of accounts, and when they select one, we load the list of properties associated with it.
// app/src/GA_Service.php
public function properties( $account_id ){
if( !$this->isLoggedIn() ){
//login
}
try {
$service = new Google_AnalyticsService($this->client);
$man_properties = $service->management_webproperties->listManagementWebproperties($account_id);
$properties = [];
foreach ($man_properties['items'] as $property) {
$properties[] = [ 'id' => $property['id'], 'name' => $property['name'] ];
}//foreach
return json_encode($properties);
} catch (Google_ServiceException $e) {
return Response::json([
'status' => 0,
'code' => 3,
'message' => $e->getMessage()
]);
}//catch
}//properties
// app/controllers/HomeController.php
public function properties( $account_id ){
$properties = $this->ga->properties( $account_id );
return $properties;
}//properties
// app/routes.php
Route::get( '/properties/{account_id}', [ 'uses' => 'HomeController@properties' ] )->where('account_id', '\d+');
The GA_Service::properties
accepts an account ID, and returns the list of properties for that account. We basically have the same process, like retrieving accounts.
[
{
id: "UA-52317977-1",
name: "Prop1"
},
{
id: "UA-52317977-2",
name: "Prop1"
}
]
Every property has a subset of views. By default, Google adds a view called All Web Site Data
for every new property.
Using an ID from the list of properties and the account ID grabbed from the first part, we will query Google Analytics API for the list of available views for a given account property.
// app/src/GA_Service.php
public function views( $account_id, $property_id ){
if( !$this->isLoggedIn() ){
//login
}
try {
$service = new Google_AnalyticsService($this->client);
$man_views = $service->management_profiles->listManagementProfiles( $account_id, $property_id );
$views = [];
foreach ($man_views['items'] as $view) {
$views[] = [ 'id' => $view['id'], 'name' => $view['name'] ];
}//foreach
return json_encode($views);
} catch (Google_ServiceException $e) {
return Response::json([
'status' => 0,
'code' => 3,
'message' => $e->getMessage()
]);
}//catch
}//views
// app/controllers/HomeController.php
public function views( $account_id, $property_id ){
$views = $this->ga->views( $account_id ,$property_id );
return $views;
}//properties
// app/routes.php
Route::get( '/views/{account_id}/{property_id}', [ 'uses' => 'HomeController@views' ] )->where([ 'account_id', '\d+', 'property_id', '\d+' ]);
In the browser, when hitting /views/{account_id}/{property_id}
route, we should get something similar to:
// http://localhost:8000/views/44493065/UA-44493083
[
{
id: "77278619",
name: "All Web Site Data"
}
]
Metadata API
To query some statistics from Google Analytics we need to provide a set of dimensions and metrics.
- Metrics: metrics are the individual measurements of user activity on your property, such as sessions and pageviews..
- Dimensions: dimensions break down metrics across some common criteria, such as country or browser.
To grab the list of available metadata you can simply use curl
to query data from the following url https://www.googleapis.com/analytics/v3/metadata/ga/columns
.
Google Analytics gives us an etag
attribute that can be used for caching the response so that we don’t have to query the API on every request.
$gcurl = new Google_CurlIO;
$response = $gcurl->makeRequest(
new Google_HttpRequest(
"https://www.googleapis.com/analytics/v3/metadata/ga/columns"
));
Google_CurlIO
: a class wrapped with somecurl
utilities for dealing with caching, authentication, etc – by using this class we ensure the response is cached using theetag
attribute.Google_HttpRequest
: is a class representing a single HTTP request.
The makeRequest
method returns a Google_HttpRequest
instance, and we can use the getResponseBody
to get our metadata response.
// app/src/GA_Service.php
public function metadata(){
$gcurl = new Google_CurlIO;
$response = $gcurl->makeRequest(
new Google_HttpRequest( "https://www.googleapis.com/analytics/v3/metadata/ga/columns" )
);
//verify returned data
$data = json_decode($response->getResponseBody());
$items = $data->items;
$data_items = [];
$dimensions_data = [];
$metrics_data = [];
foreach( $items as $item ){
if( $item->attributes->status == 'DEPRECATED' )
continue;
if( $item->attributes->type == 'DIMENSION' )
$dimensions_data[ $item->attributes->group ][] = $item;
if( $item->attributes->type == 'METRIC' )
$metrics_data[ $item->attributes->group ][] = $item;
}//foreach
$data_items['dimensions'] = $dimensions_data;
$data_items['metrics'] = $metrics_data;
return $data_items;
}//metadata
// app/controllers/HomeController.php
public function metadata(){
$metadata = $this->ga->metadata();
return $metadata;
}//metadata
// app/routes.php
Route::get('/metadata', 'HomeController@metadata');
Now, when accessing the /metadata
route in your browser you should get an array of dimensions and another one for metrics, and each one of them contains a list of grouped elements.
{
dimensions: {
User: [
{
id: "ga:userType",
kind: "analytics#column",
attributes: {
type: "DIMENSION",
dataType: "STRING",
group: "User",
status: "PUBLIC",
uiName: "User Type",
description: "A boolean indicating if a user is new or returning. Possible values: New Visitor, Returning Visitor.",
allowedInSegments: "true"
}
},
...
]
},
metrics: {
...
}
}
To speed up the process we will use bootsnipp. If the user is logged in, we will show the home page.
We need to update our HomeController@index
to show the home page view.
// app/controllers/HomeController.php
public function index(){
if( $this->ga->isLoggedIn() ){
$metadata = $this->metadata();
$dimensions = $metadata['dimensions'];
$metrics = $metadata['metrics'];
return View::make('home', [
'dimensions' => $dimensions,
'metrics' => $metrics
]);
}//if
else{
$url = $this->ga->getLoginUrl();
return View::make('login', [ 'url' => $url ]);
}
}//index
As you can see from the screenshot, when the user selects an account we asynchronously change the property and the view accordingly. To achieve that, I wrote some simple JS which you can check in the final repo.
Reporting API
By providing the selected view, metrics and dimensions we can get detailed statistics about users and interactions. The result after the user submission will be something similar to:
array(3) {
["items"]=>
array(10) {
[0]=>
array(2) {
[0]=>
string(9) "Argentina"
[1]=>
string(1) "2"
}
[1]=>
array(2) {
[0]=>
string(6) "Brazil"
[1]=>
string(2) "31"
}
[2]=>
array(2) {
[0]=>
string(6) "France"
[1]=>
string(1) "1"
}
[3]=>
array(2) {
[0]=>
string(7) "Germany"
[1]=>
string(1) "1"
}
[4]=>
array(2) {
[0]=>
string(6) "Greece"
[1]=>
string(1) "1"
}
//...
}
["columnHeaders"]=>
array(2) {
[0]=>
array(3) {
["name"]=>
string(10) "ga:country"
["columnType"]=>
string(9) "DIMENSION"
["dataType"]=>
string(6) "STRING"
}
[1]=>
array(3) {
["name"]=>
string(12) "ga:pageviews"
["columnType"]=>
string(6) "METRIC"
["dataType"]=>
string(7) "INTEGER"
}
}
["totalResults"]=>
int(15)
}
Our GA_Service::report
accepts four arguments: a view ID, a start and end date and an array of metrics.
Google can’t return all your legacy data – instead we provide a start and end date. In my example, I queried the last month’s results.
The third parameter is the list of metrics which we already have from the user selection.
The fourth optional parameter is an array of options.
– max-results
: the maximum number of results. (we used 10 to speed up the response).
– dimensions
: a comma separated list of values. (ga:country,ga:city
)
– filters
: a comma separated list of rules to be appilied to the result .(ga:country!=usa,ga:pageviews>100
)
In this example we excluded USA from the list of dimensions and only showed pageviews that are greater than 100.
– segment
: advanced segment ID to be applied to the data.
– sort
: order results by dimensions or metrics. Can combine multiple dimensions and metrics. (ga:country,-ga:pageviews
= order by ga:country
ascending, and by ga:pageviews
descending.
– start-index
: can be used for pagination.
// app/src/GA_Service.php
public function report( $view, $dimensions, $metrics ){
// to make the request quicker
$max_results = 10;
// query the last month analytics
$now = new DateTime();
$end_date = $now->format('Y-m-d');
$start_date = $now->modify('-1 month')->format('Y-m-d');
// if( !is_array( $dimensions ) )
// $dimensions = array( $dimensions );
$dimensions = implode( ",", $dimensions );
$metrics = implode( ",", $metrics );
try{
$analytics = new Google_AnalyticsService($this->client);
$options = [];
$options['dimensions'] = $dimensions;
$options['max-results'] = $max_results;
$data = $analytics->data_ga->get( $view, $start_date, $end_date, $metrics,
$options
);
$res = [
'items' => isset($data['rows']) ? $data['rows'] : [],
'columnHeaders' => $data['columnHeaders'],
'totalResults' => $data['totalResults']
];
}catch( Google_ServiceException $ex ){
return Response::json([
'status' => 0,
'code' => 2,
'message' => 'Google analytics internal server error: (Technical details) ' . $ex->getErrors()[0]['message']
]);
}//catch
return $res;
}//report
// app/controller/HomeController.php
public function report(){
if( !$this->ga->isLoggedIn() )
return Response::json([
'status' => 0,
'code' => 1,
'message' => 'Login required'
]);
if( !Input::has('dimensions') || !Input::has('metrics') || !Input::has('view') )
return Response::json([
'status' => 0,
'code' => 1,
'message' => 'Invalid request parametter'
]);
$view = 'ga:' . Input::get('view');
$dimensions = Input::get('dimensions');
$metrics = Input::get('metrics');
$report = $this->ga->report( $view, $dimensions, $metrics );
return View::make('report', [ 'columns' => $report['columnHeaders'], 'items' => $report['items'], 'totalResults' => $report['totalResults' ] ]);
}//metadata
// app/routes.php
Route::post('/report', 'HomeController@report');
After calling the get Google_AnalyticsService::get
method, we use the list of result items, column headers, and total results to output the result as a table.
Extending The Demo
Now let’s see how we can extend our demo with filters, sorting and segments.
Filters
Filters are a way to exclude some data from the returned result. They take the following form:
ga:column operator value
ga:column
: dimension or metric id (Ex:ga:country
)operator
: the operator depends on the choice of metric or dimension column id, check the docs for the list of operators.value
: the value can be a number, a string, or a regex.
You can combine multiple filters: you can use comma (,) as an OR operator and a semi-colon (;) as an AND operator.
Segments
By default Google Analytics group all your data in one group called All Sessions
. However, you can always choose from the built in segments or create a new one depending on your needs. You can group data by referral, device type, age, gender, etc.
You can extend the demo by adding a new select element with the available segment list, and pass the ID to the get
method as discussed previously.
// app/src/GA_Service.php
public function segments(){
if( !$this->isLoggedIn() ){
//login
}
$service = new Google_AnalyticsService($this->client);
$segments = $service->management_segments->listManagementSegments();
return $segments;
}//segments
// app/controllers/HomeController.php
public function segments(){
$segments = $this->ga->segments();
return $segments;
}//segments
// app/routes.php
Route::get('/segments', 'HomeController@segments');
You can visit the /segments
page to see the list of available segments with their IDs, and you can of course use this as an option as we saw earlier.
Wrapping up
The Google Analytics API is very flexible and provides a lot of features, but the documentation is not complete yet, and doesn’t provide good examples of use. You get a lot more by digging into the source code and testing possibilities and limits.
In this series, we focused on the basic usage of Google Analytics, but you can extend the demo with options from the Google Analytics dashboard.
You can check the final repo for the source code of this tutorial.
Questions? Comments? Let me know!