Skip to content

Commit 4a041d2

Browse files
committed
ACP2E-748: Layered navigation filters don't work when "show out of stock" is enabled- Revert changes from ACP2E-322
1 parent ac11c66 commit 4a041d2

File tree

13 files changed

+571
-0
lines changed

13 files changed

+571
-0
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Catalog\Model;
9+
10+
use Magento\Catalog\Model\ResourceModel\Product\Collection;
11+
12+
class OutOfStock implements OutOfStockInterface
13+
{
14+
15+
/**
16+
* @inheritDoc
17+
*/
18+
public function isOutOfStockBottom(Category $category, Collection $collection): bool
19+
{
20+
return true;
21+
}
22+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Catalog\Model;
9+
10+
use Magento\Catalog\Model\ResourceModel\Product\Collection;
11+
12+
interface OutOfStockInterface
13+
{
14+
/**
15+
* @param Category $category
16+
* @param Collection $collection
17+
* @return bool
18+
*/
19+
public function isOutOfStockBottom(Category $category, Collection $collection): bool;
20+
}

app/code/Magento/Catalog/etc/di.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1343,4 +1343,5 @@
13431343
</argument>
13441344
</arguments>
13451345
</type>
1346+
<preference for="Magento\Catalog\Model\OutOfStockInterface" type="Magento\Catalog\Model\OutOfStock" />
13461347
</config>
Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\CatalogSearch\Plugin\Model\ResourceModel\Fulltext;
9+
10+
use Magento\Catalog\Api\CategoryRepositoryInterface;
11+
use Magento\Catalog\Helper\Data;
12+
use Magento\Catalog\Model\Category;
13+
use Magento\Catalog\Model\OutOfStockInterface;
14+
use Magento\CatalogInventory\Model\Configuration;
15+
use Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection;
16+
use Zend_Db_Select;
17+
18+
class CollectionPlugin
19+
{
20+
public const OUT_OF_STOCK_TO_BOTTOM = 2;
21+
22+
/**
23+
* @var array
24+
*/
25+
private $skipFlags = [];
26+
27+
/**
28+
* @var Configuration
29+
*/
30+
private $configuration;
31+
32+
/**
33+
* @var Data
34+
*/
35+
private $categoryHelper;
36+
37+
/**
38+
* @var OutOfStockInterface
39+
*/
40+
private $outOfStock;
41+
42+
/**
43+
* @var CategoryRepositoryInterface
44+
*/
45+
private $categoryRepository;
46+
47+
/**
48+
* @param Configuration $configuration
49+
* @param Data $categoryHelper
50+
* @param OutOfStockInterface $outOfStock
51+
* @param CategoryRepositoryInterface $categoryRepository
52+
*/
53+
public function __construct(
54+
Configuration $configuration,
55+
Data $categoryHelper,
56+
OutOfStockInterface $outOfStock,
57+
CategoryRepositoryInterface $categoryRepository
58+
) {
59+
$this->configuration = $configuration;
60+
$this->categoryHelper = $categoryHelper;
61+
$this->outOfStock = $outOfStock;
62+
$this->categoryRepository = $categoryRepository;
63+
}
64+
65+
public function beforeSetOrder(
66+
Collection $subject,
67+
$attribute,
68+
$dir = Zend_Db_Select::SQL_DESC
69+
): array {
70+
if (!$subject->getFlag('is_sorted_by_oos')) {
71+
$subject->setFlag('is_sorted_by_oos', true);
72+
$currentCategory = $this->categoryHelper->getCategory();
73+
/* @var Category $category */
74+
$category = $this->categoryRepository->get($currentCategory->getId());
75+
76+
if ($this->outOfStock->isOutOfStockBottom($category, $subject)
77+
&& $this->configuration->isShowOutOfStock($subject->getStoreId())) {
78+
$subject->addAttributeToSort('is_out_of_stock', Zend_Db_Select::SQL_DESC);
79+
}
80+
}
81+
82+
$flagName = $this->_getFlag($attribute);
83+
84+
if ($subject->getFlag($flagName)) {
85+
$this->skipFlags[] = $flagName;
86+
}
87+
88+
return [$attribute, $dir];
89+
}
90+
91+
public function aroundSetOrder(
92+
Collection $subject,
93+
callable $proceed,
94+
$attribute,
95+
string $dir = Zend_Db_Select::SQL_DESC
96+
): Collection {
97+
$flagName = $this->_getFlag($attribute);
98+
if (!in_array($flagName, $this->skipFlags, true)) {
99+
$proceed($attribute, $dir);
100+
}
101+
102+
return $subject;
103+
}
104+
105+
/**
106+
* @return bool
107+
*/
108+
private function isOutOfStockBottom():bool
109+
{
110+
//It'll work only when EE repository will be there
111+
$attributeCode = 'automatic_sorting';
112+
$currentCategory = $this->categoryHelper->getCategory();
113+
114+
return (int)$currentCategory->getData($attributeCode) === self::OUT_OF_STOCK_TO_BOTTOM;
115+
}
116+
117+
/**
118+
* Get flag by attribute
119+
*
120+
* @param string $attribute
121+
* @return string
122+
*/
123+
private function _getFlag(string $attribute): string
124+
{
125+
return 'sorted_by_' . $attribute;
126+
}
127+
128+
/**
129+
* Apply sort orders
130+
*
131+
* @param Collection $collection
132+
*/
133+
private function applyOutOfStockAtLastOrders(Collection $collection)
134+
{
135+
if (!$collection->getFlag('is_sorted_by_oos')) {
136+
$collection->setFlag('is_sorted_by_oos', true);
137+
$collection->setOrder('out_of_stock_at_last', Zend_Db_Select::SQL_DESC);
138+
}
139+
}
140+
}

app/code/Magento/CatalogSearch/etc/frontend/di.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,7 @@
2121
<argument name="catalogLayer" xsi:type="object">Magento\Catalog\Model\Layer\Search</argument>
2222
</arguments>
2323
</virtualType>
24+
<type name="Magento\CatalogSearch\Model\ResourceModel\Fulltext\Collection">
25+
<plugin name="is_salable_filter" type="Magento\CatalogSearch\Plugin\Model\ResourceModel\Fulltext\CollectionPlugin"/>
26+
</type>
2427
</config>
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Elasticsearch\Model\Adapter\BatchDataMapper;
9+
10+
use Magento\AdvancedSearch\Model\Adapter\DataMapper\AdditionalFieldsProviderInterface;
11+
use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\AttributeProvider;
12+
use Magento\Elasticsearch\Model\Adapter\FieldMapper\Product\FieldProvider\FieldName\ResolverInterface;
13+
use Magento\Elasticsearch\Model\ResourceModel\StockInventory;
14+
use Magento\Framework\Exception\NoSuchEntityException;
15+
use Magento\Store\Model\StoreManagerInterface;
16+
17+
/**
18+
* Provide data mapping for price fields
19+
*/
20+
class IsOutOfStockFieldsProvider implements AdditionalFieldsProviderInterface
21+
{
22+
/**
23+
* @var StoreManagerInterface
24+
*/
25+
private $storeManager;
26+
27+
/**
28+
* @var AttributeProvider
29+
*/
30+
private $attributeAdapterProvider;
31+
32+
/**
33+
* @var ResolverInterface
34+
*/
35+
private $fieldNameResolver;
36+
37+
/**
38+
* @var StockInventory
39+
*/
40+
private $stockInventory;
41+
42+
43+
/**
44+
* @param StoreManagerInterface $storeManager
45+
* @param AttributeProvider $attributeAdapterProvider
46+
* @param ResolverInterface $fieldNameResolver
47+
* @param StockInventory $stockInventory
48+
*/
49+
public function __construct(
50+
StoreManagerInterface $storeManager,
51+
AttributeProvider $attributeAdapterProvider,
52+
ResolverInterface $fieldNameResolver,
53+
StockInventory $stockInventory
54+
) {
55+
$this->storeManager = $storeManager;
56+
$this->attributeAdapterProvider = $attributeAdapterProvider;
57+
$this->fieldNameResolver = $fieldNameResolver;
58+
$this->stockInventory = $stockInventory;
59+
}
60+
61+
/**
62+
* @param array $productIds
63+
* @param int $storeId
64+
* @return array
65+
* @throws NoSuchEntityException
66+
*/
67+
public function getFields(array $productIds, $storeId): array
68+
{
69+
$this->stockInventory->saveRelation($productIds);
70+
71+
$websiteId = $this->storeManager->getStore($storeId)->getWebsiteId();
72+
$fields = [];
73+
foreach ($productIds as $productId) {
74+
$fields[$productId] = $this->getProductStockData($productId, $websiteId, $storeId);
75+
}
76+
77+
$this->stockInventory->clearRelation();
78+
79+
return $fields;
80+
}
81+
82+
/**
83+
* Prepare saleability status for product
84+
*
85+
* @param int $productId
86+
* @param int $websiteId
87+
* @param int $storeId
88+
* @return array
89+
* @throws NoSuchEntityException
90+
*/
91+
private function getProductStockData($productId, $websiteId, $storeId): array
92+
{
93+
$result = [];
94+
$saleabilityAttribute = $this->attributeAdapterProvider->getByAttributeCode('is_out_of_stock');
95+
$fieldName = $this->fieldNameResolver->getFieldName(
96+
$saleabilityAttribute,
97+
['websiteId' => $websiteId]
98+
);
99+
100+
$sku = $this->stockInventory->getSkuRelation($productId);
101+
if (!$sku) {
102+
return ['is_out_of_stock' => 1];
103+
}
104+
$value = $this->stockInventory->getStockStatus(
105+
$sku,
106+
$this->storeManager->getStore($storeId)->getWebsite()->getCode()
107+
);
108+
109+
$result[$fieldName] = $value;
110+
111+
return $result;
112+
}
113+
}
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
declare(strict_types=1);
7+
8+
namespace Magento\Elasticsearch\Model\Adapter\DataMapper;
9+
10+
use Magento\Elasticsearch\Model\ResourceModel\StockInventory;
11+
use Magento\Framework\Exception\NoSuchEntityException;
12+
use Magento\Store\Model\StoreManagerInterface;
13+
14+
class Stock
15+
{
16+
/**
17+
* @var StockInventory
18+
*/
19+
private $inventory;
20+
21+
/**
22+
* @var StoreManagerInterface
23+
*/
24+
private $storeManager;
25+
26+
/**
27+
* @param StockInventory $inventory
28+
* @param StoreManagerInterface $storeManager
29+
*/
30+
public function __construct(
31+
StockInventory $inventory,
32+
StoreManagerInterface $storeManager
33+
) {
34+
$this->inventory = $inventory;
35+
$this->storeManager = $storeManager;
36+
}
37+
38+
/**
39+
* @param $entityId
40+
* @param $storeId
41+
* @return array|int[]
42+
* @throws NoSuchEntityException
43+
*/
44+
public function map($entityId, $storeId): array
45+
{
46+
$sku = $this->inventory->getSkuRelation((int)$entityId);
47+
48+
if (!$sku) {
49+
return ['is_salable' => 1];
50+
}
51+
52+
$value = $this->inventory->getStockStatus(
53+
$sku,
54+
$this->storeManager->getStore($storeId)->getWebsite()->getCode()
55+
);
56+
57+
return ['is_salable' => $value];
58+
}
59+
}

app/code/Magento/Elasticsearch/Model/Adapter/FieldMapper/Product/FieldProvider/DynamicField.php

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -156,6 +156,16 @@ public function getFields(array $context = []): array
156156
];
157157
}
158158

159+
$isOutOfStockAttribute = $this->attributeAdapterProvider->getByAttributeCode('is_out_of_stock');
160+
$groupStockItemKey = $this->fieldNameResolver->getFieldName(
161+
$isOutOfStockAttribute,
162+
$ctx
163+
);
164+
$allAttributes[$groupStockItemKey] = [
165+
'type' => $this->fieldTypeConverter->convert(FieldTypeConverterInterface::INTERNAL_DATA_TYPE_INT),
166+
'store' => true
167+
];
168+
159169
return $allAttributes;
160170
}
161171
}

0 commit comments

Comments
 (0)