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!