Skip to content

Commit ecc1dfc

Browse files
[Magento Community Engineering] Community Contributions - 2.4-develop-expedited-prs
- merged with '2.4-develop-fast-lane-prs' branch
2 parents 427717a + c6c2ef0 commit ecc1dfc

File tree

52 files changed

+1183
-349
lines changed

Some content is hidden

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

52 files changed

+1183
-349
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: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1735,7 +1735,9 @@ public function addAttributeToSort($attribute, $dir = self::SORT_ORDER_ASC)
17351735
if ($attribute == 'price' && $storeId != 0) {
17361736
$this->addPriceData();
17371737
if ($this->_productLimitationFilters->isUsingPriceIndex()) {
1738-
$this->getSelect()->order("price_index.min_price {$dir}");
1738+
$this->getSelect()->order(
1739+
new \Zend_Db_Expr("price_index.min_price = 0, price_index.min_price {$dir}")
1740+
);
17391741
return $this;
17401742
}
17411743
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
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" xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/actionGroupSchema.xsd">
9+
<!-- Check the product not exist in recently compared widget -->
10+
<actionGroup name="StorefrontAssertNotExistProductInRecentlyComparedWidgetActionGroup">
11+
<annotations>
12+
<description>Validate that the provided Product does not appear in the Recently Compared Products widget.</description>
13+
</annotations>
14+
<arguments>
15+
<argument name="product"/>
16+
</arguments>
17+
18+
<waitForElementVisible selector="{{StorefrontWidgetsSection.widgetRecentlyComparedProductsGrid}}" stepKey="waitLoadingRecentlyComparedProductsGrid"/>
19+
<dontSee selector="{{StorefrontWidgetsSection.widgetRecentlyComparedProductsGrid}}" userInput="{{product.name}}" stepKey="dontSeeProductInRecentlyComparedWidget"/>
20+
</actionGroup>
21+
</actionGroups>
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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+
<tests xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
9+
xsi:noNamespaceSchemaLocation="urn:magento:mftf:Test/etc/testSchema.xsd">
10+
<test name="StoreFrontRecentlyComparedAtWebsiteLevelTest">
11+
<annotations>
12+
<stories value="Recently Compared Product"/>
13+
<title value="Recently Compared Product at website level"/>
14+
<description value="Recently Compared Products widget appears on a page immediately after adding product to compare"/>
15+
<useCaseId value="MC-32763"/>
16+
<testCaseId value="MC-33099"/>
17+
<severity value="MAJOR"/>
18+
<group value="catalog"/>
19+
<group value="widget"/>
20+
</annotations>
21+
<before>
22+
<!--Create Simple Products and Category -->
23+
<createData entity="SimpleSubCategory" stepKey="createCategory"/>
24+
<createData entity="SimpleProduct" stepKey="createSimpleProductToCompareFirst">
25+
<requiredEntity createDataKey="createCategory"/>
26+
</createData>
27+
<createData entity="SimpleProduct" stepKey="createSimpleProductToCompareSecond">
28+
<requiredEntity createDataKey="createCategory"/>
29+
</createData>
30+
<createData entity="SimpleProduct" stepKey="createSimpleProductNotVisibleFirst">
31+
<requiredEntity createDataKey="createCategory"/>
32+
</createData>
33+
<createData entity="SimpleProduct" stepKey="createSimpleProductNotVisibleSecond">
34+
<requiredEntity createDataKey="createCategory"/>
35+
</createData>
36+
<createData entity="Simple_US_Customer" stepKey="createCustomer"/>
37+
<!-- Login as admin -->
38+
<actionGroup ref="LoginAsAdmin" stepKey="loginAsAdmin"/>
39+
<!-- Create product widget -->
40+
<actionGroup ref="AdminCreateRecentlyProductsWidgetActionGroup" stepKey="createRecentlyComparedProductsWidget">
41+
<argument name="widget" value="RecentlyComparedProductsWidget"/>
42+
</actionGroup>
43+
<!-- Set Stores > Configurations > Catalog > Recently Viewed/Compared Products > Show for Current = Website -->
44+
<magentoCLI command="config:set {{RecentlyViewedProductScopeWebsite.path}} {{RecentlyViewedProductScopeWebsite.value}}" stepKey="RecentlyViewedProductScopeWebsiteGroup"/>
45+
</before>
46+
<after>
47+
<!-- Customer Logout -->
48+
<actionGroup ref="StorefrontCustomerLogoutActionGroup" stepKey="logoutFromCustomer"/>
49+
<!-- Delete product widget -->
50+
<actionGroup ref="AdminDeleteWidgetActionGroup" stepKey="deleteRecentlyComparedProductsWidget">
51+
<argument name="widget" value="RecentlyComparedProductsWidget"/>
52+
</actionGroup>
53+
<!-- Logout Admin -->
54+
<actionGroup ref="logout" stepKey="logout"/>
55+
<!-- Reset Stores > Configurations > Catalog > Recently Viewed/Compared Products > Show for Current = Website-->
56+
<magentoCLI command="config:set {{RecentlyViewedProductScopeWebsite.path}} {{RecentlyViewedProductScopeWebsite.value}}" stepKey="RecentlyViewedProductScopeWebsite"/>
57+
<!-- Delete Products and Category -->
58+
<deleteData createDataKey="createSimpleProductToCompareFirst" stepKey="deleteSimpleProductToCompareFirst"/>
59+
<deleteData createDataKey="createSimpleProductToCompareSecond" stepKey="deleteSimpleProductToCompareSecond"/>
60+
<deleteData createDataKey="createSimpleProductNotVisibleFirst" stepKey="deleteSimpleProductNotVisibleFirst"/>
61+
<deleteData createDataKey="createSimpleProductNotVisibleSecond" stepKey="deleteSimpleProductNotVisibleSecond"/>
62+
<deleteData createDataKey="createCategory" stepKey="deleteCategory"/>
63+
<deleteData createDataKey="createCustomer" stepKey="deleteCustomer"/>
64+
</after>
65+
<!--Login to storefront from customer-->
66+
<actionGroup ref="LoginToStorefrontActionGroup" stepKey="loginCustomer">
67+
<argument name="Customer" value="$$createCustomer$$"/>
68+
</actionGroup>
69+
<see userInput="Welcome, $$createCustomer.firstname$$ $$createCustomer.lastname$$!" selector="{{StorefrontPanelHeaderSection.WelcomeMessage}}" stepKey="checkWelcomeMessage"/>
70+
<amOnPage url="{{StorefrontCategoryPage.url($$createCategory.custom_attributes[url_key]$$)}}" stepKey="openCategoryPageAfterAddedProductToCart"/>
71+
<!--Add to compare Simple Product and Simple Product 2-->
72+
<actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="addSimpleProduct1ToCompare" >
73+
<argument name="productVar" value="$$createSimpleProductToCompareFirst$$"/>
74+
</actionGroup>
75+
<actionGroup ref="StorefrontAddCategoryProductToCompareActionGroup" stepKey="addSimpleProduct2ToCompare" >
76+
<argument name="productVar" value="$$createSimpleProductToCompareSecond$$"/>
77+
</actionGroup>
78+
<!--The Compare Products widget displays Simple Product 1 and Simple Product 2-->
79+
<actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="checkSimpleProduct1InCompareSidebar">
80+
<argument name="productVar" value="$$createSimpleProductToCompareFirst$$"/>
81+
</actionGroup>
82+
<actionGroup ref="StorefrontCheckCompareSidebarProductActionGroup" stepKey="checkSimpleProduct2InCompareSidebar">
83+
<argument name="productVar" value="$$createSimpleProductToCompareSecond$$"/>
84+
</actionGroup>
85+
86+
<!--Click Clear all in the Compare Products widget-->
87+
<actionGroup ref="StorefrontClearCompareActionGroup" stepKey="clearCompareList"/>
88+
<!--The Recently Compared widget displays Simple Product 1 and Simple Product 2-->
89+
<amOnPage url="{{StorefrontCategoryPage.url($$createCategory.custom_attributes[url_key]$$)}}" stepKey="openCategoryPageToCheckProductsInRecentlyComparedSidebar"/>
90+
<actionGroup ref="StorefrontAssertProductInRecentlyComparedWidgetActionGroup" stepKey="checkSimpleProduct1ExistInRecentlyComparedWidget">
91+
<argument name="product" value="$$createSimpleProductToCompareFirst$$"/>
92+
</actionGroup>
93+
<actionGroup ref="StorefrontAssertProductInRecentlyComparedWidgetActionGroup" stepKey="checkSimpleProduct2ExistInRecentlyComparedWidget">
94+
<argument name="product" value="$$createSimpleProductToCompareSecond$$"/>
95+
</actionGroup>
96+
<!--The Recently Compared widget not displays Simple Product 3 and Simple Product 4-->
97+
<actionGroup ref="StorefrontAssertNotExistProductInRecentlyComparedWidgetActionGroup" stepKey="checkSimpleProduct3NotExistInRecentlyComparedWidget">
98+
<argument name="product" value="$$createSimpleProductNotVisibleFirst$$"/>
99+
</actionGroup>
100+
<actionGroup ref="StorefrontAssertNotExistProductInRecentlyComparedWidgetActionGroup" stepKey="checkSimpleProduct4NotExistInRecentlyComparedWidget">
101+
<argument name="product" value="$$createSimpleProductNotVisibleSecond$$"/>
102+
</actionGroup>
103+
<amOnPage url="customer/account/logout/" stepKey="logoutCustomer"/>
104+
<waitForPageLoad time="30" stepKey="waitForPageLoad2"/>
105+
</test>
106+
</tests>

app/code/Magento/Catalog/view/frontend/web/js/product/provider-compared.js

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,13 +31,27 @@ define([
3131
*/
3232
dataFilter: function (data) {
3333
var providerData = this.idsStorage.prepareData(customerData.get(this.identifiersConfig.provider)().items),
34-
result = {};
34+
result = {},
35+
productCurrentScope,
36+
scopeId;
3537

36-
_.each(data, function (value, key) {
37-
if (!providerData[key]) {
38-
result[key] = value;
39-
}
40-
});
38+
if (typeof this.data.productCurrentScope !== 'undefined') {
39+
productCurrentScope = this.data.productCurrentScope;
40+
scopeId = productCurrentScope === 'store' ? window.checkout.storeId :
41+
productCurrentScope === 'group' ? window.checkout.storeGroupId :
42+
window.checkout.websiteId;
43+
_.each(data, function (value, key) {
44+
if (!providerData[productCurrentScope + '-' + scopeId + '-' + key]) {
45+
result[key] = value;
46+
}
47+
});
48+
} else {
49+
_.each(data, function (value, key) {
50+
if (!providerData[key]) {
51+
result[key] = value;
52+
}
53+
});
54+
}
4155

4256
return result;
4357
},

0 commit comments

Comments
 (0)