Skip to content

Commit 95ac4c1

Browse files
author
cspruiell
committed
MAGETWO-58401: Cannot create configurable product with child by REST API - for 2.1.x
- load options correctly before saving product with children - update tests
1 parent dcc2686 commit 95ac4c1

File tree

3 files changed

+164
-9
lines changed

3 files changed

+164
-9
lines changed

app/code/Magento/ConfigurableProduct/Model/LinkManagement.php

Lines changed: 98 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,16 @@ class LinkManagement implements \Magento\ConfigurableProduct\Api\LinkManagementI
3232
*/
3333
private $dataObjectHelper;
3434

35+
/**
36+
* @var \Magento\ConfigurableProduct\Helper\Product\Options\Factory;
37+
*/
38+
private $optionsFactory;
39+
40+
/**
41+
* @var \Magento\Catalog\Model\ResourceModel\Eav\AttributeFactory
42+
*/
43+
private $attributeFactory;
44+
3545
/**
3646
* @param \Magento\Catalog\Api\ProductRepositoryInterface $productRepository
3747
* @param \Magento\Catalog\Api\Data\ProductInterfaceFactory $productFactory
@@ -102,9 +112,28 @@ public function addChild($sku, $childSku)
102112
throw new StateException(__('Product has been already attached'));
103113
}
104114

115+
$configurableProductOptions = $product->getExtensionAttributes()->getConfigurableProductOptions();
116+
if (empty($configurableProductOptions)) {
117+
throw new StateException(__('Parent product does not have configurable product options'));
118+
}
119+
120+
$configurableOptionData = [];
121+
foreach ($configurableProductOptions as $configurableProductOption) {
122+
$attributeCode = $configurableProductOption->getProductAttribute()->getAttributeCode();
123+
if (!$child->getData($attributeCode)) {
124+
throw new StateException(__('Child product does not have attribute value %1', $attributeCode));
125+
}
126+
$configurableOptionData = array_merge($configurableOptionData,
127+
$this->getConfigurableAttributesData($configurableProductOption->getAttributeId()));
128+
}
129+
130+
/** @var Factory $optionFactory */
131+
$optionFactory = $this->getOptionsFactory();
132+
$options = $optionFactory->create($configurableOptionData);
105133
$childrenIds[] = $child->getId();
106-
$product->setAssociatedProductIds($childrenIds);
107-
$product->save();
134+
$product->getExtensionAttributes()->setConfigurableProductOptions($options);
135+
$product->getExtensionAttributes()->setConfigurableProductLinks($childrenIds);
136+
$this->productRepository->save($product);
108137
return true;
109138
}
110139

@@ -132,8 +161,73 @@ public function removeChild($sku, $childSku)
132161
if (count($options) == count($ids)) {
133162
throw new NoSuchEntityException(__('Requested option doesn\'t exist'));
134163
}
135-
$product->setAssociatedProductIds($ids);
136-
$product->save();
164+
$product->getExtensionAttributes()->setConfigurableProductLinks($ids);
165+
$this->productRepository->save($product);
137166
return true;
138167
}
168+
169+
/**
170+
* Get Options Factory
171+
*
172+
* @return \Magento\ConfigurableProduct\Helper\Product\Options\Factory
173+
*
174+
* @deprecated
175+
*/
176+
private function getOptionsFactory()
177+
{
178+
if (!$this->optionsFactory) {
179+
$this->optionsFactory = \Magento\Framework\App\ObjectManager::getInstance()
180+
->get(\Magento\ConfigurableProduct\Helper\Product\Options\Factory::class);
181+
}
182+
return $this->optionsFactory;
183+
}
184+
185+
/**
186+
* Get Attribute Factory
187+
*
188+
* @return \Magento\Catalog\Model\ResourceModel\Eav\AttributeFactory
189+
*
190+
* @deprecated
191+
*/
192+
private function getAttributeFactory()
193+
{
194+
if (!$this->attributeFactory) {
195+
$this->attributeFactory = \Magento\Framework\App\ObjectManager::getInstance()
196+
->get(\Magento\Catalog\Model\ResourceModel\Eav\AttributeFactory::class);
197+
}
198+
return $this->attributeFactory;
199+
}
200+
201+
/**
202+
* Get Configurable Attribute Data
203+
*
204+
* @param int $attributeId
205+
* @return array
206+
*/
207+
private function getConfigurableAttributesData($attributeId)
208+
{
209+
$attributeValues = [];
210+
$attribute = $this->getAttributeFactory()->create();
211+
$attribute->load($attributeId);
212+
foreach ($attribute->getOptions() as $option) {
213+
if ($option->getValue()) {
214+
$attributeValues[] = [
215+
'label' => $option->getLabel(),
216+
'attribute_id' => $attribute->getId(),
217+
'value_index' => $option->getValue(),
218+
];
219+
}
220+
}
221+
222+
$configurableAttributesData = [
223+
[
224+
'attribute_id' => $attribute->getId(),
225+
'code' => $attribute->getAttributeCode(),
226+
'label' => $attribute->getStoreLabel(),
227+
'values' => $attributeValues,
228+
],
229+
];
230+
231+
return $configurableAttributesData;
232+
}
139233
}

app/code/Magento/ConfigurableProduct/Test/Unit/Model/LinkManagementTest.php

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
namespace Magento\ConfigurableProduct\Test\Unit\Model;
88

9+
use Magento\ConfigurableProduct\Model\LinkManagement;
910
use Magento\ConfigurableProduct\Model\Product\Type\Configurable;
1011

1112
class LinkManagementTest extends \PHPUnit_Framework_TestCase
@@ -142,15 +143,52 @@ public function testAddChild()
142143

143144
$configurable = $this->getMockBuilder('Magento\Catalog\Model\Product')
144145
->disableOriginalConstructor()
146+
->setMethods(['getId', 'getExtensionAttributes', 'save'])
147+
->getMock();
148+
$simple = $this->getMockBuilder('Magento\Catalog\Model\Product')
149+
->disableOriginalConstructor()
150+
->setMethods(['getId', 'getData'])
145151
->getMock();
146152

147-
$configurable->expects($this->any())->method('getId')->will($this->returnValue(666));
153+
$extensionAttributesMock = $this->getMockBuilder('Magento\Catalog\Api\Data\ProductExtension')
154+
->disableOriginalConstructor()
155+
->setMethods([
156+
'getConfigurableProductOptions', 'setConfigurableProductOptions', 'setConfigurableProductLinks'
157+
])
158+
->getMock();
159+
$optionMock = $this->getMockBuilder('Magento\ConfigurableProduct\Api\Data\Option')
160+
->disableOriginalConstructor()
161+
->setMethods(['getProductAttribute', 'getAttributeId'])
162+
->getMock();
163+
$productAttributeMock = $this->getMockBuilder('Magento\Eav\Model\Entity\Attribute\AbstractAttribute')
164+
->disableOriginalConstructor()
165+
->setMethods(['getAttributeCode'])
166+
->getMock();
167+
$optionsFactoryMock = $this->getMockBuilder('Magento\ConfigurableProduct\Helper\Product\Options\Factory')
168+
->disableOriginalConstructor()
169+
->setMethods(['create'])
170+
->getMock();
171+
$reflectionClass = new \ReflectionClass('Magento\ConfigurableProduct\Model\LinkManagement');
172+
$optionsFactoryReflectionProperty = $reflectionClass->getProperty('optionsFactory');
173+
$optionsFactoryReflectionProperty->setAccessible(true);
174+
$optionsFactoryReflectionProperty->setValue($this->object, $optionsFactoryMock);
148175

149-
$simple = $this->getMockBuilder('Magento\Catalog\Model\Product')
176+
$attributeFactoryMock = $this->getMockBuilder('Magento\Catalog\Model\ResourceModel\Eav\AttributeFactory')
150177
->disableOriginalConstructor()
178+
->setMethods(['create'])
151179
->getMock();
180+
$attributeFactoryReflectionProperty = $reflectionClass->getProperty('attributeFactory');
181+
$attributeFactoryReflectionProperty->setAccessible(true);
182+
$attributeFactoryReflectionProperty->setValue($this->object, $attributeFactoryMock);
152183

153-
$simple->expects($this->any())->method('getId')->will($this->returnValue(999));
184+
$attributeMock = $this->getMockBuilder('Magento\Catalog\Model\ResourceModel\Eav\Attribute')
185+
->disableOriginalConstructor()
186+
->setMethods(['load', 'getOptions', 'getId', 'getAttributeCode', 'getStoreLabel'])
187+
->getMock();
188+
$attributeOptionMock = $this->getMockBuilder('Magento\Eav\Model\Entity\Attribute\Option')
189+
->disableOriginalConstructor()
190+
->setMethods(['getValue', 'getLabel'])
191+
->getMock();
154192

155193
$this->productRepository->expects($this->at(0))->method('get')->with($productSku)->willReturn($configurable);
156194
$this->productRepository->expects($this->at(1))->method('get')->with($childSku)->willReturn($simple);
@@ -159,7 +197,28 @@ public function testAddChild()
159197
->will(
160198
$this->returnValue([0 => [1, 2, 3]])
161199
);
162-
$configurable->expects($this->once())->method('save');
200+
201+
$configurable->expects($this->any())->method('getId')->will($this->returnValue(666));
202+
$simple->expects($this->any())->method('getId')->will($this->returnValue(999));
203+
204+
$configurable->expects($this->any())->method('getExtensionAttributes')->willReturn($extensionAttributesMock);
205+
$extensionAttributesMock->expects($this->any())
206+
->method('getConfigurableProductOptions')
207+
->willReturn([$optionMock]);
208+
$optionMock->expects($this->any())->method('getProductAttribute')->willReturn($productAttributeMock);
209+
$productAttributeMock->expects($this->any())->method('getAttributeCode')->willReturn('color');
210+
$simple->expects($this->any())->method('getData')->willReturn('color');
211+
$optionMock->expects($this->any())->method('getAttributeId')->willReturn('1');
212+
213+
$optionsFactoryMock->expects($this->any())->method('create')->willReturn([$optionMock]);
214+
$attributeFactoryMock->expects($this->any())->method('create')->willReturn($attributeMock);
215+
$attributeMock->expects($this->any())->method('load');
216+
$attributeMock->expects($this->any())->method('getOptions')->willReturn([$attributeOptionMock]);
217+
218+
$extensionAttributesMock->expects($this->any())->method('setConfigurableProductOptions');
219+
$extensionAttributesMock->expects($this->any())->method('setConfigurableProductLinks');
220+
221+
$this->productRepository->expects($this->once())->method('save');
163222

164223
$this->assertTrue(true, $this->object->addChild($productSku, $childSku));
165224
}
@@ -236,7 +295,7 @@ public function testRemoveChild()
236295
->getMock();
237296

238297
$product->expects($this->once())->method('getExtensionAttributes')->willReturn($extensionAttributesMock);
239-
$product->expects($this->once())->method('save');
298+
$this->productRepository->expects($this->once())->method('save');
240299
$this->assertTrue($this->object->removeChild($productSku, $childSku));
241300
}
242301

app/code/Magento/ConfigurableProduct/i18n/en_US.csv

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -117,6 +117,8 @@ Done,Done
117117
"Please select attribute(s).","Please select attribute(s)."
118118
"Generate Products","Generate Products"
119119
"Configurable Product Image","Configurable Product Image"
120+
"Parent product does not have configurable product options","Parent product does not have configurable product options"
121+
"Child product does not have attribute value %1","Child product does not have attribute value %1"
120122
Select...,Select...
121123
Status,Status
122124
ID,ID

0 commit comments

Comments
 (0)