Google Analytics with Visualization Graphs (PHP)

This project is a synthesis of PHP server side scripting, Javascript/jQuery, and HTML/CSS, but on this page we’ll cover some of the PHP highlights.

First, have a look at the demo. This is not live data, it displays from a semi-static data set I built that uses no secure keys or data. Play around with it for a bit and see what it does. Resize your window and note there is some animation (see notes below.)

The Scenario

A company hosts hundreds of company sites. A single Google Analytics account records all the stats for them, not individual Analytics accounts. (An admitted lack of foresight, but in place before the company accounts grew.) On each page is a Google Analytics Javascript with a custom dimension, dimension3. This dimension is recorded for every visit to every page.

The goal is to make high level user visits data available to the individual companies. There were no requests for page stats, just overall site visitors stats for visits, device usage, and time of day statistics. Gather the data and present the stats in an interface available only to company administrators.

All graphs (and the demo) have a default start date of one week prior to today. The sessions/users graph is actually two requests, with the blue lines representing sessions/users this week and red lines representing sessions/users the week before last.

The Logic

Pull data from the Google Analytics API parent account and filter the data into a view identified by the value in dimension3 for the individual company only. Mutate the data into the formats required by the Google Visualization API (formerly known as Google Charts,) and present the graphs on the administrative analytics page. This required

  • Creating a Google Services account, setting up an app, setting up keys, and within our app receive an authorization token with which to query the required Analytics account view, the view being "just the current company" specified by dimension3. *
  • Interfacing and testing responses from the Analytics API to observe the data source.
  • Create a "middleware" of sorts to convert the Analytics data to a format that can be digested by the Visualization API.
  • Implement graphic and GUI options to display the graphs. This involves a great deal of Javascript/jQuery and HTML/CSS which is discussed in the Javascript/jQuery and HTML/CSS pages for this app.

The Challenges

  • The implementation takes place in a large legacy code base and many of the tools provided by Google could not be used mostly due to the lack of namespace support in the environment. Google provides classes to make this integration easier. Ground-up code was written, and special considerations were taken to implement this page within the environment.
  • The initial request to retrieve the secure token involves a private key and cert. For security reasons, it is not safe to store these on the server system. The solution implemented was to store the keys in the Amazon Secrets Manager and retrieve them on request from the app, and only from the app.*
  • Many of the data formats returned by Analytics don’t play nicely with the formats required by the Visualization API for the graphs and associated tooltips. This is especially true of date formats. The mapping portion of the code handles these conversions.
  • When changing graph type from one to another, the new graph may or may not require a different data set to properly render. Some of these can change types with the data stored in the graph objects, others need a new query for data. Optimizing the graph changes required special attention.
  • For the sessions/users graphs, the way you are **supposed** to combine two data sets is to do a table join on the data, see documentation. That didn’t work for us, so I had to combine the data sets in code to display the joined data. See the Javascript/jQuery discussion of this project for how I did that.
  • The Visualization API also doesn’t support heat maps as a graph type. We had to grow our own using the data received for time of day.

The Code and Structure

  • Front end Javascript/jQuery composes the initial request for each graph, both on load and when DOM elements change. Not shown in the demo is the ability to select the start date for each graph.
  • The server side PHP script receives the post, fires up an instance of the GoogleServicesAuth object, then an instance of the AnalyticsRequestComposer object, and both are injected into the AnalyticsQuery object.
  • The AnalyticsQuery object instructs the GoogleServicesAuth object to obtain a valid token. If successful, it then instructs the AnalyticsRequestComposer to compose the Analytics request from user input. It then uses the token to validate the request in Analytics, obtain the data, and return it to the front end as JSON.
  • The front end Javascript/jQuery digests the data and renders it in the GUI. See Javascript/jQuery and HTML/CSS discussions.
    <?php
    /**
     * Recipient of AJAX request for analytics data to render charts with the
     * Google Visualization API. Normally the keyfile is securely stored in
     * the cloud in a "secrets manager."
     */
    require_once (__DIR__ . '/classes/GoogleServicesAuth.php');
    require_once (__DIR__ . '/classes/AnalyticsRequestComposer.php');
    require_once (__DIR__ . '/classes/AnalyticsQueryInterface.php');
    require_once (__DIR__ . '/classes/AnalyticsQuery.php');
    
    // Normally this is in the system somewhere and dynamically populated.
    $company_id       = 'Acme Sledgehammer';
    $view_id          = '445277782211';
    $keyfile          = __DIR__ . '/mock-resources/mock-secure-key.json';
    $analytics_scope  = 'https://www.example.com/auth/analytics.readonly';
    
    try {
        $ServiceAuth      = new GoogleServicesAuth($keyfile, $analytics_scope);
        $RequestComposer  = new AnalyticsRequestComposer($company_id, $view_id);
        $AnalyticsQuery   = new AnalyticsQuery($ServiceAuth, $RequestComposer);
        echo $AnalyticsQuery->getAnalyticsData();
    
    } catch (\Exception $e) {
        echo "Exception: {$e->getMessage()}";
    }
    

Notes and Comments

  • The demo does not query live data, reload/play with it all you like. I have created a MockAnalyticsResponse object that dynamically sets the dates from a configuration file. The metrics are all static, it will show the same numbers each day you return.
  • The Time of Day map was fun! Basically it walks the Analytics data, does some basic math to determine the range for the colors from dark to light, and outputs an HTML table with table cell classes and title tags for tool tips. Mouse over the heat map zones for the tool tips. See the HTML/CSS version for details.
  • You will notice I have not implemented namespaces. I probably should, but it’s a demo and does what I need it to do.
  • See the Javascript/jQuery and HTML/CSS for details on the Javascript/CSS involved in this project, they play a large part in rendering the graphs.
  • Many of the graphs can animate from one state to the next, but is mostly hidden in this demo because selecting start dates is disabled due to the static data. Resize the window, the top graph will demonstrate some animation.
  • "Show me the codez!" This code is not open source, however serious inquiries from potential clients can request samples.

* The demo doesn’t actually connect with Google Services to obtain a token. A mock responder is set up to respond with a fake token so I didn’t have to rewrite large chunks of working code.