77
88namespace Magento \Catalog \Test \Fixture ;
99
10+ use Magento \Catalog \Api \Data \ProductAttributeInterface ;
11+ use Magento \Catalog \Api \Data \ProductAttributeInterfaceFactory ;
1012use Magento \Catalog \Api \ProductAttributeManagementInterface ;
1113use Magento \Catalog \Api \ProductAttributeRepositoryInterface ;
1214use Magento \Catalog \Model \Product ;
13- use Magento \Catalog \Model \ResourceModel \Attribute as ResourceModelAttribute ;
14- use Magento \Catalog \Model \ResourceModel \Eav \Attribute as EavAttribute ;
15- use Magento \Eav \Model \AttributeFactory ;
1615use Magento \Eav \Setup \EavSetup ;
16+ use Magento \Framework \Api \DataObjectHelper ;
1717use Magento \Framework \DataObject ;
1818use Magento \TestFramework \Fixture \Api \DataMerger ;
1919use Magento \TestFramework \Fixture \Api \ServiceFactory ;
2020use Magento \TestFramework \Fixture \RevertibleDataFixtureInterface ;
2121use Magento \TestFramework \Fixture \Data \ProcessorInterface ;
2222
23+ /**
24+ * Product attribute fixture
25+ *
26+ * Usage examples:
27+ *
28+ * 1. Create an attribute with default data
29+ * <pre>
30+ * #[
31+ * DataFixture(AttributeFixture::class, as: 'attribute')
32+ * ]
33+ * </pre>
34+ * 2. Create an attribute with custom data
35+ * <pre>
36+ * #[
37+ * DataFixture(AttributeFixture::class, ['is_filterable' => true], 'attribute')
38+ * ]
39+ * </pre>
40+ */
2341class Attribute implements RevertibleDataFixtureInterface
2442{
2543 private const DEFAULT_DATA = [
26- 'is_wysiwyg_enabled ' => false ,
27- 'is_html_allowed_on_front ' => true ,
28- 'used_for_sort_by ' => false ,
29- 'is_filterable ' => false ,
30- 'is_filterable_in_search ' => false ,
31- 'is_used_in_grid ' => true ,
32- 'is_visible_in_grid ' => true ,
33- 'is_filterable_in_grid ' => true ,
34- 'position ' => 0 ,
35- 'apply_to ' => [],
36- 'is_searchable ' => false ,
37- 'is_visible_in_advanced_search ' => false ,
38- 'is_comparable ' => false ,
39- 'is_used_for_promo_rules ' => false ,
40- 'is_visible_on_front ' => false ,
41- 'used_in_product_listing ' => false ,
42- 'is_visible ' => true ,
43- 'scope ' => 'store ' ,
44- 'attribute_code ' => 'product_attribute%uniqid% ' ,
45- 'frontend_input ' => 'text ' ,
46- 'entity_type_id ' => '4 ' ,
47- 'is_required ' => false ,
48- 'options ' => [],
49- 'is_user_defined ' => true ,
44+ ProductAttributeInterface::ATTRIBUTE_ID => null ,
45+ ProductAttributeInterface::ATTRIBUTE_CODE => 'product_attribute%uniqid% ' ,
46+ ProductAttributeInterface::ENTITY_TYPE_ID => '4 ' ,
47+ ProductAttributeInterface::SCOPE => ProductAttributeInterface::SCOPE_GLOBAL_TEXT ,
48+ ProductAttributeInterface::IS_USER_DEFINED => true ,
49+ ProductAttributeInterface::IS_SEARCHABLE => false ,
50+ ProductAttributeInterface::IS_FILTERABLE => false ,
51+ ProductAttributeInterface::IS_FILTERABLE_IN_SEARCH => false ,
52+ ProductAttributeInterface::IS_FILTERABLE_IN_GRID => true ,
53+ ProductAttributeInterface::IS_VISIBLE => true ,
54+ ProductAttributeInterface::IS_VISIBLE_IN_GRID => true ,
55+ ProductAttributeInterface::IS_VISIBLE_IN_ADVANCED_SEARCH => false ,
56+ ProductAttributeInterface::IS_VISIBLE_ON_FRONT => false ,
57+ ProductAttributeInterface::IS_USED_IN_GRID => true ,
58+ ProductAttributeInterface::IS_COMPARABLE => false ,
59+ ProductAttributeInterface::IS_USED_FOR_PROMO_RULES => false ,
60+ ProductAttributeInterface::IS_REQUIRED => false ,
61+ ProductAttributeInterface::IS_UNIQUE => false ,
62+ ProductAttributeInterface::IS_WYSIWYG_ENABLED => false ,
63+ ProductAttributeInterface::IS_HTML_ALLOWED_ON_FRONT => true ,
64+ ProductAttributeInterface::USED_IN_PRODUCT_LISTING => false ,
65+ ProductAttributeInterface::USED_FOR_SORT_BY => false ,
66+ ProductAttributeInterface::POSITION => 0 ,
67+ ProductAttributeInterface::APPLY_TO => [],
68+ ProductAttributeInterface::OPTIONS => [],
69+ ProductAttributeInterface::NOTE => null ,
70+ ProductAttributeInterface::BACKEND_TYPE => 'varchar ' ,
71+ ProductAttributeInterface::BACKEND_MODEL => null ,
72+ ProductAttributeInterface::FRONTEND_INPUT => 'text ' ,
73+ ProductAttributeInterface::FRONTEND_CLASS => null ,
74+ ProductAttributeInterface::SOURCE_MODEL => null ,
75+ ProductAttributeInterface::EXTENSION_ATTRIBUTES_KEY => [],
76+ ProductAttributeInterface::CUSTOM_ATTRIBUTES => [],
77+ ProductAttributeInterface::FRONTEND_LABELS => [],
5078 'default_frontend_label ' => 'Product Attribute%uniqid% ' ,
51- 'frontend_labels ' => [],
52- 'backend_type ' => 'varchar ' ,
53- 'is_unique ' => '0 ' ,
54- 'validation_rules ' => []
79+ 'validation_rules ' => [],
80+ "default_value " => null ,
5581 ];
5682
5783 private const DEFAULT_ATTRIBUTE_SET_DATA = [
@@ -60,96 +86,60 @@ class Attribute implements RevertibleDataFixtureInterface
6086 '_sort_order ' => 0 ,
6187 ];
6288
63- /**
64- * @var ServiceFactory
65- */
66- private $ serviceFactory ;
67-
68- /**
69- * @var ProcessorInterface
70- */
71- private $ dataProcessor ;
72-
73- /**
74- * @var EavSetup
75- */
76- private $ eavSetup ;
77-
78- /**
79- * @var ProductAttributeManagementInterface
80- */
81- private $ productAttributeManagement ;
82-
83- /**
84- * @var AttributeFactory
85- */
86- private AttributeFactory $ attributeFactory ;
87-
88- /**
89- * @var DataMerger
90- */
91- private DataMerger $ dataMerger ;
92-
93- /**
94- * @var ResourceModelAttribute
95- */
96- private ResourceModelAttribute $ resourceModelAttribute ;
97-
98- /**
99- * @var ProductAttributeRepositoryInterface
100- */
101- private ProductAttributeRepositoryInterface $ productAttributeRepository ;
102-
10389 /**
10490 * @param ServiceFactory $serviceFactory
10591 * @param ProcessorInterface $dataProcessor
10692 * @param EavSetup $eavSetup
10793 * @param ProductAttributeManagementInterface $productAttributeManagement
108- * @param AttributeFactory $attributeFactory
109- * @param DataMerger $dataMerger
110- * @param ResourceModelAttribute $resourceModelAttribute
94+ * @param ProductAttributeInterfaceFactory $attributeFactory
11195 * @param ProductAttributeRepositoryInterface $productAttributeRepository
96+ * @param DataObjectHelper $dataObjectHelper
97+ * @param DataMerger $dataMerger
11298 */
11399 public function __construct (
114- ServiceFactory $ serviceFactory ,
115- ProcessorInterface $ dataProcessor ,
116- EavSetup $ eavSetup ,
117- ProductAttributeManagementInterface $ productAttributeManagement ,
118- AttributeFactory $ attributeFactory ,
119- DataMerger $ dataMerger ,
120- ResourceModelAttribute $ resourceModelAttribute ,
121- ProductAttributeRepositoryInterface $ productAttributeRepository
100+ private readonly ServiceFactory $ serviceFactory ,
101+ private readonly ProcessorInterface $ dataProcessor ,
102+ private readonly EavSetup $ eavSetup ,
103+ private readonly ProductAttributeManagementInterface $ productAttributeManagement ,
104+ private readonly ProductAttributeInterfaceFactory $ attributeFactory ,
105+ private readonly ProductAttributeRepositoryInterface $ productAttributeRepository ,
106+ private readonly DataObjectHelper $ dataObjectHelper ,
107+ private readonly DataMerger $ dataMerger
122108 ) {
123- $ this ->serviceFactory = $ serviceFactory ;
124- $ this ->dataProcessor = $ dataProcessor ;
125- $ this ->eavSetup = $ eavSetup ;
126- $ this ->productAttributeManagement = $ productAttributeManagement ;
127- $ this ->attributeFactory = $ attributeFactory ;
128- $ this ->dataMerger = $ dataMerger ;
129- $ this ->resourceModelAttribute = $ resourceModelAttribute ;
130- $ this ->productAttributeRepository = $ productAttributeRepository ;
131109 }
132110
133111 /**
134112 * {@inheritdoc}
135113 * @param array $data Parameters. Same format as Attribute::DEFAULT_DATA.
114+ *
115+ * Additional fields:
116+ * - `_set_id`: int - attribute set ID to assign the attribute to
117+ * - `_group_id`: int - attribute group ID to assign the attribute to
118+ * - `_sort_order`: int - sort order within the attribute group
119+ *
136120 * @return DataObject|null
137121 */
138122 public function apply (array $ data = []): ?DataObject
139123 {
140- if (array_key_exists ('additional_data ' , $ data )) {
141- return $ this ->applyAttributeWithAdditionalData ($ data );
142- }
143-
144- $ attribute = $ this ->attributeFactory ->createAttribute (
145- EavAttribute::class,
146- $ this ->prepareData (array_diff_key ($ data , self ::DEFAULT_ATTRIBUTE_SET_DATA ))
147- );
148- $ attribute = $ this ->productAttributeRepository ->save ($ attribute );
149-
124+ $ attributeData = array_diff_key ($ data , self ::DEFAULT_ATTRIBUTE_SET_DATA );
150125 $ attributeSetData = $ this ->prepareAttributeSetData (
151126 array_intersect_key ($ data , self ::DEFAULT_ATTRIBUTE_SET_DATA )
152127 );
128+
129+ $ attribute = $ this ->attributeFactory ->create ();
130+ $ attributeData = $ this ->prepareData ($ attributeData );
131+
132+ $ this ->dataObjectHelper ->populateWithArray (
133+ $ attribute ,
134+ $ attributeData ,
135+ ProductAttributeInterface::class
136+ );
137+ // Add data that are not part of the interface
138+ $ attribute ->addData (array_diff_key ($ attributeData , self ::DEFAULT_DATA ));
139+ if (isset ($ attributeData ['scope ' ])) {
140+ $ attribute ->setScope ($ attributeData ['scope ' ]);
141+ }
142+ $ attribute = $ this ->productAttributeRepository ->save ($ attribute );
153143
154144 $ this ->productAttributeManagement ->assign (
155145 $ attributeSetData ['_set_id ' ],
@@ -174,26 +164,6 @@ public function revert(DataObject $data): void
174164 );
175165 }
176166
177- /**
178- * @param array $data Parameters. Same format as Attribute::DEFAULT_DATA.
179- * @return DataObject|null
180- */
181- private function applyAttributeWithAdditionalData (array $ data = []): ?DataObject
182- {
183- $ defaultData = array_merge (self ::DEFAULT_DATA , ['additional_data ' => null ]);
184- /** @var EavAttribute $attr */
185- $ attr = $ this ->attributeFactory ->createAttribute (EavAttribute::class, $ defaultData );
186- $ mergedData = $ this ->dataProcessor ->process ($ this , $ this ->dataMerger ->merge ($ defaultData , $ data ));
187-
188- $ attributeSetData = $ this ->prepareAttributeSetData (
189- array_intersect_key ($ data , self ::DEFAULT_ATTRIBUTE_SET_DATA )
190- );
191-
192- $ attr ->setData (array_merge ($ mergedData , $ attributeSetData ));
193- $ this ->resourceModelAttribute ->save ($ attr );
194- return $ attr ;
195- }
196-
197167 /**
198168 * Prepare attribute data
199169 *
@@ -202,7 +172,7 @@ private function applyAttributeWithAdditionalData(array $data = []): ?DataObject
202172 */
203173 private function prepareData (array $ data ): array
204174 {
205- $ data = array_merge (self ::DEFAULT_DATA , $ data );
175+ $ data = $ this -> dataMerger -> merge (self ::DEFAULT_DATA , $ data, false );
206176 $ data ['frontend_label ' ] ??= $ data ['default_frontend_label ' ];
207177
208178 return $ this ->dataProcessor ->process ($ this , $ data );
0 commit comments