Skip to content

Commit

Permalink
Merge pull request #542 from magento-okapis/MAGETWO-58348-Cannot-crea…
Browse files Browse the repository at this point in the history
…te-configurable-product-with-child-by-REST-API-2.2

Fixed issue:
 - MAGETWO-58348 Cannot create configurable product with child by REST API
  • Loading branch information
Oleksii Korshenko authored Oct 31, 2016
2 parents d0688f2 + 6ae1fce commit 8bde202
Show file tree
Hide file tree
Showing 5 changed files with 357 additions and 24 deletions.
13 changes: 12 additions & 1 deletion app/code/Magento/Catalog/Model/Product.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
* @method Product setHasError(bool $value)
* @method \Magento\Catalog\Model\ResourceModel\Product getResource()
* @method null|bool getHasError()
* @method Product setAssociatedProductIds(array $productIds)
* @method array getAssociatedProductIds()
* @method Product setNewVariationsAttributeSetId(int $value)
* @method int getNewVariationsAttributeSetId()
Expand Down Expand Up @@ -2614,4 +2613,16 @@ private function getMediaGalleryProcessor()
}
return $this->mediaGalleryProcessor;
}

/**
* Set the associated products
*
* @param array $productIds
* @return $this
*/
public function setAssociatedProductIds(array $productIds)
{
$this->getExtensionAttributes()->setConfigurableProductLinks($productIds);
return $this;
}
}
101 changes: 99 additions & 2 deletions app/code/Magento/ConfigurableProduct/Model/LinkManagement.php
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,16 @@ class LinkManagement implements \Magento\ConfigurableProduct\Api\LinkManagementI
*/
private $dataObjectHelper;

/**
* @var \Magento\ConfigurableProduct\Helper\Product\Options\Factory;
*/
private $optionsFactory;

/**
* @var \Magento\Catalog\Model\ResourceModel\Eav\AttributeFactory
*/
private $attributeFactory;

/**
* @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository
* @param \Magento\Catalog\Api\Data\ProductInterfaceFactory $productFactory
Expand Down Expand Up @@ -102,9 +112,28 @@ public function addChild($sku, $childSku)
throw new StateException(__('Product has been already attached'));
}

$configurableProductOptions = $product->getExtensionAttributes()->getConfigurableProductOptions();
if (empty($configurableProductOptions)) {
throw new StateException(__('Parent product does not have configurable product options'));
}

$attributeIds = [];
foreach ($configurableProductOptions as $configurableProductOption) {
$attributeCode = $configurableProductOption->getProductAttribute()->getAttributeCode();
if (!$child->getData($attributeCode)) {
throw new StateException(__('Child product does not have attribute value %1', $attributeCode));
}
$attributeIds[] = $configurableProductOption->getAttributeId();
}
$configurableOptionData = $this->getConfigurableAttributesData($attributeIds);

/** @var \Magento\ConfigurableProduct\Helper\Product\Options\Factory $optionFactory */
$optionFactory = $this->getOptionsFactory();
$options = $optionFactory->create($configurableOptionData);
$childrenIds[] = $child->getId();
$product->getExtensionAttributes()->setConfigurableProductOptions($options);
$product->getExtensionAttributes()->setConfigurableProductLinks($childrenIds);
$product->save();
$this->productRepository->save($product);
return true;
}

Expand Down Expand Up @@ -133,7 +162,75 @@ public function removeChild($sku, $childSku)
throw new NoSuchEntityException(__('Requested option doesn\'t exist'));
}
$product->getExtensionAttributes()->setConfigurableProductLinks($ids);
$product->save();
$this->productRepository->save($product);
return true;
}

/**
* Get Options Factory
*
* @return \Magento\ConfigurableProduct\Helper\Product\Options\Factory
*
* @deprecated
*/
private function getOptionsFactory()
{
if (!$this->optionsFactory) {
$this->optionsFactory = \Magento\Framework\App\ObjectManager::getInstance()
->get(\Magento\ConfigurableProduct\Helper\Product\Options\Factory::class);
}
return $this->optionsFactory;
}

/**
* Get Attribute Factory
*
* @return \Magento\Catalog\Model\ResourceModel\Eav\AttributeFactory
*
* @deprecated
*/
private function getAttributeFactory()
{
if (!$this->attributeFactory) {
$this->attributeFactory = \Magento\Framework\App\ObjectManager::getInstance()
->get(\Magento\Catalog\Model\ResourceModel\Eav\AttributeFactory::class);
}
return $this->attributeFactory;
}

/**
* Get Configurable Attribute Data
*
* @param int[] $attributeIds
* @return array
*/
private function getConfigurableAttributesData($attributeIds)
{
$configurableAttributesData = [];
$attributeValues = [];
$attributes = $this->getAttributeFactory()->create()
->getCollection()
->addFieldToFilter('attribute_id', $attributeIds)
->getItems();
foreach ($attributes as $attribute) {
foreach ($attribute->getOptions() as $option) {
if ($option->getValue()) {
$attributeValues[] = [
'label' => $option->getLabel(),
'attribute_id' => $attribute->getId(),
'value_index' => $option->getValue(),
];
}
}
$configurableAttributesData[] =
[
'attribute_id' => $attribute->getId(),
'code' => $attribute->getAttributeCode(),
'label' => $attribute->getStoreLabel(),
'values' => $attributeValues,
];
}

return $configurableAttributesData;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@

namespace Magento\ConfigurableProduct\Test\Unit\Model;

use Magento\ConfigurableProduct\Model\LinkManagement;
use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
use Magento\ConfigurableProduct\Test\Unit\Model\Product\ProductExtensionAttributes;

/**
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
Expand Down Expand Up @@ -146,15 +146,59 @@ public function testAddChild()

$configurable = $this->getMockBuilder(\Magento\Catalog\Model\Product::class)
->disableOriginalConstructor()
->setMethods(['getId', 'getExtensionAttributes'])
->getMock();
$simple = $this->getMockBuilder(\Magento\Catalog\Model\Product::class)
->disableOriginalConstructor()
->setMethods(['getId', 'getData'])
->getMock();

$configurable->expects($this->any())->method('getId')->will($this->returnValue(666));
$extensionAttributesMock = $this->getMockBuilder(\Magento\Catalog\Api\Data\ProductExtension::class)
->disableOriginalConstructor()
->setMethods([
'getConfigurableProductOptions', 'setConfigurableProductOptions', 'setConfigurableProductLinks'
])
->getMock();
$optionMock = $this->getMockBuilder(\Magento\ConfigurableProduct\Api\Data\Option::class)
->disableOriginalConstructor()
->setMethods(['getProductAttribute', 'getAttributeId'])
->getMock();
$productAttributeMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\AbstractAttribute::class)
->disableOriginalConstructor()
->setMethods(['getAttributeCode'])
->getMock();
$optionsFactoryMock = $this->getMockBuilder(\Magento\ConfigurableProduct\Helper\Product\Options\Factory::class)
->disableOriginalConstructor()
->setMethods(['create'])
->getMock();
$reflectionClass = new \ReflectionClass(\Magento\ConfigurableProduct\Model\LinkManagement::class);
$optionsFactoryReflectionProperty = $reflectionClass->getProperty('optionsFactory');
$optionsFactoryReflectionProperty->setAccessible(true);
$optionsFactoryReflectionProperty->setValue($this->object, $optionsFactoryMock);

$simple = $this->getMockBuilder(\Magento\Catalog\Model\Product::class)
$attributeFactoryMock = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\AttributeFactory::class)
->disableOriginalConstructor()
->setMethods(['create'])
->getMock();
$attributeFactoryReflectionProperty = $reflectionClass->getProperty('attributeFactory');
$attributeFactoryReflectionProperty->setAccessible(true);
$attributeFactoryReflectionProperty->setValue($this->object, $attributeFactoryMock);

$simple->expects($this->any())->method('getId')->will($this->returnValue(999));
$attributeMock = $this->getMockBuilder(\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class)
->disableOriginalConstructor()
->setMethods(['getCollection', 'getOptions', 'getId', 'getAttributeCode', 'getStoreLabel'])
->getMock();
$attributeOptionMock = $this->getMockBuilder(\Magento\Eav\Model\Entity\Attribute\Option::class)
->disableOriginalConstructor()
->setMethods(['getValue', 'getLabel'])
->getMock();

$attributeCollectionMock = $this->getMockBuilder(
\Magento\ConfigurableProduct\Model\ResourceModel\Product\Type\Configurable\Attribute\Collection::class
)
->disableOriginalConstructor()
->setMethods(['addFieldToFilter', 'getItems'])
->getMock();

$this->productRepository->expects($this->at(0))->method('get')->with($productSku)->willReturn($configurable);
$this->productRepository->expects($this->at(1))->method('get')->with($childSku)->willReturn($simple);
Expand All @@ -164,15 +208,30 @@ public function testAddChild()
$this->returnValue([0 => [1, 2, 3]])
);

$extensionAttributes = $this->getMockBuilder(ProductExtensionAttributes::class)
->setMethods(['setConfigurableProductLinks'])
->disableOriginalConstructor()
->getMockForAbstractClass();
$configurable->expects($this->any())->method('getId')->will($this->returnValue(666));
$simple->expects($this->any())->method('getId')->will($this->returnValue(999));

$configurable->expects($this->any())->method('getExtensionAttributes')->willReturn($extensionAttributesMock);
$extensionAttributesMock->expects($this->any())
->method('getConfigurableProductOptions')
->willReturn([$optionMock]);
$optionMock->expects($this->any())->method('getProductAttribute')->willReturn($productAttributeMock);
$productAttributeMock->expects($this->any())->method('getAttributeCode')->willReturn('color');
$simple->expects($this->any())->method('getData')->willReturn('color');
$optionMock->expects($this->any())->method('getAttributeId')->willReturn('1');

$configurable->expects($this->once())->method('getExtensionAttributes')->willReturn($extensionAttributes);
$extensionAttributes->expects($this->once())->method('setConfigurableProductLinks')->willReturnSelf();
$optionsFactoryMock->expects($this->any())->method('create')->willReturn([$optionMock]);
$attributeFactoryMock->expects($this->any())->method('create')->willReturn($attributeMock);
$attributeMock->expects($this->any())->method('getCollection')->willReturn($attributeCollectionMock);
$attributeCollectionMock->expects($this->any())->method('addFieldToFilter')->willReturnSelf();
$attributeCollectionMock->expects($this->any())->method('getItems')->willReturn([$attributeMock]);

$configurable->expects($this->once())->method('save');
$attributeMock->expects($this->any())->method('getOptions')->willReturn([$attributeOptionMock]);

$extensionAttributesMock->expects($this->any())->method('setConfigurableProductOptions');
$extensionAttributesMock->expects($this->any())->method('setConfigurableProductLinks');

$this->productRepository->expects($this->once())->method('save');

$this->assertTrue(true, $this->object->addChild($productSku, $childSku));
}
Expand Down Expand Up @@ -243,15 +302,13 @@ public function testRemoveChild()
$productType->expects($this->once())->method('getUsedProducts')
->will($this->returnValue([$option]));

$extensionAttributes = $this->getMockBuilder(ProductExtensionAttributes::class)
$extensionAttributesMock = $this->getMockBuilder(\Magento\Framework\Api\ExtensionAttributesInterface::class)
->setMethods(['setConfigurableProductLinks'])
->disableOriginalConstructor()
->getMockForAbstractClass();

$product->expects($this->once())->method('getExtensionAttributes')->willReturn($extensionAttributes);
$extensionAttributes->expects($this->once())->method('setConfigurableProductLinks')->willReturnSelf();

$product->expects($this->once())->method('save');
$product->expects($this->once())->method('getExtensionAttributes')->willReturn($extensionAttributesMock);
$this->productRepository->expects($this->once())->method('save');
$this->assertTrue($this->object->removeChild($productSku, $childSku));
}

Expand Down
Loading

0 comments on commit 8bde202

Please sign in to comment.