Download plugins/Ecommerce/VisitorDetails.php from Leon4gr45/matomo: direct link, hf CLI and curl.
- Browser
- Download file 13.9 kB
-
https://huggingface.co/spaces/Leon4gr45/matomo/resolve/main/plugins/Ecommerce/VisitorDetails.php
- Command line
-
hf download hf://spaces/Leon4gr45/matomo/plugins/Ecommerce/VisitorDetails.php
-
curl -L -o VisitorDetails.php https://huggingface.co/spaces/Leon4gr45/matomo/resolve/main/plugins/Ecommerce/VisitorDetails.php
13.9 kB
| /** | |
| * Matomo - free/libre analytics platform | |
| * | |
| * @link https://matomo.org | |
| * @license https://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later | |
| */ | |
| namespace Piwik\Plugins\Ecommerce; | |
| use Piwik\Common; | |
| use Piwik\Config; | |
| use Piwik\DataAccess\LogAggregator; | |
| use Piwik\Date; | |
| use Piwik\DbHelper; | |
| use Piwik\Piwik; | |
| use Piwik\Plugins\Ecommerce\Columns\ProductCategory; | |
| use Piwik\Plugins\Live\Model; | |
| use Piwik\Plugins\Live\VisitorDetailsAbstract; | |
| use Piwik\Site; | |
| use Piwik\Tracker\GoalManager; | |
| use Piwik\View; | |
| use function Piwik\Plugins\Referrers\getReferrerTypeFromShortName; | |
| class VisitorDetails extends VisitorDetailsAbstract | |
| { | |
| public const CATEGORY_COUNT = 5; | |
| public const DEFAULT_LIFETIME_STAT = array( | |
| 'lifeTimeRevenue' => 0, | |
| 'lifeTimeConversions' => 0, | |
| 'lifeTimeEcommerceItems' => 0); | |
| public function extendVisitorDetails(&$visitor) | |
| { | |
| if (Site::isEcommerceEnabledFor($visitor['idSite'])) { | |
| $ecommerceMetrics = $this->queryEcommerceConversionsVisitorLifeTimeMetricsForVisitor( | |
| $visitor['idSite'], | |
| $visitor['visitorId'] | |
| ); | |
| $visitor['totalEcommerceRevenue'] = $ecommerceMetrics['totalEcommerceRevenue']; | |
| $visitor['totalEcommerceConversions'] = $ecommerceMetrics['totalEcommerceConversions']; | |
| $visitor['totalEcommerceItems'] = $ecommerceMetrics['totalEcommerceItems']; | |
| $visitor['totalAbandonedCartsRevenue'] = $ecommerceMetrics['totalAbandonedCartsRevenue']; | |
| $visitor['totalAbandonedCarts'] = $ecommerceMetrics['totalAbandonedCarts']; | |
| $visitor['totalAbandonedCartsItems'] = $ecommerceMetrics['totalAbandonedCartsItems']; | |
| } | |
| } | |
| public function extendActionDetails(&$action, $nextAction, $visitorDetails) | |
| { | |
| if (empty($action['productViewName'])) { | |
| unset($action['productViewName']); | |
| } | |
| if (empty($action['productViewSku'])) { | |
| unset($action['productViewSku']); | |
| } | |
| if (empty($action['productViewPrice'])) { | |
| unset($action['productViewPrice']); | |
| } | |
| $categories = []; | |
| for ($i = 1; $i <= ProductCategory::PRODUCT_CATEGORY_COUNT; $i++) { | |
| if (!empty($action['productViewCategory' . $i])) { | |
| $categories[] = $action['productViewCategory' . $i]; | |
| } | |
| unset($action['productViewCategory' . $i]); | |
| } | |
| if (!empty($categories)) { | |
| $action['productViewCategories'] = $categories; | |
| } | |
| } | |
| public function renderActionTooltip($action, $visitInfo) | |
| { | |
| if ( | |
| !isset($action['productViewName']) && !isset($action['productViewSku']) && | |
| !isset($action['productViewPrice']) && !isset($action['productViewCategories']) | |
| ) { | |
| return []; | |
| } | |
| $view = new View('@Ecommerce/_actionTooltip'); | |
| $view->sendHeadersWhenRendering = false; | |
| $view->action = $action; | |
| $view->visitInfo = $visitInfo; | |
| return [[ 15, $view->render() ]]; | |
| } | |
| public function provideActionsForVisitIds(&$actions, $idVisits) | |
| { | |
| $ecommerceDetails = $this->queryEcommerceConversionsForVisits($idVisits); | |
| // use while / array_shift combination instead of foreach to save memory | |
| while (is_array($ecommerceDetails) && count($ecommerceDetails)) { | |
| $ecommerceDetail = array_shift($ecommerceDetails); | |
| $idVisit = $ecommerceDetail['idvisit']; | |
| unset($ecommerceDetail['idvisit']); | |
| if ($ecommerceDetail['type'] == Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART) { | |
| unset($ecommerceDetail['orderId']); | |
| unset($ecommerceDetail['revenueSubTotal']); | |
| unset($ecommerceDetail['revenueTax']); | |
| unset($ecommerceDetail['revenueShipping']); | |
| unset($ecommerceDetail['revenueDiscount']); | |
| } | |
| $ecommerceDetail['referrerType'] = $this->getReferrerType($ecommerceDetail['referrerType']); | |
| // 25.00 => 25 | |
| foreach ($ecommerceDetail as $column => $value) { | |
| if (strpos($column, 'revenue') !== false) { | |
| if (!is_numeric($value)) { | |
| $ecommerceDetail[$column] = 0; | |
| } elseif ($value == round($value)) { | |
| $ecommerceDetail[$column] = round($value); | |
| } | |
| } | |
| } | |
| $idOrder = isset($ecommerceDetail['orderId']) ? $ecommerceDetail['orderId'] : GoalManager::ITEM_IDORDER_ABANDONED_CART; | |
| $itemsDetails = $this->queryEcommerceItemsForOrder($idVisit, $idOrder); | |
| foreach ($itemsDetails as &$detail) { | |
| if ($detail['price'] == round($detail['price'])) { | |
| $detail['price'] = round($detail['price']); | |
| } | |
| } | |
| $ecommerceDetail['itemDetails'] = $itemsDetails; | |
| $actions[$idVisit][] = $ecommerceDetail; | |
| } | |
| } | |
| /** | |
| * @param $idSite | |
| * @param $idVisitor | |
| * @return array | |
| * @throws \Exception | |
| */ | |
| protected function queryEcommerceConversionsVisitorLifeTimeMetricsForVisitor($idSite, $idVisitor) | |
| { | |
| $sql = $this->getSqlEcommerceConversionsLifeTimeMetricsForIdGoal(); | |
| $lifeTimeStats = $this->getDb()->fetchAll($sql, array($idSite, @Common::hex2bin($idVisitor))); | |
| $defaultStats = array_fill_keys([GoalManager::IDGOAL_CART, GoalManager::IDGOAL_ORDER], self::DEFAULT_LIFETIME_STAT); | |
| $lifeTimeStatsByGoal = array_reduce($lifeTimeStats, function ($carry, $statRow) { | |
| $idgoal = $statRow['idgoal']; | |
| $carry[$idgoal] = array_merge($carry[$idgoal], $statRow); | |
| return $carry; | |
| }, $defaultStats); | |
| $ecommerceOrders = $lifeTimeStatsByGoal[GoalManager::IDGOAL_ORDER]; | |
| $abandonedCarts = $lifeTimeStatsByGoal[GoalManager::IDGOAL_CART]; | |
| return array( | |
| 'totalEcommerceRevenue' => $ecommerceOrders['lifeTimeRevenue'], | |
| 'totalEcommerceConversions' => $ecommerceOrders['lifeTimeConversions'], | |
| 'totalEcommerceItems' => $ecommerceOrders['lifeTimeEcommerceItems'], | |
| 'totalAbandonedCartsRevenue' => $abandonedCarts['lifeTimeRevenue'], | |
| 'totalAbandonedCarts' => $abandonedCarts['lifeTimeConversions'], | |
| 'totalAbandonedCartsItems' => $abandonedCarts['lifeTimeEcommerceItems'], | |
| ); | |
| } | |
| /** | |
| * Returns and SQL string that queries for `lifeTimeRevenue`, `lifeTimeConversions`, and `lifeTimeEcommerceItems` grouped by | |
| * `idgoal` for abandoned carts and orders. | |
| * @return string | |
| */ | |
| protected function getSqlEcommerceConversionsLifeTimeMetricsForIdGoal() | |
| { | |
| $sql = "SELECT | |
| idgoal, | |
| COALESCE(SUM(" . LogAggregator::getSqlRevenue('revenue') . "), 0) as lifeTimeRevenue, | |
| COUNT(*) as lifeTimeConversions, | |
| COALESCE(SUM(" . LogAggregator::getSqlRevenue('items') . "), 0) as lifeTimeEcommerceItems | |
| FROM " . Common::prefixTable('log_visit') . " AS log_visit | |
| STRAIGHT_JOIN " . Common::prefixTable('log_conversion') . " AS log_conversion | |
| ON log_visit.idvisit = log_conversion.idvisit | |
| WHERE | |
| log_visit.idsite = ? | |
| AND log_visit.idvisitor = ? | |
| AND log_conversion.idgoal IN ( " . GoalManager::IDGOAL_CART . ", " . GoalManager::IDGOAL_ORDER . " ) | |
| GROUP BY idgoal | |
| "; | |
| return $sql; | |
| } | |
| /** | |
| * @param $idVisit | |
| * @param $limit | |
| * @return array | |
| * @throws \Exception | |
| */ | |
| protected function queryEcommerceConversionsForVisits($idVisits) | |
| { | |
| $sql = "SELECT | |
| log_conversion.idvisit, | |
| case idgoal when " . GoalManager::IDGOAL_CART | |
| . " then '" . Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_CART | |
| . "' else '" . Piwik::LABEL_ID_GOAL_IS_ECOMMERCE_ORDER . "' end as type, | |
| idorder as orderId, | |
| " . LogAggregator::getSqlRevenue('revenue') . " as revenue, | |
| " . LogAggregator::getSqlRevenue('revenue_subtotal') . " as revenueSubTotal, | |
| " . LogAggregator::getSqlRevenue('revenue_tax') . " as revenueTax, | |
| " . LogAggregator::getSqlRevenue('revenue_shipping') . " as revenueShipping, | |
| " . LogAggregator::getSqlRevenue('revenue_discount') . " as revenueDiscount, | |
| items as items, | |
| log_conversion.server_time as serverTimePretty, | |
| log_conversion.idlink_va, | |
| log_link_visit_action.idpageview, | |
| log_conversion.referer_type as referrerType, | |
| log_conversion.referer_name as referrerName, | |
| log_conversion.referer_keyword as referrerKeyword | |
| FROM " . Common::prefixTable('log_conversion') . " AS log_conversion | |
| LEFT JOIN " . Common::prefixTable('log_link_visit_action') . " AS log_link_visit_action | |
| ON log_link_visit_action.idlink_va = log_conversion.idlink_va | |
| WHERE log_conversion.idvisit IN ('" . implode("','", $idVisits) . "') | |
| AND idgoal <= " . GoalManager::IDGOAL_ORDER . " | |
| ORDER BY log_conversion.idvisit, log_conversion.server_time ASC"; | |
| $sql = DbHelper::addMaxExecutionTimeHintToQuery($sql, $this->getLiveQueryMaxExecutionTime()); | |
| try { | |
| $ecommerceDetails = $this->getDb()->fetchAll($sql); | |
| } catch (\Exception $e) { | |
| $now = Date::now(); | |
| Model::handleMaxExecutionTimeError($this->getDb(), $e, '', $now, $now, null, 0, ['sql' => $sql]); | |
| throw $e; | |
| } | |
| return $ecommerceDetails; | |
| } | |
| /** | |
| * @param $idVisit | |
| * @param $idOrder | |
| * @param $actionsLimit | |
| * @return array | |
| * @throws \Exception | |
| */ | |
| protected function queryEcommerceItemsForOrder($idVisit, $idOrder) | |
| { | |
| $categorySelects = []; | |
| $categoryJoins = []; | |
| for ($i = 0; $i < self::CATEGORY_COUNT; ++$i) { | |
| $suffix = $i === 0 ? '' : $i; | |
| $column = $i === 0 ? 'idaction_category' : ('idaction_category' . ($i + 1)); | |
| $categorySelects[] = 'log_action_category' . $suffix . '.name as itemCategory' . $suffix; | |
| $categoryJoins[] = 'LEFT JOIN ' . Common::prefixTable('log_action') . " AS log_action_category$suffix | |
| ON $column = log_action_category$suffix.idaction"; | |
| } | |
| $categorySelects = implode(',', $categorySelects); | |
| $categoryJoins = implode("\n", $categoryJoins); | |
| $sql = "SELECT | |
| log_action_sku.name as itemSKU, | |
| log_action_name.name as itemName, | |
| $categorySelects, | |
| " . LogAggregator::getSqlRevenue('price') . " as price, | |
| quantity as quantity | |
| FROM " . Common::prefixTable('log_conversion_item') . " | |
| INNER JOIN " . Common::prefixTable('log_action') . " AS log_action_sku | |
| ON idaction_sku = log_action_sku.idaction | |
| LEFT JOIN " . Common::prefixTable('log_action') . " AS log_action_name | |
| ON idaction_name = log_action_name.idaction | |
| $categoryJoins | |
| WHERE idvisit = ? | |
| AND idorder = ? | |
| AND deleted = 0 | |
| "; | |
| $bind = array($idVisit, $idOrder); | |
| $itemsDetails = $this->getDb()->fetchAll($sql, $bind); | |
| // create categories array for each item | |
| foreach ($itemsDetails as &$item) { | |
| $categories = []; | |
| for ($i = 0; $i < self::CATEGORY_COUNT; ++$i) { | |
| $suffix = $i === 0 ? '' : $i; | |
| if (empty($item['itemCategory' . $suffix])) { | |
| continue; | |
| } | |
| $categories[] = trim($item['itemCategory' . $suffix]); | |
| } | |
| $item['categories'] = array_filter($categories); | |
| // remove itemCategotyN properties, except 'itemCategory' property for BC | |
| for ($i = 1; $i < self::CATEGORY_COUNT; ++$i) { | |
| unset($item['itemCategory' . $i]); | |
| } | |
| } | |
| return $itemsDetails; | |
| } | |
| public function initProfile($visits, &$profile) | |
| { | |
| if (Site::isEcommerceEnabledFor($visits->getFirstRow()->getColumn('idSite'))) { | |
| $profile['totalEcommerceRevenue'] = 0; | |
| $profile['totalEcommerceConversions'] = 0; | |
| $profile['totalEcommerceItems'] = 0; | |
| $profile['totalAbandonedCarts'] = 0; | |
| $profile['totalAbandonedCartsRevenue'] = 0; | |
| $profile['totalAbandonedCartsItems'] = 0; | |
| } | |
| } | |
| public function finalizeProfile($visits, &$profile) | |
| { | |
| $lastVisit = $visits->getLastRow(); | |
| if ($lastVisit && Site::isEcommerceEnabledFor($lastVisit->getColumn('idSite'))) { | |
| $profile['totalEcommerceRevenue'] = $lastVisit->getColumn('totalEcommerceRevenue'); | |
| $profile['totalEcommerceConversions'] = $lastVisit->getColumn('totalEcommerceConversions'); | |
| $profile['totalEcommerceItems'] = $lastVisit->getColumn('totalEcommerceItems'); | |
| $profile['totalAbandonedCartsRevenue'] = $lastVisit->getColumn('totalAbandonedCartsRevenue'); | |
| $profile['totalAbandonedCarts'] = $lastVisit->getColumn('totalAbandonedCarts'); | |
| $profile['totalAbandonedCartsItems'] = $lastVisit->getColumn('totalAbandonedCartsItems'); | |
| } | |
| } | |
| protected function getReferrerType($referrerTypeId) | |
| { | |
| try { | |
| $referrerType = getReferrerTypeFromShortName($referrerTypeId); | |
| } catch (\Exception $e) { | |
| $referrerType = ''; | |
| } | |
| return $referrerType; | |
| } | |
| private function getLiveQueryMaxExecutionTime() | |
| { | |
| return Config::getInstance()->General['live_query_max_execution_time']; | |
| } | |
| } | |