diff --git a/vendor/magento/module-analytics/ReportXml/ReportProvider.php b/vendor/magento/module-analytics/ReportXml/ReportProvider.php index 05b2f8b4465..82b7c37628c 100644 --- a/vendor/magento/module-analytics/ReportXml/ReportProvider.php +++ b/vendor/magento/module-analytics/ReportXml/ReportProvider.php @@ -6,7 +6,7 @@ namespace Magento\Analytics\ReportXml; -use Magento\Framework\Api\SearchCriteria; +use PDO; /** * Providers for reports data @@ -48,6 +48,16 @@ class ReportProvider implements BatchReportProviderInterface */ private $dataSelect; + /** + * @var int|null Last cursor value for cursor-based pagination + */ + private ?int $lastCursor = null; + + /** + * @var string|null Cursor column name for cursor-based pagination + */ + private ?string $cursorColumn = null; + /** * ReportProvider constructor. * @@ -98,15 +108,64 @@ class ReportProvider implements BatchReportProviderInterface { if (!$this->dataSelect || $this->dataSelect->getConfig()['name'] !== $name) { $this->dataSelect = $this->queryFactory->create($name); + $this->lastCursor = null; $this->currentPosition = 0; + $this->countTotal = 0; $this->connection = $this->connectionFactory->getConnection($this->dataSelect->getConnectionName()); - $this->countTotal = $this->connection->fetchOne($this->dataSelect->getSelectCountSql()); + $this->cursorColumn = $this->getCursorColumn(); + if (!$this->cursorColumn) { + $this->countTotal = $this->connection->fetchOne($this->dataSelect->getSelectCountSql()); + } + } + if (!$this->cursorColumn) { + return $this->getBatchReportWithOffset(); } - if ($this->currentPosition >= $this->countTotal) { + $select = clone $this->dataSelect->getSelect(); + $cursorValue = $this->lastCursor ?? 0; + $select->where(sprintf('%s > ?', $this->cursorColumn), $cursorValue); + $select->order(sprintf('%s ASC', $this->cursorColumn)); + $select->limit(self::BATCH_SIZE); + $statement = $this->connection->query($select); + $rows = $statement->fetchAll(PDO::FETCH_ASSOC); + if (empty($rows)) { return $this->iteratorFactory->create(new \ArrayIterator([]), $this->getIteratorName($this->dataSelect)); } + $lastRow = $rows[count($rows) - 1]; + $this->lastCursor = $lastRow[$this->cursorColumn] ?? null; + return $this->iteratorFactory->create(new \ArrayIterator($rows), $this->getIteratorName($this->dataSelect)); + } + + /** + * Detect cursor column based on the source table's primary key + * + * @return string|null Returns the primary key column name, or null if not found + */ + private function getCursorColumn(): ?string + { + $config = $this->dataSelect->getConfig(); + $tableName = $config['source']['name'] ?? null; + $analyticTables = ["customer_entity", "sales_order", "sales_order_address", "quote", "catalog_product_entity"]; + if ($tableName) { + if (in_array($tableName, $analyticTables)) { + return "entity_id"; + } elseif ($tableName == "sales_order_item") { + return "item_id"; + } + } + return null; + } + /** + * Fallback to offset-based pagination when cursor column cannot be detected + * + * @return \IteratorIterator + */ + private function getBatchReportWithOffset(): \IteratorIterator + { + if ($this->currentPosition >= $this->countTotal) { + return $this->iteratorFactory->create(new \ArrayIterator([]), $this->getIteratorName($this->dataSelect)); + } $statement = $this->connection->query( $this->dataSelect->getSelect()->limit(self::BATCH_SIZE, $this->currentPosition) );