10
10
11
11
use GraphQL \Error \UserError ;
12
12
13
+
13
14
/**
14
15
* Class - Order_Mutation
15
16
*/
@@ -86,8 +87,8 @@ public static function create_order( $input, $context, $info ) {
86
87
/**
87
88
* Action called before order is created.
88
89
*
89
- * @param array $input Input data describing order.
90
- * @param \WPGraphQL\AppContext $context Request AppContext instance.
90
+ * @param array $input Input data describing order.
91
+ * @param \WPGraphQL\AppContext $context Request AppContext instance.
91
92
* @param \GraphQL\Type\Definition\ResolveInfo $info Request ResolveInfo instance.
92
93
*/
93
94
do_action ( 'graphql_woocommerce_before_order_create ' , $ input , $ context , $ info );
@@ -118,78 +119,186 @@ public static function create_order( $input, $context, $info ) {
118
119
* @param \WPGraphQL\AppContext $context AppContext instance.
119
120
* @param \GraphQL\Type\Definition\ResolveInfo $info ResolveInfo instance.
120
121
*
122
+ * @throws \Exception Failed to retrieve order.
123
+ *
121
124
* @return void
122
125
*/
123
126
public static function add_items ( $ input , $ order_id , $ context , $ info ) {
127
+ /** @var \WC_Order|false $order */
128
+ $ order = \WC_Order_Factory::get_order ( $ order_id );
129
+ if ( false === $ order ) {
130
+ throw new \Exception ( __ ( 'Failed to retrieve order. ' , 'wp-graphql-woocommerce ' ) );
131
+ }
132
+
124
133
$ item_group_keys = [
125
134
'lineItems ' => 'line_item ' ,
126
135
'shippingLines ' => 'shipping ' ,
127
136
'feeLines ' => 'fee ' ,
128
137
];
129
138
130
- $ item_groups = [];
131
- foreach ( $ input as $ key => $ items ) {
139
+ $ order_items = [];
140
+ foreach ( $ input as $ key => $ group_items ) {
132
141
if ( array_key_exists ( $ key , $ item_group_keys ) ) {
133
- $ type = $ item_group_keys [ $ key ];
142
+ $ type = $ item_group_keys [ $ key ];
143
+ $ order_items [ $ type ] = [];
134
144
135
145
/**
136
146
* Action called before an item group is added to an order.
137
147
*
138
- * @param array $items Item data being added.
139
- * @param integer $order_id ID of target order.
140
- * @param \WPGraphQL\AppContext $context Request AppContext instance.
141
- * @param \GraphQL\Type\Definition\ResolveInfo $info Request ResolveInfo instance.
148
+ * @param array $group_items Items data being added.
149
+ * @param \WC_Order $ order Order object .
150
+ * @param \WPGraphQL\AppContext $context Request AppContext instance.
151
+ * @param \GraphQL\Type\Definition\ResolveInfo $info Request ResolveInfo instance.
142
152
*/
143
- do_action ( "graphql_woocommerce_before_ {$ type }s_added_to_order " , $ items , $ order_id , $ context , $ info );
144
-
145
- foreach ( $ items as $ item_data ) {
146
- // Create Order item.
147
- $ item_id = ( ! empty ( $ item_data ['id ' ] ) && \WC_Order_Factory::get_order_item ( $ item_data ['id ' ] ) )
148
- ? $ item_data ['id ' ]
149
- : \wc_add_order_item ( $ order_id , [ 'order_item_type ' => $ type ] );
150
-
151
- // Continue if order item creation failed.
152
- if ( ! $ item_id ) {
153
- continue ;
153
+ do_action ( "graphql_woocommerce_before_ {$ type }s_added_to_order " , $ group_items , $ order , $ context , $ info );
154
+
155
+ foreach ( $ group_items as $ item_data ) {
156
+ $ item = self ::set_item (
157
+ $ item_data ,
158
+ $ type ,
159
+ $ order ,
160
+ $ context ,
161
+ $ info
162
+ );
163
+
164
+ /**
165
+ * Action called before an item group is added to an order.
166
+ *
167
+ * @param \WC_Order_Item $item Order item object.
168
+ * @param array $item_data Item data being added.
169
+ * @param \WC_Order $order Order object.
170
+ * @param \WPGraphQL\AppContext $context Request AppContext instance.
171
+ * @param \GraphQL\Type\Definition\ResolveInfo $info Request ResolveInfo instance.
172
+ */
173
+ do_action ( "graphql_woocommerce_before_ {$ type }_added_to_order " , $ item , $ item_data , $ order , $ context , $ info );
174
+
175
+ if ( 0 === $ item ->get_id () ) {
176
+ $ order ->add_item ( $ item );
177
+ $ order_items [ $ type ][] = $ item ;
178
+ } else {
179
+ $ item ->save ();
180
+ $ order_items [ $ type ][] = $ item ;
154
181
}
155
-
156
- // Add input item data to order item.
157
- $ item_keys = self ::get_order_item_keys ( $ type );
158
- self ::map_input_to_item ( $ item_id , $ item_data , $ item_keys , $ context , $ info );
159
182
}
160
183
161
184
/**
162
- * Action called after an item group is added to an order.
185
+ * Action called after an item group is added to an order, and before the order has been saved with the new items .
163
186
*
164
- * @param array $items Item data being added.
165
- * @param integer $order_id ID of target order.
166
- * @param \WPGraphQL\AppContext $context Request AppContext instance.
167
- * @param \GraphQL\Type\Definition\ResolveInfo $info Request ResolveInfo instance.
187
+ * @param array $group_items Item data being added.
188
+ * @param \WC_Order $ order Order object .
189
+ * @param \WPGraphQL\AppContext $context Request AppContext instance.
190
+ * @param \GraphQL\Type\Definition\ResolveInfo $info Request ResolveInfo instance.
168
191
*/
169
- do_action ( "graphql_woocommerce_after_ {$ type }s_added_to_order " , $ items , $ order_id , $ context , $ info );
192
+ do_action ( "graphql_woocommerce_after_ {$ type }s_added_to_order " , $ group_items , $ order , $ context , $ info );
170
193
}//end if
171
194
}//end foreach
195
+
196
+ /**
197
+ * Action called after all items have been added and right before the new items have been saved.
198
+ *
199
+ * @param array<string, array<\WC_Order_Item>> $order_items Order items.
200
+ * @param \WC_Order $order WC_Order instance.
201
+ * @param array $input Input data describing order.
202
+ * @param \WPGraphQL\AppContext $context Request AppContext instance.
203
+ * @param \GraphQL\Type\Definition\ResolveInfo $info Request ResolveInfo instance.
204
+ */
205
+ do_action ( 'graphql_woocommerce_before_new_order_items_save ' , $ order_items , $ order , $ input , $ context , $ info );
206
+
207
+ $ order ->save ();
172
208
}
173
209
174
210
/**
175
- * Return array of item mapped with the provided $item_keys and extracts $meta_data
176
211
*
177
- * @param integer $item_id Order item ID .
178
- * @param array $input Item input data .
179
- * @param array $item_keys Item key map .
212
+ * @param array<string, mixed> $item_data Item data .
213
+ * @param string $type Item type .
214
+ * @param \WC_Order $order Order object .
180
215
* @param \WPGraphQL\AppContext $context AppContext instance.
181
216
* @param \GraphQL\Type\Definition\ResolveInfo $info ResolveInfo instance.
182
217
*
183
- * @throws \Exception Failed to retrieve order item | Failed to retrieve connected product.
218
+ * @return \WC_Order_Item
219
+ */
220
+ public static function set_item ( $ item_data , $ type , $ order , $ context , $ info ) {
221
+ $ item_id = ! empty ( $ item_data ['id ' ] ) ? $ item_data ['id ' ] : 0 ;
222
+ $ item_class = self ::get_order_item_classname ( $ type , $ item_id );
223
+
224
+ /** @var \WC_Order_Item $item */
225
+ $ item = new $ item_class ( $ item_id );
226
+
227
+ /**
228
+ * Filter the order item object before it is created.
229
+ *
230
+ * @param \WC_Order_Item $item Order item object.
231
+ * @param array $item_data Item data.
232
+ * @param \WC_Order $order Order object.
233
+ * @param \WPGraphQL\AppContext $context AppContext instance.
234
+ * @param \GraphQL\Type\Definition\ResolveInfo $info ResolveInfo instance.
235
+ */
236
+ $ item = apply_filters ( "graphql_create_order_ {$ type }_object " , $ item , $ item_data , $ order , $ context , $ info );
237
+
238
+ self ::map_input_to_item ( $ item , $ item_data , $ type );
239
+
240
+ /**
241
+ * Action called after an order item is created.
242
+ *
243
+ * @param \WC_Order_Item $item Order item object.
244
+ * @param array $item_data Item data.
245
+ * @param \WC_Order $order Order object.
246
+ * @param \WPGraphQL\AppContext $context AppContext instance.
247
+ * @param \GraphQL\Type\Definition\ResolveInfo $info ResolveInfo instance.
248
+ */
249
+ do_action ( "graphql_create_order_ {$ type }" , $ item , $ item_data , $ order , $ context , $ info );
250
+
251
+ return $ item ;
252
+ }
253
+
254
+ /**
255
+ * Get order item class name.
184
256
*
185
- * @return int
257
+ * @param string $type Order item type.
258
+ * @param int $id Order item ID.
259
+ *
260
+ * @return string
186
261
*/
187
- protected static function map_input_to_item ( $ item_id , $ input , $ item_keys , $ context , $ info ) {
188
- $ order_item = \WC_Order_Factory::get_order_item ( $ item_id );
189
- if ( ! is_object ( $ order_item ) ) {
190
- throw new \Exception ( __ ( 'Failed to retrieve order item. ' , 'wp-graphql-woocommerce ' ) );
262
+ public static function get_order_item_classname ( $ type , $ id = 0 ) {
263
+ $ classname = false ;
264
+ switch ( $ type ) {
265
+ case 'line_item ' :
266
+ case 'product ' :
267
+ $ classname = 'WC_Order_Item_Product ' ;
268
+ break ;
269
+ case 'coupon ' :
270
+ $ classname = 'WC_Order_Item_Coupon ' ;
271
+ break ;
272
+ case 'fee ' :
273
+ $ classname = 'WC_Order_Item_Fee ' ;
274
+ break ;
275
+ case 'shipping ' :
276
+ $ classname = 'WC_Order_Item_Shipping ' ;
277
+ break ;
278
+ case 'tax ' :
279
+ $ classname = 'WC_Order_Item_Tax ' ;
280
+ break ;
191
281
}
192
282
283
+ $ classname = apply_filters ( 'woocommerce_get_order_item_classname ' , $ classname , $ type , $ id ); // phpcs:ignore WordPress.NamingConventions
284
+
285
+ return $ classname ;
286
+ }
287
+
288
+ /**
289
+ * Return array of item mapped with the provided $item_keys and extracts $meta_data
290
+ *
291
+ * @param \WC_Order_Item &$item Order item.
292
+ * @param array $input Item input data.
293
+ * @param string $type Item type.
294
+ *
295
+ * @throws \Exception Failed to retrieve connected product.
296
+ *
297
+ * @return void
298
+ */
299
+ protected static function map_input_to_item ( &$ item , $ input , $ type ) {
300
+ $ item_keys = self ::get_order_item_keys ( $ type );
301
+
193
302
$ args = [];
194
303
$ meta_data = null ;
195
304
foreach ( $ input as $ key => $ value ) {
@@ -203,10 +312,9 @@ protected static function map_input_to_item( $item_id, $input, $item_keys, $cont
203
312
}
204
313
205
314
// Calculate to subtotal/total for line items.
206
-
207
315
if ( isset ( $ args ['quantity ' ] ) ) {
208
- $ product = ( ! empty ( $ order_item ['product_id ' ] ) )
209
- ? wc_get_product ( $ order_item ['product_id ' ] )
316
+ $ product = ( ! empty ( $ item ['product_id ' ] ) )
317
+ ? wc_get_product ( $ item ['product_id ' ] )
210
318
: wc_get_product ( self ::get_product_id ( $ args ) );
211
319
if ( ! is_object ( $ product ) ) {
212
320
throw new \Exception ( __ ( 'Failed to retrieve product connected to order item. ' , 'wp-graphql-woocommerce ' ) );
@@ -219,18 +327,24 @@ protected static function map_input_to_item( $item_id, $input, $item_keys, $cont
219
327
220
328
// Set item props.
221
329
foreach ( $ args as $ key => $ value ) {
222
- if ( is_callable ( [ $ order_item , "set_ {$ key }" ] ) ) {
223
- $ order_item ->{"set_ {$ key }" }( $ value );
330
+ if ( is_callable ( [ $ item , "set_ {$ key }" ] ) ) {
331
+ $ item ->{"set_ {$ key }" }( $ value );
224
332
}
225
333
}
226
334
227
335
// Update item meta data if any is found.
228
- if ( 0 !== $ item_id && ! empty ( $ meta_data ) ) {
229
- // Update item meta data.
230
- self ::update_item_meta_data ( $ item_id , $ meta_data , $ context , $ info );
336
+ if ( empty ( $ meta_data ) ) {
337
+ return ;
231
338
}
232
339
233
- return $ order_item ->save ();
340
+ foreach ( $ meta_data as $ entry ) {
341
+ $ exists = $ item ->get_meta ( $ entry ['key ' ], true , 'edit ' );
342
+ if ( '' !== $ exists && $ exists !== $ entry ['value ' ] ) {
343
+ $ item ->update_meta_data ( $ entry ['key ' ], $ entry ['value ' ] );
344
+ } else {
345
+ $ item ->add_meta_data ( $ entry ['key ' ], $ entry ['value ' ] );
346
+ }
347
+ }
234
348
}
235
349
236
350
/**
@@ -285,10 +399,10 @@ protected static function get_order_item_keys( $type ) {
285
399
protected static function get_product_id ( $ data ) {
286
400
if ( ! empty ( $ data ['sku ' ] ) ) {
287
401
$ product_id = (int ) wc_get_product_id_by_sku ( $ data ['sku ' ] );
288
- } elseif ( ! empty ( $ data ['product_id ' ] ) && empty ( $ data ['variation_id ' ] ) ) {
289
- $ product_id = (int ) $ data ['product_id ' ];
290
402
} elseif ( ! empty ( $ data ['variation_id ' ] ) ) {
291
403
$ product_id = (int ) $ data ['variation_id ' ];
404
+ } elseif ( ! empty ( $ data ['product_id ' ] ) ) {
405
+ $ product_id = (int ) $ data ['product_id ' ];
292
406
} else {
293
407
throw new UserError ( __ ( 'Product ID or SKU is required. ' , 'wp-graphql-woocommerce ' ) );
294
408
}
0 commit comments