|
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