Skip to content

Commit 436d046

Browse files
author
Yaroslav Onischenko
authored
Merge pull request #989 from magento-dragons/perf-pr-swatches
[Dragons][Performance] Swatches
2 parents ae331f5 + 3e3f7fc commit 436d046

29 files changed

+1238
-92
lines changed

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

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@
1818
use Magento\Swatches\Helper\Data as SwatchData;
1919
use Magento\Swatches\Helper\Media;
2020
use Magento\Swatches\Model\Swatch;
21+
use Magento\Framework\App\ObjectManager;
22+
use Magento\Swatches\Model\SwatchAttributesProvider;
2123

2224
/**
2325
* Swatch renderer block
@@ -60,10 +62,17 @@ class Configurable extends \Magento\ConfigurableProduct\Block\Product\View\Type\
6062
/**
6163
* Indicate if product has one or more Swatch attributes
6264
*
65+
* @deprecated unused
66+
*
6367
* @var boolean
6468
*/
6569
protected $isProductHasSwatchAttribute;
6670

71+
/**
72+
* @var SwatchAttributesProvider
73+
*/
74+
private $swatchAttributesProvider;
75+
6776
/**
6877
* @param Context $context
6978
* @param ArrayUtils $arrayUtils
@@ -76,6 +85,7 @@ class Configurable extends \Magento\ConfigurableProduct\Block\Product\View\Type\
7685
* @param SwatchData $swatchHelper
7786
* @param Media $swatchMediaHelper
7887
* @param array $data other data
88+
* @param SwatchAttributesProvider $swatchAttributesProvider
7989
* @SuppressWarnings(PHPMD.ExcessiveParameterList)
8090
*/
8191
public function __construct(
@@ -89,11 +99,13 @@ public function __construct(
8999
ConfigurableAttributeData $configurableAttributeData,
90100
SwatchData $swatchHelper,
91101
Media $swatchMediaHelper,
92-
array $data = []
102+
array $data = [],
103+
SwatchAttributesProvider $swatchAttributesProvider = null
93104
) {
94105
$this->swatchHelper = $swatchHelper;
95106
$this->swatchMediaHelper = $swatchMediaHelper;
96-
107+
$this->swatchAttributesProvider = $swatchAttributesProvider
108+
?: ObjectManager::getInstance()->get(SwatchAttributesProvider::class);
97109
parent::__construct(
98110
$context,
99111
$arrayUtils,
@@ -201,6 +213,8 @@ protected function getSwatchAttributesData()
201213
}
202214

203215
/**
216+
* @deprecated Method isProductHasSwatchAttribute() is used instead of this.
217+
*
204218
* @codeCoverageIgnore
205219
* @return void
206220
*/
@@ -209,6 +223,17 @@ protected function initIsProductHasSwatchAttribute()
209223
$this->isProductHasSwatchAttribute = $this->swatchHelper->isProductHasSwatch($this->getProduct());
210224
}
211225

226+
/**
227+
* Check that product has at least one swatch attribute
228+
*
229+
* @return bool
230+
*/
231+
protected function isProductHasSwatchAttribute()
232+
{
233+
$swatchAttributes = $this->swatchAttributesProvider->provide($this->getProduct());
234+
return count($swatchAttributes) > 0;
235+
}
236+
212237
/**
213238
* Add Swatch Data for attribute
214239
*
@@ -371,7 +396,6 @@ protected function getConfigurableOptionsIds(array $attributeData)
371396
*/
372397
public function toHtml()
373398
{
374-
$this->initIsProductHasSwatchAttribute();
375399
$this->setTemplate(
376400
$this->getRendererTemplate()
377401
);
@@ -391,12 +415,15 @@ protected function _toHtml()
391415
}
392416

393417
/**
394-
* @codeCoverageIgnore
418+
* Return renderer template
419+
*
420+
* Template for product with swatches is different from product without swatches
421+
*
395422
* @return string
396423
*/
397424
protected function getRendererTemplate()
398425
{
399-
return $this->isProductHasSwatchAttribute ?
426+
return $this->isProductHasSwatchAttribute() ?
400427
self::SWATCH_RENDERER_TEMPLATE : self::CONFIGURABLE_RENDERER_TEMPLATE;
401428
}
402429

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

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,6 @@
77

88
/**
99
* Swatch renderer block in Category page
10-
*
11-
* @codeCoverageIgnore
12-
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
1310
*/
1411
class Configurable extends \Magento\Swatches\Block\Product\Renderer\Configurable
1512
{
@@ -27,7 +24,7 @@ protected function getRendererTemplate()
2724
protected function getHtmlOutput()
2825
{
2926
$output = '';
30-
if ($this->isProductHasSwatchAttribute) {
27+
if ($this->isProductHasSwatchAttribute()) {
3128
$output = parent::getHtmlOutput();
3229
}
3330

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

Lines changed: 15 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
use Magento\Store\Model\StoreManagerInterface;
1919
use Magento\Swatches\Model\ResourceModel\Swatch\CollectionFactory as SwatchCollectionFactory;
2020
use Magento\Swatches\Model\Swatch;
21+
use Magento\Swatches\Model\SwatchAttributesProvider;
2122

2223
/**
2324
* Class Helper Data
@@ -70,6 +71,11 @@ class Data
7071
*/
7172
private $metadataPool;
7273

74+
/**
75+
* @var SwatchAttributesProvider
76+
*/
77+
private $swatchAttributesProvider;
78+
7379
/**
7480
* Data key which should populated to Attribute entity from "additional_data" field
7581
*
@@ -95,21 +101,25 @@ class Data
95101
* @param SwatchCollectionFactory $swatchCollectionFactory
96102
* @param Image $imageHelper
97103
* @param Json|null $serializer
104+
* @param SwatchAttributesProvider $swatchAttributesProvider
98105
*/
99106
public function __construct(
100107
CollectionFactory $productCollectionFactory,
101108
ProductRepositoryInterface $productRepository,
102109
StoreManagerInterface $storeManager,
103110
SwatchCollectionFactory $swatchCollectionFactory,
104111
Image $imageHelper,
105-
Json $serializer = null
112+
Json $serializer = null,
113+
SwatchAttributesProvider $swatchAttributesProvider = null
106114
) {
107115
$this->productCollectionFactory = $productCollectionFactory;
108116
$this->productRepository = $productRepository;
109117
$this->storeManager = $storeManager;
110118
$this->swatchCollectionFactory = $swatchCollectionFactory;
111119
$this->imageHelper = $imageHelper;
112120
$this->serializer = $serializer ?: ObjectManager::getInstance()->create(Json::class);
121+
$this->swatchAttributesProvider = $swatchAttributesProvider
122+
?: ObjectManager::getInstance()->get(SwatchAttributesProvider::class);
113123
}
114124

115125
/**
@@ -358,14 +368,8 @@ private function getAllSizeImages(ModelProduct $product, $imageFile)
358368
*/
359369
private function getSwatchAttributes(Product $product)
360370
{
361-
$attributes = $this->getAttributesFromConfigurable($product);
362-
$result = [];
363-
foreach ($attributes as $attribute) {
364-
if ($this->isSwatchAttribute($attribute)) {
365-
$result[] = $attribute;
366-
}
367-
}
368-
return $result;
371+
$swatchAttributes = $this->swatchAttributesProvider->provide($product);
372+
return $swatchAttributes;
369373
}
370374

371375
/**
@@ -470,7 +474,8 @@ private function addFallbackOptions(array $fallbackValues, array $swatches)
470474
*/
471475
public function isProductHasSwatch(Product $product)
472476
{
473-
return sizeof($this->getSwatchAttributes($product));
477+
$swatchAttributes = $this->getSwatchAttributes($product);
478+
return count($swatchAttributes) > 0;
474479
}
475480

476481
/**
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Swatches\Model;
7+
8+
use Magento\Framework\App\CacheInterface;
9+
use Magento\Framework\App\ResourceConnection;
10+
use Magento\Framework\DB\Select;
11+
12+
/**
13+
* Class SwatchAttributeCodes for getting codes of swatch attributes.
14+
*/
15+
class SwatchAttributeCodes
16+
{
17+
/**
18+
* @var string
19+
*/
20+
private $cacheKey;
21+
22+
/**
23+
* @var CacheInterface
24+
*/
25+
private $cache;
26+
27+
/**
28+
* @var ResourceConnection
29+
*/
30+
private $resourceConnection;
31+
32+
/**
33+
* Key is attribute_id, value is attribute_code
34+
*
35+
* @var array
36+
*/
37+
private $swatchAttributeCodes;
38+
39+
/**
40+
* @var array
41+
*/
42+
private $cacheTags;
43+
44+
/**
45+
* SwatchAttributeList constructor.
46+
*
47+
* @param CacheInterface $cache
48+
* @param ResourceConnection $resourceConnection
49+
* @param string $cacheKey
50+
* @param array $cacheTags
51+
*/
52+
public function __construct(
53+
CacheInterface $cache,
54+
ResourceConnection $resourceConnection,
55+
$cacheKey,
56+
array $cacheTags
57+
) {
58+
$this->cache = $cache;
59+
$this->resourceConnection = $resourceConnection;
60+
$this->cacheKey = $cacheKey;
61+
$this->cacheTags = $cacheTags;
62+
}
63+
64+
/**
65+
* Returns list of known swatch attribute codes. Check cache and database.
66+
* Key is attribute_id, value is attribute_code
67+
*
68+
* @return array
69+
*/
70+
public function getCodes()
71+
{
72+
if ($this->swatchAttributeCodes === null) {
73+
$swatchAttributeCodesCache = $this->cache->load($this->cacheKey);
74+
if (false === $swatchAttributeCodesCache) {
75+
$swatchAttributeCodes = $this->getSwatchAttributeCodes();
76+
$this->cache->save(json_encode($swatchAttributeCodes), $this->cacheKey, $this->cacheTags);
77+
} else {
78+
$swatchAttributeCodes = json_decode($swatchAttributeCodesCache, true);
79+
}
80+
$this->swatchAttributeCodes = $swatchAttributeCodes;
81+
}
82+
83+
return $this->swatchAttributeCodes;
84+
}
85+
86+
/**
87+
* Returns list of known swatch attributes.
88+
*
89+
* Returns a map of id and code for all EAV attributes with swatches
90+
*
91+
* @return array
92+
*/
93+
private function getSwatchAttributeCodes()
94+
{
95+
$select = $this->resourceConnection->getConnection()->select()
96+
->from(
97+
['a' => $this->resourceConnection->getTableName('eav_attribute')],
98+
[
99+
'attribute_id' => 'a.attribute_id',
100+
'attribute_code' => 'a.attribute_code',
101+
]
102+
)->where(
103+
'a.attribute_id IN (?)',
104+
new \Zend_Db_Expr($this->getAttributeIdsSelect())
105+
);
106+
$result = $this->resourceConnection->getConnection()->fetchPairs($select);
107+
return $result;
108+
}
109+
110+
/**
111+
* Returns Select for attributes Ids.
112+
*
113+
* Builds a "Select" object which loads all EAV attributes that has "swatch" options
114+
*
115+
* @return Select
116+
*/
117+
private function getAttributeIdsSelect()
118+
{
119+
return $this->resourceConnection->getConnection()->select()
120+
->from(
121+
['o' => $this->resourceConnection->getTableName('eav_attribute_option')],
122+
['attribute_id' => 'o.attribute_id']
123+
)->join(
124+
['s' => $this->resourceConnection->getTableName('eav_attribute_option_swatch')],
125+
'o.option_id = s.option_id',
126+
[]
127+
);
128+
}
129+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Swatches\Model;
7+
8+
use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
9+
use Magento\Catalog\Model\Product;
10+
use Magento\ConfigurableProduct\Model\Product\Type\Configurable\Attribute;
11+
12+
/**
13+
* Provide list of swatch attributes for product.
14+
*/
15+
class SwatchAttributesProvider
16+
{
17+
/**
18+
* @var Configurable
19+
*/
20+
private $typeConfigurable;
21+
22+
/**
23+
* @var SwatchAttributeCodes
24+
*/
25+
private $swatchAttributeCodes;
26+
27+
/**
28+
* Key is productId, value is list of attributes
29+
* @var Attribute[]
30+
*/
31+
private $attributesPerProduct;
32+
33+
/**
34+
* @param Configurable $typeConfigurable
35+
* @param SwatchAttributeCodes $swatchAttributeCodes
36+
*/
37+
public function __construct(
38+
Configurable $typeConfigurable,
39+
SwatchAttributeCodes $swatchAttributeCodes
40+
) {
41+
$this->typeConfigurable = $typeConfigurable;
42+
$this->swatchAttributeCodes = $swatchAttributeCodes;
43+
}
44+
45+
/**
46+
* Provide list of swatch attributes for product. If product is not configurable return empty array
47+
* Key is productId, value is list of attributes
48+
*
49+
* @param Product $product
50+
* @return Attribute[]
51+
*/
52+
public function provide(Product $product)
53+
{
54+
if ($product->getTypeId() !== Configurable::TYPE_CODE) {
55+
return [];
56+
}
57+
if (!isset($this->attributesPerProduct[$product->getId()])) {
58+
$configurableAttributes = $this->typeConfigurable->getConfigurableAttributes($product);
59+
$swatchAttributeCodeMap = $this->swatchAttributeCodes->getCodes();
60+
61+
$swatchAttributes = [];
62+
foreach ($configurableAttributes as $configurableAttribute) {
63+
if (array_key_exists($configurableAttribute->getAttributeId(), $swatchAttributeCodeMap)) {
64+
$swatchAttributes[$configurableAttribute->getAttributeId()]
65+
= $configurableAttribute->getProductAttribute();
66+
}
67+
}
68+
$this->attributesPerProduct[$product->getId()] = $swatchAttributes;
69+
}
70+
return $this->attributesPerProduct[$product->getId()];
71+
}
72+
}

0 commit comments

Comments
 (0)