RSS

Neil Crookes

Learnings and Teachings on Web Application Development & CakePHP

Sep

27

Get Google Analytics data in your CakePHP app

A CakePHP plugin for displaying Google Analytics data in your own application. The bulk of the code is in 3 datasources, a generic one for working with any REST APIs, a Gdata datasource for handling authentication with any of the 18 Gdata APIs and the Gdata Analytics datasource driver itself.

Share and Enjoy:

  • Digg
  • del.icio.us
  • StumbleUpon
  • Technorati
  • Slashdot

I’ve written a CakePHP plugin for accessing Google Analytics data in your own application using the Google Analytics Data Export API.

See a demo showing traffic to my site and download the code form my github account.

The plugin itself contains the usual model/controller/view but these are really just for the demo, the real magic lies in the datasource. Well, there’s actually 3 datasources, more on that later.

As the demo indicates, you can access any of the metrics you can get in Google Analytics itself and break it down by any of the dimensions GA supports, for any date range.

Click on the red labels to toggle the list of check boxes allowing you to select the metrics and dimensions you want to see, then click the show button.

The results are shown in the table below, the initial view is showing the visitors metric broken down by year and month for the last month.

Don’t laugh at my meagre visitor stats ;-)

Note, I don’t have any campaigns, eCommerce or goals or anything like that set up on my site, so you won’t see much if you select those metrics.

If say you wanted to see pageviews broken down by browser, continent, operating system and operating system version, you’ll get a few more rows in the table, and you’ll see the pagination kicking in. This is using CakePHP built-in pagination with custom paginate() and paginateCount() methods in the model.

To achieve this however, I had to modify the core very slightly.

In cake/libs/controller/controller.php I changed the following in the paginate() method:

$extra = array_diff_key($defaults, compact(
‘conditions’, ‘fields’, ‘order’, ‘limit’, ‘page’, ‘recursive’
));
$extra = array_diff_key($defaults, compact(
  'conditions', 'fields', 'order', 'limit', 'page', 'recursive'
));

To this…

$extra = array_diff_key($options, compact(
  'conditions', 'recursive'
));

Note, $defaults has changed to $options and I’ve removed some of the keys that get removed from the $defaults variable to make the $extra variable.

This is because, unlike database queries for pagination, most APIs will return you both the total number of results and the result set for the selected page in one call, using an XML or JSON data structure, as opposed to when you are getting the results from the database, and you issue 2 queries, the first to get the total number of results, and the second to get the results for the selected page.

CakePHP’s Controller::paginate() method allows you to write your own paginate() and paginateCount() methods [see custom query pagination] but it doesn’t send the fields, order, limit and page options to your custom paginateCount() method… unless you make this change above.

So, by sending all the information to the custom paginateCount() method in the analytic model, I can issue the call to the API once, store the results and return the number of rows, then in the paginate() method, I can just return the result that I stored previously.

Not sure whether this would cock anything up for anyone, but it seems pretty harmless to me, maybe someone reading this could comment on it if I am wrong?

So, now that’s out the way, let’s have a look under the hood at those 3 datasources I mentioned.

Firstly there’s a REST source, which is a simple base datasource for interacting with REST APIs. It uses CakePHP’s HttpSocket class to issue requests using the appropriate HTTP verb according the the CRUD operation and returns the response converted to an array – currently just handles XML responses, but if you have a look at it, you can see how easy it could be extended to handle JSON or other formats.

On top of that is a Gdata datasource. This is intended to be able to be used as a base datasource for all Gdata APIs, of which there are 18. It handles authentication with the Google web service. Please note I haven’t yet had the opportunity to test this with any other Gdata API, but the theory is sound… I think ;-)

Finally, there is the actual Google Analytics datasource. This sits in a gdata directory inside the models/datasources directory in the plugin. The intention is to simulate the concept in CakePHP core where there is a base dbo source datasource for interacting with databases, then several drivers for specific databases. In this way I can write more gdata datasources, stick them in this gdata directory, and have them build on the base gdata datasource mentioned in the previous paragraph. It is achieved in the config file by setting the datasource key to gdata and the driver key to analytics.

The analytics datasource just has a read() method which constructs the http request to send to Google based on the $queryData parameter passed in the datasource read() method by Cake. This is typically an array containing the keys for things like conditions, page, limit, order etc etc.

I realised that the metrics and dimensions parameters that the Google Analytics API expects map quite neatly to SQL fields and group parameters that you might have in your SQL queries, and are supported by CakePHP Model::find() method, so they are passed in in those keys.

It also supports sorting the result set by any of the metrics or dimensions, in ascending or descending order. One thing I haven’t done yet is the filtering parameter that Google Analytics API supports, but that will be dead easy to add if you need it in your app.

As I said earlier, the model, controller and view in the plugin contain code mainly for the demo, apart from in the model constructor, there’s Matt Curry’s tip for loading datasources from plugins. So if you just want to get some GA data in your app, you can just use the datasources.

So, if you want to try it out on your own Google Analytics account, just:

  1. Download the code form my github account and save it in a /app/plugings/gdata/
  2. Make the change to /cake/libs/controller/controller.php as described above
  3. Add your Google Analytics email, password and profile id* to /app/plugins/gdata/config/gdata_config.php
  4. Go to http://www.example.com/gdata/analytics

* You can find your profile id by logging in to Google Analytics and clicking the edit link at the end of the table row containing the profile you want to access the data of. The id parameter in the querystring is the profile id you need to add to the config file – mine is a 7-digit integer. Note it is not the UA-XXXXX-X string.

Hope you like it.

Share and Enjoy:

  • Digg
  • del.icio.us
  • StumbleUpon
  • Technorati
  • Slashdot
(8 votes, average: 4.13 out of 5)
Loading ... Loading ...

19 Responses so far

You’re a badass, Niel. Thanks for such an extensive writeup, as well.

Hey Jon, great to meet you in Berlin, say hi to your bro.

Great job !

A very, very, very big, thank you.

I needed a simple way to authenticate users vs. Google Apps and I’ve got it with your datasources. I’ve just tried it and works very well. In fact, I only need to check that Google Apps aknowledge user credentials.

I’m writing an intranet application for the school I work for, and we have our email, calendar, etc. services hosted by Google Apps Edu Edition. So I wanted users to use the same username/password to authenticate in intranet. I need to work a bit on the subject but the basics are provided by your good work.

[...] Neil Crookes rocks it once again with his Google Analytics plugin. [...]

[...] Neil Crookes » Get Google Analytics data in your CakePHP app (tags: google api analytics cakephp) Possibly related posts: (automatically generated)links for 2009-10-13links for 2009-07-22All Cakephp (Indonesian server) [...]

why am i always getting invalid combinations :(

please check http://ebook.orangerdigiart.com/gdata/analytics/

@empe, that message is displayed if $results == false. It probably should be a bit more explicit than that I guess. Anyway, you need to find out what is causing the call to the paginate method in the controller to return false. I’m not sure off the top of my head what this could be I’m afraid. Could it be something to do with your GA Account? Try turning debug up to 2 to see what the errors are?

I’m having the same result as empe – out of the box. I installed the plugin, changed the paginate code, edited /config/gdata_config.php to my details, and I’m getting:

Invalid combination, here’s a list of valid combinations…

My GA account is working fine, and I tried testing out the php GA library, and I’m getting data with that.

I’m using CakePHP 1.2.5, could there be a difference in versions?

@nfofunky, I’ve just downloaded 1.2.5, cloned my git repo, changed the Controller::paginate() code and added my email, password and profile id to the config and it worked fine… I can’t think of any reason why it would not work for you unless for example you have used incorrect GA account details – you are using the 7 digit profile id not the the UA-XXXXX-X string aren’t you? Or are you sure your machine can make socket connections? If all else fails, start debugging as you would normally to find out the bit in the code that is failing. Sorry can’t be of more help.

Thanks for the explanation, this was helpful.

Very helpful – shame the current cake documentation has nothing to say about this.
I tried your approach and it worked fine, however I was uncomfortable with modifying the core to pass through page and limit. Indeed someone tried to raise this as a bug (http://cakephp.lighthouseapp.com/projects/42648/tickets/231-should-controllerpaginate-remove-fields-from-extra-array) but the cake team won’t fix it.

Instead I’ve tried the approach by loadsys (http://blog.loadsys.com/2009/06/19/cakephp-rss-feed-datasource/) where the code goes into the datasource instead of the model. With quite a lot of tweaking I’ve got this to work with the twitter example from the the cake cookbook.

I’ve blogged about it here: http://www.willis-owen.co.uk/wp/?p=81

@Richard, thanks for your comment. I totally agree with you about modifying the core – it sucks – it also can break pagination on other parts of your application!

I have discussed the issue with Joel Perras on the core team, but no progress has been made as yet.

I’ve been working on a few more plugins that consume REST APIs and have come up with a slick way of doing it that doesn’t involve hacking the core. I’ll be releasing them soon and then I’ll refactor the GData plugin too. I’ll be sure to check out your post and the LoadSys one again too. Thanks for sharing.

@Neil, that would be awesome ! (a untouched core solution to the paginate pb)

I’m getting the following error…

Notice (8): Undefined index: Auth [APP\plugins\gdata\models\datasources\gdata_source.php, line 57]
Notice (8): Use of undefined constant ERROR_WARN – assumed ‘ERROR_WARN’ [APP\plugins\gdata\models\datasources\gdata_source.php, line 58]
Warning (2): Invalid error type specified [APP\plugins\gdata\models\datasources\gdata_source.php, line 58]

Thanks @Neil, great plugin! I’m getting the following errors when using the you_tube_videos controller:

“ConnectionManager::loadDataSource – Unable to import DataSource class .GdataYoutube”

Any ideas anyone?! Thanks in advance

Thanks neil for this plugin, its really easy.

it shows nothing. and i got following error
Notice (8): Undefined index: Auth [APP/plugins/gdata/models/datasources/gdata_source.php, line 50]

[...] Get Google Analytics data in your CakePHP app [...]

Leave a comment