Skip to content

Commit 7e6ad1d

Browse files
ENGCOM-7011: #26986 REST API Pagination issue #26988
- Merge Pull Request #26988 from lbajsarowicz/magento2:bugfix/rest-api-pagination - Merged commits: 1. a6f76f3 2. b254764 3. 11567f6 4. 6771122 5. e001493 6. ecb3a8d 7. 015c5fc 8. 18e43a8 9. 9d0ae13 10. 2439e17 11. 14ea335 12. 0fe9cf2 13. dab0cda 14. c403c52 15. d7cdd7b 16. 05f1a2b 17. f25de7b 18. 51a18f6 19. 0bae5d5 20. 1e7082d 21. f5aa61d 22. e8d3b78 23. 6c171ce 24. 8c9189f 25. 68cf64d 26. 0db9220 27. c44bbf5 28. 84fdc7a 29. 162eb14 30. 11c3eea 31. 986a4ef 32. 80cb06e 33. db71c46 34. e105152 35. bd5c98e
2 parents 176ba2b + bd5c98e commit 7e6ad1d

File tree

12 files changed

+424
-188
lines changed

12 files changed

+424
-188
lines changed

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
*
@@ -1765,30 +1789,19 @@ public function addAttributeToSort($attribute, $dir = self::SORT_ORDER_ASC)
17651789
*/
17661790
protected function _prepareProductLimitationFilters()
17671791
{
1768-
if (isset(
1769-
$this->_productLimitationFilters['visibility']
1770-
) && !isset(
1771-
$this->_productLimitationFilters['store_id']
1772-
)
1773-
) {
1792+
if (isset($this->_productLimitationFilters['visibility'])
1793+
&& !isset($this->_productLimitationFilters['store_id'])) {
17741794
$this->_productLimitationFilters['store_id'] = $this->getStoreId();
17751795
}
1776-
if (isset(
1777-
$this->_productLimitationFilters['category_id']
1778-
) && !isset(
1779-
$this->_productLimitationFilters['store_id']
1780-
)
1781-
) {
1796+
1797+
if (isset($this->_productLimitationFilters['category_id'])
1798+
&& !isset($this->_productLimitationFilters['store_id'])) {
17821799
$this->_productLimitationFilters['store_id'] = $this->getStoreId();
17831800
}
1784-
if (isset(
1785-
$this->_productLimitationFilters['store_id']
1786-
) && isset(
1787-
$this->_productLimitationFilters['visibility']
1788-
) && !isset(
1789-
$this->_productLimitationFilters['category_id']
1790-
)
1791-
) {
1801+
1802+
if (isset($this->_productLimitationFilters['store_id'])
1803+
&& isset($this->_productLimitationFilters['visibility'])
1804+
&& !isset($this->_productLimitationFilters['category_id'])) {
17921805
$this->_productLimitationFilters['category_id'] = $this->_storeManager->getStore(
17931806
$this->_productLimitationFilters['store_id']
17941807
)->getRootCategoryId();
@@ -1819,14 +1832,8 @@ protected function _productLimitationJoinWebsite()
18191832
$filters['website_ids'],
18201833
'int'
18211834
);
1822-
} elseif (isset(
1823-
$filters['store_id']
1824-
) && (!isset(
1825-
$filters['visibility']
1826-
) && !isset(
1827-
$filters['category_id']
1828-
)) && !$this->isEnabledFlat()
1829-
) {
1835+
} elseif (isset($filters['store_id']) && !$this->isEnabledFlat()
1836+
&& (!isset($filters['visibility']) && !isset($filters['category_id']))) {
18301837
$joinWebsite = true;
18311838
$websiteId = $this->_storeManager->getStore($filters['store_id'])->getWebsiteId();
18321839
$conditions[] = $this->getConnection()->quoteInto('product_website.website_id = ?', $websiteId, 'int');
@@ -1901,9 +1908,9 @@ protected function _productLimitationJoinPrice()
19011908
/**
19021909
* Join Product Price Table with left-join possibility
19031910
*
1904-
* @see \Magento\Catalog\Model\ResourceModel\Product\Collection::_productLimitationJoinPrice()
19051911
* @param bool $joinLeft
19061912
* @return $this
1913+
* @see \Magento\Catalog\Model\ResourceModel\Product\Collection::_productLimitationJoinPrice()
19071914
* @SuppressWarnings(PHPMD.CyclomaticComplexity)
19081915
*/
19091916
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
@@ -159,7 +159,7 @@
159159

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

165165
<!-- 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 -->

app/code/Magento/Catalog/view/frontend/web/js/product/list/toolbar.js

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ define([
2323
direction: 'product_list_dir',
2424
order: 'product_list_order',
2525
limit: 'product_list_limit',
26+
page: 'p',
2627
modeDefault: 'grid',
2728
directionDefault: 'asc',
2829
orderDefault: 'position',
@@ -81,24 +82,63 @@ define([
8182
},
8283

8384
/**
84-
* @param {String} paramName
85-
* @param {*} paramValue
86-
* @param {*} defaultValue
85+
* @private
8786
*/
88-
changeUrl: function (paramName, paramValue, defaultValue) {
87+
getUrlParams: function () {
8988
var decode = window.decodeURIComponent,
9089
urlPaths = this.options.url.split('?'),
91-
baseUrl = urlPaths[0],
9290
urlParams = urlPaths[1] ? urlPaths[1].split('&') : [],
93-
paramData = {},
94-
parameters, i, form, params, key, input, formKey;
91+
params = {},
92+
parameters, i;
9593

9694
for (i = 0; i < urlParams.length; i++) {
9795
parameters = urlParams[i].split('=');
98-
paramData[decode(parameters[0])] = parameters[1] !== undefined ?
96+
params[decode(parameters[0])] = parameters[1] !== undefined ?
9997
decode(parameters[1].replace(/\+/g, '%20')) :
10098
'';
10199
}
100+
101+
return params;
102+
},
103+
104+
/**
105+
* @returns {String}
106+
* @private
107+
*/
108+
getCurrentLimit: function () {
109+
return this.getUrlParams()[this.options.limit] || this.options.limitDefault;
110+
},
111+
112+
/**
113+
* @returns {String}
114+
* @private
115+
*/
116+
getCurrentPage: function () {
117+
return this.getUrlParams()[this.options.page] || 1;
118+
},
119+
120+
/**
121+
* @param {String} paramName
122+
* @param {*} paramValue
123+
* @param {*} defaultValue
124+
*/
125+
changeUrl: function (paramName, paramValue, defaultValue) {
126+
var urlPaths = this.options.url.split('?'),
127+
baseUrl = urlPaths[0],
128+
paramData = this.getUrlParams(),
129+
currentPage = this.getCurrentPage(),
130+
form, params, key, input, formKey, newPage;
131+
132+
if (currentPage > 1 && paramName === this.options.limit) {
133+
newPage = Math.floor(this.getCurrentLimit() * (currentPage - 1) / paramValue) + 1;
134+
135+
if (newPage > 1) {
136+
paramData[this.options.page] = newPage;
137+
} else {
138+
delete paramData[this.options.page];
139+
}
140+
}
141+
102142
paramData[paramName] = paramValue;
103143

104144
if (this.options.post) {
@@ -130,6 +170,7 @@ define([
130170
if (paramValue == defaultValue) { //eslint-disable-line eqeqeq
131171
delete paramData[paramName];
132172
}
173+
133174
paramData = $.param(paramData);
134175
location.href = baseUrl + (paramData.length ? '?' + paramData : '');
135176
}

app/code/Magento/Elasticsearch/Model/ResourceModel/Fulltext/Collection/SearchCriteriaResolver.php

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,9 @@ public function resolve(): SearchCriteria
8080
$searchCriteria->setRequestName($this->searchRequestName);
8181
$searchCriteria->setSortOrders($this->orders);
8282
$searchCriteria->setCurrentPage($this->currentPage - 1);
83+
if ($this->size) {
84+
$searchCriteria->setPageSize($this->size);
85+
}
8386

8487
return $searchCriteria;
8588
}

app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6Test.xml

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,16 @@
3030
<field key="name">Product Simple AAA</field>
3131
</createData>
3232
<magentoCLI command="config:set {{CustomGridPerPageValuesConfigData.path}} {{CustomGridPerPageValuesConfigData.value}}" stepKey="setCustomGridPerPageValues"/>
33+
<magentoCLI command="config:set {{CustomGridPerPageDefaultConfigData.path}} {{CustomGridPerPageDefaultConfigData.value}}" stepKey="setCustomGridPerPageDefaults"/>
34+
<magentoCLI stepKey="flushConfigCache" command="cache:flush config"/>
35+
<magentoCron groups="index" stepKey="runCronIndex"/>
3336
</before>
3437

3538
<after>
3639
<deleteData createDataKey="createFirstProduct" stepKey="deleteFirstProduct"/>
3740
<deleteData createDataKey="createSecondProduct" stepKey="deleteSecondProduct"/>
3841
<magentoCLI command="config:set {{DefaultGridPerPageValuesConfigData.path}} {{DefaultGridPerPageValuesConfigData.value}}" stepKey="setDefaultGridPerPageValues"/>
42+
<magentoCLI command="config:set {{DefaultGridPerPageDefaultConfigData.path}} {{DefaultGridPerPageDefaultConfigData.value}}" stepKey="setDefaultGridPerPageDefaults"/>
3943
</after>
4044

4145
<actionGroup ref="StorefrontOpenHomePageActionGroup" stepKey="openStorefrontHomePage"/>
@@ -57,6 +61,9 @@
5761
</actionGroup>
5862
<selectOption selector="{{StorefrontCategoryBottomToolbarSection.perPage}}" userInput="2" stepKey="selectDisplayedProductInGridPerPage"/>
5963
<waitForPageLoad stepKey="waitForPageLoad"/>
64+
65+
<dontSeeInCurrentUrl stepKey="assertRedirectedToFirstPage" url="?p=2"/>
66+
6067
<actionGroup ref="AssertProductOnCategoryPageActionGroup" stepKey="assertFirstProductDisplayedOnCatalogSearchPage">
6168
<argument name="product" value="$createFirstProduct$"/>
6269
</actionGroup>

app/code/Magento/Elasticsearch6/Test/Mftf/Test/StorefrontProductQuickSearchUsingElasticSearch6WithNotAvailablePageTest.xml

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -25,22 +25,11 @@
2525
<remove keyForRemoval="selectDisplayedProductInGridPerPage"/>
2626
<remove keyForRemoval="assertFirstProductDisplayedOnCatalogSearchPage"/>
2727
<remove keyForRemoval="assertSecondProductDisplayedOnCatalogSearchPage"/>
28-
<grabTextFrom selector="{{StorefrontCategoryBottomToolbarSection.currentPage}}" stepKey="grabNumberOfLastPage"/>
29-
<actionGroup ref="StorefrontQuickSearchWithPaginationActionGroup" stepKey="navigateToUnavailableCatalogSearchResultPage">
28+
<remove keyForRemoval="selectDisplayedProductInGridPerPage"/>
29+
<remove keyForRemoval="assertStillOnSecondPage"/>
30+
<actionGroup ref="StorefrontQuickSearchWithPaginationActionGroup" stepKey="navigateToUnavailableCatalogSearchResultPage" before="waitForPageLoad">
3031
<argument name="phrase" value="AAA"/>
3132
<argument name="pageNumber" value="999"/>
3233
</actionGroup>
33-
<scrollTo selector="{{StorefrontCategoryBottomToolbarSection.currentPage}}" stepKey="scrollToBottomToolbarPager"/>
34-
<grabTextFrom selector="{{StorefrontCategoryBottomToolbarSection.currentPage}}" stepKey="grabNumberOfCurrentPage"/>
35-
<assertEquals stepKey="assertCurrentPageIsLastPageOfCatalogSearchResult">
36-
<expectedResult type="variable">grabNumberOfLastPage</expectedResult>
37-
<actualResult type="variable">grabNumberOfCurrentPage</actualResult>
38-
</assertEquals>
39-
<actionGroup ref="AssertProductOnCategoryPageActionGroup" stepKey="assertProductOnLastCatalogSearchPage">
40-
<argument name="product" value="$createSecondProduct$"/>
41-
</actionGroup>
42-
<actionGroup ref="StorefrontCheckProductIsMissingInCategoryProductsPageActionGroup" stepKey="assertFirstProductIsMissingOnLastCatalogSearchPage">
43-
<argument name="productName" value="$createFirstProduct.name$"/>
44-
</actionGroup>
4534
</test>
4635
</tests>

0 commit comments

Comments
 (0)