]> _ Git - cubist_matomo.git/commitdiff
Cubist Matomo Reporting API library. Wait #5316 @20
authorStephen Cameron <stephen@cubedesigners.com>
Wed, 21 Sep 2022 16:20:01 +0000 (18:20 +0200)
committerStephen Cameron <stephen@cubedesigners.com>
Wed, 21 Sep 2022 16:20:01 +0000 (18:20 +0200)
src/Reporting.php

index b54c9fbc023d781d8e83919bed43d77c2cbe399a..92801435a570aced6c3f029309811d5e8b33317a 100644 (file)
 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
+}