UPDATE: Ich habe eine Antwort auf meine Frage hinzugefügt, die mein Problem löst (obwohl es nicht 100% getestet) - muss immer noch auf die Token-Aktualisierung warten. Würde immer noch gerne andere Eingaben hören.Spaß mit Google Analytics API; Token-Fehler
Ich habe ein Problem bei der Arbeit mit Google Analytics API für PHP.
Ich initiiere die API (das vollständige Skript davon kann here gefunden werden; es ist eine ältere Version) über eine Funktion in meinem Konstruktor aufgerufen, wo ich alle Variablen der API einrichten und definieren und dann überprüfen, um sicherzustellen Ich habe ein gültiges Token. Ich habe einen AJAX-Controller, der 4 Funktionen in meiner Klasse asynchron aufruft, um dem Benutzer über googles chart api relevante Analytics-Statistiken anzuzeigen. Wenn ein Fehler auftritt, wird in diesem bestimmten Diagrammblock ein Fehler angezeigt. Wenn wir einen Fehler haben, besteht eine 100% ige Chance, dass der Fehler davon kommt, ob wir ein richtiges Token erstellen oder nicht, da die Anweisungen, die die Daten von GA erhalten, vorbereitet werden.
Da alle 4 Diagramme/Anzeigen verschiedenen Funktionen in der Klasse entsprechen, führen wir 4 Abfragen zu Google Analytics aus. Manchmal erhält eines der Diagramme jedoch eine Token-Fehler-Antwort, manchmal mehr als eines der Diagramme ... Manchmal passiert es gar nicht. Ich dachte mir, dass dies möglicherweise auf die Tatsache zurückzuführen war, dass ich im AJAX-Aufruf meine Klasse als new
initiierte, anstatt eine Instanz der Klasse zu bekommen, und daher nur 1 Anruf, z. ein Konstrukt der Klasse, um unser Token zu erhalten. Also implementierte ich ein Singleton-Design, das die Klasse einmal über $ga = Google_Analytics_Stats::getInstance($gaInfo);
aufruft, aber das Problem bleibt bestehen.
Weiß jemand, was diesen intermittierenden Token-Fehler verursachen könnte?
Aus Gründen der Klarheit
Google_Analytics_Stats
initialisiert GoogleAnalyticsAPI
in initAnalytics($gaInfo)
wo $gaInfo
ist das entsprechende Konto und E-Mail-Informationen. Ajax.php schaltet zwischen Funktionen auf Typ basiert, die in Google_Analytics_Stats
sind, die nur einmal initialisiert wird mit $ga = Google_Analytics_Stats::getInstance($gaInfo);
AJAX-Aufruf:
<?php
require ('../framework.php');
$login->protect(false);
if (!isAjax()) {
redirect('index.php');
}
// dont forget these functions use date so user timezone effects it!!!
error_reporting(0);
$ga = Google_Analytics_Stats::getInstance($gaInfo);
switch (isset($_GET['type']) ? $_GET['type'] : '') {
case 'map':
if (isset($_GET['ga_length'])) {
echo $ga->getVisitsByCountries($_GET['ga_length']);
}
else {
$error = array(
"status" => "error",
"error" => 'No length defined'
);
echo json_encode($error);
}
break;
case 'bounceRate':
echo $ga->displayBounceRate();
break;
case 'visitsToday':
echo $ga->getVisitsToday();
break;
case 'momVisits':
echo $ga->displayMonthOverMonthVisits();
break;
case 'newVsReturn':
if (isset($_GET['ga_length'])) {
echo $ga->getNewVsReturn($_GET['ga_length']);
}
else {
$error = array(
"status" => "error",
"error" => 'No length defined'
);
echo json_encode($error);
}
break;
case 'visitsByDate':
if (isset($_GET['ga_length'])) {
echo $ga->getVisitsByDate($_GET['ga_length']);
}
else {
$error = array(
"status" => "error",
"error" => 'No length defined'
);
echo json_encode($error);
}
break;
}
?>
Hauptcode (nur 1. erforderlichen Teile für Konzeptualisierung):
class Google_Analytics_Stats {
private $ga;
// array for default $len
private $bounce_rate_current = '';
private $monthly_visitors_current = '';
// rounding precision
private $precision = 1;
private $len;
public static $instance;
function __construct($gaInfo)
{
$this->initAnalytics($gaInfo);
$this->len = array(
'start-date' => date(DATE_FORMAT, strtotime('-1 month')),
'end-date' => date(DATE_FORMAT)
);
}
public static function getInstance($gaInfo)
{
if (!isset(self::$instance)) {
self::$instance = new Google_Analytics_Stats($gaInfo);
}
return self::$instance;
}
private function initAnalytics($gaInfo)
{
try {
$ga = new GoogleAnalyticsAPI('service');
$accountId = $gaInfo['account_id'];
$ga->auth->setClientId($gaInfo['client_id']);
$ga->auth->setEmail($gaInfo['email']);
$ga->auth->setPrivateKey($gaInfo['key_src']);
$auth = $ga->auth->getAccessToken();
// try and get access token
if ($auth['http_code'] == 200) {
$accessToken = $auth['access_token'];
$tokenExpires = $auth['expires_in'];
$tokenCreated = time();
}
else {
throw new Exception('Token error');
}
// set token and account id
$ga->setAccessToken($accessToken);
$ga->setAccountId($accountId);
// set defaults
/*
if (isset($this->len)) {
$ga->setDefaultQueryParams($this->len);
}
*/
$this->ga = $ga;
}
catch (Exception $e) {
$error = array(
"status" => "error",
"error" => $e->getMessage()
);
//output result
echo json_encode($error);
}
}
public function getBounceRate($params = '')
{
$defaults = array('metrics' => 'ga:bounceRate');
$_params = array_merge($defaults, $params);
$result = $this->ga->_query($_params);
return $result['rows'][0][0];
}
public function getBounceRateCurrent()
{
$params = array(
'start-date' => date(DATE_FORMAT, strtotime('-1 month')),
'end-date' => date(DATE_FORMAT) //now
);
$result = $this->getBounceRate($params);
return round($result, $this->precision);
}
public function bounceRateTextual($percentage)
{
if ($this->inRange($percentage, 0, 25.9)) {
return array('display' => 'success', 'text' => 'Extremel low');
}
elseif ($this->inRange($percentage, 26, 40.9)) {
return array('display' => 'success', 'text' => 'Below average');
}
elseif ($this->inRange($percentage, 41, 55.9)) {
return array('display' => 'warning', 'text' => 'Average');
}
elseif ($this->inRange($percentage, 56, 70.9)) {
return array('display' => 'danger', 'text' => 'Higher than average');
}
else {
return array('display' => 'danger', 'text' => 'Extremely high');
}
}
public function displayBounceRate()
{
// has to be called first or current will be empty
$current = $this->getBounceRateCurrent();
$out = '<h6 class="text-uppercase">Bounce Rate this month</h6>';
$out .= '<h1>' . $current . '%</h1>';
$changeWrapper = "<span class='text-muted mr-0-5'>Rating:</span><span class='label label-%s label-small'>%s</span>";
$textual = $this->bounceRateTextual($current);
$out .= sprintf($changeWrapper, $textual['display'], $textual['text']);
return $out;
}
Singleton oder nicht, jeder Ajax-Aufruf wird geladen ein brandneues Anfrage an Ihren Backend und werden daher neue Instanzen Ihrer erstellen Klassen. Verschiedene Ajax-Aufrufe können nicht dieselbe Instanz verwenden. Sie könnten einen Ajax-Aufruf machen, der alle benötigten Charts auf einmal zurückgibt und nur im Frontend separat anzeigt? –
Hey magnus danke für die Antwort.Leider kann ich das nicht tun, da ich direkt in Google-Diagramme rendere, nicht zu erwähnen, dass es nicht überschaubar wäre. Ich denke, was das lässt, ist eine andere Lösung zu finden, vielleicht basierend darauf, warum nachfolgende Token-Anfragen (manchmal) verweigert werden – Alex