55 */
66namespace Magento \Catalog \Model \ResourceModel \Category ;
77
8+ use Magento \Catalog \Model \Category ;
9+ use Magento \Catalog \Model \Product \Visibility ;
810use Magento \CatalogUrlRewrite \Model \CategoryUrlRewriteGenerator ;
911use Magento \Framework \App \Config \ScopeConfigInterface ;
12+ use Magento \Framework \DB \Select ;
1013use Magento \Store \Model \ScopeInterface ;
1114
1215/**
@@ -68,6 +71,11 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac
6871 */
6972 private $ scopeConfig ;
7073
74+ /**
75+ * @var Visibility
76+ */
77+ private $ catalogProductVisibility ;
78+
7179 /**
7280 * Constructor
7381 * @param \Magento\Framework\Data\Collection\EntityFactory $entityFactory
@@ -82,6 +90,7 @@ class Collection extends \Magento\Catalog\Model\ResourceModel\Collection\Abstrac
8290 * @param \Magento\Store\Model\StoreManagerInterface $storeManager
8391 * @param \Magento\Framework\DB\Adapter\AdapterInterface $connection
8492 * @param \Magento\Framework\App\Config\ScopeConfigInterface $scopeConfig
93+ * @param Visibility|null $catalogProductVisibility
8594 * @SuppressWarnings(PHPMD.ExcessiveParameterList)
8695 */
8796 public function __construct (
@@ -96,7 +105,8 @@ public function __construct(
96105 \Magento \Framework \Validator \UniversalFactory $ universalFactory ,
97106 \Magento \Store \Model \StoreManagerInterface $ storeManager ,
98107 \Magento \Framework \DB \Adapter \AdapterInterface $ connection = null ,
99- \Magento \Framework \App \Config \ScopeConfigInterface $ scopeConfig = null
108+ \Magento \Framework \App \Config \ScopeConfigInterface $ scopeConfig = null ,
109+ Visibility $ catalogProductVisibility = null
100110 ) {
101111 parent ::__construct (
102112 $ entityFactory ,
@@ -113,6 +123,8 @@ public function __construct(
113123 );
114124 $ this ->scopeConfig = $ scopeConfig ?:
115125 \Magento \Framework \App \ObjectManager::getInstance ()->get (ScopeConfigInterface::class);
126+ $ this ->catalogProductVisibility = $ catalogProductVisibility ?:
127+ \Magento \Framework \App \ObjectManager::getInstance ()->get (Visibility::class);
116128 }
117129
118130 /**
@@ -122,7 +134,7 @@ public function __construct(
122134 */
123135 protected function _construct ()
124136 {
125- $ this ->_init (\ Magento \ Catalog \ Model \ Category::class, \Magento \Catalog \Model \ResourceModel \Category::class);
137+ $ this ->_init (Category::class, \Magento \Catalog \Model \ResourceModel \Category::class);
126138 }
127139
128140 /**
@@ -259,6 +271,7 @@ protected function _loadProductCount()
259271 * @return $this
260272 * @SuppressWarnings(PHPMD.CyclomaticComplexity)
261273 * @SuppressWarnings(PHPMD.UnusedLocalVariable)
274+ * @throws \Magento\Framework\Exception\NoSuchEntityException
262275 */
263276 public function loadProductCount ($ items , $ countRegular = true , $ countAnchor = true )
264277 {
@@ -310,34 +323,14 @@ public function loadProductCount($items, $countRegular = true, $countAnchor = tr
310323
311324 if ($ countAnchor ) {
312325 // Retrieve Anchor categories product counts
326+ $ categoryIds = array_keys ($ anchor );
327+ $ countSelect = $ this ->getProductsCountQuery ($ categoryIds , (bool )$ websiteId );
328+ $ categoryProductsCount = $ this ->_conn ->fetchPairs ($ countSelect );
313329 foreach ($ anchor as $ item ) {
314- if ($ allChildren = $ item ->getAllChildren ()) {
315- $ bind = ['entity_id ' => $ item ->getId (), 'c_path ' => $ item ->getPath () . '/% ' ];
316- $ select = $ this ->_conn ->select ();
317- $ select ->from (
318- ['main_table ' => $ this ->getProductTable ()],
319- new \Zend_Db_Expr ('COUNT(DISTINCT main_table.product_id) ' )
320- )->joinInner (
321- ['e ' => $ this ->getTable ('catalog_category_entity ' )],
322- 'main_table.category_id=e.entity_id ' ,
323- []
324- )->where (
325- '(e.entity_id = :entity_id OR e.path LIKE :c_path) '
326- );
327- if ($ websiteId ) {
328- $ select ->join (
329- ['w ' => $ this ->getProductWebsiteTable ()],
330- 'main_table.product_id = w.product_id ' ,
331- []
332- )->where (
333- 'w.website_id = ? ' ,
334- $ websiteId
335- );
336- }
337- $ item ->setProductCount ((int )$ this ->_conn ->fetchOne ($ select , $ bind ));
338- } else {
339- $ item ->setProductCount (0 );
340- }
330+ $ productsCount = isset ($ categoriesProductsCount [$ item ->getId ()])
331+ ? (int )$ categoryProductsCount [$ item ->getId ()]
332+ : $ this ->getProductsCountFromCategoryTable ($ item , $ websiteId );
333+ $ item ->setProductCount ($ productsCount );
341334 }
342335 }
343336 return $ this ;
@@ -513,4 +506,69 @@ public function getProductTable()
513506 }
514507 return $ this ->_productTable ;
515508 }
509+
510+ /**
511+ * Get products count using catalog_category_entity table
512+ *
513+ * @param Category $item
514+ * @param string $websiteId
515+ * @return int
516+ */
517+ private function getProductsCountFromCategoryTable (Category $ item , string $ websiteId ): int
518+ {
519+ $ productCount = 0 ;
520+
521+ if ($ item ->getAllChildren ()) {
522+ $ bind = ['entity_id ' => $ item ->getId (), 'c_path ' => $ item ->getPath () . '/% ' ];
523+ $ select = $ this ->_conn ->select ();
524+ $ select ->from (
525+ ['main_table ' => $ this ->getProductTable ()],
526+ new \Zend_Db_Expr ('COUNT(DISTINCT main_table.product_id) ' )
527+ )->joinInner (
528+ ['e ' => $ this ->getTable ('catalog_category_entity ' )],
529+ 'main_table.category_id=e.entity_id ' ,
530+ []
531+ )->where (
532+ '(e.entity_id = :entity_id OR e.path LIKE :c_path) '
533+ );
534+ if ($ websiteId ) {
535+ $ select ->join (
536+ ['w ' => $ this ->getProductWebsiteTable ()],
537+ 'main_table.product_id = w.product_id ' ,
538+ []
539+ )->where (
540+ 'w.website_id = ? ' ,
541+ $ websiteId
542+ );
543+ }
544+ $ productCount = (int )$ this ->_conn ->fetchOne ($ select , $ bind );
545+ }
546+ return $ productCount ;
547+ }
548+
549+ /**
550+ * Get query for retrieve count of products per category
551+ *
552+ * @param array $categoryIds
553+ * @param bool $addVisibilityFilter
554+ * @return Select
555+ */
556+ private function getProductsCountQuery (array $ categoryIds , $ addVisibilityFilter = true ): Select
557+ {
558+ $ categoryTable = $ this ->getTable ('catalog_category_product_index ' );
559+ $ select = $ this ->_conn ->select ()
560+ ->from (
561+ ['cat_index ' => $ categoryTable ],
562+ ['category_id ' => 'cat_index.category_id ' , 'count ' => 'count(cat_index.product_id) ' ]
563+ )
564+ ->where ('cat_index.category_id in (?) ' , \array_map ('\intval ' , $ categoryIds ));
565+ if (true === $ addVisibilityFilter ) {
566+ $ select ->where ('cat_index.visibility in (?) ' , $ this ->catalogProductVisibility ->getVisibleInSiteIds ());
567+ }
568+ if (count ($ categoryIds ) > 1 ) {
569+ $ select ->group ('cat_index.category_id ' );
570+ }
571+
572+ return $ select ;
573+ }
516574}
0 commit comments