Skip to content

Commit 1d5d114

Browse files
author
Mykola Palamar
committed
MAGETWO-56487: [GitHub] Multi-select option attribute values do not appear in admin #5877
1 parent d6bb4de commit 1d5d114

File tree

6 files changed

+145
-75
lines changed

6 files changed

+145
-75
lines changed

app/code/Magento/Catalog/Controller/Adminhtml/Product/Attribute/Validate.php

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -104,10 +104,13 @@ public function execute()
104104
$multipleOption = null == $multipleOption ? 'select' : $multipleOption;
105105

106106
if (isset($this->multipleAttributeList[$multipleOption]) && !(null == ($multipleOption))) {
107+
$options = $this->getRequest()->getParam($this->multipleAttributeList[$multipleOption]);
107108
$this->checkUniqueOption(
108109
$response,
109-
$this->getRequest()->getParam($this->multipleAttributeList[$multipleOption])
110+
$options
110111
);
112+
$valueOptions = is_array($options['value']) ? $options['value'] : [];
113+
$this->checkEmptyOption($response, $valueOptions);
111114
}
112115

113116
return $this->resultJsonFactory->create()->setJsonData($response->toJson());
@@ -155,13 +158,29 @@ private function setMessageToResponse($response, $messages)
155158
private function checkUniqueOption(DataObject $response, array $options = null)
156159
{
157160
if (is_array($options)
158-
&& !empty($options['value'])
159-
&& !empty($options['delete'])
161+
&& isset($options['value'])
162+
&& isset($options['delete'])
160163
&& !$this->isUniqueAdminValues($options['value'], $options['delete'])
161164
) {
162165
$this->setMessageToResponse($response, [__("The value of Admin must be unique.")]);
163166
$response->setError(true);
164167
}
165168
return $this;
166169
}
170+
171+
/**
172+
* Check that admin does not try to create option with empty admin scope option.
173+
*
174+
* @param DataObject $response
175+
* @param array $optionsForCheck
176+
*/
177+
private function checkEmptyOption(DataObject $response, array $optionsForCheck = null)
178+
{
179+
foreach ($optionsForCheck as $optionValues) {
180+
if (isset($optionValues[0]) && $optionValues[0] == '') {
181+
$this->setMessageToResponse($response, [__("The value of Admin scope can't be empty.")]);
182+
$response->setError(true);
183+
}
184+
}
185+
}
167186
}

app/code/Magento/Catalog/Model/Product/Attribute/DataProvider.php

Lines changed: 22 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,14 @@
55
*/
66
namespace Magento\Catalog\Model\Product\Attribute;
77

8-
use Magento\Catalog\Model\Category;
98
use Magento\Framework\Stdlib\ArrayManager;
109
use Magento\Store\Api\StoreRepositoryInterface;
1110
use Magento\Catalog\Model\ResourceModel\Product\Attribute\CollectionFactory;
1211
use Magento\Eav\Model\Entity\Attribute as EavAttribute;
13-
use Magento\Ui\Component\Form;
1412
use Magento\Ui\Component\Form\Element\Input;
1513
use Magento\Ui\Component\Form\Field;
1614
use Magento\Ui\Component\Form\Element\DataType\Text;
15+
use Magento\Store\Model\Store;
1716

1817
/**
1918
* Data provider for the form of adding new product attribute.
@@ -149,40 +148,36 @@ private function customizeOptions($meta)
149148
$sortOrder = 1;
150149
foreach ($this->storeRepository->getList() as $store) {
151150
$storeId = $store->getId();
152-
151+
$storeLabelConfiguration = [
152+
'dataType' => 'text',
153+
'formElement' => 'input',
154+
'component' => 'Magento_Catalog/js/form/element/input',
155+
'template' => 'Magento_Catalog/form/element/input',
156+
'prefixName' => 'option.value',
157+
'prefixElementName' => 'option_',
158+
'suffixName' => (string)$storeId,
159+
'label' => $store->getName(),
160+
'sortOrder' => $sortOrder,
161+
'componentType' => Field::NAME,
162+
];
163+
// JS code can't understand 'required-entry' => false|null, we have to avoid even empty property.
164+
if ($store->getCode() == Store::ADMIN_CODE) {
165+
$storeLabelConfiguration['validation'] = [
166+
'required-entry' => true,
167+
];
168+
}
153169
$meta['attribute_options_select_container']['children']['attribute_options_select']['children']
154170
['record']['children']['value_option_' . $storeId] = $this->arrayManager->set(
155171
'arguments/data/config',
156172
[],
157-
[
158-
'dataType' => 'text',
159-
'formElement' => 'input',
160-
'component' => 'Magento_Catalog/js/form/element/input',
161-
'template' => 'Magento_Catalog/form/element/input',
162-
'prefixName' => 'option.value',
163-
'prefixElementName' => 'option_',
164-
'suffixName' => (string)$storeId,
165-
'label' => $store->getName(),
166-
'sortOrder' => $sortOrder,
167-
'componentType' => Field::NAME,
168-
]
173+
$storeLabelConfiguration
169174
);
175+
170176
$meta['attribute_options_multiselect_container']['children']['attribute_options_multiselect']['children']
171177
['record']['children']['value_option_' . $storeId] = $this->arrayManager->set(
172178
'arguments/data/config',
173179
[],
174-
[
175-
'dataType' => 'text',
176-
'formElement' => 'input',
177-
'component' => 'Magento_Catalog/js/form/element/input',
178-
'template' => 'Magento_Catalog/form/element/input',
179-
'prefixName' => 'option.value',
180-
'prefixElementName' => 'option_',
181-
'suffixName' => (string)$storeId,
182-
'label' => $store->getName(),
183-
'sortOrder' => $sortOrder,
184-
'componentType' => Field::NAME,
185-
]
180+
$storeLabelConfiguration
186181
);
187182
++$sortOrder;
188183
}

app/code/Magento/Catalog/Test/Unit/Controller/Adminhtml/Product/Attribute/ValidateTest.php

Lines changed: 98 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
use Magento\Framework\ObjectManagerInterface;
1616
use Magento\Framework\View\LayoutFactory;
1717
use Magento\Framework\View\LayoutInterface;
18+
use Magento\Framework\DataObject;
1819

1920
/**
2021
* @SuppressWarnings(PHPMD.CouplingBetweenObjects)
@@ -61,20 +62,24 @@ class ValidateTest extends AttributeTest
6162
*/
6263
protected $layoutMock;
6364

65+
/**
66+
* @var DataObject
67+
*/
68+
protected $responseDataObject;
69+
6470
protected function setUp()
6571
{
6672
parent::setUp();
6773
$this->resultJsonFactoryMock = $this->getMockBuilder(ResultJsonFactory::class)
6874
->disableOriginalConstructor()
75+
->setMethods(['create'])
6976
->getMock();
7077
$this->resultJson = $this->getMockBuilder(ResultJson::class)
7178
->disableOriginalConstructor()
7279
->getMock();
7380
$this->layoutFactoryMock = $this->getMockBuilder(LayoutFactory::class)
7481
->disableOriginalConstructor()
7582
->getMock();
76-
$this->objectManagerMock = $this->getMockBuilder(ObjectManagerInterface::class)
77-
->getMockForAbstractClass();
7883
$this->attributeMock = $this->getMockBuilder(Attribute::class)
7984
->disableOriginalConstructor()
8085
->getMock();
@@ -86,10 +91,9 @@ protected function setUp()
8691
->getMock();
8792
$this->layoutMock = $this->getMockBuilder(LayoutInterface::class)
8893
->getMockForAbstractClass();
89-
90-
$this->contextMock->expects($this->any())
91-
->method('getObjectManager')
92-
->willReturn($this->objectManagerMock);
94+
$this->responseDataObject = $this->getMockBuilder(DataObject::class)
95+
->setMethods(null)
96+
->getMock();
9397
}
9498

9599
/**
@@ -100,13 +104,17 @@ protected function getModel()
100104
return $this->objectManager->getObject(
101105
Validate::class,
102106
[
103-
'context' => $this->contextMock,
104-
'attributeLabelCache' => $this->attributeLabelCacheMock,
105-
'coreRegistry' => $this->coreRegistryMock,
106-
'resultPageFactory' => $this->resultPageFactoryMock,
107-
'resultJsonFactory' => $this->resultJsonFactoryMock,
108-
'layoutFactory' => $this->layoutFactoryMock,
109-
'multipleAttributeList' => ['select' => 'option']
107+
'context' => $this->contextMock,
108+
'attributeLabelCache' => $this->attributeLabelCacheMock,
109+
'coreRegistry' => $this->coreRegistryMock,
110+
'resultPageFactory' => $this->resultPageFactoryMock,
111+
'resultJsonFactory' => $this->resultJsonFactoryMock,
112+
'layoutFactory' => $this->layoutFactoryMock,
113+
'multipleAttributeList' => ['select' => 'option', 'multipleselect' => 'option'],
114+
'attributeResource' => $this->attributeMock,
115+
'attributeSetResource' => $this->attributeSetMock,
116+
'escaper' => $this->escaperMock,
117+
'responseObject' => $this->responseDataObject,
110118
]
111119
);
112120
}
@@ -120,22 +128,9 @@ public function testExecute()
120128
['attribute_code', null, 'test_attribute_code'],
121129
['new_attribute_set_name', null, 'test_attribute_set_name'],
122130
]);
123-
$this->objectManagerMock->expects($this->exactly(2))
124-
->method('create')
125-
->willReturnMap([
126-
[\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class, [], $this->attributeMock],
127-
[\Magento\Eav\Model\Entity\Attribute\Set::class, [], $this->attributeSetMock]
128-
]);
129131
$this->attributeMock->expects($this->once())
130132
->method('loadByCode')
131133
->willReturnSelf();
132-
$this->requestMock->expects($this->once())
133-
->method('has')
134-
->with('new_attribute_set_name')
135-
->willReturn(true);
136-
$this->attributeSetMock->expects($this->once())
137-
->method('setEntityTypeId')
138-
->willReturnSelf();
139134
$this->attributeSetMock->expects($this->once())
140135
->method('load')
141136
->willReturnSelf();
@@ -155,13 +150,11 @@ public function testExecute()
155150
/**
156151
* @dataProvider provideUniqueData
157152
* @param array $options
158-
* @param boolean $isError
159153
* @throws \Magento\Framework\Exception\NotFoundException
160154
*/
161-
public function testUniqueValidation(array $options, $isError)
155+
public function testUniqueValidation(array $options)
162156
{
163-
$countFunctionCalls = ($isError) ? 6 : 5;
164-
$this->requestMock->expects($this->exactly($countFunctionCalls))
157+
$this->requestMock->expects($this->any())
165158
->method('getParam')
166159
->willReturnMap([
167160
['frontend_label', null, null],
@@ -171,19 +164,10 @@ public function testUniqueValidation(array $options, $isError)
171164
['message_key', null, Validate::DEFAULT_MESSAGE_KEY]
172165
]);
173166

174-
$this->objectManagerMock->expects($this->once())
175-
->method('create')
176-
->willReturn($this->attributeMock);
177-
178167
$this->attributeMock->expects($this->once())
179168
->method('loadByCode')
180169
->willReturnSelf();
181170

182-
$this->requestMock->expects($this->once())
183-
->method('has')
184-
->with('new_attribute_set_name')
185-
->willReturn(false);
186-
187171
$this->resultJsonFactoryMock->expects($this->once())
188172
->method('create')
189173
->willReturn($this->resultJson);
@@ -205,7 +189,7 @@ public function provideUniqueData()
205189
"option_1" => "",
206190
"option_2" => "",
207191
]
208-
], false
192+
]
209193
],
210194
'valid options' => [
211195
[
@@ -219,7 +203,7 @@ public function provideUniqueData()
219203
"option_1" => "",
220204
"option_2" => "",
221205
]
222-
], false
206+
]
223207
],
224208
'duplicate options' => [
225209
[
@@ -233,7 +217,7 @@ public function provideUniqueData()
233217
"option_1" => "",
234218
"option_2" => "",
235219
]
236-
], true
220+
]
237221
],
238222
'duplicate and deleted' => [
239223
[
@@ -247,8 +231,79 @@ public function provideUniqueData()
247231
"option_1" => "1",
248232
"option_2" => "",
249233
]
250-
], false
234+
]
251235
],
252236
];
253237
}
238+
239+
/**
240+
* Check that empty admin scope labels will trigger error.
241+
*
242+
* @dataProvider provideEmptyOption
243+
* @param array $options
244+
* @throws \Magento\Framework\Exception\NotFoundException
245+
*/
246+
public function testEmptyOption(array $options, $result)
247+
{
248+
$this->requestMock->expects($this->any())
249+
->method('getParam')
250+
->willReturnMap([
251+
['frontend_label', null, null],
252+
['frontend_input', 'select', 'multipleselect'],
253+
['attribute_code', null, "test_attribute_code"],
254+
['new_attribute_set_name', null, 'test_attribute_set_name'],
255+
['option', null, $options],
256+
[Validate::ARRAY_MESSAGE_KEY, Validate::DEFAULT_MESSAGE_KEY, Validate::DEFAULT_MESSAGE_KEY]
257+
]);
258+
259+
$this->attributeMock->expects($this->once())
260+
->method('loadByCode')
261+
->willReturnSelf();
262+
263+
$this->resultJsonFactoryMock->expects($this->once())
264+
->method('create')
265+
->willReturn($this->resultJson);
266+
267+
$this->resultJson->expects($this->once())
268+
->method('setJsonData')
269+
->willReturnArgument(0);
270+
271+
$response = $this->getModel()->execute();
272+
$responseObject = json_decode($response);
273+
$this->assertEquals($responseObject, $result);
274+
}
275+
276+
/**
277+
* Dataprovider for testEmptyOption.
278+
*
279+
* @return array
280+
*/
281+
public function provideEmptyOption()
282+
{
283+
return [
284+
'empty admin scope options' => [
285+
[
286+
'value' => [
287+
"option_0" => [''],
288+
],
289+
],
290+
(object) [
291+
'error' => true,
292+
'message' => [
293+
'The value of Admin scope can\'t be empty.'
294+
]
295+
]
296+
],
297+
'not empty admin scope options' => [
298+
[
299+
'value' => [
300+
"option_0" => ['asdads'],
301+
],
302+
],
303+
(object) [
304+
'error' => false,
305+
]
306+
]
307+
];
308+
}
254309
}

app/code/Magento/Catalog/etc/adminhtml/di.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@
5151
<arguments>
5252
<argument name="multipleAttributeList" xsi:type="array">
5353
<item name="select" xsi:type="string">option</item>
54+
<item name="multiselect" xsi:type="string">option</item>
5455
</argument>
5556
</arguments>
5657
</type>

app/code/Magento/Catalog/view/adminhtml/web/template/form/element/input.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@
1717
id: uid,
1818
disabled: disabled
1919
}"/>
20+
<label class="admin__field-error" if="error" attr="for: uid" text="error"/>

app/code/Magento/Ui/view/base/web/templates/dynamic-rows/templates/default.html

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,9 +22,8 @@
2222
<table class="admin__dynamic-rows admin__control-table" data-role="grid" attr="{'data-index': index}">
2323
<thead if="element.columnsHeader">
2424
<tr>
25-
<th if="dndConfig.enabled"/>
25+
<th if="dndConfig.enabled" />
2626
<th repeat="foreach: labels, item: '$label'"
27-
text="$label().label"
2827
css="setClasses($label())"
2928
visible="$label().visible"
3029
disable="$label().disabled">

0 commit comments

Comments
 (0)