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