Firesphere/silverstripe-google-api

View on GitHub
code/Services/GoogleAnalyticsReportService.php

Summary

Maintainability
A
0 mins
Test Coverage
<?php

/**
 * Class GoogleAnalyticsReportService
 *
 * Base service for getting the Analytics report per page from Google.
 */
class GoogleAnalyticsReportService
{

    /**
     * @var GoogleClientService
     */
    protected $client;

    /**
     * @var Google_Service_AnalyticsReporting_DateRange
     */
    protected $dateRange;

    /**
     * @var Google_Service_AnalyticsReporting
     */
    protected $analytics;

    /**
     * @var Google_Service_AnalyticsReporting_Metric
     */
    protected $metrics;

    /**
     * Should the filter being batched
     *
     * @var bool
     */
    public $batched = false;

    /**
     * GoogleReportService constructor.
     * @param GoogleClientService $client
     */
    public function __construct($client)
    {
        $config = SiteConfig::current_site_config();
        $this->client = $client;
        $this->setDateRange($config->DateRange);
        $this->setAnalytics(new Google_Service_AnalyticsReporting($client->getClient()));
        $this->setMetrics($config->Metric);
    }

    /**
     * @return Google_Service_AnalyticsReporting_GetReportsResponse
     */
    public function getReport()
    {
        $request = $this->getReportRequest();

        $body = $this->getGetReportRequest($request);

        return $this->analytics->reports->batchGet($body);
    }

    /**
     * @return Google_Service_AnalyticsReporting_ReportRequest
     */
    public function getReportRequest()
    {
        $config = SiteConfig::current_site_config();

        $request = new Google_Service_AnalyticsReporting_ReportRequest();
        $dimension = $this->getDimensions();
        $request->setDimensions($dimension);
        $request->setViewId($config->Viewid);
        $request->setDimensionFilterClauses($this->getDimensionFilterClauses());
        $request->setDateRanges($this->getDateRange());
        $request->setMetrics([$this->getMetrics()]);

        return $request;
    }

    /**
     * Yes, thanks to Google's naming, we're doubling up the get here
     *
     * @param $request
     * @return Google_Service_AnalyticsReporting_GetReportsRequest
     */
    public function getGetReportRequest($request)
    {
        $body = new Google_Service_AnalyticsReporting_GetReportsRequest();
        $body->setReportRequests([$request]);

        return $body;
    }

    public function getDimensions()
    {
        $dimension = new Google_Service_AnalyticsReporting_Dimension();
        $dimension->setName('ga:pagePath');

        return [$dimension];
    }

    /**
     * @return mixed
     */
    public function getClient()
    {
        return $this->client;
    }

    /**
     * @param mixed $client
     */
    public function setClient($client)
    {
        $this->client = $client;
    }

    /**
     * @return Google_Service_AnalyticsReporting_DateRange
     */
    public function getDateRange()
    {
        return $this->dateRange;
    }

    /**
     * @param int $dateRange
     */
    public function setDateRange($dateRange)
    {
        $analyticsRange = new Google_Service_AnalyticsReporting_DateRange();
        $analyticsRange->setStartDate($dateRange . 'daysAgo');
        $analyticsRange->setEndDate('today');
        $this->dateRange = $analyticsRange;
    }

    /**
     * @return Google_Service_AnalyticsReporting
     */
    public function getAnalytics()
    {
        return $this->analytics;
    }

    /**
     * @param Google_Service_AnalyticsReporting $analytics
     */
    public function setAnalytics($analytics)
    {
        $this->analytics = $analytics;
    }

    /**
     * @return mixed
     */
    public function getMetrics()
    {
        return $this->metrics;
    }

    /**
     * @param mixed $metrics
     */
    public function setMetrics($metrics)
    {
        $metric = new Google_Service_AnalyticsReporting_Metric();
        $metric->setExpression($metrics);
        $this->metrics = $metric;
    }

    /**
     * Set up the Dimension Filters
     *
     * @return array|Google_Service_AnalyticsReporting_DimensionFilter[]
     */
    public function getDimensionFilters()
    {
        $filters = [];
        $startWith = config::inst()->get(static::class, 'starts_with');
        $endWith = config::inst()->get(static::class, 'ends_with');
        if ($startWith) {
            return $this->createFilter('BEGINS_WITH', $startWith, $filters);
        }
        if ($endWith) {
            return $this->createFilter('ENDS_WITH', $endWith, $filters);
        }

        return $this->getPageDimensionFilters();
    }

    /**
     * @return array|Google_Service_AnalyticsReporting_DimensionFilter[]
     */
    public function getPageDimensionFilters()
    {
        $filters = [];
        // Operators are not constants, see here: https://developers.google.com/analytics/devguides/reporting/core/v4/rest/v4/reports/batchGet#Operator
        $operator = 'ENDS_WITH';
        $pages = $this->getPages();

        foreach ($pages as $page) {
            // Home is recorded as just a slash in GA, we need an
            // exception for that
            if ($page === 'home') {
                $page = '/';
                $operator = 'EXACT';
            }

            $filters = $this->createFilter($operator, $page, $filters);
            // Reset the operator
            if ($operator === 'EXACT') {
                $operator = 'ENDS_WITH';
            }
        }

        return $filters;
    }

    /**
     * @return Google_Service_AnalyticsReporting_DimensionFilterClause
     */
    public function getDimensionFilterClauses()
    {
        $dimensionFilters = $this->getDimensionFilters();
        $dimensionClauses = new Google_Service_AnalyticsReporting_DimensionFilterClause();
        $dimensionClauses->setFilters($dimensionFilters);
        $dimensionClauses->setOperator('OR');

        return $dimensionClauses;
    }

    /**
     * Get the pages filtered by optional blacklisting and whitelisting
     * Return an array of the URLSegments, to limit memory abuse
     * @return array
     */
    public function getPages()
    {
        $blacklist = Config::inst()->get(static::class, 'blacklist');
        $whitelist = Config::inst()->get(static::class, 'whitelist');
        /** @var DataList|Page[] $pages */
        $pages = SiteTree::get();
        if ($blacklist) {
            $pages = $this->getBlacklist($blacklist, $pages);
        }
        if ($whitelist) {
            $pages = $this->getWhitelistPages($whitelist, $pages);
        }

        if ($pages->count() > 20) {
            $this->batched = true;
        }

        // Google limits to 20 reports per complex filter setup
        return $pages->limit(20)->column('URLSegment');
    }

    /**
     * @param array $blacklist
     * @param DataList $pages
     * @return DataList
     */
    protected function getBlacklist($blacklist, $pages)
    {
        $ids = [];
        foreach ($blacklist as $class) {
            $blacklistpages = $class::get();
            $ids = array_merge($ids, $blacklistpages->column('ID'));
        }
        $pages = $pages->exclude(['ID' => $ids]);

        return $pages;
    }

    /**
     * @param array $whitelist
     * @param DataList $pages
     * @return DataList
     */
    protected function getWhitelistPages($whitelist, $pages)
    {
        $ids = [];
        foreach ($whitelist as $class) {
            $nowDate = date('Y-m-d');
            $whitelistpages = $class::get()
                // This needs to be a where because of `IS NULL`
                ->where("(`Page`.`LastAnalyticsUpdate` < $nowDate)
                        OR (`Page`.`LastAnalyticsUpdate` IS NULL)");
            $ids = array_merge($ids, $whitelistpages->column('ID'));
        }
        $pages = $pages->filter(['ID' => $ids]);

        return $pages;
    }

    /**
     * @param $operator
     * @param $page
     * @param $filters
     * @return array|Google_Service_AnalyticsReporting_DimensionFilter[]
     */
    protected function createFilter($operator, $page, $filters)
    {
        $filter = new Google_Service_AnalyticsReporting_DimensionFilter();
        $filter->setOperator($operator);
        $filter->setCaseSensitive(false);
        $filter->setExpressions([$page]);
        $filter->setDimensionName('ga:pagePath');
        $filters[] = $filter;

        return $filters;
    }
}