Skip to content

Commit b22c60e

Browse files
committed
MAGETWO-65145: Performance degradation on front end on configurable products with huge amount of variation
1 parent 63fd342 commit b22c60e

File tree

3 files changed

+94
-57
lines changed

3 files changed

+94
-57
lines changed

app/code/Magento/ConfigurableProduct/Model/ResourceModel/Attribute/OptionSelectBuilder.php

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,6 @@ public function getSelect(AbstractAttribute $superAttribute, int $productId, Sco
5151
'product_id' => 'product_entity.entity_id',
5252
'attribute_code' => 'attribute.attribute_code',
5353
'value_index' => 'entity_value.value',
54-
'option_title' => $this->attributeResource->getConnection()->getIfNullSql(
55-
'option_value.value',
56-
'default_option_value.value'
57-
),
58-
'default_title' => 'default_option_value.value',
5954
'super_attribute_label' => 'attribute_label.value',
6055
]
6156
)->joinInner(
@@ -82,27 +77,7 @@ public function getSelect(AbstractAttribute $superAttribute, int $productId, Sco
8277
'entity_value.attribute_id = super_attribute.attribute_id',
8378
'entity_value.store_id = 0',
8479
"entity_value.{$this->attributeOptionProvider->getProductEntityLinkField()} = "
85-
. "entity.{$this->attributeOptionProvider->getProductEntityLinkField()}"
86-
]
87-
),
88-
[]
89-
)->joinLeft(
90-
['option_value' => $this->attributeResource->getTable('eav_attribute_option_value')],
91-
implode(
92-
' AND ',
93-
[
94-
'option_value.option_id = entity_value.value',
95-
'option_value.store_id = ' . $scope->getId()
96-
]
97-
),
98-
[]
99-
)->joinLeft(
100-
['default_option_value' => $this->attributeResource->getTable('eav_attribute_option_value')],
101-
implode(
102-
' AND ',
103-
[
104-
'default_option_value.option_id = entity_value.value',
105-
'default_option_value.store_id = ' . \Magento\Store\Model\Store::DEFAULT_STORE_ID
80+
. "entity.{$this->attributeOptionProvider->getProductEntityLinkField()}",
10681
]
10782
),
10883
[]
@@ -112,7 +87,7 @@ public function getSelect(AbstractAttribute $superAttribute, int $productId, Sco
11287
' AND ',
11388
[
11489
'super_attribute.product_super_attribute_id = attribute_label.product_super_attribute_id',
115-
'attribute_label.store_id = ' . \Magento\Store\Model\Store::DEFAULT_STORE_ID
90+
'attribute_label.store_id = ' . \Magento\Store\Model\Store::DEFAULT_STORE_ID,
11691
]
11792
),
11893
[]
@@ -124,6 +99,38 @@ public function getSelect(AbstractAttribute $superAttribute, int $productId, Sco
12499
$superAttribute->getAttributeId()
125100
);
126101

102+
if (!$superAttribute->getSourceModel()) {
103+
$select->columns(
104+
[
105+
'option_title' => $this->attributeResource->getConnection()->getIfNullSql(
106+
'option_value.value',
107+
'default_option_value.value'
108+
),
109+
'default_title' => 'default_option_value.value',
110+
]
111+
)->joinLeft(
112+
['option_value' => $this->attributeResource->getTable('eav_attribute_option_value')],
113+
implode(
114+
' AND ',
115+
[
116+
'option_value.option_id = entity_value.value',
117+
'option_value.store_id = ' . $scope->getId(),
118+
]
119+
),
120+
[]
121+
)->joinLeft(
122+
['default_option_value' => $this->attributeResource->getTable('eav_attribute_option_value')],
123+
implode(
124+
' AND ',
125+
[
126+
'default_option_value.option_id = entity_value.value',
127+
'default_option_value.store_id = ' . \Magento\Store\Model\Store::DEFAULT_STORE_ID,
128+
]
129+
),
130+
[]
131+
);
132+
}
133+
127134
return $select;
128135
}
129136
}

app/code/Magento/ConfigurableProduct/Test/Unit/Model/AttributeOptionProviderTest.php

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
use Magento\ConfigurableProduct\Model\AttributeOptionProvider;
1010
use Magento\ConfigurableProduct\Model\ResourceModel\Attribute\OptionSelectBuilderInterface;
11+
use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource;
1112
use Magento\Framework\App\ScopeInterface;
1213
use Magento\Framework\App\ScopeResolverInterface;
1314
use Magento\Framework\DB\Select;
@@ -92,10 +93,10 @@ protected function setUp()
9293
$this->optionSelectBuilder = $this->getMockBuilder(OptionSelectBuilderInterface::class)
9394
->disableOriginalConstructor()
9495
->getMockForAbstractClass();
95-
96+
9697
$this->abstractAttribute = $this->getMockBuilder(AbstractAttribute::class)
98+
->setMethods(['getSourceModel', 'getSource'])
9799
->disableOriginalConstructor()
98-
->setMethods([])
99100
->getMockForAbstractClass();
100101

101102
$this->objectManagerHelper = new ObjectManagerHelper($this);
@@ -141,46 +142,45 @@ public function testGetAttributeOptions(array $options)
141142

142143
/**
143144
* @param array $options
144-
* @dataProvider testOptionsWithBackendModelDataProvider
145+
* @dataProvider optionsWithBackendModelDataProvider
145146
*/
146147
public function testGetAttributeOptionsWithBackendModel(array $options)
147148
{
148-
$this->scopeResolver->expects($this->any())->method('getScope')->willReturn($this->scope);
149-
$this->scope->expects($this->any())->method('getId')->willReturn(123);
150-
151-
$this->select->expects($this->exactly(1))->method('from')->willReturnSelf();
152-
$this->select->expects($this->exactly(0))->method('columns')->willReturnSelf();
153-
$this->select->expects($this->exactly(5))->method('joinInner')->willReturnSelf();
154-
$this->select->expects($this->exactly(1))->method('joinLeft')->willReturnSelf();
155-
$this->select->expects($this->exactly(2))->method('where')->willReturnSelf();
149+
$this->scopeResolver->expects($this->any())
150+
->method('getScope')
151+
->willReturn($this->scope);
156152

157153
$source = $this->getMockBuilder(AbstractSource::class)
158154
->disableOriginalConstructor()
159155
->setMethods(['getAllOptions'])
160156
->getMockForAbstractClass();
161-
$source->expects($this->any())
157+
$source->expects($this->once())
162158
->method('getAllOptions')
163159
->willReturn([
164160
['value' => 13, 'label' => 'Option Value for index 13'],
165161
['value' => 14, 'label' => 'Option Value for index 14'],
166162
['value' => 15, 'label' => 'Option Value for index 15']
167163
]);
168-
169-
$this->abstractAttribute->expects($this->atLeastOnce())
164+
165+
$this->abstractAttribute->expects($this->any())
170166
->method('getSource')
171167
->willReturn($source);
172-
$this->abstractAttribute->expects($this->any())
173-
->method('getBackendTable')
174-
->willReturn('getBackendTable value');
175-
$this->abstractAttribute->expects($this->any())
168+
$this->abstractAttribute->expects($this->atLeastOnce())
176169
->method('getSourceModel')
177170
->willReturn('getSourceModel value');
178-
$this->abstractAttribute->expects($this->any())
179-
->method('getAttributeId')
180-
->willReturn('getAttributeId value');
171+
172+
$this->optionSelectBuilder->expects($this->any())
173+
->method('getSelect')
174+
->with($this->abstractAttribute, 1, $this->scope)
175+
->willReturn($this->select);
176+
177+
$this->attributeResource->expects($this->once())
178+
->method('getConnection')
179+
->willReturn($this->connectionMock);
181180

182181
$this->connectionMock->expects($this->once())
183182
->method('fetchAll')
183+
->with($this->select)
184184
->willReturn($options);
185185

186186
$this->assertEquals(
@@ -226,7 +226,7 @@ public function getAttributeOptionsDataProvider()
226226
/**
227227
* @return array
228228
*/
229-
public function testOptionsWithBackendModelDataProvider()
229+
public function optionsWithBackendModelDataProvider()
230230
{
231231
return [
232232
[

app/code/Magento/ConfigurableProduct/Test/Unit/Model/ResourceModel/Attribute/OptionSelectBuilderTest.php

Lines changed: 38 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -66,11 +66,11 @@ protected function setUp()
6666
->disableOriginalConstructor()
6767
->getMockForAbstractClass();
6868
$this->select = $this->getMockBuilder(Select::class)
69-
->setMethods(['from', 'joinInner', 'joinLeft', 'where'])
69+
->setMethods(['from', 'joinInner', 'joinLeft', 'where', 'columns'])
7070
->disableOriginalConstructor()
7171
->getMock();
7272
$this->connectionMock->expects($this->atLeastOnce())
73-
->method('select')
73+
->method('select', 'getIfNullSql')
7474
->willReturn($this->select);
7575

7676
$this->attributeResourceMock = $this->getMockBuilder(Attribute::class)
@@ -87,7 +87,7 @@ protected function setUp()
8787
->getMock();
8888

8989
$this->abstractAttributeMock = $this->getMockBuilder(AbstractAttribute::class)
90-
->setMethods(['getBackendTable', 'getAttributeId'])
90+
->setMethods(['getBackendTable', 'getAttributeId', 'getSourceModel'])
9191
->disableOriginalConstructor()
9292
->getMockForAbstractClass();
9393

@@ -110,10 +110,11 @@ protected function setUp()
110110
*/
111111
public function testGetSelect()
112112
{
113-
$this->select->expects($this->any())->method('from')->willReturnSelf();
114-
$this->select->expects($this->any())->method('joinInner')->willReturnSelf();
115-
$this->select->expects($this->any())->method('joinLeft')->willReturnSelf();
116-
$this->select->expects($this->any())->method('where')->willReturnSelf();
113+
$this->select->expects($this->exactly(1))->method('from')->willReturnSelf();
114+
$this->select->expects($this->exactly(1))->method('columns')->willReturnSelf();
115+
$this->select->expects($this->exactly(5))->method('joinInner')->willReturnSelf();
116+
$this->select->expects($this->exactly(3))->method('joinLeft')->willReturnSelf();
117+
$this->select->expects($this->exactly(2))->method('where')->willReturnSelf();
117118

118119
$this->abstractAttributeMock->expects($this->atLeastOnce())
119120
->method('getAttributeId')
@@ -122,7 +123,36 @@ public function testGetSelect()
122123
->method('getBackendTable')
123124
->willReturn('getMainTable value');
124125

125-
$this->scope->expects($this->atLeastOnce())->method('getId')->willReturn(123);
126+
$this->scope->expects($this->any())->method('getId')->willReturn(123);
127+
128+
$this->assertEquals(
129+
$this->select,
130+
$this->model->getSelect($this->abstractAttributeMock, 4, $this->scope)
131+
);
132+
}
133+
134+
/**
135+
* Test for method getSelect with backend table
136+
*/
137+
public function testGetSelectWithBackendModel()
138+
{
139+
$this->select->expects($this->exactly(1))->method('from')->willReturnSelf();
140+
$this->select->expects($this->exactly(0))->method('columns')->willReturnSelf();
141+
$this->select->expects($this->exactly(5))->method('joinInner')->willReturnSelf();
142+
$this->select->expects($this->exactly(1))->method('joinLeft')->willReturnSelf();
143+
$this->select->expects($this->exactly(2))->method('where')->willReturnSelf();
144+
145+
$this->abstractAttributeMock->expects($this->atLeastOnce())
146+
->method('getAttributeId')
147+
->willReturn('getAttributeId value');
148+
$this->abstractAttributeMock->expects($this->atLeastOnce())
149+
->method('getBackendTable')
150+
->willReturn('getMainTable value');
151+
$this->abstractAttributeMock->expects($this->atLeastOnce())
152+
->method('getSourceModel')
153+
->willReturn('source model value');
154+
155+
$this->scope->expects($this->any())->method('getId')->willReturn(123);
126156

127157
$this->assertEquals(
128158
$this->select,

0 commit comments

Comments
 (0)