Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 21 additions & 8 deletions app/code/Magento/CatalogWidget/Block/Product/ProductsList.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php
/**
* Copyright 2014 Adobe
* Copyright 2026 Adobe
* All Rights Reserved.
*/

Expand Down Expand Up @@ -187,10 +187,10 @@ public function __construct(
$this->layoutFactory = $layoutFactory ?: ObjectManager::getInstance()->get(LayoutFactory::class);
$this->urlEncoder = $urlEncoder ?: ObjectManager::getInstance()->get(EncoderInterface::class);
$this->categoryRepository = $categoryRepository ?? ObjectManager::getInstance()
->get(CategoryRepositoryInterface::class);
->get(CategoryRepositoryInterface::class);
$this->optionsData = $optionsData ?: ObjectManager::getInstance()->get(OptionsData::class);
$this->categoryConditionProcessor = $categoryConditionProcessor ?: ObjectManager::getInstance()
->get(CategoryConditionProcessor::class);
->get(CategoryConditionProcessor::class);
parent::__construct(
$context,
$data
Expand Down Expand Up @@ -255,8 +255,8 @@ public function getCacheKeyInfo()
*/
public function getProductPriceHtml(
Product $product,
$priceType = null,
$renderZone = \Magento\Framework\Pricing\Render::ZONE_ITEM_LIST,
$priceType = null,
$renderZone = \Magento\Framework\Pricing\Render::ZONE_ITEM_LIST,
array $arguments = []
) {
if (!isset($arguments['zone'])) {
Expand Down Expand Up @@ -383,9 +383,7 @@ public function getBaseCollection(): Collection
$collection = $this->_addProductAttributesAndPrices($collection)
->addStoreFilter()
->addAttributeToFilter(Product::STATUS, ProductStatus::STATUS_ENABLED)
->addAttributeToSort('entity_id', 'desc')
->setPageSize($this->getPageSize())
->setCurPage($this->getRequest()->getParam($this->getData('page_var_name'), 1));
->addAttributeToSort('entity_id', 'desc');

$conditions = $this->getConditions();
$conditions->collectValidatedAttributes($collection);
Expand All @@ -397,6 +395,21 @@ public function getBaseCollection(): Collection
*/
$collection->distinct(true);

$currentPage = (int)$this->getRequest()->getParam($this->getData('page_var_name'), 1);

// Apply pagination with total limit after all other operations
if ($this->showPager()) {
$pageSize = $this->getProductsPerPage();
$totalLimit = $this->getProductsCount();

$offset = ($currentPage - 1) * $pageSize;
$limit = min($pageSize, max(0, $totalLimit - $offset));

$collection->getSelect()->limit($limit, $offset);
} else {
$collection->getSelect()->limit($this->getProductsCount());
}

return $collection;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<?php
/**
* Copyright 2015 Adobe
* Copyright 2026 Adobe
* All Rights Reserved.
*/
declare(strict_types=1);
Expand Down Expand Up @@ -277,13 +277,20 @@ public function testGetPagerHtml()
* @param bool $pagerEnable
* @param int $productsCount
* @param int $productsPerPage
* @param int $expectedPageSize
* @param int $expectedLimit
*/
#[DataProvider('createCollectionDataProvider')]
public function testCreateCollection($pagerEnable, $productsCount, $productsPerPage, $expectedPageSize)
public function testCreateCollection($pagerEnable, $productsCount, $productsPerPage, $expectedLimit)
{
$this->visibility->expects($this->once())->method('getVisibleInCatalogIds')
->willReturn([Visibility::VISIBILITY_IN_CATALOG, Visibility::VISIBILITY_BOTH]);

$select = $this->createMock(\Magento\Framework\DB\Select::class);
$select->expects($this->once())
->method('limit')
->with($expectedLimit, 0)
->willReturnSelf();

$collection = $this->createMock(Collection::class);
$collection->expects($this->once())->method('setVisibility')
->with([Visibility::VISIBILITY_IN_CATALOG, Visibility::VISIBILITY_BOTH])
Expand All @@ -299,9 +306,8 @@ public function testCreateCollection($pagerEnable, $productsCount, $productsPerP
->with(Product::STATUS, ProductStatus::STATUS_ENABLED)
->willReturnSelf();
$collection->expects($this->once())->method('addAttributeToSort')->with('entity_id', 'desc')->willReturnSelf();
$collection->expects($this->once())->method('setPageSize')->with($expectedPageSize)->willReturnSelf();
$collection->expects($this->once())->method('setCurPage')->willReturnSelf();
$collection->expects($this->once())->method('distinct')->willReturnSelf();
$collection->expects($this->once())->method('getSelect')->willReturn($select);

$this->collectionFactory->expects($this->once())->method('create')->willReturn($collection);
$this->productsList->setData('conditions_encoded', 'some_serialized_conditions');
Expand All @@ -323,6 +329,9 @@ public function testCreateCollection($pagerEnable, $productsCount, $productsPerP

$this->productsList->setData('show_pager', $pagerEnable);
$this->productsList->setData('products_count', $productsCount);
$this->productsList->setData('page_var_name', 'page');

$this->request->expects($this->any())->method('getParam')->with('page')->willReturn(1);

$this->assertSame($collection, $this->productsList->createCollection());
}
Expand All @@ -333,10 +342,10 @@ public function testCreateCollection($pagerEnable, $productsCount, $productsPerP
public static function createCollectionDataProvider()
{
return [
[true, 1, null, 5],
[true, 1, null, 1],
[true, 5, null, 5],
[true, 10, null, 5],
[true, 1, 2, 2],
[true, 1, 2, 1],
[true, 5, 3, 3],
[true, 10, 7, 7],
[false, 1, null, 1],
Expand All @@ -348,6 +357,88 @@ public static function createCollectionDataProvider()
];
}

/**
* Test that collection limit respects total products count on subsequent pages
*
* @param int $currentPage
* @param int $productsPerPage
* @param int $totalProducts
* @param int $expectedLimit
* @param int $expectedOffset
*/
#[DataProvider('createCollectionWithTotalLimitDataProvider')]
public function testCreateCollectionWithTotalLimit(
$currentPage,
$productsPerPage,
$totalProducts,
$expectedLimit,
$expectedOffset
) {
$this->visibility->expects($this->once())->method('getVisibleInCatalogIds')
->willReturn([Visibility::VISIBILITY_IN_CATALOG, Visibility::VISIBILITY_BOTH]);

$select = $this->createMock(\Magento\Framework\DB\Select::class);
$select->expects($this->once())
->method('limit')
->with($expectedLimit, $expectedOffset)
->willReturnSelf();

$collection = $this->createMock(Collection::class);
$collection->expects($this->once())->method('setVisibility')->willReturnSelf();
$collection->expects($this->once())->method('addMinimalPrice')->willReturnSelf();
$collection->expects($this->once())->method('addFinalPrice')->willReturnSelf();
$collection->expects($this->once())->method('addTaxPercents')->willReturnSelf();
$collection->expects($this->once())->method('addAttributeToSelect')->willReturnSelf();
$collection->expects($this->once())->method('addUrlRewrite')->willReturnSelf();
$collection->expects($this->once())->method('addStoreFilter')->willReturnSelf();
$collection->expects($this->once())
->method('addAttributeToFilter')
->with(Product::STATUS, ProductStatus::STATUS_ENABLED)
->willReturnSelf();
$collection->expects($this->once())->method('addAttributeToSort')->with('entity_id', 'desc')->willReturnSelf();
$collection->expects($this->once())->method('distinct')->willReturnSelf();
$collection->expects($this->once())->method('getSelect')->willReturn($select);

$this->collectionFactory->expects($this->once())->method('create')->willReturn($collection);
$this->productsList->setData('conditions_encoded', 'some_serialized_conditions');

$this->widgetConditionsHelper->expects($this->once())
->method('decode')
->with('some_serialized_conditions')
->willReturn([]);

$this->builder->expects($this->once())->method('attachConditionToCollection')
->with($collection, $this->getConditionsForCollection($collection))
->willReturnSelf();

$this->productsList->setData('products_per_page', $productsPerPage);
$this->productsList->setData('show_pager', true);
$this->productsList->setData('products_count', $totalProducts);
$this->productsList->setData('page_var_name', 'page');

$this->request->expects($this->once())->method('getParam')->with('page')->willReturn($currentPage);

$this->assertSame($collection, $this->productsList->createCollection());
}

/**
* Data provider for testCreateCollectionWithTotalLimit
*
* @return array
*/
public static function createCollectionWithTotalLimitDataProvider()
{
return [
// [currentPage, productsPerPage, totalProducts, expectedLimit, expectedOffset]
'page 1 of 2 with 9 total' => [1, 5, 9, 5, 0],
'page 2 of 2 with 9 total' => [2, 5, 9, 4, 5],
'page 1 of 3 with 12 total' => [1, 5, 12, 5, 0],
'page 2 of 3 with 12 total' => [2, 5, 12, 5, 5],
'page 3 of 3 with 12 total' => [3, 5, 12, 2, 10],
'page beyond limit' => [3, 5, 9, 0, 10],
];
}

public function testGetProductsCount()
{
$this->assertEquals(10, $this->productsList->getProductsCount());
Expand Down