| <?php |
|
|
| |
| |
| |
| |
| |
| |
|
|
| namespace Piwik\Tracker; |
|
|
| use Exception; |
| use Piwik\Common; |
| use Piwik\Container\StaticContainer; |
| use Piwik\Tracker; |
| use Piwik\Log\LoggerInterface; |
|
|
| class Model |
| { |
| public const CACHE_KEY_INDEX_IDSITE_IDVISITOR_TIME = 'log_visit_has_index_idsite_idvisitor_time'; |
|
|
| |
| |
| |
| |
| |
| |
| |
| public function createAction($visitAction) |
| { |
| $fields = implode(", ", array_keys($visitAction)); |
| $values = Common::getSqlStringFieldsArray($visitAction); |
| $table = Common::prefixTable('log_link_visit_action'); |
|
|
| $sql = "INSERT INTO $table ($fields) VALUES ($values)"; |
| $bind = array_values($visitAction); |
|
|
| $db = $this->getDb(); |
| $db->query($sql, $bind); |
|
|
| $id = $db->lastInsertId(); |
|
|
| return $id; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| public function createConversion($conversion) |
| { |
|
|
| $fields = implode(", ", array_keys($conversion)); |
| $bindFields = Common::getSqlStringFieldsArray($conversion); |
| $table = Common::prefixTable('log_conversion'); |
|
|
| $sql = "INSERT IGNORE INTO $table ($fields) VALUES ($bindFields) "; |
| $bind = array_values($conversion); |
|
|
| $db = $this->getDb(); |
| $result = $db->query($sql, $bind); |
|
|
| |
| return $db->rowCount($result) > 0; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public function updateConversion($idVisit, $idGoal, $newConversion) |
| { |
| $updateWhere = [ |
| 'idvisit' => $idVisit, |
| 'idgoal' => $idGoal, |
| 'buster' => 0, |
| ]; |
|
|
| $updateParts = $sqlBind = $updateWhereParts = []; |
|
|
| foreach ($newConversion as $name => $value) { |
| $updateParts[] = $name . " = ?"; |
| $sqlBind[] = $value; |
| } |
|
|
| foreach ($updateWhere as $name => $value) { |
| $updateWhereParts[] = $name . " = ?"; |
| $sqlBind[] = $value; |
| } |
|
|
| $parts = implode(', ', $updateParts); |
| $table = Common::prefixTable('log_conversion'); |
|
|
| $sql = "UPDATE $table SET $parts WHERE " . implode(' AND ', $updateWhereParts); |
|
|
| try { |
| $this->getDb()->query($sql, $sqlBind); |
| } catch (Exception $e) { |
| StaticContainer::get(LoggerInterface::class)->error("There was an error while updating the Conversion: {exception}", [ |
| 'exception' => $e, |
| ]); |
|
|
| return false; |
| } |
|
|
| return true; |
| } |
|
|
|
|
| |
| |
| |
| |
| |
| |
| |
| public function getAllItemsCurrentlyInTheCart($goal, $defaultIdOrder) |
| { |
| $sql = "SELECT idaction_sku, idaction_name, idaction_category, idaction_category2, idaction_category3, idaction_category4, idaction_category5, price, quantity, deleted, idorder AS idorder_original_value |
| FROM `" . Common::prefixTable('log_conversion_item') . "` |
| WHERE idvisit = ? AND (idorder = ? OR idorder = ?)"; |
|
|
| $bind = [ |
| $goal['idvisit'], |
| isset($goal['idorder']) ? $goal['idorder'] : $defaultIdOrder, |
| $defaultIdOrder, |
| ]; |
|
|
| $itemsInDb = $this->getDb()->fetchAll($sql, $bind); |
|
|
| Common::printDebug("Items found in current cart, for conversion_item (visit,idorder)=" . var_export($bind, true)); |
| Common::printDebug($itemsInDb); |
|
|
| return $itemsInDb; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| public function createEcommerceItems($ecommerceItems) |
| { |
| $sql = "INSERT IGNORE INTO " . Common::prefixTable('log_conversion_item'); |
| $i = 0; |
| $bind = []; |
|
|
| foreach ($ecommerceItems as $item) { |
| if ($i === 0) { |
| $fields = implode(', ', array_keys($item)); |
| $sql .= ' (' . $fields . ') VALUES '; |
| } elseif ($i > 0) { |
| $sql .= ','; |
| } |
|
|
| $newRow = array_values($item); |
| $sql .= " ( " . Common::getSqlStringFieldsArray($newRow) . " ) "; |
| $bind = array_merge($bind, $newRow); |
| $i++; |
| } |
|
|
| Common::printDebug($sql); |
| Common::printDebug($bind); |
|
|
| try { |
| $this->getDb()->query($sql, $bind); |
| } catch (Exception $e) { |
| if ( |
| $e->getCode() == 23000 || |
| false !== strpos($e->getMessage(), 'Duplicate entry') || |
| false !== strpos($e->getMessage(), 'Integrity constraint violation') |
| ) { |
| Common::printDebug('Did not create ecommerce item as item was already created'); |
| } else { |
| throw $e; |
| } |
| } |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public function createNewIdAction($name, $type, $urlPrefix) |
| { |
| $newActionId = $this->insertNewAction($name, $type, $urlPrefix); |
|
|
| $realFirstActionId = $this->getIdActionMatchingNameAndType($name, $type); |
|
|
| |
| |
| if ($realFirstActionId != $newActionId) { |
| $this->deleteDuplicateAction($newActionId); |
| } |
|
|
| return $realFirstActionId; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| private function insertNewAction($name, $type, $urlPrefix) |
| { |
| $table = Common::prefixTable('log_action'); |
| $sql = "INSERT INTO $table (name, hash, type, url_prefix) VALUES (?,CRC32(?),?,?)"; |
|
|
| $db = $this->getDb(); |
| $db->query($sql, [$name, $name, $type, $urlPrefix]); |
|
|
| $actionId = $db->lastInsertId(); |
|
|
| return $actionId; |
| } |
|
|
| |
| |
| |
| |
| |
| private function getSqlSelectActionId() |
| { |
| |
| |
| $sql = "SELECT idaction, type, name FROM `" . Common::prefixTable('log_action') . "`" |
| . " WHERE " . $this->getSqlConditionToMatchSingleAction() . " " |
| . "ORDER BY idaction ASC LIMIT 1"; |
|
|
| return $sql; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public function getIdActionMatchingNameAndType($name, $type) |
| { |
| $sql = $this->getSqlSelectActionId(); |
| $bind = [$name, $name, $type]; |
|
|
| $idAction = $this->getDb()->fetchOne($sql, $bind); |
|
|
| return $idAction; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| public function getIdsAction($actionsNameAndType) |
| { |
| $sql = "SELECT `idaction`, `type`, `name` FROM `" . Common::prefixTable('log_action') . "` WHERE"; |
| $bind = []; |
|
|
| $i = 0; |
| foreach ($actionsNameAndType as $actionNameType) { |
| $name = $actionNameType['name']; |
|
|
| if (empty($name)) { |
| continue; |
| } |
|
|
| if ($i > 0) { |
| $sql .= " OR"; |
| } |
|
|
| $sql .= " " . $this->getSqlConditionToMatchSingleAction() . " "; |
|
|
| $bind[] = $name; |
| $bind[] = $name; |
| $bind[] = $actionNameType['type']; |
| $i++; |
| } |
|
|
| |
| if (empty($bind)) { |
| return false; |
| } |
|
|
| $rows = $this->getDb()->fetchAll($sql, $bind); |
|
|
| $actionsPerType = []; |
|
|
| foreach ($rows as $row) { |
| $name = $row['name']; |
| $type = $row['type']; |
|
|
| if (!isset($actionsPerType[$type])) { |
| $actionsPerType[$type] = []; |
| } |
|
|
| if (!isset($actionsPerType[$type][$name])) { |
| $actionsPerType[$type][$name] = $row; |
| } elseif ($row['idaction'] < $actionsPerType[$type][$name]['idaction']) { |
| |
| $actionsPerType[$type][$name] = $row; |
| } |
| } |
|
|
| $actionsToReturn = []; |
| foreach ($actionsPerType as $type => $actionsPerName) { |
| foreach ($actionsPerName as $actionPerName) { |
| $actionsToReturn[] = $actionPerName; |
| } |
| } |
|
|
| return $actionsToReturn; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| public function updateEcommerceItem($originalIdOrder, $newItem) |
| { |
| $updateParts = $sqlBind = []; |
| foreach ($newItem as $name => $value) { |
| $updateParts[] = $name . " = ?"; |
| $sqlBind[] = $value; |
| } |
|
|
| $parts = implode(', ', $updateParts); |
| $table = Common::prefixTable('log_conversion_item'); |
|
|
| $sql = "UPDATE $table SET $parts WHERE idvisit = ? AND idorder = ? AND idaction_sku = ?"; |
|
|
| $sqlBind[] = $newItem['idvisit']; |
| $sqlBind[] = $originalIdOrder; |
| $sqlBind[] = $newItem['idaction_sku']; |
|
|
| $this->getDb()->query($sql, $sqlBind); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| public function createVisit($visit) |
| { |
| $fields = array_keys($visit); |
| $fields = implode(", ", $fields); |
| $values = Common::getSqlStringFieldsArray($visit); |
| $table = Common::prefixTable('log_visit'); |
|
|
| $sql = "INSERT INTO $table ($fields) VALUES ($values)"; |
| $bind = array_values($visit); |
|
|
| $db = $this->getDb(); |
| $db->query($sql, $bind); |
|
|
| return $db->lastInsertId(); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public function updateVisit($idSite, $idVisit, $valuesToUpdate) |
| { |
| [$updateParts, $sqlBind] = $this->fieldsToQuery($valuesToUpdate); |
|
|
| $parts = implode(', ', $updateParts); |
| $table = Common::prefixTable('log_visit'); |
|
|
| $sqlQuery = "UPDATE $table SET $parts WHERE idsite = ? AND idvisit = ?"; |
|
|
| $sqlBind[] = $idSite; |
| $sqlBind[] = $idVisit; |
|
|
| $db = $this->getDb(); |
| $result = $db->query($sqlQuery, $sqlBind); |
| $wasInserted = $db->rowCount($result) != 0; |
|
|
| if (!$wasInserted) { |
| Common::printDebug("Visitor with this idvisit wasn't found in the DB."); |
| Common::printDebug("$sqlQuery --- "); |
| Common::printDebug($sqlBind); |
| } |
|
|
| return $wasInserted; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public function updateAction($idLinkVa, $valuesToUpdate) |
| { |
| if (empty($idLinkVa)) { |
| return; |
| } |
|
|
| [$updateParts, $sqlBind] = $this->fieldsToQuery($valuesToUpdate); |
|
|
| $parts = implode(', ', $updateParts); |
| $table = Common::prefixTable('log_link_visit_action'); |
|
|
| $sqlQuery = "UPDATE $table SET $parts WHERE idlink_va = ?"; |
|
|
| $sqlBind[] = $idLinkVa; |
|
|
| $db = $this->getDb(); |
| $result = $db->query($sqlQuery, $sqlBind); |
| $wasInserted = $db->rowCount($result) != 0; |
|
|
| if (!$wasInserted) { |
| Common::printDebug("Action with this idLinkVa wasn't found in the DB."); |
| Common::printDebug("$sqlQuery --- "); |
| Common::printDebug($sqlBind); |
| } |
|
|
| return $wasInserted; |
| } |
|
|
| public function updateIdVisitorInLogTable(string $logTable, string $idVisitor, array $conditions): bool |
| { |
| if (empty($idVisitor) || empty($conditions)) { |
| return false; |
| } |
|
|
| $table = Common::prefixTable($logTable); |
|
|
| $sqlQuery = "UPDATE `$table` SET `idvisitor` = ? WHERE "; |
| $sqlConditions = []; |
| $sqlBind = [$idVisitor]; |
|
|
| foreach ($conditions as $name => $value) { |
| $sqlConditions[] = $name . " = ?"; |
| $sqlBind[] = $value; |
| } |
|
|
| $sqlQuery .= implode(' AND ', $sqlConditions); |
|
|
| $db = $this->getDb(); |
| $result = $db->query($sqlQuery, $sqlBind); |
| return $db->rowCount($result) != 0; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public function findVisitor( |
| $idSite, |
| $configId, |
| $idVisitor, |
| $userId, |
| $fieldsToRead, |
| $shouldMatchOneFieldOnly, |
| $isVisitorIdToLookup, |
| $timeLookBack, |
| $timeLookAhead |
| ) { |
| $selectFields = implode(', ', $fieldsToRead); |
|
|
| $select = "SELECT $selectFields "; |
| $from = "FROM `" . Common::prefixTable('log_visit') . "`"; |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| $configIdWhere = "visit_last_action_time >= ? AND visit_last_action_time <= ? AND idsite = ?"; |
| $configIdbindSql = [ |
| $timeLookBack, |
| $timeLookAhead, |
| $idSite, |
| ]; |
|
|
| $visitorIdWhere = 'idsite = ? AND visit_last_action_time <= ?'; |
| $visitorIdbindSql = [$idSite, $timeLookAhead]; |
|
|
| if ($shouldMatchOneFieldOnly && $isVisitorIdToLookup) { |
| $visitRow = $this->findVisitorByVisitorId($idVisitor, $select, $from, $visitorIdWhere, $visitorIdbindSql); |
| } elseif ($shouldMatchOneFieldOnly) { |
| $visitRow = $this->findVisitorByConfigId($configId, $select, $from, $configIdWhere, $configIdbindSql); |
| } else { |
| if (!empty($idVisitor)) { |
| $visitRow = $this->findVisitorByVisitorId($idVisitor, $select, $from, $visitorIdWhere, $visitorIdbindSql); |
| } else { |
| $visitRow = false; |
| } |
|
|
| if (empty($visitRow)) { |
| if (!empty($userId)) { |
| $configIdWhere .= ' AND ( user_id IS NULL OR user_id = ? )'; |
| $configIdbindSql[] = $userId; |
| } |
| $visitRow = $this->findVisitorByConfigId($configId, $select, $from, $configIdWhere, $configIdbindSql); |
| } |
| } |
|
|
| return $visitRow; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| public function hasVisit($idSite, $idVisit) |
| { |
| |
| $sql = 'SELECT idsite FROM `' . Common::prefixTable('log_visit') . '` WHERE idvisit = ? LIMIT 1'; |
| $bindSql = [$idVisit]; |
|
|
| $val = $this->getDb()->fetchOne($sql, $bindSql); |
| return $val == $idSite; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| private function findVisitorByVisitorId($idVisitor, $select, $from, $where, $bindSql) |
| { |
| $cache = Cache::getCacheGeneral(); |
|
|
| |
| if ( |
| array_key_exists(self::CACHE_KEY_INDEX_IDSITE_IDVISITOR_TIME, $cache) |
| && true === $cache[self::CACHE_KEY_INDEX_IDSITE_IDVISITOR_TIME] |
| ) { |
| $from .= ' FORCE INDEX (index_idsite_idvisitor_time) '; |
| } |
|
|
| $where .= ' AND idvisitor = ?'; |
| $bindSql[] = $idVisitor; |
|
|
| return $this->fetchVisitor($select, $from, $where, $bindSql); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| private function findVisitorByConfigId($configId, $select, $from, $where, $bindSql) |
| { |
| |
| $where .= ' AND config_id = ?'; |
| $bindSql[] = $configId; |
|
|
| return $this->fetchVisitor($select, $from, $where, $bindSql); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| |
| private function fetchVisitor($select, $from, $where, $bindSql) |
| { |
| $sql = "$select $from WHERE " . $where . " |
| ORDER BY visit_last_action_time DESC |
| LIMIT 1"; |
|
|
| $visitRow = $this->getDb()->fetch($sql, $bindSql); |
|
|
| return $visitRow; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| public function isSiteEmpty($siteId) |
| { |
| $sql = sprintf('SELECT idsite FROM `%s` WHERE idsite = ? limit 1', Common::prefixTable('log_visit')); |
|
|
| $result = \Piwik\Db::fetchOne($sql, [$siteId]); |
|
|
| return $result == null; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| private function fieldsToQuery($valuesToUpdate) |
| { |
| $updateParts = []; |
| $sqlBind = []; |
|
|
| foreach ($valuesToUpdate as $name => $value) { |
| |
| if ($value === $name . ' + 1') { |
| |
| |
| $updateParts[] = " $name = $value "; |
| } else { |
| $updateParts[] = $name . " = ?"; |
| $sqlBind[] = $value; |
| } |
| } |
|
|
| return [$updateParts, $sqlBind]; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| |
| private function deleteDuplicateAction($newActionId) |
| { |
| $sql = "DELETE FROM `" . Common::prefixTable('log_action') . "` WHERE idaction = ?"; |
|
|
| $db = $this->getDb(); |
| $db->query($sql, [$newActionId]); |
| } |
|
|
| |
| |
| |
| |
| |
| |
| private function getDb() |
| { |
| return Tracker::getDatabase(); |
| } |
|
|
| |
| |
| |
| |
| |
| private function getSqlConditionToMatchSingleAction() |
| { |
| return "( hash = CRC32(?) AND name = ? AND type = ? )"; |
| } |
| } |
|
|