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
7 changes: 6 additions & 1 deletion src/Adapter/MySQL.php
Original file line number Diff line number Diff line change
Expand Up @@ -404,8 +404,13 @@ protected function computeOrderByField(array $filterToTableMapping)
* to order products by their position in the search results we got from the core, with inverted order
*/
if ($orderField == 'p.position' && !empty($this->getInitialPopulation()->getFilters()['id_product']['='][0])) {
return 'FIELD(p.id_product,' . implode(',', $this->getInitialPopulation()->getFilters()['id_product']['='][0]) . ') ' .
$orderField = 'FIELD(p.id_product,' . implode(',', $this->getInitialPopulation()->getFilters()['id_product']['='][0]) . ') ' .
($this->getOrderDirection() === 'asc' ? 'DESC' : 'ASC');

// Push out-of-stock products to the end on search pages too. Without this, the relevance
// ordering was returned as is and bypassed the "out of stock last" behaviour that the
// category, manufacturer and other listings get below through computeShowLast().
return $this->computeShowLast($orderField, $filterToTableMapping);
}

// Alter order by field and add some products to the end of the list, if required
Expand Down
35 changes: 35 additions & 0 deletions tests/php/FacetedSearch/Adapter/MySQLTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,41 @@ public function testGetEmptyQuery()
);
}

/**
* On search pages the products are ordered by their relevance (their position in the pool
* returned by the core search). The "out of stock last" behaviour must still be applied on
* top of that relevance ordering, exactly like on category/manufacturer listings.
*
* @see https://github.com/PrestaShop/ps_facetedsearch/issues/1236
*/
public function testGetQueryAppliesOutOfStockLastOnSearchRelevanceOrdering()
{
$configurationMock = Mockery::mock(Configuration::class);
$configurationMock->shouldReceive('get')
->with('PS_LAYERED_FILTER_SHOW_OUT_OF_STOCK_LAST')
->andReturn(true);
Configuration::setStaticExpectations($configurationMock);

$productMock = Mockery::namedMock(Product::class);
$productMock->shouldReceive('isAvailableWhenOutOfStock')
->with(2)
->andReturn(false);

// A search query: the pool of product ids becomes the initial population and the order
// field is the relevance "position".
$this->adapter->addFilter('id_product', [1, 2, 3], '=');
$this->adapter->useFiltersAsInitialPopulation();
$this->adapter->setOrderField('position');
$this->adapter->setOrderDirection('asc');

$query = $this->adapter->getQuery();

// Out-of-stock products are pushed to the end...
$this->assertContains('IFNULL(p.quantity, 0) <= 0', $query);
// ...while the relevance ordering is preserved within each group.
$this->assertContains('FIELD(p.id_product,1,2,3) DESC', $query);
}

/**
* @dataProvider oneSelectFieldDataProvider
*/
Expand Down
Loading