Skip to content

Commit 0c82649

Browse files
committed
11946: Layer navigation showing wrong product count
1 parent b61725e commit 0c82649

File tree

4 files changed

+380
-89
lines changed

4 files changed

+380
-89
lines changed

app/code/Magento/CatalogSearch/Model/Adapter/Mysql/Aggregation/DataProvider.php

Lines changed: 17 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
* Copyright © Magento, Inc. All rights reserved.
44
* See COPYING.txt for license details.
55
*/
6+
67
namespace Magento\CatalogSearch\Model\Adapter\Mysql\Aggregation;
78

89
use Magento\Catalog\Model\Product;
9-
use Magento\CatalogInventory\Model\Stock;
10+
use Magento\CatalogSearch\Model\Adapter\Mysql\Aggregation\DataProvider\QueryBuilder;
1011
use Magento\Customer\Model\Session;
1112
use Magento\Eav\Model\Config;
1213
use Magento\Framework\App\ResourceConnection;
@@ -19,7 +20,7 @@
1920
use Magento\Framework\App\ObjectManager;
2021

2122
/**
22-
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
23+
* DataProvider for Catalog search Mysql.
2324
*/
2425
class DataProvider implements DataProviderInterface
2526
{
@@ -48,23 +49,31 @@ class DataProvider implements DataProviderInterface
4849
*/
4950
private $connection;
5051

52+
/**
53+
* @var QueryBuilder;
54+
*/
55+
private $queryBuilder;
56+
5157
/**
5258
* @param Config $eavConfig
5359
* @param ResourceConnection $resource
5460
* @param ScopeResolverInterface $scopeResolver
5561
* @param Session $customerSession
62+
* @param QueryBuilder|null $queryBuilder
5663
*/
5764
public function __construct(
5865
Config $eavConfig,
5966
ResourceConnection $resource,
6067
ScopeResolverInterface $scopeResolver,
61-
Session $customerSession
68+
Session $customerSession,
69+
QueryBuilder $queryBuilder = null
6270
) {
6371
$this->eavConfig = $eavConfig;
6472
$this->resource = $resource;
6573
$this->connection = $resource->getConnection();
6674
$this->scopeResolver = $scopeResolver;
6775
$this->customerSession = $customerSession;
76+
$this->queryBuilder = $queryBuilder ?: ObjectManager::getInstance()->get(QueryBuilder::class);
6877
}
6978

7079
/**
@@ -79,47 +88,13 @@ public function getDataSet(
7988

8089
$attribute = $this->eavConfig->getAttribute(Product::ENTITY, $bucket->getField());
8190

82-
$select = $this->getSelect();
83-
84-
$select->joinInner(
85-
['entities' => $entityIdsTable->getName()],
86-
'main_table.entity_id = entities.entity_id',
87-
[]
91+
$select = $this->queryBuilder->build(
92+
$attribute,
93+
$entityIdsTable->getName(),
94+
$currentScope,
95+
$this->customerSession->getCustomerGroupId()
8896
);
8997

90-
if ($attribute->getAttributeCode() === 'price') {
91-
/** @var \Magento\Store\Model\Store $store */
92-
$store = $this->scopeResolver->getScope($currentScope);
93-
if (!$store instanceof \Magento\Store\Model\Store) {
94-
throw new \RuntimeException('Illegal scope resolved');
95-
}
96-
$table = $this->resource->getTableName('catalog_product_index_price');
97-
$select->from(['main_table' => $table], null)
98-
->columns([BucketInterface::FIELD_VALUE => 'main_table.min_price'])
99-
->where('main_table.customer_group_id = ?', $this->customerSession->getCustomerGroupId())
100-
->where('main_table.website_id = ?', $store->getWebsiteId());
101-
} else {
102-
$currentScopeId = $this->scopeResolver->getScope($currentScope)
103-
->getId();
104-
$table = $this->resource->getTableName(
105-
'catalog_product_index_eav' . ($attribute->getBackendType() === 'decimal' ? '_decimal' : '')
106-
);
107-
$subSelect = $select;
108-
$subSelect->from(['main_table' => $table], ['main_table.entity_id', 'main_table.value'])
109-
->distinct()
110-
->joinLeft(
111-
['stock_index' => $this->resource->getTableName('cataloginventory_stock_status')],
112-
'main_table.source_id = stock_index.product_id',
113-
[]
114-
)
115-
->where('main_table.attribute_id = ?', $attribute->getAttributeId())
116-
->where('main_table.store_id = ? ', $currentScopeId)
117-
->where('stock_index.stock_status = ?', Stock::STOCK_IN_STOCK);
118-
$parentSelect = $this->getSelect();
119-
$parentSelect->from(['main_table' => $subSelect], ['main_table.value']);
120-
$select = $parentSelect;
121-
}
122-
12398
return $select;
12499
}
125100

@@ -130,12 +105,4 @@ public function execute(Select $select)
130105
{
131106
return $this->connection->fetchAssoc($select);
132107
}
133-
134-
/**
135-
* @return Select
136-
*/
137-
private function getSelect()
138-
{
139-
return $this->connection->select();
140-
}
141108
}
Lines changed: 183 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,183 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
7+
namespace Magento\CatalogSearch\Model\Adapter\Mysql\Aggregation\DataProvider;
8+
9+
use Magento\CatalogInventory\Model\Configuration as CatalogInventoryConfiguration;
10+
use Magento\CatalogInventory\Model\Stock;
11+
use Magento\Eav\Model\Entity\Attribute\AbstractAttribute;
12+
use Magento\Framework\App\ObjectManager;
13+
use Magento\Framework\App\ResourceConnection;
14+
use Magento\Framework\App\ScopeResolverInterface;
15+
use Magento\Framework\DB\Adapter\AdapterInterface;
16+
use Magento\Framework\DB\Select;
17+
use Magento\Framework\Search\Request\BucketInterface;
18+
19+
/**
20+
* Class for query building for Magento\CatalogSearch\Model\Adapter\Mysql\Aggregation\DataProvider.
21+
*/
22+
class QueryBuilder
23+
{
24+
/**
25+
* @var Resource
26+
*/
27+
private $resource;
28+
29+
/**
30+
* @var ScopeResolverInterface
31+
*/
32+
private $scopeResolver;
33+
34+
/**
35+
* @var CatalogInventoryConfiguration
36+
*/
37+
private $inventoryConfig;
38+
39+
/**
40+
* @var AdapterInterface
41+
*/
42+
private $connection;
43+
44+
/**
45+
* @param ResourceConnection $resource
46+
* @param ScopeResolverInterface $scopeResolver
47+
* @param CatalogInventoryConfiguration|null $inventoryConfig
48+
*/
49+
public function __construct(
50+
ResourceConnection $resource,
51+
ScopeResolverInterface $scopeResolver,
52+
CatalogInventoryConfiguration $inventoryConfig = null
53+
) {
54+
$this->resource = $resource;
55+
$this->scopeResolver = $scopeResolver;
56+
$this->inventoryConfig = $inventoryConfig ?: ObjectManager::getInstance()->get(
57+
CatalogInventoryConfiguration::class
58+
);
59+
$this->connection = $resource->getConnection();
60+
}
61+
62+
/**
63+
* Build select.
64+
*
65+
* @param AbstractAttribute $attribute
66+
* @param string $tableName
67+
* @param int $currentScope
68+
* @param int $customerGroupId
69+
*
70+
* @return Select
71+
*/
72+
public function build(
73+
AbstractAttribute $attribute,
74+
$tableName,
75+
$currentScope,
76+
$customerGroupId
77+
) {
78+
$select = $this->getSelect();
79+
80+
$select->joinInner(
81+
['entities' => $tableName],
82+
'main_table.entity_id = entities.entity_id',
83+
[]
84+
);
85+
86+
if ($attribute->getAttributeCode() === 'price') {
87+
/** @var \Magento\Store\Model\Store $store */
88+
$store = $this->scopeResolver->getScope($currentScope);
89+
if (!$store instanceof \Magento\Store\Model\Store) {
90+
throw new \RuntimeException('Illegal scope resolved');
91+
}
92+
93+
$select = $this->buildIfPrice(
94+
$store->getWebsiteId(),
95+
$customerGroupId,
96+
$select
97+
);
98+
} else {
99+
$currentScopeId = $this->scopeResolver->getScope($currentScope)
100+
->getId();
101+
102+
$select = $this->buildIfNotPrice(
103+
$currentScopeId,
104+
$attribute,
105+
$select
106+
);
107+
}
108+
109+
return $select;
110+
}
111+
112+
/**
113+
* Build select if it is price attribute.
114+
*
115+
* @param int $websiteId
116+
* @param int $customerGroupId
117+
* @param Select $select
118+
*
119+
* @return Select
120+
*/
121+
private function buildIfPrice(
122+
$websiteId,
123+
$customerGroupId,
124+
Select $select
125+
) {
126+
$table = $this->resource->getTableName('catalog_product_index_price');
127+
$select->from(['main_table' => $table], null)
128+
->columns([BucketInterface::FIELD_VALUE => 'main_table.min_price'])
129+
->where('main_table.customer_group_id = ?', $customerGroupId)
130+
->where('main_table.website_id = ?', $websiteId);
131+
132+
return $select;
133+
}
134+
135+
/**
136+
* Build select if it is not price attribute.
137+
*
138+
* @param int $currentScopeId
139+
* @param AbstractAttribute $attribute
140+
* @param Select $select
141+
*
142+
* @return Select
143+
*/
144+
private function buildIfNotPrice(
145+
$currentScopeId,
146+
AbstractAttribute $attribute,
147+
Select $select
148+
) {
149+
$table = $this->resource->getTableName(
150+
'catalog_product_index_eav' . ($attribute->getBackendType() === 'decimal' ? '_decimal' : '')
151+
);
152+
$subSelect = $select;
153+
$subSelect->from(['main_table' => $table], ['main_table.entity_id', 'main_table.value'])
154+
->distinct()
155+
->joinLeft(
156+
['stock_index' => $this->resource->getTableName('cataloginventory_stock_status')],
157+
'main_table.source_id = stock_index.product_id',
158+
[]
159+
)
160+
->where('main_table.attribute_id = ?', $attribute->getAttributeId())
161+
->where('main_table.store_id = ? ', $currentScopeId);
162+
163+
if (!$this->inventoryConfig->isShowOutOfStock($currentScopeId)) {
164+
$subSelect->where('stock_index.stock_status = ?', Stock::STOCK_IN_STOCK);
165+
}
166+
167+
$parentSelect = $this->getSelect();
168+
$parentSelect->from(['main_table' => $subSelect], ['main_table.value']);
169+
$select = $parentSelect;
170+
171+
return $select;
172+
}
173+
174+
/**
175+
* Get empty select.
176+
*
177+
* @return Select
178+
*/
179+
private function getSelect()
180+
{
181+
return $this->connection->select();
182+
}
183+
}

0 commit comments

Comments
 (0)