diff --git a/core/Tracker/GoalManager.php b/core/Tracker/GoalManager.php index ce1532d8608b724b3b394c5a4963b6fe69332d42..8b3c86734fa76d61f1733e75f86d35a60085e33d 100644 --- a/core/Tracker/GoalManager.php +++ b/core/Tracker/GoalManager.php @@ -55,54 +55,24 @@ class GoalManager const INTERNAL_ITEM_PRICE = 7; const INTERNAL_ITEM_QUANTITY = 8; - public $idGoal; - public $requestIsEcommerce; - private $isGoalAnOrder; - /** - * @var Action + * TODO: should remove this, but it is used by getGoalColumn which is used by dimensions. should replace w/ value object. + * + * @var array */ - protected $action = null; - protected $convertedGoals = array(); - private $currentGoal = array(); - /** - * @var Request - */ - protected $request; - protected $orderId; - - protected $isThereExistingCartInVisit = false; - - /** - * Constructor - * @param Request $request - */ - public function __construct(Request $request) - { - $this->request = $request; - $this->orderId = $request->getParam('ec_id'); - $this->idGoal = $request->getParam('idgoal'); - - $this->isGoalAnOrder = !empty($this->orderId); - $this->requestIsEcommerce = (0 == $this->idGoal); - } - - public function isGoalAnOrder() - { - return $this->isGoalAnOrder; - } - public function detectIsThereExistingCartInVisit($visitInformation) { - if (!empty($visitInformation['visit_goal_buyer'])) { - $goalBuyer = $visitInformation['visit_goal_buyer']; - $types = array(GoalManager::TYPE_BUYER_OPEN_CART, GoalManager::TYPE_BUYER_ORDERED_AND_OPEN_CART); - - // Was there a Cart for this visit prior to the order? - $this->isThereExistingCartInVisit = in_array($goalBuyer, $types); + if (empty($visitInformation['visit_goal_buyer'])) { + return false; } + + $goalBuyer = $visitInformation['visit_goal_buyer']; + $types = array(GoalManager::TYPE_BUYER_OPEN_CART, GoalManager::TYPE_BUYER_ORDERED_AND_OPEN_CART); + + // Was there a Cart for this visit prior to the order? + return in_array($goalBuyer, $types); } public static function getGoalDefinitions($idSite) @@ -147,17 +117,18 @@ class GoalManager * @param int $idSite * @param Action $action * @throws Exception - * @return int Number of goals matched + * @return array[] Goals matched */ public function detectGoalsMatchingUrl($idSite, $action) { if (!Common::isGoalPluginEnabled()) { - return false; + return array(); } $actionType = $action->getActionType(); $goals = $this->getGoalDefinitions($idSite); + $convertedGoals = array(); foreach ($goals as $goal) { $attribute = $goal['match_attribute']; // if the attribute to match is not the type of the current action @@ -196,37 +167,32 @@ class GoalManager $match = $this->isUrlMatchingGoal($goal, $pattern_type, $url); if ($match) { $goal['url'] = $action->getActionUrl(); - $this->convertedGoals[] = $goal; + $convertedGoals[] = $goal; } } - return count($this->convertedGoals) > 0; + return $convertedGoals; } - public function isManualGoalConversion() - { - return $this->idGoal > 0; - } - - public function detectGoalId($idSite) + public function detectGoalId($idSite, Request $request) { if (!Common::isGoalPluginEnabled()) { - return false; + return null; } + $idGoal = $request->getParam('idgoal'); + $goals = $this->getGoalDefinitions($idSite); - if (!isset($goals[$this->idGoal])) { - return false; + if (!isset($goals[$idGoal])) { + return null; } - $goal = $goals[$this->idGoal]; + $goal = $goals[$idGoal]; - $url = $this->request->getParam('url'); + $url = $request->getParam('url'); $goal['url'] = PageUrl::excludeQueryParametersFromUrl($url, $idSite); - $this->convertedGoals[] = $goal; - - return true; + return $goal; } /** @@ -237,7 +203,7 @@ class GoalManager * @param array $visitCustomVariables * @param Action $action */ - public function recordGoals(VisitProperties $visitProperties) + public function recordGoals(VisitProperties $visitProperties, Request $request) { $visitorInformation = $visitProperties->visitorInfo; $visitCustomVariables = $visitProperties->getRequestMetadata('CustomVariables', 'visitCustomVariables'); @@ -245,7 +211,7 @@ class GoalManager /** @var Action $action */ $action = $visitProperties->getRequestMetadata('Actions', 'action'); - $goal = $this->getGoalFromVisitor($visitProperties, $action); + $goal = $this->getGoalFromVisitor($visitProperties, $request, $action); // Copy Custom Variables from Visit row to the Goal conversion // Otherwise, set the Custom Variables found in the cookie sent with this request @@ -266,10 +232,11 @@ class GoalManager } // some goals are converted, so must be ecommerce Order or Cart Update - if ($this->requestIsEcommerce) { - $this->recordEcommerceGoal($visitProperties, $goal, $action); + $isRequestEcommerce = $visitProperties->getRequestMetadata('Ecommerce', 'isRequestEcommerce'); + if ($isRequestEcommerce) { + $this->recordEcommerceGoal($visitProperties, $request, $goal, $action); } else { - $this->recordStandardGoals($visitProperties, $goal, $action); + $this->recordStandardGoals($visitProperties, $request, $goal, $action); } } @@ -299,23 +266,27 @@ class GoalManager * @param Action $action * @param array $visitInformation */ - protected function recordEcommerceGoal(VisitProperties $visitProperties, $conversion, $action) + protected function recordEcommerceGoal(VisitProperties $visitProperties, Request $request, $conversion, $action) { - if ($this->isThereExistingCartInVisit) { + $isThereExistingCartInVisit = $visitProperties->getRequestMetadata('Goals', 'isThereExistingCartInVisit'); + if ($isThereExistingCartInVisit) { Common::printDebug("There is an existing cart for this visit"); } $visitor = Visitor::makeFromVisitProperties($visitProperties); - if ($this->isGoalAnOrder) { + $isGoalAnOrder = $visitProperties->getRequestMetadata('Ecommerce', 'isGoalAnOrder'); + if ($isGoalAnOrder) { $debugMessage = 'The conversion is an Ecommerce order'; - $conversion['idorder'] = $this->orderId; + $orderId = $request->getParam('ec_id'); + + $conversion['idorder'] = $orderId; $conversion['idgoal'] = self::IDGOAL_ORDER; - $conversion['buster'] = Common::hashStringToInt($this->orderId); + $conversion['buster'] = Common::hashStringToInt($orderId); $conversionDimensions = ConversionDimension::getAllDimensions(); - $conversion = $this->triggerHookOnDimensions($conversionDimensions, 'onEcommerceOrderConversion', $visitor, $action, $conversion); + $conversion = $this->triggerHookOnDimensions($request, $conversionDimensions, 'onEcommerceOrderConversion', $visitor, $action, $conversion); } // If Cart update, select current items in the previous Cart else { $debugMessage = 'The conversion is an Ecommerce Cart Update'; @@ -324,13 +295,13 @@ class GoalManager $conversion['idgoal'] = self::IDGOAL_CART; $conversionDimensions = ConversionDimension::getAllDimensions(); - $conversion = $this->triggerHookOnDimensions($conversionDimensions, 'onEcommerceCartUpdateConversion', $visitor, $action, $conversion); + $conversion = $this->triggerHookOnDimensions($request, $conversionDimensions, 'onEcommerceCartUpdateConversion', $visitor, $action, $conversion); } Common::printDebug($debugMessage . ':' . var_export($conversion, true)); // INSERT or Sync items in the Cart / Order for this visit & order - $items = $this->getEcommerceItemsFromRequest(); + $items = $this->getEcommerceItemsFromRequest($request); if (false === $items) { return; @@ -343,10 +314,10 @@ class GoalManager $conversion['items'] = $itemsCount; - if ($this->isThereExistingCartInVisit) { + if ($isThereExistingCartInVisit) { $recorded = $this->getModel()->updateConversion($visitProperties->visitorInfo['idvisit'], self::IDGOAL_CART, $conversion); } else { - $recorded = $this->insertNewConversion($conversion, $visitProperties->visitorInfo); + $recorded = $this->insertNewConversion($conversion, $visitProperties->visitorInfo, $request); } if ($recorded) { @@ -371,9 +342,9 @@ class GoalManager * Returns Items read from the request string * @return array|bool */ - private function getEcommerceItemsFromRequest() + private function getEcommerceItemsFromRequest(Request $request) { - $items = $this->request->getParam('ec_items'); + $items = $request->getParam('ec_items'); if (empty($items)) { Common::printDebug("There are no Ecommerce items in the request"); @@ -674,11 +645,12 @@ class GoalManager * @param Action $action * @param $visitorInformation */ - protected function recordStandardGoals(VisitProperties $visitProperties, $goal, $action) + protected function recordStandardGoals(VisitProperties $visitProperties, Request $request, $goal, $action) { $visitor = Visitor::makeFromVisitProperties($visitProperties); - foreach ($this->convertedGoals as $convertedGoal) { + $convertedGoals = $visitProperties->getRequestMetadata('Goals', 'goalsConverted') ?: array(); + foreach ($convertedGoals as $convertedGoal) { $this->currentGoal = $convertedGoal; Common::printDebug("- Goal " . $convertedGoal['idgoal'] . " matched. Recording..."); $conversion = $goal; @@ -696,9 +668,9 @@ class GoalManager : $visitProperties->visitorInfo['visit_last_action_time']; $conversionDimensions = ConversionDimension::getAllDimensions(); - $conversion = $this->triggerHookOnDimensions($conversionDimensions, 'onGoalConversion', $visitor, $action, $conversion); + $conversion = $this->triggerHookOnDimensions($request, $conversionDimensions, 'onGoalConversion', $visitor, $action, $conversion); - $this->insertNewConversion($conversion, $visitProperties->visitorInfo); + $this->insertNewConversion($conversion, $visitProperties->visitorInfo, $request); /** * Triggered after successfully recording a non-ecommerce conversion. @@ -720,7 +692,7 @@ class GoalManager * @param array $visitInformation * @return bool */ - protected function insertNewConversion($conversion, $visitInformation) + protected function insertNewConversion($conversion, $visitInformation, Request $request) { /** * Triggered before persisting a new [conversion entity](/guides/persistence-and-the-mysql-backend#conversions). @@ -733,7 +705,7 @@ class GoalManager * information it contains [here](/guides/persistence-and-the-mysql-backend#visits). * @param \Piwik\Tracker\Request $request An object describing the tracking request being processed. */ - Piwik::postEvent('Tracker.newConversionInformation', array(&$conversion, $visitInformation, $this->request)); + Piwik::postEvent('Tracker.newConversionInformation', array(&$conversion, $visitInformation, $request)); $newGoalDebug = $conversion; $newGoalDebug['idvisitor'] = bin2hex($newGoalDebug['idvisitor']); @@ -796,10 +768,10 @@ class GoalManager * * @return array|null The updated $valuesToUpdate or null if no $valuesToUpdate given */ - private function triggerHookOnDimensions($dimensions, $hook, $visitor, $action, $valuesToUpdate) + private function triggerHookOnDimensions(Request $request, $dimensions, $hook, $visitor, $action, $valuesToUpdate) { foreach ($dimensions as $dimension) { - $value = $dimension->$hook($this->request, $visitor, $action, $this); + $value = $dimension->$hook($request, $visitor, $action, $this); if (false !== $value) { if (is_float($value)) { @@ -816,7 +788,7 @@ class GoalManager return $valuesToUpdate; } - private function getGoalFromVisitor(VisitProperties $visitProperties, $action) + private function getGoalFromVisitor(VisitProperties $visitProperties, Request $request, $action) { $goal = array( 'idvisit' => $visitProperties->visitorInfo['idvisit'], @@ -828,7 +800,7 @@ class GoalManager $visit = Visitor::makeFromVisitProperties($visitProperties); foreach ($visitDimensions as $dimension) { - $value = $dimension->onAnyGoalConversion($this->request, $visit, $action); + $value = $dimension->onAnyGoalConversion($request, $visit, $action); if (false !== $value) { $goal[$dimension->getColumnName()] = $value; } diff --git a/core/Tracker/RequestProcessor.php b/core/Tracker/RequestProcessor.php index d386cc33e7020c5f2bfb49c2cbcc878e2c69f078..072e9d76b52fff817d52e80c8a0b6c71e87f762b 100644 --- a/core/Tracker/RequestProcessor.php +++ b/core/Tracker/RequestProcessor.php @@ -156,8 +156,9 @@ abstract class RequestProcessor * other words, the values in the array were persisted to the DB before this method was called). * * @param VisitProperties $visitProperties + * @param Request $request */ - public function recordLogs(VisitProperties $visitProperties) + public function recordLogs(VisitProperties $visitProperties, Request $request) { // empty } diff --git a/core/Tracker/Visit.php b/core/Tracker/Visit.php index f8787b691430ce9665e4d9bb8727a438b18bda7e..6b2883bf2ba84eec08480f572f1b54ce58d2e135 100644 --- a/core/Tracker/Visit.php +++ b/core/Tracker/Visit.php @@ -166,7 +166,7 @@ class Visit implements VisitInterface foreach ($this->requestProcessors as $processor) { Common::printDebug("Executing " . get_class($processor) . "::recordLogs()..."); - $processor->recordLogs($this->visitProperties); + $processor->recordLogs($this->visitProperties, $this->request); } $this->markArchivedReportsAsInvalidIfArchiveAlreadyFinished(); diff --git a/core/Tracker/Visitor.php b/core/Tracker/Visitor.php index 61345dd9e5a2c72dd115c67bce60921b88a3dbc1..cdd4ce769349da1b7548757e6b615b2c2a7d09a5 100644 --- a/core/Tracker/Visitor.php +++ b/core/Tracker/Visitor.php @@ -16,7 +16,7 @@ use Piwik\Tracker\Visit\VisitProperties; class Visitor { private $visitorKnown = false; - private $visitProperties; + public $visitProperties; public function __construct(VisitProperties $visitProperties, $isVisitorKnown = false) { diff --git a/plugins/Actions/Tracker/ActionsRequestProcessor.php b/plugins/Actions/Tracker/ActionsRequestProcessor.php index b4c7bf9e89b1206315da62c42ed1da1666045d49..cfc9bb57934cc2a9540d170dcfc9a4604bd98e56 100644 --- a/plugins/Actions/Tracker/ActionsRequestProcessor.php +++ b/plugins/Actions/Tracker/ActionsRequestProcessor.php @@ -69,7 +69,7 @@ class ActionsRequestProcessor extends RequestProcessor } } - public function recordLogs(VisitProperties $visitProperties) + public function recordLogs(VisitProperties $visitProperties, Request $request) { /** @var Action $action */ $action = $visitProperties->getRequestMetadata('Actions', 'action'); diff --git a/plugins/CoreHome/Columns/VisitGoalBuyer.php b/plugins/CoreHome/Columns/VisitGoalBuyer.php index 8867efd70848a27d5dfad7a7c73dc46a4880cc8d..006c93a179a1d7f2b767f9e8cca3e730aa338da1 100644 --- a/plugins/CoreHome/Columns/VisitGoalBuyer.php +++ b/plugins/CoreHome/Columns/VisitGoalBuyer.php @@ -56,7 +56,7 @@ class VisitGoalBuyer extends VisitDimension */ public function onNewVisit(Request $request, Visitor $visitor, $action) { - return $this->getBuyerType($request); + return $this->getBuyerType($visitor); } /** @@ -70,7 +70,7 @@ class VisitGoalBuyer extends VisitDimension $goalBuyer = $visitor->getVisitorColumn($this->columnName); // Ecommerce buyer status - $visitEcommerceStatus = $this->getBuyerType($request, $goalBuyer); + $visitEcommerceStatus = $this->getBuyerType($visitor, $goalBuyer); if ($visitEcommerceStatus != self::TYPE_BUYER_NONE // only update if the value has changed (prevents overwriting the value in case a request has @@ -106,15 +106,15 @@ class VisitGoalBuyer extends VisitDimension return self::$visitEcommerceStatus[$id]; } - private function getBuyerType(Request $request, $existingType = self::TYPE_BUYER_NONE) + private function getBuyerType(Visitor $visitor, $existingType = self::TYPE_BUYER_NONE) { - $goalManager = new GoalManager($request); - - if (!$goalManager->requestIsEcommerce) { + $isRequestEcommerce = $visitor->visitProperties->getRequestMetadata('Ecommerce', 'isRequestEcommerce'); + if (!$isRequestEcommerce) { return $existingType; } - if ($goalManager->isGoalAnOrder()) { + $isGoalAnOrder = $visitor->visitProperties->getRequestMetadata('Ecommerce', 'isGoalAnOrder'); + if ($isGoalAnOrder) { return self::TYPE_BUYER_ORDERED; } diff --git a/plugins/CoreHome/Columns/VisitTotalTime.php b/plugins/CoreHome/Columns/VisitTotalTime.php index 5975d6c6f9a86c17bb760508095bbbbdb01932d2..22afceb6a59de61efc25027dc9fdeadc33c24305 100644 --- a/plugins/CoreHome/Columns/VisitTotalTime.php +++ b/plugins/CoreHome/Columns/VisitTotalTime.php @@ -72,14 +72,12 @@ class VisitTotalTime extends VisitDimension return false; } - $goalManager = new GoalManager($request); - $totalTime = $visitor->getVisitorColumn('visit_total_time'); // If a pageview and goal conversion in the same second, with previously a goal conversion recorded // the request would not "update" the row since all values are the same as previous // therefore the request below throws exception, instead we make sure the UPDATE will affect the row - $totalTime = $totalTime + $goalManager->idGoal; + $totalTime = $totalTime + $request->getParam('idgoal'); // +2 to offset idgoal=-1 and idgoal=0 $totalTime = $totalTime + 2; diff --git a/plugins/Ecommerce/Tracker/EcommerceRequestProcessor.php b/plugins/Ecommerce/Tracker/EcommerceRequestProcessor.php index a072a46cf285dc9246a2e7d6d47c4a32d8d027d1..fd701207d913417ae44064cb434e25691dc57b0d 100644 --- a/plugins/Ecommerce/Tracker/EcommerceRequestProcessor.php +++ b/plugins/Ecommerce/Tracker/EcommerceRequestProcessor.php @@ -16,23 +16,71 @@ use Piwik\Tracker\Visit\VisitProperties; /** * Handles ecommerce tracking requests. * - * Defines no new request metadata. + * ## Request Metadata + * + * This processor defines the following request metadata under the **Ecommerce** + * plugin: + * + * * **isRequestEcommerce**: If `true`, the request is for an ecommerce goal conversion. + * + * Set in `processRequestParams()`. + * + * * **isGoalAnOrder**: If `true` the request is tracking an ecommerce order. + * + * Set in `processRequestParams()`. */ class EcommerceRequestProcessor extends RequestProcessor { + /** + * @var GoalManager + */ + public $goalManager = null; + + public function __construct(GoalManager $goalManager) + { + $this->goalManager = $goalManager; + } + public function processRequestParams(VisitProperties $visitProperties, Request $request) { - $goalManager = new GoalManager($request); // TODO: GoalManager should be stateless and stored in DI. + $isGoalAnOrder = $this->isRequestForAnOrder($request); + $visitProperties->setRequestMetadata('Ecommerce', 'isGoalAnOrder', $isGoalAnOrder); - if ($goalManager->requestIsEcommerce) { - $visitProperties->setRequestMetadata('Goals', 'someGoalsConverted', true); + $isRequestEcommerce = $this->isRequestEcommerce($request); + $visitProperties->setRequestMetadata('Ecommerce', 'isRequestEcommerce', $isRequestEcommerce); + if ($isRequestEcommerce) { // Mark the visit as Converted only if it is an order (not for a Cart update) - if ($goalManager->isGoalAnOrder()) { + $idGoal = GoalManager::IDGOAL_CART; + if ($isGoalAnOrder) { + $idGoal = GoalManager::IDGOAL_ORDER; $visitProperties->setRequestMetadata('Goals', 'visitIsConverted', true); } + $visitProperties->setRequestMetadata('Goals', 'goalsConverted', array(array('idgoal' => $idGoal))); + $visitProperties->setRequestMetadata('Actions', 'action', null); // don't track actions when tracking ecommerce orders } } -} \ No newline at end of file + + public function afterRequestProcessed(VisitProperties $visitProperties, Request $request) + { + $goalsConverted = $visitProperties->getRequestMetadata('Goals', 'goalsConverted'); + if (!empty($goalsConverted)) { + $isThereExistingCartInVisit = $this->goalManager->detectIsThereExistingCartInVisit($visitProperties->visitorInfo); + $visitProperties->setRequestMetadata('Goals', 'isThereExistingCartInVisit', $isThereExistingCartInVisit); + } + } + + private function isRequestForAnOrder(Request $request) + { + $orderId = $request->getParam('ec_id'); + return !empty($orderId); + } + + private function isRequestEcommerce(Request $request) + { + $idGoal = $request->getParam('idgoal'); + return 0 == $idGoal; + } +} diff --git a/plugins/Goals/Tracker/GoalsRequestProcessor.php b/plugins/Goals/Tracker/GoalsRequestProcessor.php index 30439e02a5f3f863abe7f935e4584e93188a807e..e4ab93b1e9be84c41d05626a692c8904dc1e4c79 100644 --- a/plugins/Goals/Tracker/GoalsRequestProcessor.php +++ b/plugins/Goals/Tracker/GoalsRequestProcessor.php @@ -23,16 +23,17 @@ use Piwik\Tracker\Visit\VisitProperties; * This processor defines the following request metadata under the **Goals** * plugin: * - * * **someGoalsConverted**: If `true`, the request triggers one or more conversions that will - * be recorded. + * * **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()`. + * Set in `processRequestParams()`. * - * Plugins can set this to false to skip conversion recording. + * 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 someGoalsConverted to be `true` + * "converted", so it is possible for goalsConverted to be non-empty * while visitIsConverted is `false`. * * Set in `processRequestParams()`. @@ -40,28 +41,35 @@ use Piwik\Tracker\Visit\VisitProperties; class GoalsRequestProcessor extends RequestProcessor { /** - * TODO: GoalManager should be stateless and stored in DI. - * * @var GoalManager */ - public static $goalManager = null; + public $goalManager = null; + + public function __construct(GoalManager $goalManager) + { + $this->goalManager = $goalManager; + } public function processRequestParams(VisitProperties $visitProperties, Request $request) { - self::$goalManager = new GoalManager($request); + $this->goalManager = new GoalManager(); - if (self::$goalManager->isManualGoalConversion()) { + if ($this->isManualGoalConversion($request)) { // this request is from the JS call to piwikTracker.trackGoal() - $someGoalsConverted = self::$goalManager->detectGoalId($request->getIdSite()); + $goal = $this->goalManager->detectGoalId($request->getIdSite(), $request); + + $visitIsConverted = !empty($goal); + $visitProperties->setRequestMetadata('Goals', 'visitIsConverted', $visitIsConverted); - $visitProperties->setRequestMetadata('Goals', 'someGoalsConverted', $someGoalsConverted); - $visitProperties->setRequestMetadata('Goals', 'visitIsConverted', $someGoalsConverted); + $existingConvertedGoals = $visitProperties->getRequestMetadata('Goals', 'goalsConverted') ?: array(); + $visitProperties->setRequestMetadata('Goals', 'goalsConverted', array_merge($existingConvertedGoals, array($goal))); $visitProperties->setRequestMetadata('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 (!$someGoalsConverted) { - Common::printDebug('Invalid goal tracking request for goal id = ' . self::$goalManager->idGoal); + if (!$visitIsConverted) { + $idGoal = $request->getParam('idgoal'); + Common::printDebug('Invalid goal tracking request for goal id = ' . $idGoal); return true; } } @@ -71,24 +79,22 @@ class GoalsRequestProcessor extends RequestProcessor public function afterRequestProcessed(VisitProperties $visitProperties, Request $request) { - $someGoalsConverted = $visitProperties->getRequestMetadata('Goals', 'someGoalsConverted'); + $goalsConverted = $visitProperties->getRequestMetadata('Goals', 'goalsConverted'); /** @var Action $action */ $action = $visitProperties->getRequestMetadata('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 (!$someGoalsConverted + if (empty($goalsConverted) && $action ) { - $someGoalsConverted = self::$goalManager->detectGoalsMatchingUrl($request->getIdSite(), $action); + $goalsConverted = $this->goalManager->detectGoalsMatchingUrl($request->getIdSite(), $action); - $visitProperties->setRequestMetadata('Goals', 'someGoalsConverted', $someGoalsConverted); - $visitProperties->setRequestMetadata('Goals', 'visitIsConverted', $someGoalsConverted); - } + $existingGoalsConverted = $visitProperties->getRequestMetadata('Goals', 'goalsConverted') ?: array(); + $visitProperties->setRequestMetadata('Goals', 'goalsConverted', array_merge($existingGoalsConverted, $goalsConverted)); - if ($someGoalsConverted) { - self::$goalManager->detectIsThereExistingCartInVisit($visitProperties->visitorInfo); + $visitProperties->setRequestMetadata('Goals', 'visitIsConverted', !empty($goalsConverted)); } // There is an edge case when: @@ -97,25 +103,32 @@ class GoalsRequestProcessor extends RequestProcessor // 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 = self::$goalManager->isManualGoalConversion(); - $requestIsEcommerce = self::$goalManager->requestIsEcommerce; + $isManualGoalConversion = $this->isManualGoalConversion($request); + $requestIsEcommerce = $visitProperties->getRequestMetadata('Goals', 'isRequestEcommerce'); $visitorNotFoundInDb = $visitProperties->getRequestMetadata('CoreHome', 'visitorNotFoundInDb'); if ($visitorNotFoundInDb && ($isManualGoalConversion || $requestIsEcommerce) ) { - $visitProperties->setRequestMetadata('Goals', 'someGoalsConverted', false); + $visitProperties->setRequestMetadata('Goals', 'goalsConverted', array()); $visitProperties->setRequestMetadata('Goals', 'visitIsConverted', false); } } - public function recordLogs(VisitProperties $visitProperties) + 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) - if ($visitProperties->getRequestMetadata('Goals', 'someGoalsConverted')) { - self::$goalManager->recordGoals($visitProperties); + $goalsConverted = $visitProperties->getRequestMetadata('Goals', 'goalsConverted'); + if (!empty($goalsConverted)) { + $this->goalManager->recordGoals($visitProperties, $request); } } + + private function isManualGoalConversion(Request $request) + { + $idGoal = $request->getParam('idgoal'); + return $idGoal > 0; + } } diff --git a/plugins/Heartbeat/Tracker/PingRequestProcessor.php b/plugins/Heartbeat/Tracker/PingRequestProcessor.php index 80e9759df851f99d631bef0c6e513ecab815212f..99c22bfc77756101962c34ea91a290c092838dc2 100644 --- a/plugins/Heartbeat/Tracker/PingRequestProcessor.php +++ b/plugins/Heartbeat/Tracker/PingRequestProcessor.php @@ -25,7 +25,7 @@ class PingRequestProcessor extends RequestProcessor // on a ping request that is received before the standard visit length, we just update the visit time w/o adding a new action Common::printDebug("-> ping=1 request: we do not track a new action nor a new visit nor any goal."); $visitProperties->setRequestMetadata('Actions', 'action', null); - $visitProperties->setRequestMetadata('Goals', 'someGoalsConverted', false); + $visitProperties->setRequestMetadata('Goals', 'goalsConverted', array()); $visitProperties->setRequestMetadata('Goals', 'visitIsConverted', false); // When a ping request is received more than 30 min after the last request/ping,