Welcome to mirror list, hosted at ThFree Co, Russian Federation.

DataTableManipulator.php « API « core - github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 03541cf4ff600623ae06fc1f801c41c58a1ec6c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
<?php
/**
 * Piwik - Open source web analytics
 *
 * @link http://piwik.org
 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
 *
 * @category Piwik
 * @package Piwik
 */
use Piwik\DataTable\Row;
use Piwik\Period\Range;
use Piwik\DataTable;

/**
 * Base class for manipulating data tables.
 * It provides generic mechanisms like iteration and loading subtables.
 *
 * The manipulators are used in Piwik_API_ResponseBuilder and are triggered by
 * API parameters. They are not filters because they don't work on the pre-
 * fetched nested data tables. Instead, they load subtables using this base
 * class. This way, they can only load the tables they really need instead
 * of using expanded=1. Another difference between manipulators and filters
 * is that filters keep the overall structure of the table intact while
 * manipulators can change the entire thing.
 *
 * @package Piwik
 * @subpackage Piwik_API
 */
abstract class Piwik_API_DataTableManipulator
{
    protected $apiModule;
    protected $apiMethod;
    protected $request;

    private $apiMethodForSubtable;

    /**
     * Constructor
     *
     * @param bool $apiModule
     * @param bool $apiMethod
     * @param array $request
     */
    public function __construct($apiModule = false, $apiMethod = false, $request = array())
    {
        $this->apiModule = $apiModule;
        $this->apiMethod = $apiMethod;
        $this->request = $request;
    }

    /**
     * This method can be used by subclasses to iterate over data tables that might be
     * data table arrays. It calls back the template method self::doManipulate for each table.
     * This way, data table arrays can be handled in a transparent fashion.
     *
     * @param DataTable\Map|DataTable $dataTable
     * @throws Exception
     * @return DataTable\Map|DataTable
     */
    protected function manipulate($dataTable)
    {
        if ($dataTable instanceof DataTable\Map) {
            return $this->manipulateDataTableArray($dataTable);
        } else if ($dataTable instanceof DataTable) {
            return $this->manipulateDataTable($dataTable);
        } else {
            return $dataTable;
        }
    }

    /**
     * Manipulates child DataTables of a DataTable_Array. See @manipulate for more info.
     */
    protected function manipulateDataTableArray($dataTable)
    {
        $result = $dataTable->getEmptyClone();
        foreach ($dataTable->getArray() as $tableLabel => $childTable) {
            $newTable = $this->manipulate($childTable);
            $result->addTable($newTable, $tableLabel);
        }
        return $result;
    }

    /**
     * Manipulates a single DataTable instance. Derived classes must define
     * this function.
     */
    protected abstract function manipulateDataTable($dataTable);

    /**
     * Load the subtable for a row.
     * Returns null if none is found.
     *
     * @param DataTable     $dataTable
     * @param Row $row
     *
     * @return DataTable
     */
    protected function loadSubtable($dataTable, $row)
    {
        if (!($this->apiModule && $this->apiMethod && count($this->request))) {
            return null;
        }

        $request = $this->request;

        $idSubTable = $row->getIdSubDataTable();
        if ($idSubTable === null) {
            return null;
        }

        $request['idSubtable'] = $idSubTable;
        if ($dataTable) {
            $period = $dataTable->metadata['period'];
            if ($period instanceof Range) {
                $request['date'] = $period->getDateStart().','.$period->getDateEnd();
            } else {
                $request['date'] = $period->getDateStart()->toString();
            }
        }

        $class = 'Piwik_' . $this->apiModule . '_API';
        $method = $this->getApiMethodForSubtable();

        $this->manipulateSubtableRequest($request);
        $request['serialize'] = 0;
        $request['expanded'] = 0;

        // don't want to run recursive filters on the subtables as they are loaded,
        // otherwise the result will be empty in places (or everywhere). instead we
        // run it on the flattened table.
        unset($request['filter_pattern_recursive']);

        $dataTable = Piwik_API_Proxy::getInstance()->call($class, $method, $request);
        $response = new Piwik_API_ResponseBuilder($format = 'original', $request);
        $dataTable = $response->getResponse($dataTable);
        if (method_exists($dataTable, 'applyQueuedFilters')) {
            $dataTable->applyQueuedFilters();
        }

        return $dataTable;
    }

    /**
     * In this method, subclasses can clean up the request array for loading subtables
     * in order to make Piwik_API_ResponseBuilder behave correctly (e.g. not trigger the
     * manipulator again).
     *
     * @param $request
     * @return
     */
    protected abstract function manipulateSubtableRequest(&$request);

    /**
     * Extract the API method for loading subtables from the meta data
     *
     * @return string
     */
    private function getApiMethodForSubtable()
    {
        if (!$this->apiMethodForSubtable) {
            $meta = Piwik_API_API::getInstance()->getMetadata('all', $this->apiModule, $this->apiMethod);
            if (isset($meta[0]['actionToLoadSubTables'])) {
                $this->apiMethodForSubtable = $meta[0]['actionToLoadSubTables'];
            } else {
                $this->apiMethodForSubtable = $this->apiMethod;
            }
        }
        return $this->apiMethodForSubtable;
    }

}