Skip to content

Commit 784e733

Browse files
committed
Merge branch '2.4-develop' of http://github.com/magento/magento2 into fix_issue_26449
2 parents 43e7715 + a73532a commit 784e733

File tree

59 files changed

+1543
-759
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

59 files changed

+1543
-759
lines changed

app/code/Magento/Catalog/Model/ResourceModel/Category/Collection.php

Lines changed: 87 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
*/
66
namespace Magento\Catalog\Model\ResourceModel\Category;
77

8+
use Magento\Catalog\Model\Category;
9+
use Magento\Catalog\Model\Product\Visibility;
810
use Magento\CatalogUrlRewrite\Model\CategoryUrlRewriteGenerator;
911
use Magento\Framework\App\Config\ScopeConfigInterface;
12+
use Magento\Framework\DB\Select;
1013
use 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
}

app/code/Magento/Catalog/Model/ResourceModel/Product/Collection.php

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,9 +1180,33 @@ protected function _getSelectCountSql(?Select $select = null, $resetLeftJoins =
11801180
if ($resetLeftJoins) {
11811181
$countSelect->resetJoinLeft();
11821182
}
1183+
1184+
$this->removeEntityIdentifierFromGroupBy($countSelect);
1185+
11831186
return $countSelect;
11841187
}
11851188

1189+
/**
1190+
* Using `entity_id` for `GROUP BY` causes COUNT() return {n} rows of value = 1 instead of 1 row of value {n}
1191+
*
1192+
* @param Select $select
1193+
* @throws \Zend_Db_Select_Exception
1194+
*/
1195+
private function removeEntityIdentifierFromGroupBy(Select $select): void
1196+
{
1197+
$originalGroupBy = $select->getPart(Select::GROUP);
1198+
1199+
if (!is_array($originalGroupBy)) {
1200+
return;
1201+
}
1202+
1203+
$groupBy = array_filter($originalGroupBy, function ($field) {
1204+
return false === strpos($field, $this->getIdFieldName());
1205+
});
1206+
1207+
$select->setPart(Select::GROUP, $groupBy);
1208+
}
1209+
11861210
/**
11871211
* Prepare statistics data
11881212
*
@@ -1770,30 +1794,19 @@ public function addAttributeToSort($attribute, $dir = self::SORT_ORDER_ASC)
17701794
*/
17711795
protected function _prepareProductLimitationFilters()
17721796
{
1773-
if (isset(
1774-
$this->_productLimitationFilters['visibility']
1775-
) && !isset(
1776-
$this->_productLimitationFilters['store_id']
1777-
)
1778-
) {
1797+
if (isset($this->_productLimitationFilters['visibility'])
1798+
&& !isset($this->_productLimitationFilters['store_id'])) {
17791799
$this->_productLimitationFilters['store_id'] = $this->getStoreId();
17801800
}
1781-
if (isset(
1782-
$this->_productLimitationFilters['category_id']
1783-
) && !isset(
1784-
$this->_productLimitationFilters['store_id']
1785-
)
1786-
) {
1801+
1802+
if (isset($this->_productLimitationFilters['category_id'])
1803+
&& !isset($this->_productLimitationFilters['store_id'])) {
17871804
$this->_productLimitationFilters['store_id'] = $this->getStoreId();
17881805
}
1789-
if (isset(
1790-
$this->_productLimitationFilters['store_id']
1791-
) && isset(
1792-
$this->_productLimitationFilters['visibility']
1793-
) && !isset(
1794-
$this->_productLimitationFilters['category_id']
1795-
)
1796-
) {
1806+
1807+
if (isset($this->_productLimitationFilters['store_id'])
1808+
&& isset($this->_productLimitationFilters['visibility'])
1809+
&& !isset($this->_productLimitationFilters['category_id'])) {
17971810
$this->_productLimitationFilters['category_id'] = $this->_storeManager->getStore(
17981811
$this->_productLimitationFilters['store_id']
17991812
)->getRootCategoryId();
@@ -1824,14 +1837,8 @@ protected function _productLimitationJoinWebsite()
18241837
$filters['website_ids'],
18251838
'int'
18261839
);
1827-
} elseif (isset(
1828-
$filters['store_id']
1829-
) && (!isset(
1830-
$filters['visibility']
1831-
) && !isset(
1832-
$filters['category_id']
1833-
)) && !$this->isEnabledFlat()
1834-
) {
1840+
} elseif (isset($filters['store_id']) && !$this->isEnabledFlat()
1841+
&& (!isset($filters['visibility']) && !isset($filters['category_id']))) {
18351842
$joinWebsite = true;
18361843
$websiteId = $this->_storeManager->getStore($filters['store_id'])->getWebsiteId();
18371844
$conditions[] = $this->getConnection()->quoteInto('product_website.website_id = ?', $websiteId, 'int');
@@ -1906,9 +1913,9 @@ protected function _productLimitationJoinPrice()
19061913
/**
19071914
* Join Product Price Table with left-join possibility
19081915
*
1909-
* @see \Magento\Catalog\Model\ResourceModel\Product\Collection::_productLimitationJoinPrice()
19101916
* @param bool $joinLeft
19111917
* @return $this
1918+
* @see \Magento\Catalog\Model\ResourceModel\Product\Collection::_productLimitationJoinPrice()
19121919
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
19131920
*/
19141921
protected function _productLimitationPrice($joinLeft = false)
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?xml version="1.0" encoding="UTF-8"?>
2+
<!--
3+
/**
4+
* Copyright © Magento, Inc. All rights reserved.
5+
* See COPYING.txt for license details.
6+
*/
7+
-->
8+
<actionGroups xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
9+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
10+
<actionGroup name="AssertStorefrontNoProductsFoundActionGroup">
11+
<see userInput="We can't find products matching the selection." stepKey="seeEmptyNotice"/>
12+
</actionGroup>
13+
</actionGroups>

app/code/Magento/Catalog/Test/Mftf/Data/CatalogStorefrontConfigData.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,4 +69,12 @@
6969
<data key="path">catalog/frontend/grid_per_page_values</data>
7070
<data key="value">1,2</data>
7171
</entity>
72+
<entity name="DefaultGridPerPageDefaultConfigData">
73+
<data key="path">catalog/frontend/grid_per_page</data>
74+
<data key="value">12</data>
75+
</entity>
76+
<entity name="CustomGridPerPageDefaultConfigData">
77+
<data key="path">catalog/frontend/grid_per_page</data>
78+
<data key="value">1</data>
79+
</entity>
7280
</entities>

app/code/Magento/Catalog/Test/Mftf/Test/AdminMoveProductBetweenCategoriesTest.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,7 @@
158158

159159
<!-- # Category should open successfully # <product1> should be absent on the page -->
160160
<see userInput="$$createAnchoredCategory1.name$$" selector="{{StorefrontCategoryMainSection.CategoryTitle}}" stepKey="seeCategory1Name"/>
161-
<see userInput="We can't find products matching the selection." stepKey="seeEmptyNotice"/>
161+
<actionGroup ref="AssertStorefrontNoProductsFoundActionGroup" stepKey="seeEmptyNotice"/>
162162
<dontSee userInput="$$simpleProduct.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="dontseeProduct"/>
163163

164164
<!-- Log in to the backend: Admin user is logged in-->

app/code/Magento/Catalog/Test/Mftf/Test/AdminProductCategoryIndexerInUpdateOnScheduleModeTest.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@
9191

9292
<!-- The category is still empty -->
9393
<see userInput="$$createCategoryA.name$$" selector="{{StorefrontCategoryMainSection.CategoryTitle}}" stepKey="seeCategoryA1Name"/>
94-
<see userInput="We can't find products matching the selection." stepKey="seeEmptyNotice"/>
94+
<actionGroup ref="AssertStorefrontNoProductsFoundActionGroup" stepKey="seeEmptyNotice"/>
9595
<dontSee userInput="$$createProductA1.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="dontseeProductA1"/>
9696

9797
<!-- 4. Run cron to reindex -->
@@ -132,7 +132,7 @@
132132

133133
<!-- Category A is empty now -->
134134
<see userInput="$$createCategoryA.name$$" selector="{{StorefrontCategoryMainSection.CategoryTitle}}" stepKey="seeOnPageCategoryAName"/>
135-
<see userInput="We can't find products matching the selection." stepKey="seeOnPageEmptyNotice"/>
135+
<actionGroup ref="AssertStorefrontNoProductsFoundActionGroup" stepKey="seeOnPageEmptyNotice"/>
136136
<dontSee userInput="$$createProductA1.name$$" selector="{{StorefrontCategoryMainSection.productName}}" stepKey="dontseeProductA1OnPage"/>
137137

138138
<!-- Case: change product status -->

0 commit comments

Comments
 (0)