PluginProbe ʕ •ᴥ•ʔ
OttoKit: All-in-One Automation Platform / 1.1.33
OttoKit: All-in-One Automation Platform v1.1.33
1.1.33 1.1.32 1.1.31 1.1.30 1.1.29 1.1.28 1.1.27 1.1.9 trunk 1.0.10 1.0.11 1.0.12 1.0.13 1.0.14 1.0.15 1.0.16 1.0.17 1.0.18 1.0.19 1.0.20 1.0.21 1.0.22 1.0.23 1.0.24 1.0.25 1.0.26 1.0.27 1.0.28 1.0.29 1.0.30 1.0.31 1.0.32 1.0.33 1.0.34 1.0.35 1.0.36 1.0.37 1.0.38 1.0.39 1.0.40 1.0.41 1.0.42 1.0.43 1.0.44 1.0.45 1.0.46 1.0.47 1.0.48 1.0.49 1.0.50 1.0.51 1.0.52 1.0.53 1.0.54 1.0.55 1.0.56 1.0.57 1.0.58 1.0.59 1.0.60 1.0.61 1.0.62 1.0.63 1.0.64 1.0.65 1.0.66 1.0.67 1.0.68 1.0.69 1.0.7 1.0.70 1.0.71 1.0.72 1.0.73 1.0.74 1.0.75 1.0.76 1.0.77 1.0.78 1.0.79 1.0.8 1.0.80 1.0.81 1.0.82 1.0.83 1.0.84 1.0.85 1.0.86 1.0.87 1.0.88 1.0.89 1.0.9 1.0.90 1.1.0 1.1.1 1.1.10 1.1.11 1.1.12 1.1.13 1.1.14 1.1.15 1.1.16 1.1.17 1.1.18 1.1.19 1.1.2 1.1.20 1.1.21 1.1.22 1.1.23 1.1.24 1.1.25 1.1.26 1.1.3 1.1.4 1.1.5 1.1.6 1.1.7 1.1.8
suretriggers / src / Integrations / woocommerce / actions / create-variable-product.php
suretriggers / src / Integrations / woocommerce / actions Last commit date
add-attribute-terms.php 8 months ago add-customer.php 9 months ago add-emails-to-coupon.php 8 months ago add-order-note.php 10 months ago add-product-attributes.php 8 months ago add-product-to-cart.php 8 months ago add-update-order-custom-fields.php 10 months ago apply-coupon.php 8 months ago check-cart-status.php 8 months ago check-if-order-is-renewal.php 4 months ago create-attribute.php 8 months ago create-bundle-product-order.php 6 months ago create-coupon-code.php 10 months ago create-multi-product-order.php 7 months ago create-new-order.php 10 months ago create-product-variation.php 1 year ago create-product.php 1 year ago create-variable-product.php 8 months ago delete-coupon.php 1 year ago delete-customer.php 8 months ago fetch-coupon-details.php 10 months ago find-orders-by-user-id.php 10 months ago get-all-customers.php 8 months ago get-all-products.php 8 months ago get-customer-by-email.php 8 months ago get-customer-by-id.php 8 months ago get-order-details-by-order-id.php 10 months ago get-order-type-by-order-id.php 4 months ago get-orders-by-email.php 8 months ago get-product-by-id.php 1 year ago list-all-order-notes.php 8 months ago list-all-orders.php 8 months ago remove-product-from-cart.php 8 months ago retrieve-coupons-totals.php 10 months ago retrieve-customers-totals.php 10 months ago retrieve-orders-totals.php 10 months ago retrieve-products-totals.php 10 months ago retrieve-refunds-list.php 10 months ago retrieve-reviews-totals.php 10 months ago retrieve-sales-report.php 10 months ago retrieve-top-sellers-report.php 10 months ago update-product-price.php 8 months ago update-product-stock-quantity.php 8 months ago update-status-of-order.php 10 months ago
create-variable-product.php
749 lines
1 <?php
2 /**
3 * CreateVariableProduct.
4 * php version 5.6
5 *
6 * @category CreateVariableProduct
7 * @package SureTriggers
8 * @author BSF <username@example.com>
9 * @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
10 * @link https://www.brainstormforce.com/
11 * @since 1.0.0
12 */
13
14 namespace SureTriggers\Integrations\Woocommerce\Actions;
15
16 use Exception;
17 use SureTriggers\Integrations\AutomateAction;
18 use SureTriggers\Integrations\WooCommerce\WooCommerce;
19 use SureTriggers\Traits\SingletonLoader;
20
21 /**
22 * CreateVariableProduct
23 *
24 * @category CreateVariableProduct
25 * @package SureTriggers
26 * @author BSF <username@example.com>
27 * @license https://www.gnu.org/licenses/gpl-3.0.html GPLv3
28 * @link https://www.brainstormforce.com/
29 * @since 1.0.0
30 */
31 class CreateVariableProduct extends AutomateAction {
32
33 /**
34 * Integration type.
35 *
36 * @var string
37 */
38 public $integration = 'WooCommerce';
39
40 /**
41 * Action name.
42 *
43 * @var string
44 */
45 public $action = 'wc_create_variable_product';
46
47 use SingletonLoader;
48
49 /**
50 * Register a action.
51 *
52 * @param array $actions actions.
53 * @return array
54 */
55 public function register( $actions ) {
56 $actions[ $this->integration ][ $this->action ] = [
57 'label' => __( 'Create Variable Product', 'suretriggers' ),
58 'action' => 'wc_create_variable_product',
59 'function' => [ $this, 'action_listener' ],
60 ];
61 return $actions;
62 }
63
64 /**
65 * Action listener.
66 *
67 * @param int $user_id user_id.
68 * @param int $automation_id automation_id.
69 * @param array $fields fields.
70 * @param array $selected_options selectedOptions.
71 *
72 * @return void|array|bool
73 */
74 public function _action_listener( $user_id, $automation_id, $fields, $selected_options ) {
75 if ( ! function_exists( 'WC' ) || ! function_exists( 'wc_get_product' ) ) {
76 return [
77 'status' => 'error',
78 'message' => __( 'WooCommerce not available', 'suretriggers' ),
79 ];
80 }
81
82 // Validate required fields.
83 foreach ( $fields as $field ) {
84 if ( array_key_exists( 'validationProps', $field ) && empty( $selected_options[ $field['name'] ] ) ) {
85 return [
86 'status' => 'error',
87 'message' => __( 'Required field is missing: ', 'suretriggers' ) . $field['name'],
88 ];
89 }
90 }
91
92 // Get basic product parameters.
93 $product_name = ! empty( $selected_options['product_name'] ) ? sanitize_text_field( $selected_options['product_name'] ) : '';
94 $product_description = ! empty( $selected_options['product_description'] ) ? wp_kses_post( $selected_options['product_description'] ) : '';
95 $product_short_description = ! empty( $selected_options['product_short_description'] ) ? wp_kses_post( $selected_options['product_short_description'] ) : '';
96 $product_sku = ! empty( $selected_options['product_sku'] ) ? sanitize_text_field( $selected_options['product_sku'] ) : '';
97 $product_status = ! empty( $selected_options['product_status'] ) ? sanitize_text_field( $selected_options['product_status'] ) : 'publish';
98 $product_catalog_visibility = ! empty( $selected_options['product_catalog_visibility'] ) ? sanitize_text_field( $selected_options['product_catalog_visibility'] ) : 'visible';
99 $product_featured = ! empty( $selected_options['product_featured'] ) ? (bool) $selected_options['product_featured'] : false;
100 $tax_status = ! empty( $selected_options['tax_status'] ) ? sanitize_text_field( $selected_options['tax_status'] ) : 'taxable';
101 $tax_class = ! empty( $selected_options['tax_class'] ) ? sanitize_text_field( $selected_options['tax_class'] ) : '';
102
103 // Categories and tags.
104 $product_categories = ! empty( $selected_options['product_categories'] ) ? $selected_options['product_categories'] : [];
105 $product_tags = ! empty( $selected_options['product_tags'] ) ? $selected_options['product_tags'] : [];
106
107 // Images.
108 $featured_image = ! empty( $selected_options['featured_image'] ) ? $selected_options['featured_image'] : '';
109 $product_images = ! empty( $selected_options['product_images'] ) ? $selected_options['product_images'] : [];
110
111 // Attributes for variations.
112 $product_attributes = ! empty( $selected_options['product_attributes'] ) ? $selected_options['product_attributes'] : [];
113
114 // Variations data.
115 $variations_data = ! empty( $selected_options['variations'] ) ? $selected_options['variations'] : [];
116
117 // Legacy field support for variations.
118 if ( empty( $variations_data ) && ! empty( $selected_options['product_variations'] ) ) {
119 $variations_data = $selected_options['product_variations'];
120 }
121
122 if ( empty( $product_name ) ) {
123 return [
124 'status' => 'error',
125 'message' => __( 'Product name is required', 'suretriggers' ),
126 ];
127 }
128
129 try {
130 // Create the variable product.
131 $product = new \WC_Product_Variable();
132
133 // Set basic product data.
134 $product->set_name( $product_name );
135 $product->set_description( $product_description );
136 $product->set_short_description( $product_short_description );
137
138 // Set SKU if provided.
139 if ( ! empty( $product_sku ) ) {
140 $product->set_sku( $product_sku );
141 }
142
143 $product->set_status( $product_status );
144 $product->set_catalog_visibility( $product_catalog_visibility );
145 $product->set_featured( $product_featured );
146
147 // Set tax settings.
148 $product->set_tax_status( $tax_status );
149 if ( ! empty( $tax_class ) ) {
150 $product->set_tax_class( $tax_class );
151 }
152
153 // Set manage stock to false for variable product (variations will manage their own stock).
154 $product->set_manage_stock( false );
155
156 // Save the product first to get an ID.
157 $product_id = $product->save();
158
159 // Set categories.
160 if ( ! empty( $product_categories ) ) {
161 $category_ids = $product_categories;
162
163 if ( is_string( $category_ids ) ) {
164 $category_ids = array_map( 'intval', explode( ',', $category_ids ) );
165 } elseif ( is_array( $category_ids ) ) {
166 $category_ids = array_map(
167 function( $item ) {
168 return isset( $item['value'] ) ? intval( $item['value'] ) : 0;
169 },
170 $category_ids
171 );
172 $category_ids = array_filter( $category_ids );
173 }
174
175 if ( ! empty( $category_ids ) ) {
176 wp_set_object_terms( $product_id, $category_ids, 'product_cat' );
177 }
178 }
179
180 // Set tags.
181 if ( ! empty( $product_tags ) ) {
182 $tag_ids = $product_tags;
183
184 if ( is_string( $tag_ids ) ) {
185 $tag_ids = array_map( 'intval', explode( ',', $tag_ids ) );
186 } elseif ( is_array( $tag_ids ) ) {
187 $tag_ids = array_map(
188 function( $item ) {
189 return isset( $item['value'] ) ? intval( $item['value'] ) : 0;
190 },
191 $tag_ids
192 );
193 $tag_ids = array_filter( $tag_ids );
194 }
195
196 if ( ! empty( $tag_ids ) ) {
197 wp_set_object_terms( $product_id, $tag_ids, 'product_tag' );
198 }
199 }
200
201 if ( ! $product_id ) {
202 return [
203 'status' => 'error',
204 'message' => __( 'Failed to create product', 'suretriggers' ),
205 ];
206 }
207
208 // Handle product images.
209 $this->handle_product_images( $product_id, $featured_image, $product_images );
210
211 // Process attributes for variations.
212 $processed_attributes = [];
213 $created_attributes = [];
214 $failed_attributes = [];
215
216 if ( ! empty( $product_attributes ) ) {
217
218 foreach ( $product_attributes as $index => $attribute_data ) {
219 if ( ! is_array( $attribute_data ) ) {
220 $failed_attributes[] = [
221 'data' => $attribute_data,
222 'error' => __( 'Invalid attribute data format', 'suretriggers' ),
223 ];
224 continue;
225 }
226
227 // Extract attribute information.
228 $attribute_name = ! empty( $attribute_data['name'] ) ? sanitize_text_field( $attribute_data['name'] ) : '';
229 $attribute_slug = ! empty( $attribute_data['slug'] ) ? sanitize_text_field( $attribute_data['slug'] ) : '';
230 $attribute_values = ! empty( $attribute_data['values'] ) ? $attribute_data['values'] : '';
231 $is_visible = isset( $attribute_data['visible'] ) ? (bool) $attribute_data['visible'] : true;
232 $is_variation = isset( $attribute_data['variation'] ) ? (bool) $attribute_data['variation'] : true; // Default true for variable products.
233 $is_taxonomy = isset( $attribute_data['taxonomy'] ) ? (bool) $attribute_data['taxonomy'] : false;
234
235 // Legacy field support.
236 if ( empty( $attribute_name ) && ! empty( $attribute_data['attribute_name'] ) ) {
237 $attribute_name = sanitize_text_field( $attribute_data['attribute_name'] );
238 }
239 if ( empty( $attribute_slug ) && ! empty( $attribute_data['attribute_slug'] ) ) {
240 $attribute_slug = sanitize_text_field( $attribute_data['attribute_slug'] );
241 }
242 if ( empty( $attribute_values ) && ! empty( $attribute_data['attribute_values'] ) ) {
243 $attribute_values = $attribute_data['attribute_values'];
244 }
245 if ( ! isset( $attribute_data['visible'] ) && isset( $attribute_data['is_visible'] ) ) {
246 $is_visible = (bool) $attribute_data['is_visible'];
247 }
248 if ( ! isset( $attribute_data['variation'] ) && isset( $attribute_data['is_variation'] ) ) {
249 $is_variation = (bool) $attribute_data['is_variation'];
250 }
251 if ( ! isset( $attribute_data['taxonomy'] ) && isset( $attribute_data['is_taxonomy'] ) ) {
252 $is_taxonomy = (bool) $attribute_data['is_taxonomy'];
253 }
254
255 if ( empty( $attribute_name ) || empty( $attribute_values ) ) {
256 $failed_attributes[] = [
257 'data' => $attribute_data,
258 'error' => __( 'Attribute name and values are required', 'suretriggers' ),
259 ];
260 continue;
261 }
262
263 // Generate slug if not provided.
264 if ( empty( $attribute_slug ) ) {
265 $attribute_slug = sanitize_title( $attribute_name );
266 }
267
268 // Prepare values array.
269 if ( is_string( $attribute_values ) ) {
270 if ( strpos( $attribute_values, '|' ) !== false ) {
271 $values_array = array_map( 'trim', explode( '|', $attribute_values ) );
272 } elseif ( strpos( $attribute_values, ',' ) !== false ) {
273 $values_array = array_map( 'trim', explode( ',', $attribute_values ) );
274 } else {
275 $values_array = [ trim( $attribute_values ) ];
276 }
277 } elseif ( is_array( $attribute_values ) ) {
278 $values_array = array_map( 'trim', $attribute_values );
279 } else {
280 $values_array = [ trim( $attribute_values ) ];
281 }
282
283 // Remove empty values.
284 $values_array = array_filter(
285 $values_array,
286 function( $value ) {
287 return ! empty( $value );
288 }
289 );
290
291 if ( empty( $values_array ) ) {
292 $failed_attributes[] = [
293 'data' => $attribute_data,
294 'error' => __( 'No valid attribute values provided', 'suretriggers' ),
295 ];
296 continue;
297 }
298
299 try {
300 $attribute_key = sanitize_title( $attribute_name );
301 $taxonomy_name = '';
302 if ( $is_taxonomy ) {
303 // Handle taxonomy-based attributes.
304 if ( strpos( $attribute_slug, 'pa_' ) === 0 ) {
305 $taxonomy_name = $attribute_slug;
306 } else {
307 $taxonomy_name = 'pa_' . $attribute_slug;
308 }
309
310 // Ensure taxonomy exists.
311 if ( ! taxonomy_exists( $taxonomy_name ) ) {
312 // Create the attribute in WooCommerce.
313 $attribute_args = [
314 'name' => $attribute_name,
315 'slug' => str_replace( 'pa_', '', $taxonomy_name ),
316 'type' => 'select',
317 'order_by' => 'menu_order',
318 'has_archives' => false,
319 ];
320
321 $attribute_id = wc_create_attribute( $attribute_args );
322
323 if ( is_wp_error( $attribute_id ) ) {
324 $failed_attributes[] = [
325 'data' => $attribute_data,
326 'error' => sprintf( __( 'Failed to create attribute: %s', 'suretriggers' ), $attribute_id->get_error_message() ),
327 ];
328 continue;
329 }
330
331 // Register the taxonomy.
332 register_taxonomy(
333 $taxonomy_name,
334 'product',
335 [
336 'labels' => [
337 'name' => $attribute_name,
338 ],
339 'hierarchical' => false,
340 'public' => false,
341 'show_ui' => false,
342 'query_var' => true,
343 'rewrite' => false,
344 ]
345 );
346 }
347
348 // Create terms and get term IDs.
349 $term_ids = [];
350 foreach ( $values_array as $value ) {
351 $term = get_term_by( 'name', $value, $taxonomy_name );
352 if ( ! $term ) {
353 $term_result = wp_insert_term( $value, $taxonomy_name );
354 if ( ! is_wp_error( $term_result ) ) {
355 $term_ids[] = $term_result['term_id'];
356 }
357 } else {
358 $term_ids[] = $term->term_id;
359 }
360 }
361
362 if ( ! empty( $term_ids ) ) {
363 wp_set_object_terms( $product_id, $term_ids, $taxonomy_name );
364 }
365
366 // Create WC_Product_Attribute object.
367 $attribute_object = new \WC_Product_Attribute();
368 $attribute_object->set_name( $taxonomy_name );
369 $attribute_object->set_options( $term_ids );
370 $attribute_object->set_visible( $is_visible );
371 $attribute_object->set_variation( $is_variation );
372
373 $processed_attributes[ $taxonomy_name ] = $attribute_object;
374
375 } else {
376 // Handle custom (non-taxonomy) attributes.
377
378 // Create WC_Product_Attribute object.
379 $attribute_object = new \WC_Product_Attribute();
380 $attribute_object->set_name( $attribute_name );
381 $attribute_object->set_options( $values_array );
382 $attribute_object->set_visible( $is_visible );
383 $attribute_object->set_variation( $is_variation );
384
385 $processed_attributes[ $attribute_key ] = $attribute_object;
386 }
387
388 $created_attributes[] = [
389 'name' => $attribute_name,
390 'slug' => $is_taxonomy ? $taxonomy_name : $attribute_key,
391 'values' => $values_array,
392 'is_taxonomy' => $is_taxonomy,
393 'visible' => $is_visible,
394 'variation' => $is_variation,
395 ];
396
397
398 } catch ( Exception $e ) {
399 $failed_attributes[] = [
400 'data' => $attribute_data,
401 'error' => $e->getMessage(),
402 ];
403 }
404 }
405
406 // Set attributes on the product.
407 if ( ! empty( $processed_attributes ) ) {
408 $product->set_attributes( $processed_attributes );
409 $product->save();
410 }
411 }
412
413 // Create variations.
414 $created_variations = [];
415 $failed_variations = [];
416
417 if ( ! empty( $variations_data ) ) {
418
419 foreach ( $variations_data as $index => $variation_data ) {
420 if ( ! is_array( $variation_data ) ) {
421 $failed_variations[] = [
422 'data' => $variation_data,
423 'error' => __( 'Invalid variation data format', 'suretriggers' ),
424 ];
425 continue;
426 }
427
428 try {
429 // Create variation.
430 $variation = new \WC_Product_Variation();
431 $variation->set_parent_id( $product_id );
432
433 // Set variation attributes.
434 $variation_attributes = [];
435 if ( ! empty( $variation_data['attributes'] ) ) {
436 foreach ( $variation_data['attributes'] as $attr_name => $attr_value ) {
437 $variation_attributes[ $attr_name ] = sanitize_text_field( $attr_value );
438 }
439 }
440
441 if ( empty( $variation_attributes ) && ! empty( $variation_data['variation_attributes'] ) ) {
442 $attr_data = $variation_data['variation_attributes'];
443 if ( is_string( $attr_data ) ) {
444 $decoded = json_decode( $attr_data, true );
445 if ( json_last_error() === JSON_ERROR_NONE && is_array( $decoded ) ) {
446 foreach ( $decoded as $attr_name => $value ) {
447 $found_match = false;
448
449 if ( ! empty( $processed_attributes ) ) {
450 foreach ( $processed_attributes as $attr_key => $attr_obj ) {
451 $clean_name = str_replace( 'pa_', '', $attr_key );
452 $normalized_input = strtolower( str_replace( ' ', '_', $attr_name ) );
453
454 if ( $normalized_input === $clean_name || strtolower( $attr_name ) === $clean_name ) {
455 $variation_attributes[ $attr_key ] = sanitize_text_field( $value );
456 $found_match = true;
457 break;
458 }
459 }
460 }
461
462 if ( ! $found_match ) {
463 $slug = 'pa_' . strtolower( str_replace( ' ', '_', $attr_name ) );
464 $variation_attributes[ $slug ] = sanitize_text_field( $value );
465 }
466 }
467 }
468 }
469 }
470
471 if ( ! empty( $variation_attributes ) ) {
472 $variation->set_attributes( $variation_attributes );
473 }
474
475 $sku = ! empty( $variation_data['sku'] ) ? $variation_data['sku'] :
476 ( ! empty( $variation_data['variation_sku'] ) ? $variation_data['variation_sku'] : '' );
477 if ( ! empty( $sku ) ) {
478 $variation->set_sku( sanitize_text_field( $sku ) );
479 }
480
481 if ( ! empty( $variation_data['regular_price'] ) ) {
482 $variation->set_regular_price( (string) floatval( $variation_data['regular_price'] ) );
483 }
484
485 if ( ! empty( $variation_data['sale_price'] ) ) {
486 $variation->set_sale_price( (string) floatval( $variation_data['sale_price'] ) );
487 }
488
489 $manage_stock = isset( $variation_data['manage_stock'] ) ? $variation_data['manage_stock'] : false;
490 if ( $manage_stock ) {
491 $variation->set_manage_stock( true );
492 if ( ! empty( $variation_data['stock_quantity'] ) ) {
493 $variation->set_stock_quantity( intval( $variation_data['stock_quantity'] ) );
494 }
495 }
496
497 if ( ! empty( $variation_data['weight'] ) ) {
498 $variation->set_weight( floatval( $variation_data['weight'] ) );
499 }
500
501 // Separate dimension fields.
502 if ( ! empty( $variation_data['length'] ) ) {
503 $variation->set_length( floatval( $variation_data['length'] ) );
504 }
505 if ( ! empty( $variation_data['width'] ) ) {
506 $variation->set_width( floatval( $variation_data['width'] ) );
507 }
508 if ( ! empty( $variation_data['height'] ) ) {
509 $variation->set_height( floatval( $variation_data['height'] ) );
510 }
511
512 // Set tax settings for variation.
513 if ( ! empty( $variation_data['tax_status'] ) ) {
514 $variation->set_tax_status( sanitize_text_field( $variation_data['tax_status'] ) );
515 }
516 if ( ! empty( $variation_data['tax_class'] ) ) {
517 $variation->set_tax_class( sanitize_text_field( $variation_data['tax_class'] ) );
518 }
519
520 // Save variation.
521 $variation_id = $variation->save();
522
523 if ( $variation_id ) {
524 // Handle variation image.
525 if ( ! empty( $variation_data['variation_image'] ) ) {
526 $this->handle_variation_image( $variation_id, $variation_data['variation_image'] );
527 }
528
529 $created_variations[] = [
530 'id' => $variation_id,
531 'sku' => $variation->get_sku(),
532 'price' => $variation->get_price(),
533 'attributes' => $variation->get_attributes(),
534 ];
535 } else {
536 $failed_variations[] = [
537 'data' => $variation_data,
538 'error' => __( 'Failed to create variation', 'suretriggers' ),
539 ];
540 }
541 } catch ( Exception $e ) {
542 $failed_variations[] = [
543 'data' => $variation_data,
544 'error' => $e->getMessage(),
545 ];
546 }
547 }
548 }
549
550 // Sync variable product with its variations.
551 \WC_Product_Variable::sync( $product_id );
552
553 wc_delete_product_transients( $product_id );
554 wp_cache_delete( 'wc_product_' . $product_id, 'products' );
555
556 // Get final product data.
557 $final_product = wc_get_product( $product_id );
558 if ( ! $final_product ) {
559 return [
560 'status' => 'error',
561 'message' => __( 'Failed to retrieve created product', 'suretriggers' ),
562 ];
563 }
564
565 $response = [
566 'status' => 'success',
567 'message' => sprintf(
568 __( 'Variable product created successfully with %1$d attributes and %2$d variations', 'suretriggers' ),
569 count( $created_attributes ),
570 count( $created_variations )
571 ),
572 'product_id' => $product_id,
573 'product_name' => $final_product->get_name(),
574 'product_type' => $final_product->get_type(),
575 'product_status' => $final_product->get_status(),
576 'product_sku' => $final_product->get_sku(),
577 'product_permalink' => $final_product->get_permalink(),
578 'created_attributes' => $created_attributes,
579 'failed_attributes' => $failed_attributes,
580 'created_variations' => $created_variations,
581 'failed_variations' => $failed_variations,
582 'summary' => [
583 'attributes_created' => count( $created_attributes ),
584 'attributes_failed' => count( $failed_attributes ),
585 'variations_created' => count( $created_variations ),
586 'variations_failed' => count( $failed_variations ),
587 ],
588 ];
589
590 return $response;
591
592 } catch ( Exception $e ) {
593 return [
594 'status' => 'error',
595 'message' => __( 'Error creating variable product: ', 'suretriggers' ) . $e->getMessage(),
596 ];
597 }
598 }
599
600 /**
601 * Handle product images - featured and gallery images.
602 *
603 * @param int $product_id Product ID.
604 * @param string $featured_image Featured image URL.
605 * @param mixed $product_images Gallery images URLs.
606 * @return void
607 */
608 private function handle_product_images( $product_id, $featured_image, $product_images ) {
609 try {
610 // Handle featured image.
611 if ( ! empty( $featured_image ) ) {
612 $featured_attachment_id = $this->upload_image_from_url( $featured_image, $product_id );
613 if ( $featured_attachment_id ) {
614 set_post_thumbnail( $product_id, $featured_attachment_id );
615 }
616 }
617
618 // Handle gallery images.
619 if ( ! empty( $product_images ) ) {
620 $gallery_ids = [];
621
622 if ( is_string( $product_images ) ) {
623 $gallery_urls = explode( ',', $product_images );
624 foreach ( $gallery_urls as $url ) {
625 $url = trim( $url );
626 if ( ! empty( $url ) && filter_var( $url, FILTER_VALIDATE_URL ) ) {
627 $attachment_id = $this->upload_image_from_url( $url, $product_id );
628 if ( $attachment_id ) {
629 $gallery_ids[] = $attachment_id;
630 }
631 }
632 }
633 } elseif ( is_array( $product_images ) ) {
634 foreach ( $product_images as $image_url ) {
635 if ( ! is_string( $image_url ) ) {
636 continue;
637 }
638 $image_url = trim( $image_url );
639 if ( ! empty( $image_url ) && filter_var( $image_url, FILTER_VALIDATE_URL ) ) {
640 $attachment_id = $this->upload_image_from_url( $image_url, $product_id );
641 if ( $attachment_id ) {
642 $gallery_ids[] = $attachment_id;
643 }
644 }
645 }
646 }
647
648 if ( ! empty( $gallery_ids ) ) {
649 $product = wc_get_product( $product_id );
650 if ( $product ) {
651 $product->set_gallery_image_ids( $gallery_ids );
652 $product->save();
653 }
654 }
655 }
656 } catch ( Exception $e ) {
657 // Silently ignore image handling errors to prevent disrupting product creation.
658 return;
659 }
660 }
661
662 /**
663 * Upload image from URL to WordPress media library.
664 *
665 * @param string $image_url Image URL.
666 * @param int $product_id Product ID for attachment.
667 * @return int|false Attachment ID or false on failure.
668 */
669 private function upload_image_from_url( $image_url, $product_id = 0 ) {
670 try {
671 // Validate URL.
672 if ( ! filter_var( $image_url, FILTER_VALIDATE_URL ) ) {
673 return false;
674 }
675
676 // Get image data.
677 $response = wp_remote_get( $image_url, [ 'timeout' => 3 ] );
678
679 if ( is_wp_error( $response ) || wp_remote_retrieve_response_code( $response ) !== 200 ) {
680 return false;
681 }
682
683 $image_data = wp_remote_retrieve_body( $response );
684 if ( empty( $image_data ) ) {
685 return false;
686 }
687
688 // Get filename from URL.
689 $parsed_path = wp_parse_url( $image_url, PHP_URL_PATH );
690 $filename = $parsed_path ? basename( $parsed_path ) : '';
691 if ( empty( $filename ) || strpos( $filename, '.' ) === false ) {
692 $filename = 'product-image-' . time() . '.jpg';
693 }
694
695 // Upload to WordPress.
696 $upload = wp_upload_bits( $filename, null, $image_data );
697
698 if ( $upload['error'] ) {
699 return false;
700 }
701
702 // Create attachment.
703 $attachment = [
704 'post_mime_type' => $upload['type'],
705 'post_title' => sanitize_file_name( $filename ),
706 'post_content' => '',
707 'post_status' => 'inherit',
708 'post_parent' => $product_id,
709 ];
710
711 $attachment_id = wp_insert_attachment( $attachment, $upload['file'], $product_id );
712
713 if ( $attachment_id > 0 ) {
714 require_once ABSPATH . 'wp-admin/includes/image.php';
715 $attachment_data = wp_generate_attachment_metadata( $attachment_id, $upload['file'] );
716 wp_update_attachment_metadata( $attachment_id, $attachment_data );
717 return $attachment_id;
718 }
719
720 return false;
721 } catch ( Exception $e ) {
722 return false;
723 }
724 }
725
726 /**
727 * Handle variation image.
728 *
729 * @param int $variation_id Variation ID.
730 * @param string $image_url Image URL.
731 * @return void
732 */
733 private function handle_variation_image( $variation_id, $image_url ) {
734 try {
735 if ( ! empty( $image_url ) && filter_var( $image_url, FILTER_VALIDATE_URL ) ) {
736 $attachment_id = $this->upload_image_from_url( $image_url, $variation_id );
737 if ( $attachment_id ) {
738 set_post_thumbnail( $variation_id, $attachment_id );
739 }
740 }
741 } catch ( Exception $e ) {
742 // Silently ignore variation image errors.
743 return;
744 }
745 }
746 }
747
748 CreateVariableProduct::get_instance();
749