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

GoalsRequestProcessor.php « Tracker « Goals « plugins - github.com/matomo-org/matomo.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
blob: 85b4e1c809c3b93ddbe12b44c4923b604e837682 (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
<?php
/**
 * Piwik - free/libre analytics platform
 *
 * @link http://piwik.org
 * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later
 */

namespace Piwik\Plugins\Goals\Tracker;

use Piwik\Common;
use Piwik\Tracker\Action;
use Piwik\Tracker\GoalManager;
use Piwik\Tracker\Request;
use Piwik\Tracker\RequestProcessor;
use Piwik\Tracker\Visit\VisitProperties;

/**
 * Handles conversion detection and tracking for tracker requests.
 *
 * ## Request Metadata
 *
 * This processor defines the following request metadata under the **Goals**
 * plugin:
 *
 * * **goalsConverted**: The array of goals that were converted by this request. Each element
 *                       will be an array of goal column value pairs. The ecommerce goal will
 *                       only have the idgoal column set.
 *
 *                       Set in `processRequestParams()`.
 *
 *                       Plugins can set this to empty to skip conversion recording.
 *
 * * **visitIsConverted**: If `true`, the current visit should be marked as "converted". Note:
 *                         some goal conversions (ie, ecommerce) do not mark the visit as
 *                         "converted", so it is possible for goalsConverted to be non-empty
 *                         while visitIsConverted is `false`.
 *
 *                         Set in `processRequestParams()`.
 */
class GoalsRequestProcessor extends RequestProcessor
{
    /**
     * @var GoalManager
     */
    public $goalManager = null;

    public function __construct(GoalManager $goalManager)
    {
        $this->goalManager = $goalManager;
    }

    public function processRequestParams(VisitProperties $visitProperties, Request $request)
    {
        $this->goalManager = new GoalManager();

        if ($this->isManualGoalConversion($request)) {
            // this request is from the JS call to piwikTracker.trackGoal()
            $goal = $this->goalManager->detectGoalId($request->getIdSite(), $request);

            $visitIsConverted = !empty($goal);
            $request->setMetadata('Goals', 'visitIsConverted', $visitIsConverted);

            $existingConvertedGoals = $request->getMetadata('Goals', 'goalsConverted') ?: array();
            $request->setMetadata('Goals', 'goalsConverted', array_merge($existingConvertedGoals, array($goal)));

            $request->setMetadata('Actions', 'action', null); // don't track actions when doing manual goal conversions

            // if we find a idgoal in the URL, but then the goal is not valid, this is most likely a fake request
            if (!$visitIsConverted) {
                $idGoal = $request->getParam('idgoal');
                Common::printDebug('Invalid goal tracking request for goal id = ' . $idGoal);
                return true;
            }
        }

        return false;
    }

    public function afterRequestProcessed(VisitProperties $visitProperties, Request $request)
    {
        $goalsConverted = $request->getMetadata('Goals', 'goalsConverted');

        /** @var Action $action */
        $action = $request->getMetadata('Actions', 'action');

        // if the visit hasn't already been converted another way (ie, manual goal conversion or ecommerce conversion,
        // try to convert based on the action)
        if (empty($goalsConverted)
            && $action
        ) {
            $goalsConverted = $this->goalManager->detectGoalsMatchingUrl($request->getIdSite(), $action);

            $existingGoalsConverted = $request->getMetadata('Goals', 'goalsConverted') ?: array();
            $request->setMetadata('Goals', 'goalsConverted', array_merge($existingGoalsConverted, $goalsConverted));

            if (!empty($goalsConverted)) {
                $request->setMetadata('Goals', 'visitIsConverted', true);
            }
        }

        // There is an edge case when:
        // - two manual goal conversions happen in the same second
        // - which result in handleExistingVisit throwing the exception
        //   because the UPDATE didn't affect any rows (one row was found, but not updated since no field changed)
        // - the exception is caught here and will result in a new visit incorrectly
        // In this case, we cancel the current conversion to be recorded:
        $isManualGoalConversion = $this->isManualGoalConversion($request);
        $requestIsEcommerce = $request->getMetadata('Goals', 'isRequestEcommerce');
        $visitorNotFoundInDb = $request->getMetadata('CoreHome', 'visitorNotFoundInDb');

        if ($visitorNotFoundInDb
            && ($isManualGoalConversion
                || $requestIsEcommerce)
        ) {
            $request->setMetadata('Goals', 'goalsConverted', array());
            $request->setMetadata('Goals', 'visitIsConverted', false);
        }

    }

    public function recordLogs(VisitProperties $visitProperties, Request $request)
    {
        // record the goals if there were conversions in this request (even if the visit itself was not converted)
        $goalsConverted = $request->getMetadata('Goals', 'goalsConverted');
        if (!empty($goalsConverted)) {
            $this->goalManager->recordGoals($visitProperties, $request);
        }
    }

    private function isManualGoalConversion(Request $request)
    {
        $idGoal = $request->getParam('idgoal');
        return $idGoal > 0;
    }
}