From 1a857a3e3bba510db6f20ea7e122e19b37f4d3bc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Bart=C5=82omiej=20Szubert?= <bartlomiejszubert@gmail.com>
Date: Fri, 25 Sep 2020 23:31:17 +0200
Subject: [PATCH] Fix #29063 - Create configurations: not all configurable
 attributes are visible

---
 .../Model/ConfigurableAttributeHandler.php    |  45 ++++--
 .../Model/SuggestedAttributeList.php          |  15 +-
 .../Unit/Model/SuggestedAttributeListTest.php | 131 ------------------
 .../Ui/DataProvider/Attributes.php            |  18 +--
 4 files changed, 47 insertions(+), 162 deletions(-)
 delete mode 100644 app/code/Magento/ConfigurableProduct/Test/Unit/Model/SuggestedAttributeListTest.php

diff --git a/app/code/Magento/ConfigurableProduct/Model/ConfigurableAttributeHandler.php b/app/code/Magento/ConfigurableProduct/Model/ConfigurableAttributeHandler.php
index f9f00289de6a6..e906664cc66d3 100644
--- a/app/code/Magento/ConfigurableProduct/Model/ConfigurableAttributeHandler.php
+++ b/app/code/Magento/ConfigurableProduct/Model/ConfigurableAttributeHandler.php
@@ -3,22 +3,28 @@
  * Copyright © Magento, Inc. All rights reserved.
  * See COPYING.txt for license details.
  */
+declare(strict_types=1);
+
 namespace Magento\ConfigurableProduct\Model;
 
+use Magento\Catalog\Model\Product\Type;
+use Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory;
+use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
+use Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface;
+use Zend_Db_Expr;
+
 class ConfigurableAttributeHandler
 {
     /**
-     * Attribute collection factory
-     *
-     * @var \Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory
+     * @var CollectionFactory
      */
     protected $collectionFactory;
 
     /**
-     * @param \Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory $attributeColFactory
+     * @param CollectionFactory $attributeColFactory
      */
     public function __construct(
-        \Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory $attributeColFactory
+        CollectionFactory $attributeColFactory
     ) {
         $this->collectionFactory = $attributeColFactory;
     }
@@ -32,7 +38,7 @@ public function getApplicableAttributes()
     {
         /** @var $collection \Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection */
         $collection = $this->collectionFactory->create();
-        return $collection->addFieldToFilter(
+        $collection->addFieldToFilter(
             'frontend_input',
             'select'
         )->addFieldToFilter(
@@ -40,20 +46,39 @@ public function getApplicableAttributes()
             1
         )->addFieldToFilter(
             'is_global',
-            \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_GLOBAL
+            ScopedAttributeInterface::SCOPE_GLOBAL
         );
+
+        $types = [
+            Type::TYPE_SIMPLE,
+            Type::TYPE_VIRTUAL,
+            Configurable::TYPE_CODE,
+        ];
+        $applyToArr = [];
+        foreach ($types as $type) {
+            $applyToArr[] = "apply_to REGEXP '(^|(.*,))$type($|,.*)'";
+        }
+        $whereExprStr = 'apply_to IS NULL OR (' . implode(' AND ', $applyToArr) . ')';
+
+        $collection->getSelect()->where(new Zend_Db_Expr($whereExprStr));
+
+        return $collection;
     }
 
     /**
+     * Check if attribute is applicable for configurable products
+     * @deprecated is applicable check is added to collection query
+     * @see \Magento\ConfigurableProduct\Model\ConfigurableAttributeHandler::getApplicableAttributes
      * @param \Magento\Catalog\Api\Data\ProductAttributeInterface $attribute
+     *
      * @return bool
      */
     public function isAttributeApplicable($attribute)
     {
         $types = [
-            \Magento\Catalog\Model\Product\Type::TYPE_SIMPLE,
-            \Magento\Catalog\Model\Product\Type::TYPE_VIRTUAL,
-            \Magento\ConfigurableProduct\Model\Product\Type\Configurable::TYPE_CODE,
+            Type::TYPE_SIMPLE,
+            Type::TYPE_VIRTUAL,
+            Configurable::TYPE_CODE,
         ];
         return !$attribute->getApplyTo() || count(array_diff($types, $attribute->getApplyTo())) === 0;
     }
diff --git a/app/code/Magento/ConfigurableProduct/Model/SuggestedAttributeList.php b/app/code/Magento/ConfigurableProduct/Model/SuggestedAttributeList.php
index d13ba61ef1418..1ace35a49eeb3 100644
--- a/app/code/Magento/ConfigurableProduct/Model/SuggestedAttributeList.php
+++ b/app/code/Magento/ConfigurableProduct/Model/SuggestedAttributeList.php
@@ -50,15 +50,14 @@ public function getSuggestedAttributes($labelPart)
         $result = [];
         foreach ($collection->getItems() as $id => $attribute) {
             /** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */
-            if ($this->configurableAttributeHandler->isAttributeApplicable($attribute)) {
-                $result[$id] = [
-                    'id' => $attribute->getId(),
-                    'label' => $attribute->getFrontendLabel(),
-                    'code' => $attribute->getAttributeCode(),
-                    'options' => $attribute->getSource()->getAllOptions(false),
-                ];
-            }
+            $result[$id] = [
+                'id' => $attribute->getId(),
+                'label' => $attribute->getFrontendLabel(),
+                'code' => $attribute->getAttributeCode(),
+                'options' => $attribute->getSource()->getAllOptions(false),
+            ];
         }
+
         return $result;
     }
 }
diff --git a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/SuggestedAttributeListTest.php b/app/code/Magento/ConfigurableProduct/Test/Unit/Model/SuggestedAttributeListTest.php
deleted file mode 100644
index 1479c9bc03f18..0000000000000
--- a/app/code/Magento/ConfigurableProduct/Test/Unit/Model/SuggestedAttributeListTest.php
+++ /dev/null
@@ -1,131 +0,0 @@
-<?php
-/**
- * Copyright © Magento, Inc. All rights reserved.
- * See COPYING.txt for license details.
- */
-declare(strict_types=1);
-
-namespace Magento\ConfigurableProduct\Test\Unit\Model;
-
-use Magento\Catalog\Model\ResourceModel\Eav\Attribute;
-use Magento\Catalog\Model\ResourceModel\Helper;
-use Magento\Catalog\Model\ResourceModel\Product\Attribute\Collection;
-use Magento\ConfigurableProduct\Model\ConfigurableAttributeHandler;
-use Magento\ConfigurableProduct\Model\SuggestedAttributeList;
-use Magento\Eav\Model\Entity\Attribute\Source\AbstractSource;
-use PHPUnit\Framework\MockObject\MockObject;
-use PHPUnit\Framework\TestCase;
-
-class SuggestedAttributeListTest extends TestCase
-{
-    /**
-     * @var SuggestedAttributeList
-     */
-    protected $suggestedListModel;
-
-    /**
-     * @var ConfigurableAttributeHandler|MockObject
-     */
-    protected $configurableAttributeHandler;
-
-    /**
-     * @var MockObject
-     */
-    protected $resourceHelperMock;
-
-    /**
-     * @var MockObject
-     */
-    protected $collectionMock;
-
-    /**
-     * @var MockObject
-     */
-    protected $attributeMock;
-
-    /**
-     * @var string
-     */
-    protected $labelPart = 'labelPart';
-
-    protected function setUp(): void
-    {
-        $this->configurableAttributeHandler = $this->createMock(
-            ConfigurableAttributeHandler::class
-        );
-        $this->resourceHelperMock = $this->createMock(Helper::class);
-        $this->collectionMock = $this->createMock(
-            Collection::class
-        );
-        $this->resourceHelperMock->expects(
-            $this->once()
-        )->method(
-            'addLikeEscape'
-        )->with(
-            $this->labelPart,
-            ['position' => 'any']
-        )->willReturn(
-            $this->labelPart
-        );
-        $this->configurableAttributeHandler->expects(
-            $this->once()
-        )->method(
-            'getApplicableAttributes'
-        )->willReturn(
-            $this->collectionMock
-        );
-        $valueMap = [
-            ['frontend_label', ['like' => $this->labelPart], $this->collectionMock],
-        ];
-        $this->collectionMock->expects(
-            $this->any()
-        )->method(
-            'addFieldToFilter'
-        )->willReturnMap(
-            $valueMap
-        );
-        $this->attributeMock = $this->getMockBuilder(Attribute::class)
-            ->addMethods(['getFrontendLabel'])
-            ->onlyMethods(['getId', 'getAttributeCode', 'getSource'])
-            ->disableOriginalConstructor()
-            ->getMock();
-        $this->collectionMock->expects(
-            $this->once()
-        )->method(
-            'getItems'
-        )->willReturn(
-            ['id' => $this->attributeMock]
-        );
-        $this->suggestedListModel = new SuggestedAttributeList(
-            $this->configurableAttributeHandler,
-            $this->resourceHelperMock
-        );
-    }
-
-    public function testGetSuggestedAttributesIfTheyApplicable()
-    {
-        $source = $this->createMock(AbstractSource::class);
-        $result['id'] = ['id' => 'id', 'label' => 'label', 'code' => 'code', 'options' => 'options'];
-        $this->attributeMock->expects($this->once())->method('getId')->willReturn('id');
-        $this->attributeMock->expects($this->once())->method('getFrontendLabel')->willReturn('label');
-        $this->attributeMock->expects($this->once())->method('getAttributeCode')->willReturn('code');
-        $this->attributeMock->expects($this->once())->method('getSource')->willReturn($source);
-        $source->expects($this->once())->method('getAllOptions')->with(false)->willReturn('options');
-        $this->configurableAttributeHandler->expects($this->once())->method('isAttributeApplicable')
-            ->with($this->attributeMock)->willReturn(true);
-
-        $this->assertEquals($result, $this->suggestedListModel->getSuggestedAttributes($this->labelPart));
-    }
-
-    public function testGetSuggestedAttributesIfTheyNotApplicable()
-    {
-        $this->attributeMock->expects($this->never())->method('getId');
-        $this->attributeMock->expects($this->never())->method('getFrontendLabel');
-        $this->attributeMock->expects($this->never())->method('getAttributeCode');
-        $this->attributeMock->expects($this->never())->method('getSource');
-        $this->configurableAttributeHandler->expects($this->once())->method('isAttributeApplicable')
-            ->with($this->attributeMock)->willReturn(false);
-
-        $this->assertEquals([], $this->suggestedListModel->getSuggestedAttributes($this->labelPart));
-    }
-}
diff --git a/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Attributes.php b/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Attributes.php
index 04db96bc158d1..877433768f434 100644
--- a/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Attributes.php
+++ b/app/code/Magento/ConfigurableProduct/Ui/DataProvider/Attributes.php
@@ -43,22 +43,14 @@ public function getCollection()
     }
 
     /**
-     * {@inheritdoc}
+     * @inheritDoc
      */
     public function getData()
     {
-        $items = [];
-        $skippedItems = 0;
-        foreach ($this->getCollection()->getItems() as $attribute) {
-            if ($this->configurableAttributeHandler->isAttributeApplicable($attribute)) {
-                $items[] = $attribute->toArray();
-            } else {
-                $skippedItems++;
-            }
+        if (!$this->getCollection()->isLoaded()) {
+            $this->getCollection()->load();
         }
-        return [
-            'totalRecords' => $this->collection->getSize() - $skippedItems,
-            'items' => $items
-        ];
+
+        return $this->getCollection()->toArray();
     }
 }