Skip to content

Commit e49ccf7

Browse files
authored
Merge pull request #2461 from magento-performance/MAGETWO-90572
[performance] MAGETWO-90572: Optimize retrieving product attributes
2 parents 35d1377 + e6994a1 commit e49ccf7

File tree

7 files changed

+73
-54
lines changed

7 files changed

+73
-54
lines changed

app/code/Magento/Catalog/Model/Product.php

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
use Magento\Catalog\Model\Product\Attribute\Backend\Media\EntryConverterPool;
1313
use Magento\Framework\Api\AttributeValueFactory;
1414
use Magento\Framework\App\Filesystem\DirectoryList;
15+
use Magento\Framework\App\ObjectManager;
1516
use Magento\Framework\DataObject\IdentityInterface;
1617
use Magento\Framework\Pricing\SaleableInterface;
1718

@@ -270,6 +271,7 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
270271

271272
/**
272273
* @var \Magento\Catalog\Api\ProductAttributeRepositoryInterface
274+
* @deprecated Not used anymore due to performance issue (loaded all product attributes)
273275
*/
274276
protected $metadataService;
275277

@@ -346,6 +348,11 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
346348
*/
347349
protected $linkTypeProvider;
348350

351+
/**
352+
* @var \Magento\Eav\Model\Config
353+
*/
354+
private $eavConfig;
355+
349356
/**
350357
* Product constructor.
351358
* @param \Magento\Framework\Model\Context $context
@@ -383,7 +390,7 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
383390
* @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper
384391
* @param \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $joinProcessor
385392
* @param array $data
386-
*
393+
* @param \Magento\Eav\Model\Config|null $config
387394
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
388395
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
389396
*/
@@ -422,7 +429,8 @@ public function __construct(
422429
EntryConverterPool $mediaGalleryEntryConverterPool,
423430
\Magento\Framework\Api\DataObjectHelper $dataObjectHelper,
424431
\Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $joinProcessor,
425-
array $data = []
432+
array $data = [],
433+
\Magento\Eav\Model\Config $config = null
426434
) {
427435
$this->metadataService = $metadataService;
428436
$this->_itemOptionFactory = $itemOptionFactory;
@@ -461,6 +469,7 @@ public function __construct(
461469
$resourceCollection,
462470
$data
463471
);
472+
$this->eavConfig = $config ?? ObjectManager::getInstance()->get(\Magento\Eav\Model\Config::class);
464473
}
465474

466475
/**
@@ -474,12 +483,18 @@ protected function _construct()
474483
}
475484

476485
/**
477-
* {@inheritdoc}
486+
* Get a list of custom attribute codes that belongs to product attribute set. If attribute set not specified for
487+
* product will return all attribute codes
488+
*
489+
* @return string[]
478490
*/
479491
protected function getCustomAttributesCodes()
480492
{
481493
if ($this->customAttributesCodes === null) {
482-
$this->customAttributesCodes = $this->getEavAttributesCodes($this->metadataService);
494+
$this->customAttributesCodes = array_keys($this->eavConfig->getEntityAttributes(
495+
self::ENTITY,
496+
$this
497+
));
483498
$this->customAttributesCodes = array_diff($this->customAttributesCodes, $this->interfaceAttributes);
484499
}
485500
return $this->customAttributesCodes;

app/code/Magento/Catalog/Plugin/Model/ResourceModel/ReadSnapshotPlugin.php

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,9 @@ public function afterExecute(ReadSnapshot $subject, array $entityData, $entityTy
5858
$globalAttributes = [];
5959
$attributesMap = [];
6060
$eavEntityType = $metadata->getEavEntityType();
61-
$attributes = (null === $eavEntityType) ? [] : $this->config->getEntityAttributes($eavEntityType);
61+
$attributes = null === $eavEntityType
62+
? []
63+
: $this->config->getEntityAttributes($eavEntityType, new \Magento\Framework\DataObject($entityData));
6264

6365
/** @var \Magento\Eav\Model\Entity\Attribute\AbstractAttribute $attribute */
6466
foreach ($attributes as $attribute) {

app/code/Magento/Catalog/Test/Unit/Model/ProductTest.php

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,11 @@ class ProductTest extends \PHPUnit\Framework\TestCase
205205
*/
206206
private $cacheInterfaceMock;
207207

208+
/**
209+
* @var \Magento\Eav\Model\Config|\PHPUnit_Framework_MockObject_MockObject
210+
*/
211+
private $eavConfig;
212+
208213
/**
209214
* @SuppressWarnings(PHPMD.ExcessiveMethodLength)
210215
*/
@@ -364,6 +369,7 @@ protected function setUp()
364369
->setMethods(['create'])
365370
->getMock();
366371
$this->mediaConfig = $this->createMock(\Magento\Catalog\Model\Product\Media\Config::class);
372+
$this->eavConfig = $this->createMock(\Magento\Eav\Model\Config::class);
367373

368374
$this->extensionAttributes = $this->getMockBuilder(ProductExtensionInterface::class)
369375
->setMethods(['getStockItem'])
@@ -401,7 +407,8 @@ protected function setUp()
401407
'catalogProductMediaConfig' => $this->mediaConfig,
402408
'_filesystem' => $this->filesystemMock,
403409
'_collectionFactory' => $this->collectionFactoryMock,
404-
'data' => ['id' => 1]
410+
'data' => ['id' => 1],
411+
'eavConfig' => $this->eavConfig
405412
]
406413
);
407414
}
@@ -1269,39 +1276,32 @@ public function testGetCustomAttributes()
12691276
{
12701277
$priceCode = 'price';
12711278
$colorAttributeCode = 'color';
1272-
$interfaceAttribute = $this->createMock(\Magento\Framework\Api\MetadataObjectInterface::class);
1273-
$interfaceAttribute->expects($this->once())
1274-
->method('getAttributeCode')
1275-
->willReturn($priceCode);
1276-
$colorAttribute = $this->createMock(\Magento\Framework\Api\MetadataObjectInterface::class);
1277-
$colorAttribute->expects($this->once())
1278-
->method('getAttributeCode')
1279-
->willReturn($colorAttributeCode);
1280-
$customAttributesMetadata = [$interfaceAttribute, $colorAttribute];
1281-
1282-
$this->metadataServiceMock->expects($this->once())
1283-
->method('getCustomAttributesMetadata')
1279+
$customAttributesMetadata = [$priceCode => 'attribute1', $colorAttributeCode => 'attribute2'];
1280+
1281+
$this->metadataServiceMock->expects($this->never())->method('getCustomAttributesMetadata');
1282+
$this->eavConfig->expects($this->once())
1283+
->method('getEntityAttributes')
12841284
->willReturn($customAttributesMetadata);
12851285
$this->model->setData($priceCode, 10);
12861286

12871287
//The color attribute is not set, expect empty custom attribute array
12881288
$this->assertEquals([], $this->model->getCustomAttributes());
12891289

12901290
//Set the color attribute;
1291-
$this->model->setData($colorAttributeCode, "red");
1291+
$this->model->setData($colorAttributeCode, 'red');
12921292
$attributeValue = new \Magento\Framework\Api\AttributeValue();
12931293
$attributeValue2 = new \Magento\Framework\Api\AttributeValue();
12941294
$this->attributeValueFactory->expects($this->exactly(2))->method('create')
12951295
->willReturnOnConsecutiveCalls($attributeValue, $attributeValue2);
12961296
$this->assertEquals(1, count($this->model->getCustomAttributes()));
12971297
$this->assertNotNull($this->model->getCustomAttribute($colorAttributeCode));
1298-
$this->assertEquals("red", $this->model->getCustomAttribute($colorAttributeCode)->getValue());
1298+
$this->assertEquals('red', $this->model->getCustomAttribute($colorAttributeCode)->getValue());
12991299

13001300
//Change the attribute value, should reflect in getCustomAttribute
1301-
$this->model->setCustomAttribute($colorAttributeCode, "blue");
1301+
$this->model->setCustomAttribute($colorAttributeCode, 'blue');
13021302
$this->assertEquals(1, count($this->model->getCustomAttributes()));
13031303
$this->assertNotNull($this->model->getCustomAttribute($colorAttributeCode));
1304-
$this->assertEquals("blue", $this->model->getCustomAttribute($colorAttributeCode)->getValue());
1304+
$this->assertEquals('blue', $this->model->getCustomAttribute($colorAttributeCode)->getValue());
13051305
}
13061306

13071307
/**

app/code/Magento/Eav/Model/ResourceModel/ReadHandler.php

Lines changed: 20 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
*/
66
namespace Magento\Eav\Model\ResourceModel;
77

8+
use Magento\Framework\DataObject;
89
use Magento\Framework\EntityManager\MetadataPool;
910
use Magento\Framework\EntityManager\Operation\AttributeInterface;
1011
use Magento\Framework\Model\Entity\ScopeInterface;
@@ -59,13 +60,29 @@ public function __construct(
5960
* @param string $entityType
6061
* @return \Magento\Eav\Api\Data\AttributeInterface[]
6162
* @throws \Exception if for unknown entity type
63+
* @deprecated Not used anymore
64+
* @see ReadHandler::getEntityAttributes
6265
*/
6366
protected function getAttributes($entityType)
6467
{
6568
$metadata = $this->metadataPool->getMetadata($entityType);
6669
$eavEntityType = $metadata->getEavEntityType();
67-
$attributes = (null === $eavEntityType) ? [] : $this->config->getAttributes($eavEntityType);
68-
return $attributes;
70+
return null === $eavEntityType ? [] : $this->config->getEntityAttributes($eavEntityType);
71+
}
72+
73+
/**
74+
* Get attribute of given entity type
75+
*
76+
* @param string $entityType
77+
* @param DataObject $entity
78+
* @return \Magento\Eav\Api\Data\AttributeInterface[]
79+
* @throws \Exception if for unknown entity type
80+
*/
81+
private function getEntityAttributes(string $entityType, DataObject $entity): array
82+
{
83+
$metadata = $this->metadataPool->getMetadata($entityType);
84+
$eavEntityType = $metadata->getEavEntityType();
85+
return null === $eavEntityType ? [] : $this->config->getEntityAttributes($eavEntityType, $entity);
6986
}
7087

7188
/**
@@ -105,7 +122,7 @@ public function execute($entityType, $entityData, $arguments = [])
105122
$selects = [];
106123

107124
/** @var \Magento\Eav\Model\Entity\Attribute\AbstractAttribute $attribute */
108-
foreach ($this->getAttributes($entityType) as $attribute) {
125+
foreach ($this->getEntityAttributes($entityType, new DataObject($entityData)) as $attribute) {
109126
if (!$attribute->isStatic()) {
110127
$attributeTables[$attribute->getBackend()->getTable()][] = $attribute->getAttributeId();
111128
$attributesMap[$attribute->getAttributeId()] = $attribute->getAttributeCode();

app/code/Magento/Eav/Test/Unit/Model/ResourceModel/ReadHandlerTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,7 @@ public function testExecute($eavEntityType, $callNum, array $expected, $isStatic
110110
$attributeMock->method('getAttributeCode')
111111
->willReturn('attributeCode');
112112
$this->configMock->expects($this->exactly($callNum))
113-
->method('getAttributes')
113+
->method('getEntityAttributes')
114114
->willReturn([$attributeMock]);
115115
$this->assertEquals($expected, $this->readHandler->execute('entity_type', $entityData));
116116
}

app/code/Magento/Swatches/Model/Plugin/ProductImage.php

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ public function beforeGetImage(
6969
&& ($location == self::CATEGORY_PAGE_GRID_LOCATION || $location == self::CATEGORY_PAGE_LIST_LOCATION)) {
7070
$request = $this->request->getParams();
7171
if (is_array($request)) {
72-
$filterArray = $this->getFilterArray($request);
72+
$filterArray = $this->getFilterArray($request, $product);
7373
if (!empty($filterArray)) {
7474
$product = $this->loadSimpleVariation($product, $filterArray);
7575
}
@@ -99,16 +99,18 @@ protected function loadSimpleVariation(\Magento\Catalog\Model\Product $parentPro
9999
* Get filters from request
100100
*
101101
* @param array $request
102+
* @param \Magento\Catalog\Model\Product $product
102103
* @return array
103104
*/
104-
protected function getFilterArray(array $request)
105+
private function getFilterArray(array $request, \Magento\Catalog\Model\Product $product)
105106
{
106107
$filterArray = [];
107-
$attributeCodes = $this->eavConfig->getEntityAttributeCodes(\Magento\Catalog\Model\Product::ENTITY);
108+
$attributes = $this->eavConfig->getEntityAttributes(\Magento\Catalog\Model\Product::ENTITY, $product);
109+
108110
foreach ($request as $code => $value) {
109-
if (in_array($code, $attributeCodes)) {
110-
$attribute = $this->eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $code);
111-
if ($attribute->getId() && $this->canReplaceImageWithSwatch($attribute)) {
111+
if (isset($attributes[$code])) {
112+
$attribute = $attributes[$code];
113+
if ($this->canReplaceImageWithSwatch($attribute)) {
112114
$filterArray[$code] = $value;
113115
}
114116
}

app/code/Magento/Swatches/Test/Unit/Model/Plugin/ProductImageTest.php

Lines changed: 5 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,11 @@ public function testBeforeGetImage($expected)
7777
->method('getParams')
7878
->willReturn($expected['getParams']);
7979

80-
$this->getFilterArray($expected);
80+
$this->eavConfigMock
81+
->method('getEntityAttributes')
82+
->with('catalog_product')
83+
->willReturn(['color' => $this->attributeMock]);
84+
8185
$this->canReplaceImageWithSwatch($expected);
8286
$this->swatchesHelperMock
8387
->expects($this->exactly($expected['loadVariationByFallback_count']))
@@ -94,24 +98,6 @@ public function testBeforeGetImage($expected)
9498
$this->assertEquals([$this->productMock, $expected['page_handle'], []], $result);
9599
}
96100

97-
protected function getFilterArray($expected)
98-
{
99-
$this->eavConfigMock
100-
->method('getEntityAttributeCodes')
101-
->with('catalog_product')
102-
->willReturn($expected['attribute_codes_array']);
103-
104-
$this->eavConfigMock
105-
->method('getAttribute')
106-
->with('catalog_product', $expected['attribute_code'])
107-
->willReturn($this->attributeMock);
108-
109-
$this->attributeMock
110-
->expects($this->exactly($expected['getId_count']))
111-
->method('getId')
112-
->willReturn($expected['getId']);
113-
}
114-
115101
protected function canReplaceImageWithSwatch($expected)
116102
{
117103
$this->swatchesHelperMock
@@ -152,7 +138,6 @@ public function dataForTest()
152138
[
153139
'page_handle' => 'category_page_grid',
154140
'getParams' => ['color' => 31],
155-
'attribute_codes_array' => ['color'],
156141
'attribute_code' => 'color',
157142
'getId_count' => 1,
158143
'getId' => 332,
@@ -171,7 +156,6 @@ public function dataForTest()
171156
[
172157
'page_handle' => 'category_page_grid',
173158
'getParams' => ['color' => 31],
174-
'attribute_codes_array' => ['color'],
175159
'attribute_code' => 'color',
176160
'getId_count' => 1,
177161
'getId' => 332,
@@ -190,7 +174,6 @@ public function dataForTest()
190174
[
191175
'page_handle' => 'category_page_grid',
192176
'getParams' => ['color' => 31],
193-
'attribute_codes_array' => ['color'],
194177
'attribute_code' => 'color',
195178
'getId_count' => 1,
196179
'getId' => 332,

0 commit comments

Comments
 (0)