Skip to content

Commit a545290

Browse files
author
Alex Bomko
committed
MAGETWO-47666: Swatches Module generates too many DB requests on Storefornt Catagery Page
1 parent 0b5f63f commit a545290

File tree

5 files changed

+99
-45
lines changed

5 files changed

+99
-45
lines changed

app/code/Magento/Swatches/Block/Product/Renderer/Configurable.php

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ public function __construct(
119119
public function getJsonSwatchConfig()
120120
{
121121
$attributesData = $this->getSwatchAttributesData();
122-
$allOptionIds = $this->getAllOptionsIdsFromAttributeArray($attributesData);
122+
$allOptionIds = $this->getConfigurableOptionsIds($attributesData);
123123
$swatchesData = $this->swatchHelper->getSwatchesByOptionsId($allOptionIds);
124124

125125
$config = [];
@@ -209,9 +209,9 @@ protected function addSwatchDataForAttribute(
209209
$result = [];
210210
foreach ($options as $optionId => $label) {
211211
if (isset($swatchesCollectionArray[$optionId])) {
212-
$result[$optionId]['label'] = $label;
213212
$result[$optionId] = $this->extractNecessarySwatchData($swatchesCollectionArray[$optionId]);
214213
$result[$optionId] = $this->addAdditionalMediaData($result[$optionId], $optionId, $attributeDataArray);
214+
$result[$optionId]['label'] = $label;
215215
}
216216
}
217217

@@ -278,7 +278,8 @@ protected function getVariationMedia($attributeCode, $optionId)
278278
{
279279
$variationProduct = $this->swatchHelper->loadFirstVariationWithSwatchImage(
280280
$this->getProduct(),
281-
[$attributeCode => $optionId]
281+
$attributeCode,
282+
$optionId
282283
);
283284

284285
$variationMediaArray = [];
@@ -325,15 +326,20 @@ protected function isProductHasImage(Product $product, $imageType)
325326
* @param array $attributeData
326327
* @return array
327328
*/
328-
protected function getAllOptionsIdsFromAttributeArray(array $attributeData)
329+
protected function getConfigurableOptionsIds(array $attributeData)
329330
{
330331
$ids = [];
331-
foreach ($attributeData as $item) {
332-
if (isset($item['options'])) {
333-
$ids = array_merge($ids, array_keys($item['options']));
332+
foreach ($this->getAllowProducts() as $product) {
333+
/** @var \Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute $attribute */
334+
foreach ($this->helper->getAllowAttributes($this->getProduct()) as $attribute) {
335+
$productAttribute = $attribute->getProductAttribute();
336+
$productAttributeId = $productAttribute->getId();
337+
if (isset($attributeData[$productAttributeId])) {
338+
$ids[$product->getData($productAttribute->getAttributeCode())] = 1;
339+
}
334340
}
335341
}
336-
return $ids;
342+
return array_keys($ids);
337343
}
338344

339345
/**

app/code/Magento/Swatches/Helper/Data.php

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -155,30 +155,32 @@ public function populateAdditionalDataEavAttribute(Attribute $attribute)
155155

156156
/**
157157
* @param \Magento\Catalog\Model\Product $product
158-
* @param array $attributes
159-
* @return bool|null
158+
* @param string $attributeCode
159+
* @param string|int $attributeValue
160+
* @return \Magento\Catalog\Model\Product|null
160161
* @throws InputException
161162
*/
162-
public function loadFirstVariationWithSwatchImage($product, array $attributes)
163+
public function loadFirstVariationWithSwatchImage($product, $attributeCode, $attributeValue)
163164
{
164165
$product = $this->createSwatchProduct($product);
165166
if (!$product) {
166-
return false;
167+
return null;
167168
}
168169

169-
$productCollection = $this->prepareVariationCollection($product, $attributes);
170-
170+
$products = $product->getTypeInstance()->getUsedProducts($product);
171171
$variationProduct = null;
172-
foreach ($productCollection as $item) {
173-
$currentProduct = $this->productRepository->getById($item->getId());
174-
$media = $this->getProductMedia($currentProduct);
175-
if (! empty($media) && isset($media['swatch_image'])) {
176-
$variationProduct = $currentProduct;
172+
foreach ($products as $item) {
173+
if ($item->getData($attributeCode) != $attributeValue) {
174+
continue;
175+
}
176+
$media = $this->getProductMedia($item);
177+
if (!empty($media) && isset($media['swatch_image'])) {
178+
$variationProduct = $item;
177179
break;
178180
}
179181
if ($variationProduct !== false) {
180182
if (! empty($media) && isset($media['image'])) {
181-
$variationProduct = $currentProduct;
183+
$variationProduct = $item;
182184
} else {
183185
$variationProduct = false;
184186
}

app/code/Magento/Swatches/Test/Unit/Block/Product/Renderer/ConfigurableTest.php

Lines changed: 49 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,6 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
1616
/** @var Configurable */
1717
private $configurable;
1818

19-
/** @var \Magento\Catalog\Block\Product\Context|\PHPUnit_Framework_MockObject_MockObject */
20-
private $context;
21-
2219
/** @var \Magento\Framework\Stdlib\ArrayUtils|\PHPUnit_Framework_MockObject_MockObject */
2320
private $arrayUtils;
2421

@@ -60,7 +57,6 @@ class ConfigurableTest extends \PHPUnit_Framework_TestCase
6057

6158
public function setUp()
6259
{
63-
$this->context = $this->getMock('\Magento\Catalog\Block\Product\Context', [], [], '', false);
6460
$this->arrayUtils = $this->getMock('\Magento\Framework\Stdlib\ArrayUtils', [], [], '', false);
6561
$this->jsonEncoder = $this->getMock('\Magento\Framework\Json\EncoderInterface', [], [], '', false);
6662
$this->helper = $this->getMock('\Magento\ConfigurableProduct\Helper\Data', [], [], '', false);
@@ -75,15 +71,13 @@ public function setUp()
7571
$this->imageHelper = $this->getMock('\Magento\Catalog\Helper\Image', [], [], '', false);
7672
$this->urlBuilder = $this->getMock('\Magento\Framework\UrlInterface');
7773

78-
$this->context->expects($this->any())->method('getScopeConfig')->willReturn($this->scopeConfig);
79-
$this->context->expects($this->any())->method('getImageHelper')->willReturn($this->imageHelper);
80-
$this->context->expects($this->any())->method('getUrlBuilder')->willReturn($this->urlBuilder);
81-
8274
$objectManager = new \Magento\Framework\TestFramework\Unit\Helper\ObjectManager($this);
8375
$this->configurable = $objectManager->getObject(
8476
'\Magento\Swatches\Block\Product\Renderer\Configurable',
8577
[
86-
'context' => $this->context,
78+
'scopeConfig' => $this->scopeConfig,
79+
'imageHelper' => $this->imageHelper,
80+
'urlBuilder' => $this->urlBuilder,
8781
'arrayUtils' => $this->arrayUtils,
8882
'jsonEncoder' => $this->jsonEncoder,
8983
'helper' => $this->helper,
@@ -92,7 +86,6 @@ public function setUp()
9286
'catalogProduct' => $this->catalogProduct,
9387
'currentCustomer' => $this->currentCustomer,
9488
'priceCurrency' => $this->priceCurrency,
95-
'data' => [],
9689
]
9790
);
9891
}
@@ -146,8 +139,48 @@ public function testSetIsProductListingContext()
146139
);
147140
}
148141

142+
private function prepareGetJsonSwatchConfig()
143+
{
144+
$product1 = $this->getMock('\Magento\Catalog\Model\Product', [], [], '', false);
145+
$product1->expects($this->atLeastOnce())->method('isSaleable')->willReturn(true);
146+
$product1->expects($this->atLeastOnce())->method('getData')->with('code')->willReturn(1);
147+
148+
$product2 = $this->getMock('\Magento\Catalog\Model\Product', [], [], '', false);
149+
$product2->expects($this->atLeastOnce())->method('isSaleable')->willReturn(true);
150+
$product2->expects($this->atLeastOnce())->method('getData')->with('code')->willReturn(3);
151+
152+
$simpleProducts = [$product1, $product2];
153+
$configurableType = $this->getMock(
154+
'\Magento\ConfigurableProduct\Model\Product\Type\Configurable',
155+
[],
156+
[],
157+
'',
158+
false
159+
);
160+
$configurableType->expects($this->atLeastOnce())->method('getUsedProducts')->with($this->product, null)
161+
->willReturn($simpleProducts);
162+
$this->product->expects($this->any())->method('getTypeInstance')->willReturn($configurableType);
163+
164+
$productAttribute1 = $this->getMock('\Magento\Eav\Model\Entity\Attribute\AbstractAttribute', [], [], '', false);
165+
$productAttribute1->expects($this->any())->method('getId')->willReturn(1);
166+
$productAttribute1->expects($this->any())->method('getAttributeCode')->willReturn('code');
167+
168+
$attribute1 = $this->getMock(
169+
'\Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute',
170+
['getProductAttribute'],
171+
[],
172+
'',
173+
false
174+
);
175+
$attribute1->expects($this->any())->method('getProductAttribute')->willReturn($productAttribute1);
176+
177+
$this->helper->expects($this->any())->method('getAllowAttributes')->with($this->product)
178+
->willReturn([$attribute1]);
179+
}
180+
149181
public function testGetJsonSwatchConfigNotVisualImageType()
150182
{
183+
$this->prepareGetJsonSwatchConfig();
151184
$this->configurable->setProduct($this->product);
152185

153186
$this->swatchHelper->expects($this->once())->method('getSwatchAttributesAsArray')
@@ -167,7 +200,7 @@ public function testGetJsonSwatchConfigNotVisualImageType()
167200
]);
168201

169202
$this->swatchHelper->expects($this->once())->method('loadFirstVariationWithSwatchImage')
170-
->with($this->product, ['code' => 3])
203+
->with($this->product, 'code', 3)
171204
->willReturn($this->product);
172205

173206
$this->product->expects($this->exactly(4))->method('getData')
@@ -187,6 +220,7 @@ public function testGetJsonSwatchConfigNotVisualImageType()
187220

188221
public function testGetJsonSwatchConfigVisualImageType()
189222
{
223+
$this->prepareGetJsonSwatchConfig();
190224
$this->configurable->setProduct($this->product);
191225

192226
$this->swatchHelper->expects($this->once())->method('getSwatchAttributesAsArray')
@@ -206,7 +240,7 @@ public function testGetJsonSwatchConfigVisualImageType()
206240
]);
207241

208242
$this->swatchHelper->expects($this->once())->method('loadFirstVariationWithSwatchImage')
209-
->with($this->product, ['code' => 3])
243+
->with($this->product, 'code', 3)
210244
->willReturn($this->product);
211245

212246
$this->swatchMediaHelper->expects($this->exactly(2))->method('getSwatchAttributeImage')
@@ -233,6 +267,8 @@ public function testGetJsonSwatchConfigVisualImageType()
233267

234268
public function testGetJsonSwatchConfigWithoutVisualImageType()
235269
{
270+
$this->prepareGetJsonSwatchConfig();
271+
236272
$this->configurable->setProduct($this->product);
237273

238274
$this->swatchHelper->expects($this->once())->method('getSwatchAttributesAsArray')
@@ -252,7 +288,7 @@ public function testGetJsonSwatchConfigWithoutVisualImageType()
252288
]);
253289

254290
$this->swatchHelper->expects($this->once())->method('loadFirstVariationWithSwatchImage')
255-
->with($this->product, ['code' => 3])
291+
->with($this->product, 'code', 3)
256292
->willReturn($this->product);
257293

258294
$this->swatchMediaHelper->expects($this->exactly(2))->method('getSwatchAttributeImage')

app/code/Magento/Swatches/Test/Unit/Helper/DataTest.php

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -213,24 +213,21 @@ public function testLoadFirstVariationWithSwatchImage($product, $typesArray)
213213
$this->prepareVariationCollection();
214214

215215
$this->productMock->method('getId')->willReturn(95);
216-
$this->productRepoMock->expects($this->atLeastOnce())->method('getById')->with(95)->willReturn(
217-
$this->productMock
218-
);
219216

220217
$this->getProductMedia($typesArray);
221218

222-
$this->swatchHelperObject->loadFirstVariationWithSwatchImage($this->productMock, ['color' => 31]);
219+
$this->swatchHelperObject->loadFirstVariationWithSwatchImage($this->productMock, 'color', 31);
223220
}
224221

225222
public function testLoadFirstVariationWithSwatchImageWithException()
226223
{
227224
$this->setExpectedException('\Magento\Framework\Exception\InputException');
228-
$this->swatchHelperObject->loadFirstVariationWithSwatchImage(null, ['color' => 31]);
225+
$this->swatchHelperObject->loadFirstVariationWithSwatchImage(null, 'color', 31);
229226
}
230227

231228
public function testLoadFirstVariationWithSwatchImageWithoutProduct()
232229
{
233-
$this->swatchHelperObject->loadFirstVariationWithSwatchImage($this->productMock, ['color' => 31]);
230+
$this->swatchHelperObject->loadFirstVariationWithSwatchImage($this->productMock, 'color', 31);
234231
}
235232

236233
/**
@@ -394,7 +391,7 @@ protected function createSwatchProduct($product)
394391
{
395392
if (gettype($product) == 'integer') {
396393
$this->productRepoMock
397-
->expects($this->once())
394+
->expects($this->any())
398395
->method('getById')
399396
->with(95)
400397
->willReturn($this->productMock);
@@ -411,6 +408,16 @@ protected function getSwatchAttributes()
411408

412409
protected function getAttributesFromConfigurable()
413410
{
411+
$product1 = $this->getMock('\Magento\Catalog\Model\Product', [], [], '', false);
412+
$product1->expects($this->any())->method('isSaleable')->willReturn(true);
413+
$product1->expects($this->any())->method('getData')->with('color')->willReturn(1);
414+
415+
$product2 = $this->getMock('\Magento\Catalog\Model\Product', [], [], '', false);
416+
$product2->expects($this->any())->method('isSaleable')->willReturn(true);
417+
$product2->expects($this->any())->method('getData')->with('color')->willReturn(3);
418+
419+
$simpleProducts = [$product1, $product2];
420+
414421
$configurable = $this->getMock(
415422
'\Magento\ConfigurableProduct\Model\Product\Type\Configurable',
416423
[],
@@ -433,13 +440,16 @@ protected function getAttributesFromConfigurable()
433440
);
434441

435442
$configurable
436-
->expects($this->atLeastOnce())
443+
->expects($this->any())
437444
->method('getConfigurableAttributes')
438445
->with($this->productMock)
439446
->willReturn([$confAttribute, $confAttribute]);
440447

448+
$configurable->expects($this->any())->method('getUsedProducts')->with($this->productMock)
449+
->willReturn($simpleProducts);
450+
441451
$confAttribute
442-
->expects($this->atLeastOnce())
452+
->expects($this->any())
443453
->method('__call')
444454
->with('getProductAttribute')
445455
->willReturn($this->attributeMock);
@@ -448,7 +458,7 @@ protected function getAttributesFromConfigurable()
448458
protected function prepareVariationCollection()
449459
{
450460
$this->productCollectionFactoryMock
451-
->expects($this->once())
461+
->expects($this->any())
452462
->method('create')
453463
->willReturn($this->productCollectionMock);
454464

app/code/Magento/Swatches/view/frontend/templates/product/listing/renderer.phtml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,8 @@
1313
onlySwatches: true,
1414
enableControlLabel: false,
1515
numberToShow: <?php /* @escapeNotVerified */ echo $block->getNumberSwatchesPerProduct(); ?>,
16-
jsonConfig: <?php /* @escapeNotVerified */ echo $swatchOptions = $block->getJsonConfig(); ?>,
17-
jsonSwatchConfig: <?php /* @escapeNotVerified */ echo $swatchOptions = $block->getJsonSwatchConfig(); ?>,
16+
jsonConfig: <?php /* @escapeNotVerified */ echo $block->getJsonConfig(); ?>,
17+
jsonSwatchConfig: <?php /* @escapeNotVerified */ echo $block->getJsonSwatchConfig(); ?>,
1818
mediaCallback: '<?php /* @escapeNotVerified */ echo $block->getMediaCallback() ?>'
1919
});
2020
});

0 commit comments

Comments
 (0)