4
4
* Copyright © Magento, Inc. All rights reserved.
5
5
* See COPYING.txt for license details.
6
6
*/
7
+ declare (strict_types=1 );
8
+
7
9
namespace Magento \ConfigurableProduct \Controller \Adminhtml \Product \Attribute ;
8
10
9
- use Magento \Framework \App \Action \HttpPostActionInterface as HttpPostActionInterface ;
10
11
use Magento \Backend \App \Action ;
12
+ use Magento \Catalog \Api \Data \ProductAttributeInterface ;
11
13
use Magento \Catalog \Model \ResourceModel \Eav \AttributeFactory ;
14
+ use Magento \Framework \App \Action \HttpPostActionInterface as HttpPostActionInterface ;
15
+ use Magento \Framework \Exception \LocalizedException ;
16
+ use Magento \Framework \Json \Helper \Data ;
12
17
18
+ /**
19
+ * Creates options for product attributes
20
+ */
13
21
class CreateOptions extends Action implements HttpPostActionInterface
14
22
{
15
23
/**
@@ -20,28 +28,33 @@ class CreateOptions extends Action implements HttpPostActionInterface
20
28
const ADMIN_RESOURCE = 'Magento_Catalog::products ' ;
21
29
22
30
/**
23
- * @var \Magento\Framework\Json\Helper\ Data
31
+ * @var Data
24
32
*/
25
33
protected $ jsonHelper ;
26
34
27
35
/**
28
- * @var \Magento\Catalog\Model\ResourceModel\Eav\ AttributeFactory
36
+ * @var AttributeFactory
29
37
*/
30
38
protected $ attributeFactory ;
31
39
40
+ /**
41
+ * @var ProductAttributeInterface[]
42
+ */
43
+ private $ attributes ;
44
+
32
45
/**
33
46
* @param Action\Context $context
34
- * @param \Magento\Framework\Json\Helper\ Data $jsonHelper
47
+ * @param Data $jsonHelper
35
48
* @param AttributeFactory $attributeFactory
36
49
*/
37
50
public function __construct (
38
51
Action \Context $ context ,
39
- \ Magento \ Framework \ Json \ Helper \ Data $ jsonHelper ,
52
+ Data $ jsonHelper ,
40
53
AttributeFactory $ attributeFactory
41
54
) {
55
+ parent ::__construct ($ context );
42
56
$ this ->jsonHelper = $ jsonHelper ;
43
57
$ this ->attributeFactory = $ attributeFactory ;
44
- parent ::__construct ($ context );
45
58
}
46
59
47
60
/**
@@ -51,7 +64,15 @@ public function __construct(
51
64
*/
52
65
public function execute ()
53
66
{
54
- $ this ->getResponse ()->representJson ($ this ->jsonHelper ->jsonEncode ($ this ->saveAttributeOptions ()));
67
+ try {
68
+ $ output = $ this ->saveAttributeOptions ();
69
+ } catch (LocalizedException $ e ) {
70
+ $ output = [
71
+ 'error ' => true ,
72
+ 'message ' => $ e ->getMessage (),
73
+ ];
74
+ }
75
+ $ this ->getResponse ()->representJson ($ this ->jsonHelper ->jsonEncode ($ output ));
55
76
}
56
77
57
78
/**
@@ -61,31 +82,103 @@ public function execute()
61
82
* @TODO Move this logic to configurable product type model
62
83
* when full set of operations for attribute options during
63
84
* product creation will be implemented: edit labels, remove, reorder.
64
- * Currently only addition of options to end and removal of just added option is supported.
85
+ * Currently only addition of options is supported.
86
+ * @throws LocalizedException
65
87
*/
66
88
protected function saveAttributeOptions ()
67
89
{
68
- $ options = ( array ) $ this ->getRequest ()-> getParam ( ' options ' );
90
+ $ attributeIds = $ this ->getUpdatedAttributeIds ( );
69
91
$ savedOptions = [];
70
- foreach ($ options as $ option ) {
71
- if (isset ($ option ['label ' ]) && isset ($ option ['is_new ' ])) {
72
- $ attribute = $ this ->attributeFactory ->create ();
73
- $ attribute ->load ($ option ['attribute_id ' ]);
74
- $ optionsBefore = $ attribute ->getSource ()->getAllOptions (false );
75
- $ attribute ->setOption (
76
- [
77
- 'value ' => ['option_0 ' => [$ option ['label ' ]]],
78
- 'order ' => ['option_0 ' => count ($ optionsBefore ) + 1 ],
79
- ]
80
- );
81
- $ attribute ->save ();
82
- $ attribute = $ this ->attributeFactory ->create ();
83
- $ attribute ->load ($ option ['attribute_id ' ]);
84
- $ optionsAfter = $ attribute ->getSource ()->getAllOptions (false );
85
- $ newOption = array_pop ($ optionsAfter );
86
- $ savedOptions [$ option ['id ' ]] = $ newOption ['value ' ];
92
+ foreach ($ attributeIds as $ attributeId => $ newOptions ) {
93
+ $ attribute = $ this ->getAttribute ($ attributeId );
94
+ $ this ->checkUnique ($ attribute , $ newOptions );
95
+ foreach ($ newOptions as $ newOption ) {
96
+ $ lastAddedOption = $ this ->saveOption ($ attribute , $ newOption );
97
+ $ savedOptions [$ newOption ['id ' ]] = $ lastAddedOption ['value ' ];
87
98
}
88
99
}
100
+
89
101
return $ savedOptions ;
90
102
}
103
+
104
+ /**
105
+ * Checks unique values
106
+ *
107
+ * @param ProductAttributeInterface $attribute
108
+ * @param array $newOptions
109
+ * @return void
110
+ * @throws LocalizedException
111
+ */
112
+ private function checkUnique (ProductAttributeInterface $ attribute , array $ newOptions )
113
+ {
114
+ $ originalOptions = $ attribute ->getSource ()->getAllOptions (false );
115
+ $ allOptions = array_merge ($ originalOptions , $ newOptions );
116
+ $ optionValues = array_map (
117
+ function ($ option ) {
118
+ return $ option ['label ' ] ?? null ;
119
+ },
120
+ $ allOptions
121
+ );
122
+
123
+ $ uniqueValues = array_unique (array_filter ($ optionValues ));
124
+ $ duplicates = array_diff_assoc ($ optionValues , $ uniqueValues );
125
+ if ($ duplicates ) {
126
+ throw new LocalizedException (__ ('The value of attribute ""%1"" must be unique ' , $ attribute ->getName ()));
127
+ }
128
+ }
129
+
130
+ /**
131
+ * Loads the product attribute by the id
132
+ *
133
+ * @param int $attributeId
134
+ * @return ProductAttributeInterface
135
+ */
136
+ private function getAttribute (int $ attributeId )
137
+ {
138
+ if (!isset ($ this ->attributes [$ attributeId ])) {
139
+ $ attribute = $ this ->attributeFactory ->create ();
140
+ $ this ->attributes [$ attributeId ] = $ attribute ->load ($ attributeId );
141
+ }
142
+
143
+ return $ this ->attributes [$ attributeId ];
144
+ }
145
+
146
+ /**
147
+ * Retrieve updated attribute ids with new options
148
+ *
149
+ * @return array
150
+ */
151
+ private function getUpdatedAttributeIds ()
152
+ {
153
+ $ options = (array )$ this ->getRequest ()->getParam ('options ' );
154
+ $ updatedAttributeIds = [];
155
+ foreach ($ options as $ option ) {
156
+ if (isset ($ option ['label ' ], $ option ['is_new ' ], $ option ['attribute_id ' ])) {
157
+ $ updatedAttributeIds [$ option ['attribute_id ' ]][] = $ option ;
158
+ }
159
+ }
160
+
161
+ return $ updatedAttributeIds ;
162
+ }
163
+
164
+ /**
165
+ * Saves the option
166
+ *
167
+ * @param ProductAttributeInterface $attribute
168
+ * @param array $newOption
169
+ * @return array
170
+ */
171
+ private function saveOption (ProductAttributeInterface $ attribute , array $ newOption )
172
+ {
173
+ $ optionsBefore = $ attribute ->getSource ()->getAllOptions (false );
174
+ $ attribute ->setOption (
175
+ [
176
+ 'value ' => ['option_0 ' => [$ newOption ['label ' ]]],
177
+ 'order ' => ['option_0 ' => count ($ optionsBefore ) + 1 ],
178
+ ]
179
+ );
180
+ $ attribute ->save ();
181
+ $ optionsAfter = $ attribute ->getSource ()->getAllOptions (false );
182
+ return array_pop ($ optionsAfter );
183
+ }
91
184
}
0 commit comments