1<?php 2/** 3 * Matomo - free/libre analytics platform 4 * 5 * @link https://matomo.org 6 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later 7 */ 8 9namespace Piwik\Plugins\UserId; 10use Piwik\Config; 11use Piwik\DataArray; 12use Piwik\DataTable; 13use Piwik\Metrics as PiwikMetrics; 14use Piwik\RankingQuery; 15 16/** 17 * Archiver that aggregates metrics per user ID (user_id field). 18 */ 19class Archiver extends \Piwik\Plugin\Archiver 20{ 21 const USERID_ARCHIVE_RECORD = "UserId_users"; 22 23 const VISITOR_ID_FIELD = 'idvisitor'; 24 const USER_ID_FIELD = 'user_id'; 25 26 protected $maximumRowsInDataTableLevelZero; 27 28 function __construct($processor) 29 { 30 parent::__construct($processor); 31 32 $this->maximumRowsInDataTableLevelZero = Config::getInstance()->General['datatable_archiving_maximum_rows_userid_users']; 33 } 34 35 /** 36 * @var DataArray 37 */ 38 protected $arrays; 39 40 /** 41 * Array to save visitor IDs for every user ID met during archiving process. We use it to 42 * fill metadata before actual inserting rows to DB. 43 * @var array 44 */ 45 protected $visitorIdsUserIdsMap = array(); 46 47 /** 48 * Archives data for a day period. 49 */ 50 public function aggregateDayReport() 51 { 52 $this->arrays = new DataArray(); 53 $this->aggregateUsers(); 54 $this->insertDayReports(); 55 } 56 /** 57 * Period archiving: simply sums up daily archives 58 */ 59 public function aggregateMultipleReports() 60 { 61 $dataTableRecords = array(self::USERID_ARCHIVE_RECORD); 62 $columnsAggregationOperation = null; 63 $this->getProcessor()->aggregateDataTableRecords( 64 $dataTableRecords, 65 $this->maximumRowsInDataTableLevelZero, 66 $this->maximumRowsInDataTableLevelZero, 67 $columnToSort = 'nb_visits', 68 $columnsAggregationOperation, 69 $columnsToRenameAfterAggregation = null, 70 $countRowsRecursive = array()); 71 } 72 73 /** 74 * Used to aggregate daily data per user ID 75 */ 76 protected function aggregateUsers() 77 { 78 $userIdFieldName = self::USER_ID_FIELD; 79 $visitorIdFieldName = self::VISITOR_ID_FIELD; 80 81 $rankingQueryLimit = $this->getRankingQueryLimit(); 82 83 $rankingQuery = false; 84 if ($rankingQueryLimit > 0) { 85 $rankingQuery = new RankingQuery($rankingQueryLimit); 86 $rankingQuery->addLabelColumn($userIdFieldName); 87 $rankingQuery->addLabelColumn($visitorIdFieldName); 88 } 89 90 /** @var \Zend_Db_Statement $query */ 91 $query = $this->getLogAggregator()->queryVisitsByDimension( 92 array(self::USER_ID_FIELD), 93 "log_visit.$userIdFieldName IS NOT NULL AND log_visit.$userIdFieldName != ''", 94 array("LOWER(HEX($visitorIdFieldName)) as $visitorIdFieldName"), 95 $metrics = false, 96 $rankingQuery, 97 self::USER_ID_FIELD . ' ASC' 98 ); 99 100 if ($query === false) { 101 return; 102 } 103 104 $rowsCount = 0; 105 foreach ($query as $row) { 106 $rowsCount++; 107 $this->arrays->sumMetricsVisits($row[$userIdFieldName], $row); 108 $this->rememberVisitorId($row); 109 } 110 } 111 112 /** 113 * Insert aggregated daily data serialized 114 * 115 * @throws \Exception 116 */ 117 protected function insertDayReports() 118 { 119 /** @var DataTable $dataTable */ 120 $dataTable = $this->arrays->asDataTable(); 121 $this->setVisitorIds($dataTable); 122 $report = $dataTable->getSerialized($this->maximumRowsInDataTableLevelZero, null, PiwikMetrics::INDEX_NB_VISITS); 123 $this->getProcessor()->insertBlobRecord(self::USERID_ARCHIVE_RECORD, $report); 124 } 125 126 /** 127 * Remember visitor ID per user. We use it to fill metadata before actual inserting rows to DB. 128 * 129 * @param array $row 130 */ 131 private function rememberVisitorId($row) 132 { 133 if (!empty($row[self::USER_ID_FIELD]) && !empty($row[self::VISITOR_ID_FIELD])) { 134 $this->visitorIdsUserIdsMap[$row[self::USER_ID_FIELD]] = $row[self::VISITOR_ID_FIELD]; 135 } 136 } 137 138 /** 139 * Fill visitor ID as metadata before actual inserting rows to DB. 140 * 141 * @param DataTable $dataTable 142 */ 143 private function setVisitorIds(DataTable $dataTable) 144 { 145 foreach ($dataTable->getRows() as $row) { 146 $userId = $row->getColumn('label'); 147 if (isset($this->visitorIdsUserIdsMap[$userId])) { 148 $row->setMetadata(self::VISITOR_ID_FIELD, $this->visitorIdsUserIdsMap[$userId]); 149 } 150 } 151 } 152 153 private function getRankingQueryLimit() 154 { 155 $configGeneral = Config::getInstance()->General; 156 $configLimit = $configGeneral['archiving_ranking_query_row_limit']; 157 $limit = $configLimit == 0 ? 0 : max( 158 $configLimit, 159 $this->maximumRowsInDataTableLevelZero 160 ); 161 return $limit; 162 } 163 164}