From: Stephen Cameron Date: Wed, 21 Sep 2022 16:20:01 +0000 (+0200) Subject: Cubist Matomo Reporting API library. Wait #5316 @20 X-Git-Url: http://git.cubedesigners.com/?a=commitdiff_plain;h=16b89dc9bd52116a343a0ef120311e06c0f3112f;p=cubist_matomo.git Cubist Matomo Reporting API library. Wait #5316 @20 --- diff --git a/src/Reporting.php b/src/Reporting.php index b54c9fb..9280143 100644 --- a/src/Reporting.php +++ b/src/Reporting.php @@ -3,29 +3,257 @@ namespace Cubist\Matomo; use GuzzleHttp\Client; +use GuzzleHttp\Exception\RequestException; +// Matomo Reporting API Wrapper +// API Reference: https://developer.matomo.org/api-reference/reporting-api class Reporting { + const PERIOD_DAY = 'day'; + const PERIOD_WEEK = 'week'; + const PERIOD_MONTH = 'month'; + const PERIOD_YEAR = 'year'; + const PERIOD_RANGE = 'range'; + + const DATE_TODAY = 'today'; + const DATE_YESTERDAY = 'yesterday'; + + const FORMAT_XML = 'xml'; + const FORMAT_JSON = 'json'; + const FORMAT_CSV = 'csv'; + const FORMAT_TSV = 'tsv'; + const FORMAT_HTML = 'html'; + const FORMAT_RSS = 'rss'; + const FORMAT_PHP = 'php'; + const FORMAT_ORIGINAL = 'original'; + + /** + * @var string The base URL of the Matomo server + */ + protected $serverUrl; /** - * @var string + * @var string The API access token */ protected $token; /** - * @var Client + * @var integer The Matomo ID of the website + */ + protected $siteId; + + /** + * @var string The date / date range for the report + */ + protected $date = ''; + + /** + * @var string The period used to group the statistics + */ + protected $period; + + /** + * @var string What format the API response should return + */ + protected $format; + + /** + * @var string Language for translatable strings + */ + protected $language = 'en'; + + /** + * @var Client Guzzle HTTP Client for making requests */ protected $client; /** - * @var null|array + * @var string Stores the URL of the last API call for debugging purposes + */ + protected $lastApiUrl = ''; + + /** + * @var null|array Cached list of sites defined in Matomo, keyed by ID + */ + protected $allSites = null; + + public function __construct( + $baseUrl, + $token, + $siteId = null, + $date = self::DATE_YESTERDAY, + $period = self::PERIOD_DAY, + $format = self::FORMAT_JSON + ) { + $this->serverUrl = $baseUrl; + $this->token = $token; + $this->siteId = $siteId; + $this->date = $date; + $this->period = $period; + $this->format = $format; + $this->client = new Client(['base_uri' => $this->serverUrl]); + } + + /** + * Get API token + * + * @return string */ - protected $_allSites = null; + public function getToken(): string + { + return $this->token; + } - public function __construct($baseURL, $token) + /** + * Set API token + * + * @param string $token + * @return $this + */ + public function setToken(string $token): Reporting { $this->token = $token; - $this->client = new Client(['base_uri' => $baseURL]); + + return $this; + } + + /** + * Get current site ID + * + * @return mixed + */ + public function getSiteId() + { + return $this->siteId; + } + + /** + * Set current site ID + * + * @param mixed $id + * @return $this + */ + public function setSiteId($id = null): Reporting + { + $this->siteId = $id; + return $this; + } + + /** + * Get response format + * + * @return string + */ + public function getFormat(): string + { + return $this->format; + } + + /** + * Set response format + * + * @param string $format + * FORMAT_XML + * FORMAT_JSON + * FORMAT_CSV + * FORMAT_TSV + * FORMAT_HTML + * FORMAT_RSS + * FORMAT_PHP + * FORMAT_ORIGINAL + * @return $this + */ + public function setFormat(string $format): Reporting + { + $this->format = $format; + return $this; + } + + /** + * Get language + * + * @return string + */ + public function getLanguage(): string + { + return $this->language; + } + + /** + * Set language + * + * @param string $language + * @return $this + */ + public function setLanguage(string $language): Reporting + { + $this->language = $language; + return $this; + } + + /** + * Get date + * + * @return string + */ + public function getDate(): string + { + return $this->date; + } + + /** + * Set date + * + * @param string|null $date Format Y-m-d or class constant: + * DATE_TODAY + * DATE_YESTERDAY + * @return $this + */ + public function setDate(string $date = null): Reporting + { + $this->date = $date; + return $this; + } + + /** + * Get period + * + * @return string + */ + public function getPeriod(): string + { + return $this->period; + } + + /** + * Set time period + * + * @param string $period + * PERIOD_DAY + * PERIOD_MONTH + * PERIOD_WEEK + * PERIOD_YEAR + * PERIOD_RANGE + * @return $this + */ + public function setPeriod(string $period): Reporting + { + $this->period = $period; + return $this; + } + + /** + * @return string + */ + public function getServerUrl(): string { + return $this->serverUrl; + } + + /** + * @return string + */ + public function getLastApiUrl(): string { + return $this->lastApiUrl; } /** @@ -33,14 +261,14 @@ class Reporting */ public function getAllSites() { - if (null === $this->_allSites) { - $a = $this->_apicall('SitesManager.getAllSites'); - $this->_allSites = []; + if (null === $this->allSites) { + $a = $this->apiCall('SitesManager.getAllSites'); + $this->allSites = []; foreach ($a as $item) { - $this->_allSites[$item['idsite']] = $item; + $this->allSites[$item['idsite']] = $item; } } - return $this->_allSites; + return $this->allSites; } /** @@ -52,7 +280,7 @@ class Reporting */ public function createSite($name, $url, $search = true, $ecommerce = false) { - $res = $this->_apicall('SitesManager.addSite', ['siteName' => $name, 'urls' => $url, 'ecommerce' => $ecommerce, 'siteSearch' => $search]); + $res = $this->apiCall('SitesManager.addSite', ['siteName' => $name, 'urls' => $url, 'ecommerce' => $ecommerce, 'siteSearch' => $search]); return $res['value'] ?? null; } @@ -74,10 +302,10 @@ class Reporting public function deleteSite($siteId) { - if (isset($this->_allSites[$siteId])) { - unset($this->_allSites[$siteId]); + if (isset($this->allSites[$siteId])) { + unset($this->allSites[$siteId]); } - return $this->_apicall('SitesManager.deleteSite', ['idSite'=>$siteId]); + return $this->apiCall('SitesManager.deleteSite', ['idSite' => $siteId]); } /** @@ -86,22 +314,76 @@ class Reporting */ public function getSiteIdByURL($url) { - $res = $this->_apicall('SitesManager.getSitesIdFromSiteUrl', ['url' => $url]); + $res = $this->apiCall('SitesManager.getSitesIdFromSiteUrl', ['url' => $url]); return $res[0]['idsite'] ?? null; } - protected function _apicall($method, $params = [], $options = []) + public function getVisits($params = []) { + return $this->apiCall('VisitsSummary.get', $params); + } + + public function getPageViews($params = []) { + return $this->apiCall('Actions.get', $params); + } + + public function getPageUrls($params = []) { + return $this->apiCall('Actions.getPageUrls', $params); + } + + public function getSearchKeywords($params = []) { + return $this->apiCall('Actions.getSiteSearchKeywords', $params); + } + + public function getEventsByCategory($params = []) { + // Note: Passing the parameter 'expanded' => 1 will include the sub-table data + return $this->apiCall('Events.getCategory', $params + ['secondaryDimension' => 'eventName', 'expanded' => 0]); + } + + public function getOutlinks($params = []) { + return $this->apiCall('Actions.getOutlinks', $params); + } + + public function getCountries($params = []) { + return $this->apiCall('UserCountry.getCountry', $params); + } + + /** + * @throws \Exception|RequestException + */ + public function apiCall($method, $params = [], $options = []) { $default = [ - 'method' => $method, + 'token_auth' => $this->getToken(), + 'idSite' => $this->getSiteId(), 'module' => 'API', - 'token_auth' => $this->token, - 'format' => 'JSON' + 'method' => $method, + 'date' => $this->getDate(), + 'period' => $this->getPeriod(), + 'format' => $this->getFormat(), + 'language' => $this->getLanguage(), + 'filter_limit' => -1, ]; $defaultOptions = ['query' => array_merge($default, $params)]; - $response = $this->client->get('/', array_merge($defaultOptions, $options)); - return json_decode($response->getBody()->getContents(), true); + try { + $response = $this->client->get('/', array_merge($defaultOptions, $options) + + ['on_stats' => function ($stats) { + $this->lastApiUrl = $stats->getEffectiveUri(); // Store requested API URL for debugging purposes + }] + ); + + } catch (RequestException $e) { + // Just return the error instead of the full message because it contains the URL, which reveals the auth token. + throw new \Exception("Error: API call for method '{$method}' failed. {$e->getHandlerContext()['error']}"); + } + + $API_response = json_decode($response->getBody()->getContents(), true); + + if (isset($API_response['result']) && $API_response['result'] === 'error') { + throw new \Exception("Error: {$API_response['message']}"); + } + + return $API_response; } -} \ No newline at end of file +}