Skip to content

Commit

Permalink
Add Measurement Protocol (#1)
Browse files Browse the repository at this point in the history
  • Loading branch information
tjardoo authored Dec 11, 2023
1 parent 45ba80e commit 6ea33b1
Show file tree
Hide file tree
Showing 9 changed files with 270 additions and 5 deletions.
65 changes: 61 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
[![Quality Score](https://img.shields.io/scrutinizer/g/label84/laravel-tagmanager.svg?style=flat-square)](https://scrutinizer-ci.com/g/label84/laravel-tagmanager)
[![Total Downloads](https://img.shields.io/packagist/dt/label84/laravel-tagmanager.svg?style=flat-square)](https://packagist.org/packages/label84/laravel-tagmanager)

An easier way to add Google Tag Manager to your Laravel application. Including recommended GTM events support.
Easier way to add Google Tag Manager to your Laravel application. Including support for User-ID, E-commerce and Server Side Events (Measurement Protocol).

- [Laravel support](#laravel-support)
- [Installation](#installation)
Expand All @@ -15,6 +15,9 @@ An easier way to add Google Tag Manager to your Laravel application. Including r
- [Ecommerce (GA4)](#ecommerce-ga4)
- [Ecommerce Item](#ecommerce-item)
- [Ecommerce Events](#ecommerce-events)
- [Server Side Events](#server-side-events)
- [Measurement Protocol](#measurement-protocol)
- [Measurement Protocol Debug Mode](#measurement-protocol-debug-mode)
- [Tests](#tests)
- [License](#license)

Expand Down Expand Up @@ -87,8 +90,6 @@ Go to ``https://tagmanager.google.com`` and copy the 'Container ID' of the accou
## Usage

```php
// DashboardController.php (example)

use Label84\TagManager\Facades\TagManager;

TagManager::push(['foo' => 'bar']);
Expand All @@ -112,7 +113,7 @@ You can find a list of recommended events on: [https://support.google.com/analyt

This package also supports the User-ID feature.

To start using the User-ID feature you've to add the TagManagerUserIdMiddleware in your 'web' middleware group directly after the TagManagerMiddleware.
To start using the User-ID feature you've to add the ``TagManagerUserIdMiddleware`` in your 'web' middleware group directly after the ``TagManagerMiddleware``.

```php
// app/Http/Kernel.php
Expand Down Expand Up @@ -196,6 +197,62 @@ TagManager::purchase('00001', 'Google', 'EUR', 12.10, 2.10, 0, [

More information: [https://developers.google.com/analytics/devguides/collection/ga4/ecommerce?client_type=gtm](https://developers.google.com/analytics/devguides/collection/ga4/ecommerce?client_type=gtm)

## Server Side Events

The Google Analytics Measurement Protocol allows developers to make HTTP requests to send raw user interaction data directly to Google Analytics servers. This allows developers to measure how users interact with their business from almost any environment. Developers can then use the Measurement Protocol to:

- Measure user activity in new environments.
- Tie online to offline behavior.
- Send data from both the client and server.

### Measurement Protocol

You need complete the general installation steps first, such as adding the ``.env`` variables, adding the head and body tags to your layout file and adding the ``TagManagerMiddleware``.

Add the following extra variables to your .env file.

```sh
// .env

# Found in the Google Analytics UI under Admin > Data Streams > choose your stream > Measurement ID. The measurement_id isn't your Stream ID.
GOOGLE_MEASUREMENT_ID=G-XXXXXX
# Found in the Google Analytics UI under Admin > Data Streams > choose your stream > Measurement Protocol API Secrets
GOOGLE_MEASUREMENT_PROTOCOL_API_SECRET=XXXXXX
```

Add the following snippet to the head of your blade layout file (below the existing ``x-tagmanager-head`` tag).

```html
<x-tagmanager-head />
<x-tagmanager-measurement-protocol-client-id />
</head>

<body>
```

```php
use Label84\TagManager\Facades\MeasurementProtocol;

MeasurementProtocol::event('some_event', ['foo' => 'bar']);
```

The User-ID feature is set automatically if the ``TagManagerUserIdMiddleware`` is enabled.

You can view the events directly in the Google Analytics UI under Realtime > Events.

### Measurement Protocol Debug Mode

You can enable the debug mode by calling the ``debug()`` method. This will return a JSON validation response instead of sending the request to Google Analytics.
If there are any errors, they will be returned in the validation messages response. If there are no errors, the validation response array will be empty.

```php
use Label84\TagManager\Facades\MeasurementProtocol;

dd(
MeasurementProtocol::debug()->event('some_event', ['foo' => 'bar'])
);
```

## Tests

```sh
Expand Down
23 changes: 22 additions & 1 deletion config/config.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@
'session_name' => 'tagmanager',

/*
* (optional)
* User ID
* https://developers.google.com/analytics/devguides/collection/ga4/user-id
*
* The key of the User model that will be used for the User-ID feature.
*
Expand All @@ -33,4 +34,24 @@
* https://developers.google.com/analytics/devguides/collection/ga4/policy
*/
'user_id_key' => env('GOOGLE_TAG_MANAGER_USER_ID_KEY', 'id'),

/**
* Measurement Protocol
* https://developers.google.com/analytics/devguides/collection/protocol/ga4/
*
* The measurement ID associated with a stream.
* Found in the Google Analytics UI under Admin > Data Streams > choose your stream > Measurement ID. The measurement_id isn't your Stream ID.
*/
'measurement_id' => env('GOOGLE_MEASUREMENT_ID'),

/**
* The API SECRET generated in the Google Analytics UI.
* Found in the Google Analytics UI under Admin > Data Streams > choose your stream > Measurement Protocol API Secrets.
*/
'measurement_protocol_api_secret' => env('GOOGLE_MEASUREMENT_PROTOCOL_API_SECRET'),

/**
* The session key used to store the measurement protocol client id.
*/
'measurement_protocol_client_id_session_key' => 'measurement-protocol-client-id'
];
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
@if($isEnabled)
<!-- Google Tag Manager Client ID -->
<script>
function getGoogleAnalyticsClientId() {
var cookie = {};
document.cookie.split(';').forEach(function(el) {
var splitCookie = el.split('=');
var key = splitCookie[0].trim();
var value = splitCookie[1];
cookie[key] = value;
});
storeGoogleAnalyticsClientId(cookie["_ga"].substring(6));
}
function storeGoogleAnalyticsClientId(clientId) {
var data = new FormData();
data.append('client_id', clientId);
var xhr = new XMLHttpRequest();
xhr.open('POST', '/tagmanager/store-measurement-protocol-client-id', true);
xhr.setRequestHeader('X-CSRF-TOKEN', '{{ csrf_token() }}');
xhr.send(data);
}
</script>

@if(session(config('tagmanager.measurement_protocol_client_id_session_key')) === null)
<script>
getGoogleAnalyticsClientId();
</script>
@endif
<!-- End Google Tag Manager Client ID -->
@endif
8 changes: 8 additions & 0 deletions routes/web.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
<?php

use Illuminate\Support\Facades\Route;
use Label84\TagManager\Http\Controllers\StoreMeasurementProtocolClientIdController;

Route::post('/tagmanager/store-measurement-protocol-client-id', StoreMeasurementProtocolClientIdController::class)
->middleware('web')
->name('tagmanager.store-measurement-protocol-client-id');
13 changes: 13 additions & 0 deletions src/Facades/MeasurementProtocol.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Label84\TagManager\Facades;

use Illuminate\Support\Facades\Facade;

class MeasurementProtocol extends Facade
{
protected static function getFacadeAccessor(): string
{
return 'measurement_protocol';
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<?php

namespace Label84\TagManager\Http\Controllers;

class StoreMeasurementProtocolClientIdController
{
public function __invoke(): void
{
session([
config('tagmanager.measurement_protocol_client_id_session_key') => request('client_id'),
]);
}
}
90 changes: 90 additions & 0 deletions src/MeasurementProtocol.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
<?php

namespace Label84\TagManager;

use Illuminate\Foundation\Auth\User;
use Illuminate\Support\Facades\Http;

class MeasurementProtocol
{
protected string $clientId;

protected string|int|null $userId = null;

protected bool $isDebugEnabled = false;

public function __construct()
{
$this->clientId = session(config('tagmanager.measurement_protocol_client_id_session_key'));
}

public function event(string $name, array $params = null): array
{
$event = [
'name' => $name,
];

if ($params !== null) {
$event['params'] = $params;
}

$response = Http::withHeaders([
'content-type' => 'application/json',
])
->withQueryParameters([
'measurement_id' => config('tagmanager.measurement_id'),
'api_secret' => config('tagmanager.measurement_protocol_api_secret'),
])
->post($this->route(), array_merge(
[
'client_id' => $this->clientId,
'events' => [$event],
],
$this->getUserIdArray(),
));

if($this->isDebugEnabled) {
return $response->json();
}

return [
'status' => $response->successful() ? 'success' : 'error',
];
}

public function debug(): self
{
$this->isDebugEnabled = true;

return $this;
}

public function user(User $user): self
{
$this->userId = "{$user->{config('tagmanager.user_id_key')}}";

return $this;
}

private function route(): string
{
$url = 'www.google-analytics.com';

if ($this->isDebugEnabled) {
$url = 'www.google-analytics.com/debug';
}

return "https://{$url}/mp/collect";
}

private function getUserIdArray(): array
{
if ($this->userId === null) {
return [];
}

return [
'user_id' => $this->userId,
];
}
}
9 changes: 9 additions & 0 deletions src/TagManagerServiceProvider.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Illuminate\Support\ServiceProvider;
use Label84\TagManager\View\Components\Body;
use Label84\TagManager\View\Components\Head;
use Label84\TagManager\View\Components\MeasurementProtocolClientId;

class TagManagerServiceProvider extends ServiceProvider
{
Expand All @@ -16,7 +17,12 @@ public function register(): void
return new TagManager();
});

$this->app->singleton(MeasurementProtocol::class, function ($app) {
return new MeasurementProtocol();
});

$this->app->alias(TagManager::class, 'tagmanager');
$this->app->alias(MeasurementProtocol::class, 'measurement_protocol');
}

public function boot(): void
Expand All @@ -27,11 +33,14 @@ public function boot(): void
], 'config');
}

$this->loadRoutesFrom(__DIR__.'/../routes/web.php');

$this->loadViewsFrom(__DIR__.'/../resources/views', 'tagmanager');

$this->loadViewComponentsAs('tagmanager', [
Head::class,
Body::class,
MeasurementProtocolClientId::class,
]);
}
}
21 changes: 21 additions & 0 deletions src/View/Components/MeasurementProtocolClientId.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<?php

namespace Label84\TagManager\View\Components;

use Illuminate\View\Component;
use Illuminate\View\View;

class MeasurementProtocolClientId extends Component
{
public bool $isEnabled;

public function __construct()
{
$this->isEnabled = config('tagmanager.enabled');
}

public function render(): View
{
return view('tagmanager::components.measurement-protocol-client-id');
}
}

0 comments on commit 6ea33b1

Please sign in to comment.