From 14fedb5b20cf724cdaba5a9f8dbf3b52e91f31ff Mon Sep 17 00:00:00 2001
From: Stanislav Lopukhov <slopukhov@magento.com>
Date: Wed, 4 Jul 2018 14:15:15 +0300
Subject: [PATCH] MAGETWO-93083: 2.2.5 Optimize retrieving product attributes

---
 app/code/Magento/Catalog/Model/Product.php         | 23 ++++++++++++++++++----
 .../Model/ResourceModel/ReadSnapshotPlugin.php     |  4 +++-
 .../Eav/Model/ResourceModel/ReadHandler.php        | 23 +++++++++++++++++++---
 .../Magento/Swatches/Model/Plugin/ProductImage.php | 14 +++++++------
 4 files changed, 50 insertions(+), 14 deletions(-)

diff --git a/app/code/Magento/Catalog/Model/Product.php b/app/code/Magento/Catalog/Model/Product.php
index 237ac96e56ec..24c3879d947b 100644
--- a/app/code/Magento/Catalog/Model/Product.php
+++ b/app/code/Magento/Catalog/Model/Product.php
@@ -12,6 +12,7 @@
 use Magento\Catalog\Model\Product\Attribute\Backend\Media\EntryConverterPool;
 use Magento\Framework\Api\AttributeValueFactory;
 use Magento\Framework\App\Filesystem\DirectoryList;
+use Magento\Framework\App\ObjectManager;
 use Magento\Framework\DataObject\IdentityInterface;
 use Magento\Framework\Pricing\SaleableInterface;
 
@@ -270,6 +271,7 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
 
     /**
      * @var \Magento\Catalog\Api\ProductAttributeRepositoryInterface
+     * @deprecated Not used anymore due to performance issue (loaded all product attributes)
      */
     protected $metadataService;
 
@@ -346,6 +348,11 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
      */
     protected $linkTypeProvider;
 
+    /**
+     * @var \Magento\Eav\Model\Config
+     */
+    private $eavConfig;
+
     /**
      * Product constructor.
      * @param \Magento\Framework\Model\Context $context
@@ -383,7 +390,7 @@ class Product extends \Magento\Catalog\Model\AbstractModel implements
      * @param \Magento\Framework\Api\DataObjectHelper $dataObjectHelper
      * @param \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $joinProcessor
      * @param array $data
-     *
+     * @param \Magento\Eav\Model\Config|null $config
      * @SuppressWarnings(PHPMD.ExcessiveParameterList)
      * @SuppressWarnings(PHPMD.UnusedFormalParameter)
      */
@@ -422,7 +429,8 @@ public function __construct(
         EntryConverterPool $mediaGalleryEntryConverterPool,
         \Magento\Framework\Api\DataObjectHelper $dataObjectHelper,
         \Magento\Framework\Api\ExtensionAttribute\JoinProcessorInterface $joinProcessor,
-        array $data = []
+        array $data = [],
+        \Magento\Eav\Model\Config $config = null
     ) {
         $this->metadataService = $metadataService;
         $this->_itemOptionFactory = $itemOptionFactory;
@@ -461,6 +469,7 @@ public function __construct(
             $resourceCollection,
             $data
         );
+        $this->eavConfig = $config ?? ObjectManager::getInstance()->get(\Magento\Eav\Model\Config::class);
     }
 
     /**
@@ -474,12 +483,18 @@ protected function _construct()
     }
 
     /**
-     * {@inheritdoc}
+     * Get a list of custom attribute codes that belongs to product attribute set. If attribute set not specified for
+     * product will return all attribute codes
+     *
+     * @return string[]
      */
     protected function getCustomAttributesCodes()
     {
         if ($this->customAttributesCodes === null) {
-            $this->customAttributesCodes = $this->getEavAttributesCodes($this->metadataService);
+            $this->customAttributesCodes = array_keys($this->eavConfig->getEntityAttributes(
+                self::ENTITY,
+                $this
+            ));
             $this->customAttributesCodes = array_diff($this->customAttributesCodes, $this->interfaceAttributes);
         }
         return $this->customAttributesCodes;
diff --git a/app/code/Magento/Catalog/Plugin/Model/ResourceModel/ReadSnapshotPlugin.php b/app/code/Magento/Catalog/Plugin/Model/ResourceModel/ReadSnapshotPlugin.php
index 4dae4ec68efa..ff4d2f93c912 100644
--- a/app/code/Magento/Catalog/Plugin/Model/ResourceModel/ReadSnapshotPlugin.php
+++ b/app/code/Magento/Catalog/Plugin/Model/ResourceModel/ReadSnapshotPlugin.php
@@ -58,7 +58,9 @@ public function afterExecute(ReadSnapshot $subject, array $entityData, $entityTy
         $globalAttributes = [];
         $attributesMap = [];
         $eavEntityType = $metadata->getEavEntityType();
-        $attributes = (null === $eavEntityType) ? [] : $this->config->getEntityAttributes($eavEntityType);
+        $attributes = null === $eavEntityType
+            ? []
+            : $this->config->getEntityAttributes($eavEntityType, new \Magento\Framework\DataObject($entityData));
 
         /** @var \Magento\Eav\Model\Entity\Attribute\AbstractAttribute $attribute */
         foreach ($attributes as $attribute) {
diff --git a/app/code/Magento/Eav/Model/ResourceModel/ReadHandler.php b/app/code/Magento/Eav/Model/ResourceModel/ReadHandler.php
index 9febea9b7b2b..cd2fe7477ca6 100644
--- a/app/code/Magento/Eav/Model/ResourceModel/ReadHandler.php
+++ b/app/code/Magento/Eav/Model/ResourceModel/ReadHandler.php
@@ -5,6 +5,7 @@
  */
 namespace Magento\Eav\Model\ResourceModel;
 
+use Magento\Framework\DataObject;
 use Magento\Framework\EntityManager\MetadataPool;
 use Magento\Framework\EntityManager\Operation\AttributeInterface;
 use Magento\Framework\Model\Entity\ScopeInterface;
@@ -59,13 +60,29 @@ public function __construct(
      * @param string $entityType
      * @return \Magento\Eav\Api\Data\AttributeInterface[]
      * @throws \Exception if for unknown entity type
+     * @deprecated Not used anymore
+     * @see ReadHandler::getEntityAttributes
      */
     protected function getAttributes($entityType)
     {
         $metadata = $this->metadataPool->getMetadata($entityType);
         $eavEntityType = $metadata->getEavEntityType();
-        $attributes = (null === $eavEntityType) ? [] : $this->config->getAttributes($eavEntityType);
-        return $attributes;
+        return null === $eavEntityType ? [] : $this->config->getEntityAttributes($eavEntityType);
+    }
+
+    /**
+     * Get attribute of given entity type
+     *
+     * @param string $entityType
+     * @param DataObject $entity
+     * @return \Magento\Eav\Api\Data\AttributeInterface[]
+     * @throws \Exception if for unknown entity type
+     */
+    private function getEntityAttributes(string $entityType, DataObject $entity): array
+    {
+        $metadata = $this->metadataPool->getMetadata($entityType);
+        $eavEntityType = $metadata->getEavEntityType();
+        return null === $eavEntityType ? [] : $this->config->getEntityAttributes($eavEntityType, $entity);
     }
 
     /**
@@ -105,7 +122,7 @@ public function execute($entityType, $entityData, $arguments = [])
         $selects = [];
 
         /** @var \Magento\Eav\Model\Entity\Attribute\AbstractAttribute $attribute */
-        foreach ($this->getAttributes($entityType) as $attribute) {
+        foreach ($this->getEntityAttributes($entityType, new DataObject($entityData)) as $attribute) {
             if (!$attribute->isStatic()) {
                 $attributeTables[$attribute->getBackend()->getTable()][] = $attribute->getAttributeId();
                 $attributesMap[$attribute->getAttributeId()] = $attribute->getAttributeCode();
diff --git a/app/code/Magento/Swatches/Model/Plugin/ProductImage.php b/app/code/Magento/Swatches/Model/Plugin/ProductImage.php
index 0569a249260f..4b9dbab7faaa 100644
--- a/app/code/Magento/Swatches/Model/Plugin/ProductImage.php
+++ b/app/code/Magento/Swatches/Model/Plugin/ProductImage.php
@@ -69,7 +69,7 @@ public function beforeGetImage(
             && ($location == self::CATEGORY_PAGE_GRID_LOCATION || $location == self::CATEGORY_PAGE_LIST_LOCATION)) {
             $request = $this->request->getParams();
             if (is_array($request)) {
-                $filterArray = $this->getFilterArray($request);
+                $filterArray = $this->getFilterArray($request, $product);
                 if (!empty($filterArray)) {
                     $product = $this->loadSimpleVariation($product, $filterArray);
                 }
@@ -99,16 +99,18 @@ protected function loadSimpleVariation(\Magento\Catalog\Model\Product $parentPro
      * Get filters from request
      *
      * @param array $request
+     * @param \Magento\Catalog\Model\Product $product
      * @return array
      */
-    protected function getFilterArray(array $request)
+    private function getFilterArray(array $request, \Magento\Catalog\Model\Product $product)
     {
         $filterArray = [];
-        $attributeCodes = $this->eavConfig->getEntityAttributeCodes(\Magento\Catalog\Model\Product::ENTITY);
+        $attributes = $this->eavConfig->getEntityAttributes(\Magento\Catalog\Model\Product::ENTITY, $product);
+
         foreach ($request as $code => $value) {
-            if (in_array($code, $attributeCodes)) {
-                $attribute = $this->eavConfig->getAttribute(\Magento\Catalog\Model\Product::ENTITY, $code);
-                if ($attribute->getId() && $this->canReplaceImageWithSwatch($attribute)) {
+            if (isset($attributes[$code])) {
+                $attribute = $attributes[$code];
+                if ($this->canReplaceImageWithSwatch($attribute)) {
                     $filterArray[$code] = $value;
                 }
             }
