Posted on in #development #laravel #package
Wanting to use as little JavaScript as possible on my new site I didn't want to go down the traditional route of adding Google's global site tag (gtag.js) or Google Tag Manager - it feels a bit dirty these days and I personally use Ghostery to block everything anyway. Perhaps a bit hypocritical of me but I did still want to know what pages are being seen the most. I looked into a few privacy focused analytics services but in the end wanted todo something server side so that it didn't impose on the front end.
After a little digging I came across Google's Measurement Protocol that allows you to send HTTP requests for common hit types e.g. pageviews! which appears to do exactly what I'm after. A server side, simple page tracking service with the bonus of using the existing Analytics dashboard.
Initial Thoughts
Initially I thought some form of Middleware would be a good idea however it's a blaring bottleneck as the site will have to wait for the HTTP request to serve actual requests. Technically I guess it could be done by using queues to defer the request but it's an overhead I didn't want to implement. I'm also using the spatie/laravel-responsecache package to cache page responses so not sure it would play nice.
Eventually I succumbed to a tiny bit of plain Javascript to send an XMLHttpRequest request with the bare minimum to record a pageview:
var xhr = new XMLHttpRequest();
xhr.open("POST", '{{ route('pageviews.store') }}', true);
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
xhr.send("_token={{ csrf_token() }}&dp={{ urlencode(request()->getRequestUri()) }}&dt={{ urlencode(app('view')->yieldContent('title')) }}&ua={{ urlencode(request()->header('user-agent')) }}&dr={{ urlencode(url()->previous()) }}");
A few things to note, I'm sending the csrf token so that it doesn't get blocked by Laravel's VerifyCsrfToken middleware, you could allow it via the $except
variable but I've chosen not to.
The controller code is pretty straight forward, a post request to https://www.google-analytics.com/collect
using the GuzzleHttp
library with the bare parameters:
use GuzzleHttp\Client;
$client = new Client(['base_uri' => 'https://www.google-analytics.com/']);
try {
$response = $client->post('collect', [
'query' => array_merge($request->only(['dp', 'dt', 'ua', 'dr']), [
'v' => '1',
't' => 'pageview',
'tid' => 'UA-xxx',
'cid' => '555',
'dh' => config('app.url'),
])
]);
}
The response you get is usually always a 200
so to validate you can either use their debug url or as I did open the real-time dashboard to ensure that your hits are coming through.
Creating a Laravel Package
I recently bought the PHP Package Development course from Marcel which is great and it gave me a kick in the arse to create my own simple package that can be easily used across projects.
I used a few of Spatie's packages for inspiration and general organisation so I present jamesfairhurst/laravel-google-analytics-pageviews which you can use to easily track your pageviews in a Laravel application.
Install the package composer require jamesfairhurst/laravel-google-analytics-pageviews
add your Google Analytics tracking id as an .env
variable named PAGEVIEWS_GOOGLE_ANALYTICS_TRACKING_ID
and finally add the @pageviews
blade directive before the closing body tag. Simples!