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\DataTable\Filter; 10 11use Piwik\Archive\DataTableFactory; 12use Piwik\DataTable; 13use Piwik\Piwik; 14use Piwik\Plugin\Metric; 15use Piwik\Plugins\Goals\Columns\Metrics\GoalSpecific\AverageOrderRevenue; 16use Piwik\Plugins\Goals\Columns\Metrics\GoalSpecific\ConversionRate; 17use Piwik\Plugins\Goals\Columns\Metrics\GoalSpecific\Conversions; 18use Piwik\Plugins\Goals\Columns\Metrics\GoalSpecific\ItemsCount; 19use Piwik\Plugins\Goals\Columns\Metrics\GoalSpecific\Revenue; 20use Piwik\Plugins\Goals\Columns\Metrics\GoalSpecific\RevenuePerVisit as GoalSpecificRevenuePerVisit; 21use Piwik\Plugins\Goals\Columns\Metrics\RevenuePerVisit; 22 23/** 24 * Adds goal related metrics to a {@link DataTable} using metrics that already exist. 25 * 26 * Metrics added are: 27 * 28 * - **revenue_per_visit**: total goal and ecommerce revenue / nb_visits 29 * - **goal_%idGoal%_conversion_rate**: the conversion rate. There will be one of 30 * these columns for each goal that exists 31 * for the site. 32 * - **goal_%idGoal%_nb_conversions**: the number of conversions. There will be one of 33 * these columns for each goal that exists 34 * for the site. 35 * - **goal_%idGoal%_revenue_per_visit**: goal revenue / nb_visits. There will be one of 36 * these columns for each goal that exists 37 * for the site. 38 * - **goal_%idGoal%_revenue**: goal revenue. There will be one of 39 * these columns for each goal that exists 40 * for the site. 41 * - **goal_%idGoal%_avg_order_revenue**: goal revenue / number of orders or abandoned 42 * carts. Only for ecommerce order and abandoned cart 43 * reports. 44 * - **goal_%idGoal%_items**: number of items. Only for ecommerce order and abandoned cart 45 * reports. 46 * 47 * Adding the **filter_update_columns_when_show_all_goals** query parameter to 48 * an API request will trigger the execution of this Filter. 49 * 50 * _Note: This filter must be called before {@link ReplaceColumnNames} is called._ 51 * 52 * **Basic usage example** 53 * 54 * $dataTable->filter('AddColumnsProcessedMetricsGoal', 55 * array($enable = true, $idGoal = Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER)); 56 * 57 * @api 58 */ 59class AddColumnsProcessedMetricsGoal extends AddColumnsProcessedMetrics 60{ 61 /** 62 * Process main goal metrics: conversion rate, revenue per visit 63 */ 64 const GOALS_MINIMAL_REPORT = -2; 65 66 /** 67 * Process main goal metrics, and conversion rate per goal 68 */ 69 const GOALS_OVERVIEW = -1; 70 71 /** 72 * Process all goal and per-goal metrics 73 */ 74 const GOALS_FULL_TABLE = 0; 75 76 /** 77 * Constructor. 78 * 79 * @param DataTable $table The table that will eventually filtered. 80 * @param bool $enable Always set to true. 81 * @param string $processOnlyIdGoal Defines what metrics to add (don't process metrics when you don't display them). 82 * If self::GOALS_FULL_TABLE, all Goal metrics (and per goal metrics) will be processed. 83 * If self::GOALS_OVERVIEW, only the main goal metrics will be added. 84 * If an int > 0, then will process only metrics for this specific Goal. 85 */ 86 public function __construct($table, $enable, $processOnlyIdGoal, $goalsToProcess = null) 87 { 88 $this->processOnlyIdGoal = $processOnlyIdGoal; 89 $this->isEcommerce = $this->processOnlyIdGoal === Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER || $this->processOnlyIdGoal === Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART; 90 parent::__construct($table); 91 // Ensure that all rows with no visit but conversions will be displayed 92 $this->deleteRowsWithNoVisit = false; 93 $this->goalsToProcess = $goalsToProcess; 94 } 95 96 /** 97 * Adds the processed metrics. See {@link AddColumnsProcessedMetrics} for 98 * more information. 99 * 100 * @param DataTable $table 101 */ 102 public function filter($table) 103 { 104 // Add standard processed metrics 105 parent::filter($table); 106 107 $goals = $this->getGoalsInTable($table); 108 if (!empty($this->goalsToProcess)) { 109 $goals = array_unique(array_merge($goals, $this->goalsToProcess)); 110 sort($goals); 111 } 112 113 $idSite = DataTableFactory::getSiteIdFromMetadata($table); 114 115 $extraProcessedMetrics = $table->getMetadata(DataTable::EXTRA_PROCESSED_METRICS_METADATA_NAME); 116 117 $extraProcessedMetrics[] = new RevenuePerVisit(); 118 if ($this->processOnlyIdGoal != self::GOALS_MINIMAL_REPORT) { 119 foreach ($goals as $idGoal) { 120 if (($this->processOnlyIdGoal > self::GOALS_FULL_TABLE 121 || $this->isEcommerce) 122 && $this->processOnlyIdGoal != $idGoal 123 ) { 124 continue; 125 } 126 127 $extraProcessedMetrics[] = new ConversionRate($idSite, $idGoal); // PerGoal\ConversionRate 128 129 // When the table is displayed by clicking on the flag icon, we only display the columns 130 // Visits, Conversions, Per goal conversion rate, Revenue 131 if ($this->processOnlyIdGoal == self::GOALS_OVERVIEW) { 132 continue; 133 } 134 135 $extraProcessedMetrics[] = new Conversions($idSite, $idGoal); // PerGoal\Conversions or GoalSpecific\ 136 $extraProcessedMetrics[] = new GoalSpecificRevenuePerVisit($idSite, $idGoal); // PerGoal\Revenue 137 $extraProcessedMetrics[] = new Revenue($idSite, $idGoal); // PerGoal\Revenue 138 139 if ($this->isEcommerce) { 140 $extraProcessedMetrics[] = new AverageOrderRevenue($idSite, $idGoal); 141 $extraProcessedMetrics[] = new ItemsCount($idSite, $idGoal); 142 } 143 } 144 } 145 146 $table->setMetadata(DataTable::EXTRA_PROCESSED_METRICS_METADATA_NAME, $extraProcessedMetrics); 147 } 148 149 private function getGoalsInTable(DataTable $table) 150 { 151 $result = array(); 152 foreach ($table->getRows() as $row) { 153 $goals = Metric::getMetric($row, 'goals'); 154 if (!$goals) { 155 continue; 156 } 157 158 foreach ($goals as $goalId => $goalMetrics) { 159 $goalId = str_replace("idgoal=", "", $goalId); 160 $result[] = $goalId; 161 } 162 } 163 return array_unique($result); 164 } 165} 166