|
21 | 21 | */ |
22 | 22 | class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\AbstractCollection |
23 | 23 | { |
| 24 | + private const BULK_PROCESSING_LIMIT = 400; |
| 25 | + |
24 | 26 | /** |
25 | 27 | * Event prefix name |
26 | 28 | * |
@@ -282,6 +284,7 @@ protected function _loadProductCount() |
282 | 284 | * @return $this |
283 | 285 | * @SuppressWarnings(PHPMD.CyclomaticComplexity) |
284 | 286 | * @SuppressWarnings(PHPMD.UnusedLocalVariable) |
| 287 | + * @SuppressWarnings(PHPMD.NPathComplexity) |
285 | 288 | * @throws \Magento\Framework\Exception\NoSuchEntityException |
286 | 289 | */ |
287 | 290 | public function loadProductCount($items, $countRegular = true, $countAnchor = true) |
@@ -337,16 +340,111 @@ public function loadProductCount($items, $countRegular = true, $countAnchor = tr |
337 | 340 | $categoryIds = array_keys($anchor); |
338 | 341 | $countSelect = $this->getProductsCountQuery($categoryIds, (bool)$websiteId); |
339 | 342 | $categoryProductsCount = $this->_conn->fetchPairs($countSelect); |
| 343 | + $countFromCategoryTable = []; |
| 344 | + if (count($categoryIds) > self::BULK_PROCESSING_LIMIT) { |
| 345 | + $countFromCategoryTable = $this->getCountFromCategoryTableBulk($categoryIds, (int)$websiteId); |
| 346 | + } |
| 347 | + |
340 | 348 | foreach ($anchor as $item) { |
341 | | - $productsCount = isset($categoryProductsCount[$item->getId()]) |
342 | | - ? (int)$categoryProductsCount[$item->getId()] |
343 | | - : $this->getProductsCountFromCategoryTable($item, $websiteId); |
| 349 | + $productsCount = 0; |
| 350 | + if (count($categoryIds) > self::BULK_PROCESSING_LIMIT) { |
| 351 | + if (isset($categoryProductsCount[$item->getId()])) { |
| 352 | + $productsCount = (int)$categoryProductsCount[$item->getId()]; |
| 353 | + } elseif (isset($countFromCategoryTable[$item->getId()])) { |
| 354 | + $productsCount = (int)$countFromCategoryTable[$item->getId()]; |
| 355 | + } |
| 356 | + } else { |
| 357 | + $productsCount = isset($categoryProductsCount[$item->getId()]) |
| 358 | + ? (int)$categoryProductsCount[$item->getId()] |
| 359 | + : $this->getProductsCountFromCategoryTable($item, $websiteId); |
| 360 | + } |
344 | 361 | $item->setProductCount($productsCount); |
345 | 362 | } |
346 | 363 | } |
347 | 364 | return $this; |
348 | 365 | } |
349 | 366 |
|
| 367 | + /** |
| 368 | + * Get products number for each category with bulk query |
| 369 | + * |
| 370 | + * @param array $categoryIds |
| 371 | + * @param int $websiteId |
| 372 | + * @return array |
| 373 | + */ |
| 374 | + private function getCountFromCategoryTableBulk( |
| 375 | + array $categoryIds, |
| 376 | + int $websiteId |
| 377 | + ) : array { |
| 378 | + $subSelect = clone $this->_conn->select(); |
| 379 | + $subSelect->from(['ce2' => $this->getTable('catalog_category_entity')], 'ce2.entity_id') |
| 380 | + ->where("ce2.path LIKE CONCAT(ce.path, '/%') OR ce2.path = ce.path"); |
| 381 | + |
| 382 | + $select = clone $this->_conn->select(); |
| 383 | + $select->from( |
| 384 | + ['ce' => $this->getTable('catalog_category_entity')], |
| 385 | + 'ce.entity_id' |
| 386 | + ); |
| 387 | + $joinCondition = new \Zend_Db_Expr("cp.category_id IN ({$subSelect})"); |
| 388 | + $select->joinLeft( |
| 389 | + ['cp' => $this->getProductTable()], |
| 390 | + $joinCondition, |
| 391 | + 'COUNT(DISTINCT cp.product_id) AS product_count' |
| 392 | + ); |
| 393 | + if ($websiteId) { |
| 394 | + $select->join( |
| 395 | + ['w' => $this->getProductWebsiteTable()], |
| 396 | + 'cp.product_id = w.product_id', |
| 397 | + [] |
| 398 | + )->where( |
| 399 | + 'w.website_id = ?', |
| 400 | + $websiteId |
| 401 | + ); |
| 402 | + } |
| 403 | + $select->where('ce.entity_id IN(?)', $categoryIds); |
| 404 | + $select->group('ce.entity_id'); |
| 405 | + |
| 406 | + return $this->_conn->fetchPairs($select); |
| 407 | + } |
| 408 | + |
| 409 | + /** |
| 410 | + * Get products count using catalog_category_entity table |
| 411 | + * |
| 412 | + * @param Category $item |
| 413 | + * @param string $websiteId |
| 414 | + * @return int |
| 415 | + */ |
| 416 | + private function getProductsCountFromCategoryTable(Category $item, string $websiteId): int |
| 417 | + { |
| 418 | + $productCount = 0; |
| 419 | + |
| 420 | + if ($item->getAllChildren()) { |
| 421 | + $bind = ['entity_id' => $item->getId(), 'c_path' => $item->getPath() . '/%']; |
| 422 | + $select = $this->_conn->select(); |
| 423 | + $select->from( |
| 424 | + ['main_table' => $this->getProductTable()], |
| 425 | + new \Zend_Db_Expr('COUNT(DISTINCT main_table.product_id)') |
| 426 | + )->joinInner( |
| 427 | + ['e' => $this->getTable('catalog_category_entity')], |
| 428 | + 'main_table.category_id=e.entity_id', |
| 429 | + [] |
| 430 | + )->where( |
| 431 | + '(e.entity_id = :entity_id OR e.path LIKE :c_path)' |
| 432 | + ); |
| 433 | + if ($websiteId) { |
| 434 | + $select->join( |
| 435 | + ['w' => $this->getProductWebsiteTable()], |
| 436 | + 'main_table.product_id = w.product_id', |
| 437 | + [] |
| 438 | + )->where( |
| 439 | + 'w.website_id = ?', |
| 440 | + $websiteId |
| 441 | + ); |
| 442 | + } |
| 443 | + $productCount = (int)$this->_conn->fetchOne($select, $bind); |
| 444 | + } |
| 445 | + return $productCount; |
| 446 | + } |
| 447 | + |
350 | 448 | /** |
351 | 449 | * Add category path filter |
352 | 450 | * |
@@ -519,45 +617,6 @@ public function getProductTable() |
519 | 617 | return $this->_productTable; |
520 | 618 | } |
521 | 619 |
|
522 | | - /** |
523 | | - * Get products count using catalog_category_entity table |
524 | | - * |
525 | | - * @param Category $item |
526 | | - * @param string $websiteId |
527 | | - * @return int |
528 | | - */ |
529 | | - private function getProductsCountFromCategoryTable(Category $item, string $websiteId): int |
530 | | - { |
531 | | - $productCount = 0; |
532 | | - |
533 | | - if ($item->getAllChildren()) { |
534 | | - $bind = ['entity_id' => $item->getId(), 'c_path' => $item->getPath() . '/%']; |
535 | | - $select = $this->_conn->select(); |
536 | | - $select->from( |
537 | | - ['main_table' => $this->getProductTable()], |
538 | | - new \Zend_Db_Expr('COUNT(DISTINCT main_table.product_id)') |
539 | | - )->joinInner( |
540 | | - ['e' => $this->getTable('catalog_category_entity')], |
541 | | - 'main_table.category_id=e.entity_id', |
542 | | - [] |
543 | | - )->where( |
544 | | - '(e.entity_id = :entity_id OR e.path LIKE :c_path)' |
545 | | - ); |
546 | | - if ($websiteId) { |
547 | | - $select->join( |
548 | | - ['w' => $this->getProductWebsiteTable()], |
549 | | - 'main_table.product_id = w.product_id', |
550 | | - [] |
551 | | - )->where( |
552 | | - 'w.website_id = ?', |
553 | | - $websiteId |
554 | | - ); |
555 | | - } |
556 | | - $productCount = (int)$this->_conn->fetchOne($select, $bind); |
557 | | - } |
558 | | - return $productCount; |
559 | | - } |
560 | | - |
561 | 620 | /** |
562 | 621 | * Get query for retrieve count of products per category |
563 | 622 | * |
|
0 commit comments