tutorial

Tutorial: Google Analytics 4 Custom Dashboard + Serverside Tracking în WordPress

Partea 1: Setup Google Analytics 4 API

Pas 1: Creează Service Account în Google Cloud

  1. Mergi la Google Cloud Console: console.cloud.google.com/

  2. Click Create Project → nume: “Analytics Blog” → Create

  3. Selectează project-ul nou creat din dropdown (sus)

  4. Mergi la APIs & ServicesLibrary

  5. Caută “Google Analytics Data API” → Click → Enable

Pas 2: Creează Service Account

  1. APIs & ServicesCredentials

  2. Click Create CredentialsService Account

  3. Nume: “analytics-reader” → Create

  4. Skip permisiuni (click ContinueDone)

Pas 3: Generează JSON Key

  1. Click pe service account creat (email care se termină în @...iam.gserviceaccount.com)

  2. Tab KeysAdd KeyCreate new key

  3. Format: JSONCreate

  4. Se descarcă automat credentials.jsonsalvează-l!

Pas 4: Adaugă Service Account la Google Analytics

  1. Mergi la Google Analytics: analytics.google.com/

  2. Click Admin (gear icon, jos stânga)

  3. Property Settings → Property Access Management

  4. Click +Add users

  5. Email: copiază email-ul service account (din step 2.3: analytics-reader@...iam.gserviceaccount.com)

  6. Role: ViewerAdd

Pas 5: Găsește Property ID

  1. În Google Analytics: AdminProperty Settings

  2. Copiază Property ID (ex: 448899004) – îl vei folosi mai târziu


Partea 2: Setup WordPress – Google Analytics Dashboard

Pas 6: Creează folder pentru credentials

bash
# Via FTP sau File Manager
wp-content/ga-credentials/

Încarcă credentials.json în acest folder.

IMPORTANT: Adaugă fișier index.php gol în folder pentru securitate:

php
<?php
// Empty index for security
?>

Pas 7: Creează Template pentru dashboard

Locație: /wp-content/themes/YOUR-THEME/page-trafic.php

php
<?php
/**
* Template Name: GA Traffic Dashboard
*/
get_header();

$cache_key = 'ga_analytics_data_all';
$cache_time = 30 * MINUTE_IN_SECONDS;

$cached_data = get_transient($cache_key);
if ($cached_data) {
$ga_data = $cached_data['ga_data'];
$ga2_data = $cached_data['ga2_data'];
$pages_data = $cached_data['pages_data'];
$devices_data = $cached_data['devices_data'];
} else {

$credentials_path = WP_CONTENT_DIR . '/ga-credentials/credentials.json';
$credentials = json_decode(file_get_contents($credentials_path), true);
$property_id = 'YOUR_PROPERTY_ID'; // Paste Property ID din Pas 5

$client_email = $credentials['client_email'];
$private_key = $credentials['private_key'];

$now = time();
$header = ['alg' => 'RS256', 'typ' => 'JWT'];
$claim = [
'iss' => $client_email,
'scope' => 'https://www.googleapis.com/auth/analytics.readonly',
'aud' => 'https://oauth2.googleapis.com/token',
'exp' => $now + 3600,
'iat' => $now
];

$header64 = strtr(rtrim(base64_encode(json_encode($header)), '='), '+/', '-_');
$claim64 = strtr(rtrim(base64_encode(json_encode($claim)), '='), '+/', '-_');
$sign_input = $header64 . '.' . $claim64;

openssl_sign($sign_input, $signature, $private_key, 'RSA-SHA256');
$signature64 = strtr(rtrim(base64_encode($signature), '='), '+/', '-_');
$jwt = $sign_input . '.' . $signature64;

$ch = curl_init('https://oauth2.googleapis.com/token');
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true, CURLOPT_POST => true, CURLOPT_POSTFIELDS => http_build_query(['grant_type' => 'urn:ietf:params:oauth:grant-type:jwt-bearer', 'assertion' => $jwt]), CURLOPT_TIMEOUT => 10]);
$token_response = curl_exec($ch);
curl_close($ch);

$token_data = json_decode($token_response, true);
$access_token = $token_data['access_token'] ?? null;

if (!$access_token) {
echo '<p>Error: No token</p>';
get_footer();
return;
}

// 30 DAYS METRICS
$ch = curl_init("https://analyticsdata.googleapis.com/v1beta/properties/{$property_id}:runReport");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $access_token, 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'dateRanges' => [['startDate' => '30daysAgo', 'endDate' => 'today']],
'metrics' => [
['name' => 'screenPageViews'],
['name' => 'activeUsers'],
['name' => 'sessions'],
['name' => 'averageSessionDuration'],
['name' => 'engagementRate'],
['name' => 'eventCount'],
['name' => 'conversions'],
['name' => 'totalUsers']
]
]),
CURLOPT_TIMEOUT => 10
]);
$ga_response = curl_exec($ch);
curl_close($ch);
$ga_data = json_decode($ga_response, true);

// TODAY vs YESTERDAY
$ch = curl_init("https://analyticsdata.googleapis.com/v1beta/properties/{$property_id}:runReport");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $access_token, 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'dateRanges' => [
['startDate' => 'today', 'endDate' => 'today'],
['startDate' => 'yesterday', 'endDate' => 'yesterday']
],
'metrics' => [
['name' => 'screenPageViews'],
['name' => 'activeUsers'],
['name' => 'sessions']
]
]),
CURLOPT_TIMEOUT => 10
]);
$ga2_response = curl_exec($ch);
curl_close($ch);
$ga2_data = json_decode($ga2_response, true);

// TOP PAGES
$ch = curl_init("https://analyticsdata.googleapis.com/v1beta/properties/{$property_id}:runReport");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $access_token, 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'dateRanges' => [['startDate' => '7daysAgo', 'endDate' => 'today']],
'metrics' => [['name' => 'screenPageViews']],
'dimensions' => [['name' => 'pagePath']],
'orderBys' => [['metric' => ['metricName' => 'screenPageViews'], 'desc' => true]],
'limit' => 10
]),
CURLOPT_TIMEOUT => 10
]);
$pages_response = curl_exec($ch);
curl_close($ch);
$pages_data = json_decode($pages_response, true);

// DEVICES
$ch = curl_init("https://analyticsdata.googleapis.com/v1beta/properties/{$property_id}:runReport");
curl_setopt_array($ch, [
CURLOPT_RETURNTRANSFER => true,
CURLOPT_POST => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . $access_token, 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode([
'dateRanges' => [['startDate' => '7daysAgo', 'endDate' => 'today']],
'metrics' => [['name' => 'screenPageViews']],
'dimensions' => [['name' => 'deviceCategory']],
'orderBys' => [['metric' => ['metricName' => 'screenPageViews'], 'desc' => true]]
]),
CURLOPT_TIMEOUT => 10
]);
$devices_response = curl_exec($ch);
curl_close($ch);
$devices_data = json_decode($devices_response, true);

// SAVE CACHE
set_transient($cache_key, [
'ga_data' => $ga_data,
'ga2_data' => $ga2_data,
'pages_data' => $pages_data,
'devices_data' => $devices_data
], $cache_time);
}
?>

<div style="padding: 40px 20px; background: #f8f9fa; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;">
<h1 style="text-align: center; margin-bottom: 40px; color: #2d2d2d;">📊 Analytics</h1>

<?php if (isset($ga_data['rows'][0])): $row = $ga_data['rows'][0]; ?>
<div style="background: white; padding: 20px; border-radius: 8px; margin-bottom: 20px;">
<h2 style="margin-top: 0; border-bottom: 2px solid #4285f4; padding-bottom: 10px;">Ultimele 30 de zile</h2>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 15px;">
<div style="text-align: center; padding: 15px; background: #f9f9f9; border-radius: 4px;">
<small style="color: #999; display: block; text-transform: uppercase; font-size: 11px; font-weight: 600;">Views</small>
<strong style="font-size: 26px; color: #4285f4; display: block;"><?php echo number_format((int)($row['metricValues'][0]['value'] ?? 0)); ?></strong>
</div>
<div style="text-align: center; padding: 15px; background: #f9f9f9; border-radius: 4px;">
<small style="color: #999; display: block; text-transform: uppercase; font-size: 11px; font-weight: 600;">Active Users</small>
<strong style="font-size: 26px; color: #34a853; display: block;"><?php echo number_format((int)($row['metricValues'][1]['value'] ?? 0)); ?></strong>
</div>
<div style="text-align: center; padding: 15px; background: #f9f9f9; border-radius: 4px;">
<small style="color: #999; display: block; text-transform: uppercase; font-size: 11px; font-weight: 600;">Sessions</small>
<strong style="font-size: 26px; color: #fbbc04; display: block;"><?php echo number_format((int)($row['metricValues'][2]['value'] ?? 0)); ?></strong>
</div>
<div style="text-align: center; padding: 15px; background: #f9f9f9; border-radius: 4px;">
<small style="color: #999; display: block; text-transform: uppercase; font-size: 11px; font-weight: 600;">Events</small>
<strong style="font-size: 26px; color: #fbbc04; display: block;"><?php echo number_format((int)($row['metricValues'][5]['value'] ?? 0)); ?></strong>
</div>
</div>
</div>
<?php endif; ?>

<?php if (isset($ga2_data['rows'][0]) && isset($ga2_data['rows'][1])): $today = $ga2_data['rows'][0]; $yesterday = $ga2_data['rows'][1]; ?>
<div style="background: white; padding: 20px; border-radius: 8px; margin-bottom: 20px;">
<h2 style="margin-top: 0; border-bottom: 2px solid #34a853; padding-bottom: 10px;">Today vs Yesterday</h2>
<div style="display: grid; grid-template-columns: 1fr 1fr; gap: 20px;">
<div>
<h3 style="color: #2d2d2d; margin-top: 0;">📅 Today</h3>
<div style="display: grid; gap: 8px;">
<div style="display: flex; justify-content: space-between; padding: 8px; background: #f9f9f9; border-radius: 4px;"><span>Views:</span><strong><?php echo number_format((int)($today['metricValues'][0]['value'] ?? 0)); ?></strong></div>
<div style="display: flex; justify-content: space-between; padding: 8px; background: #f9f9f9; border-radius: 4px;"><span>Users:</span><strong><?php echo number_format((int)($today['metricValues'][1]['value'] ?? 0)); ?></strong></div>
</div>
</div>
<div>
<h3 style="color: #2d2d2d; margin-top: 0;">📅 Yesterday</h3>
<div style="display: grid; gap: 8px;">
<div style="display: flex; justify-content: space-between; padding: 8px; background: #f9f9f9; border-radius: 4px;"><span>Views:</span><strong><?php echo number_format((int)($yesterday['metricValues'][0]['value'] ?? 0)); ?></strong></div>
<div style="display: flex; justify-content: space-between; padding: 8px; background: #f9f9f9; border-radius: 4px;"><span>Users:</span><strong><?php echo number_format((int)($yesterday['metricValues'][1]['value'] ?? 0)); ?></strong></div>
</div>
</div>
</div>
</div>
<?php endif; ?>

<?php if (isset($pages_data['rows']) && count($pages_data['rows']) > 0): ?>
<div style="background: white; padding: 20px; border-radius: 8px; margin-bottom: 20px;">
<h2 style="margin-top: 0; border-bottom: 2px solid #fbbc04; padding-bottom: 10px;">🔥 Top Pages (7 days)</h2>
<table style="width: 100%; border-collapse: collapse;">
<?php foreach (array_slice($pages_data['rows'], 0, 10) as $page): ?>
<tr style="border-bottom: 1px solid #f0f0f0;">
<td style="padding: 10px; font-size: 14px;"><?php echo htmlspecialchars($page['dimensionValues'][0]['value']); ?></td>
<td style="padding: 10px; text-align: right; font-weight: 600; color: #4285f4;"><?php echo number_format((int)($page['metricValues'][0]['value'] ?? 0)); ?></td>
</tr>
<?php endforeach; ?>
</table>
</div>
<?php endif; ?>

<?php if (isset($devices_data['rows']) && count($devices_data['rows']) > 0): ?>
<div style="background: white; padding: 20px; border-radius: 8px;">
<h2 style="margin-top: 0; border-bottom: 2px solid #ea4335; padding-bottom: 10px;">📱 Devices (7 days)</h2>
<div style="display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr)); gap: 15px;">
<?php foreach ($devices_data['rows'] as $device): ?>
<div style="text-align: center; padding: 15px; background: #f9f9f9; border-radius: 4px;">
<small style="color: #999; display: block; text-transform: uppercase; font-size: 11px; font-weight: 600;"><?php echo htmlspecialchars($device['dimensionValues'][0]['value']); ?></small>
<strong style="font-size: 24px; color: #4285f4; display: block;"><?php echo number_format((int)($device['metricValues'][0]['value'] ?? 0)); ?></strong>
</div>
<?php endforeach; ?>
</div>
</div>
<?php endif; ?>

</div>

<?php get_footer();

Pas 8: Creează pagină în WordPress

  1. WordPress AdminPagesAdd New

  2. Titlu: “Trafic”

  3. Template: selectează GA Traffic Dashboard

  4. Publish

  5. Vizualizează pagina: yoursite.com/trafic



Checklist Final

  • ✅ Google Cloud Project creat

  • ✅ Analytics Data API activată

  • ✅ Service Account creat cu JSON key

  • ✅ Service Account adăugat în GA cu rol Viewer

  • credentials.json încărcat în /wp-content/ga-credentials/

  • page-trafic.php creat cu Property ID setat

  • ✅ Pagină WordPress creată cu template

  • Gata! Accesează /trafic și vezi dashboard-ul tău custom!

Ce a mai cautat lumea pe Google

  • afinele beneficii
  • boala celine dion
  • artă naivă
  • apartamente mamaia nord
  • periferisan prospect
  • povesti cu swingeri
  • bratara cu ochi
  • biciclete copii 4 ani
  • arsuri de gradul 2
  • aparate fitnes pentru acasa

Mulțumesc că ai citit postarea.

Poți să îmi susții eforturile pe Patreon.

Susține pe Patreon
My Story

ADAUGA COMENTARIU

Dacă ești la primele comentarii aici, nu încerca să faci pe deșteptul

  • Comentacii pesedisti nu sunt bineveniti
  • Nu incerca sa fii mai arogant decat mine, la mine in casa
  • Linkurile sunt foarte utile, daca nu sunt spam