Skip to content

Commit 8d275f7

Browse files
committed
MAGETWO-91570: [2.2.x] - [Github]Can not save attribute #5907
- swatches test
1 parent d3c682e commit 8d275f7

File tree

4 files changed

+216
-3
lines changed
  • app/code/Magento
    • Catalog/Controller/Adminhtml/Product/Attribute
    • Swatches/Controller/Adminhtml/Product/Attribute/Plugin
  • dev/tests/integration/testsuite/Magento

4 files changed

+216
-3
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ private function extractOptionsData(&$data)
333333
foreach ($serializedOptions as $serializedOption) {
334334
$option = [];
335335
parse_str($serializedOption, $option);
336-
$data = array_replace_recursive($data, $option);
336+
$data = array_merge_recursive($data, $option);
337337
}
338338
}
339339
}

app/code/Magento/Swatches/Controller/Adminhtml/Product/Attribute/Plugin/Save.php

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
use Magento\Swatches\Model\Swatch;
1212

1313
/**
14-
* Class Save
14+
* Plugin for product attribute save controller.
1515
*/
1616
class Save
1717
{
@@ -24,7 +24,11 @@ class Save
2424
public function beforeDispatch(Attribute\Save $subject, RequestInterface $request)
2525
{
2626
$data = $request->getPostValue();
27-
$data['serialized_options'] = $data['serialized_swatch_values'];
27+
//Data is serialized to overcome issues caused by max_input_vars value if it's modification is unavailable.
28+
//See subject controller code and comments for more info.
29+
if (isset($data['serialized_swatch_values'])) {
30+
$data['serialized_options'] = $data['serialized_swatch_values'];
31+
}
2832
unset($data['serialized_swatch_values']);
2933
if (isset($data['frontend_input'])) {
3034
switch ($data['frontend_input']) {

dev/tests/integration/testsuite/Magento/Catalog/Controller/Adminhtml/Product/AttributeTest.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
* See COPYING.txt for license details.
55
*/
66
namespace Magento\Catalog\Controller\Adminhtml\Product;
7+
78
use Magento\Framework\Exception\LocalizedException;
89

910
/**
Lines changed: 208 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,208 @@
1+
<?php
2+
/**
3+
* Copyright © Magento, Inc. All rights reserved.
4+
* See COPYING.txt for license details.
5+
*/
6+
namespace Magento\Swatches\Controller\Adminhtml\Product;
7+
8+
use Magento\Framework\Exception\LocalizedException;
9+
10+
/**
11+
* @magentoAppArea adminhtml
12+
* @magentoDbIsolation enabled
13+
*/
14+
class AttributeTest extends \Magento\TestFramework\TestCase\AbstractBackendController
15+
{
16+
/**
17+
* Generate random hex color.
18+
*
19+
* @return string
20+
*/
21+
private function getRandomColor()
22+
{
23+
return '#' . str_pad(dechex(mt_rand(0, 0xFFFFFF)), 6, '0', STR_PAD_LEFT);
24+
}
25+
26+
/**
27+
* Get visual swatches data set.
28+
*
29+
* @param int $optionsCount
30+
* @return array
31+
*/
32+
private function getSwatchVisualDataSet($optionsCount)
33+
{
34+
$optionsData = [];
35+
$expectedOptionsLabels = [];
36+
for ($i = 0; $i < $optionsCount; $i++) {
37+
$order = $i + 1;
38+
$expectedOptionLabelOnStoreView = "value_{$i}_store_1";
39+
$expectedOptionsLabels[$i+1] = $expectedOptionLabelOnStoreView;
40+
$optionsData []= "optionvisual[order][option_{$i}]={$order}";
41+
$optionsData []= "defaultvisual[]=option_{$i}";
42+
$optionsData []= "swatchvisual[value][option_{$i}]={$this->getRandomColor()}";
43+
$optionsData []= "optionvisual[value][option_{$i}][0]=value_{$i}_admin";
44+
$optionsData []= "optionvisual[value][option_{$i}][1]={$expectedOptionLabelOnStoreView}";
45+
$optionsData []= "optionvisual[delete][option_{$i}]=";
46+
}
47+
$optionsData []= "visual_swatch_validation=";
48+
$optionsData []= "visual_swatch_validation_unique=";
49+
return [
50+
'attribute_data' => array_merge_recursive(
51+
[
52+
'serialized_swatch_values' => json_encode($optionsData),
53+
],
54+
$this->getAttributePreset(),
55+
[
56+
'frontend_input' => 'swatch_visual'
57+
]
58+
),
59+
'expected_options_count' => $optionsCount + 1,
60+
'expected_store_labels' => $expectedOptionsLabels
61+
];
62+
}
63+
64+
/**
65+
* Get text swatches data set.
66+
*
67+
* @param int $optionsCount
68+
* @return array
69+
*/
70+
private function getSwatchTextDataSet($optionsCount)
71+
{
72+
$optionsData = [];
73+
$expectedOptionsLabels = [];
74+
for ($i = 0; $i < $optionsCount; $i++) {
75+
$order = $i + 1;
76+
$expectedOptionLabelOnStoreView = "value_{$i}_store_1";
77+
$expectedOptionsLabels[$i+1] = $expectedOptionLabelOnStoreView;
78+
$optionsData []= "optiontext[order][option_{$i}]={$order}";
79+
$optionsData []= "defaulttext[]=option_{$i}";
80+
$optionsData []= "swatchtext[value][option_{$i}]=x{$i}";
81+
$optionsData []= "optiontext[value][option_{$i}][0]=value_{$i}_admin";
82+
$optionsData []= "optiontext[value][option_{$i}][1]={$expectedOptionLabelOnStoreView}";
83+
$optionsData []= "optiontext[delete][option_{$i}]=";
84+
}
85+
$optionsData []= "text_swatch_validation=";
86+
$optionsData []= "text_swatch_validation_unique=";
87+
return [
88+
'attribute_data' => array_merge_recursive(
89+
[
90+
'serialized_swatch_values' => json_encode($optionsData),
91+
],
92+
$this->getAttributePreset(),
93+
[
94+
'frontend_input' => 'swatch_text'
95+
]
96+
),
97+
'expected_options_count' => $optionsCount + 1,
98+
'expected_store_labels' => $expectedOptionsLabels
99+
];
100+
}
101+
102+
// ["optiontext[order][option_0]=1","defaulttext[]=option_0","swatchtext[value][option_0][0]=1","optiontext[value][option_0][0]=111","swatchtext[value][option_0][1]=1","optiontext[value][option_0][1]=111","swatchtext[value][option_0][2]=","optiontext[value][option_0][2]=","optiontext[delete][option_0]=","optiontext[order][option_1]=2","defaulttext[]=option_1","swatchtext[value][option_1][0]=2","optiontext[value][option_1][0]=222","swatchtext[value][option_1][1]=2","optiontext[value][option_1][1]=222","swatchtext[value][option_1][2]=","optiontext[value][option_1][2]=","optiontext[delete][option_1]=","text_swatch_validation=","text_swatch_validation_unique="]
103+
104+
/**
105+
* Get data preset for new attribute.
106+
*
107+
* @return array
108+
*/
109+
private function getAttributePreset()
110+
{
111+
return [
112+
'serialized_options' => '[]',
113+
'form_key' => 'XxtpPYjm2YPYUlAt',
114+
'frontend_label' => [
115+
0 => 'asdasd',
116+
1 => '',
117+
2 => '',
118+
],
119+
'is_required' => '0',
120+
'update_product_preview_image' => '0',
121+
'use_product_image_for_swatch' => '0',
122+
'is_global' => '0',
123+
'default_value_text' => '512',
124+
'default_value_yesno' => '1',
125+
'default_value_date' => '1/1/70',
126+
'default_value_textarea' => '512',
127+
'is_unique' => '0',
128+
'is_used_in_grid' => '1',
129+
'is_visible_in_grid' => '1',
130+
'is_filterable_in_grid' => '1',
131+
'is_searchable' => '0',
132+
'is_comparable' => '0',
133+
'is_filterable' => '0',
134+
'is_filterable_in_search' => '0',
135+
'position' => '0',
136+
'is_used_for_promo_rules' => '0',
137+
'is_html_allowed_on_front' => '1',
138+
'is_visible_on_front' => '0',
139+
'used_in_product_listing' => '0',
140+
'used_for_sort_by' => '0',
141+
'attribute_code' => 'test_many_swatches',
142+
];
143+
}
144+
145+
/**
146+
* Data provider for large swatches amount test.
147+
*
148+
* @return array
149+
*/
150+
public function getLargeSwatchesAmountAttributeData()
151+
{
152+
$maxInputVars = ini_get('max_input_vars');
153+
// Each option is at least 7 variables array for a visual swatch.
154+
// Set options count to exceed max_input_vars by 20 options (140 variables).
155+
$swatchVisualOptionsCount = floor($maxInputVars / 7) + 20;
156+
$swatchTextOptionsCount = floor($maxInputVars / 4) + 80;
157+
return [
158+
'visual swatches' => $this->getSwatchVisualDataSet($swatchVisualOptionsCount),
159+
'text swatches' => $this->getSwatchTextDataSet($swatchTextOptionsCount)
160+
];
161+
}
162+
163+
/**
164+
* Test attribute saving with large amount of options exceeding maximum allowed by max_input_vars limit.
165+
* @dataProvider getLargeSwatchesAmountAttributeData()
166+
* @param array $attributeData
167+
* @param int $expectedOptionsCount
168+
* @param array $expectedLabels
169+
* @return void
170+
*/
171+
public function testLargeOptionsDataSet($attributeData, $expectedOptionsCount, $expectedLabels)
172+
{
173+
$this->getRequest()->setPostValue($attributeData);
174+
$this->dispatch('backend/catalog/product_attribute/save');
175+
$entityTypeId = $this->_objectManager->create(
176+
\Magento\Eav\Model\Entity::class
177+
)->setType(
178+
\Magento\Catalog\Model\Product::ENTITY
179+
)->getTypeId();
180+
181+
/** @var $attribute \Magento\Catalog\Model\ResourceModel\Eav\Attribute */
182+
$attribute = $this->_objectManager->create(
183+
\Magento\Catalog\Model\ResourceModel\Eav\Attribute::class
184+
)->setEntityTypeId(
185+
$entityTypeId
186+
);
187+
try {
188+
$attribute->loadByCode($entityTypeId, 'test_many_swatches');
189+
$options = $attribute->getOptions();
190+
// assert that all options are saved without truncation
191+
$this->assertEquals(
192+
$expectedOptionsCount,
193+
count($options),
194+
'Expected options count does not match (regarding first empty option for non-required attribute)'
195+
);
196+
197+
foreach ($expectedLabels as $optionOrderNum => $label) {
198+
$this->assertEquals(
199+
$label,
200+
$options[$optionOrderNum]->getLabel(),
201+
"Label for option #{$optionOrderNum} does not match expected."
202+
);
203+
}
204+
} catch (LocalizedException $e) {
205+
$this->fail('Test failed with exception on attribute model load: ' . $e);
206+
}
207+
}
208+
}

0 commit comments

Comments
 (0)