1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/** |
4
|
|
|
* Manage Products. |
5
|
|
|
* |
6
|
|
|
* @since 2.5.0 |
7
|
|
|
* @package WooCommerce/CLI |
8
|
|
|
* @category CLI |
9
|
|
|
* @author WooThemes |
10
|
|
|
*/ |
11
|
|
|
class WC_CLI_Product extends WC_CLI_Command { |
12
|
|
|
|
13
|
|
|
/** |
14
|
|
|
* Create a product. |
15
|
|
|
* |
16
|
|
|
* ## OPTIONS |
17
|
|
|
* |
18
|
|
|
* [--<field>=<value>] |
19
|
|
|
* : Associative args for the new product. |
20
|
|
|
* |
21
|
|
|
* [--porcelain] |
22
|
|
|
* : Outputs just the new product id. |
23
|
|
|
* |
24
|
|
|
* ## AVAILABLE FIELDS |
25
|
|
|
* |
26
|
|
|
* Required fields: |
27
|
|
|
* |
28
|
|
|
* * title |
29
|
|
|
* |
30
|
|
|
* These fields are optionally available for create command: |
31
|
|
|
* |
32
|
|
|
* * type |
33
|
|
|
* * status |
34
|
|
|
* * downloadable |
35
|
|
|
* * virtual |
36
|
|
|
* * sku |
37
|
|
|
* * regular_price |
38
|
|
|
* * sale_price |
39
|
|
|
* * sale_price_dates_from |
40
|
|
|
* * sale_price_dates_to |
41
|
|
|
* * tax_status |
42
|
|
|
* * tax_class |
43
|
|
|
* * managing_stock |
44
|
|
|
* * stock_quantity |
45
|
|
|
* * in_stock |
46
|
|
|
* * backorders |
47
|
|
|
* * sold_individually |
48
|
|
|
* * featured |
49
|
|
|
* * shipping_class |
50
|
|
|
* * description |
51
|
|
|
* * enable_html_description |
52
|
|
|
* * short_description |
53
|
|
|
* * enable_html_short_description |
54
|
|
|
* * reviews_allowed |
55
|
|
|
* * upsell_ids |
56
|
|
|
* * cross_sell_ids |
57
|
|
|
* * parent_id |
58
|
|
|
* * categories |
59
|
|
|
* * tags |
60
|
|
|
* |
61
|
|
|
* Dimensions fields: |
62
|
|
|
* |
63
|
|
|
* * dimensions.length |
64
|
|
|
* * dimensions.width |
65
|
|
|
* * dimensions.height |
66
|
|
|
* * dimensions.unit |
67
|
|
|
* |
68
|
|
|
* Images is an array in which element can be set by specifying its index: |
69
|
|
|
* |
70
|
|
|
* * images |
71
|
|
|
* * images.size |
72
|
|
|
* * images.0.id |
73
|
|
|
* * images.0.created_at |
74
|
|
|
* * images.0.updated_at |
75
|
|
|
* * images.0.src |
76
|
|
|
* * images.0.title |
77
|
|
|
* * images.0.alt |
78
|
|
|
* * images.0.position |
79
|
|
|
* |
80
|
|
|
* Attributes is an array in which element can be set by specifying its index: |
81
|
|
|
* |
82
|
|
|
* * attributes |
83
|
|
|
* * attributes.size |
84
|
|
|
* * attributes.0.name |
85
|
|
|
* * attributes.0.slug |
86
|
|
|
* * attributes.0.position |
87
|
|
|
* * attributes.0.visible |
88
|
|
|
* * attributes.0.variation |
89
|
|
|
* * attributes.0.options |
90
|
|
|
* |
91
|
|
|
* Downloads is an array in which element can be accessed by specifying its index: |
92
|
|
|
* |
93
|
|
|
* * downloads |
94
|
|
|
* * downloads.size |
95
|
|
|
* * downloads.0.id |
96
|
|
|
* * downloads.0.name |
97
|
|
|
* * downloads.0.file |
98
|
|
|
* |
99
|
|
|
* Variations is an array in which element can be accessed by specifying its index: |
100
|
|
|
* |
101
|
|
|
* * variations |
102
|
|
|
* * variations.size |
103
|
|
|
* * variations.0.id |
104
|
|
|
* * variations.0.created_at |
105
|
|
|
* * variations.0.updated_at |
106
|
|
|
* * variations.0.downloadable |
107
|
|
|
* * variations.0.virtual |
108
|
|
|
* * variations.0.permalink |
109
|
|
|
* * variations.0.sku |
110
|
|
|
* * variations.0.price |
111
|
|
|
* * variations.0.regular_price |
112
|
|
|
* * variations.0.sale_price |
113
|
|
|
* * variations.0.sale_price_dates_from |
114
|
|
|
* * variations.0.sale_price_dates_to |
115
|
|
|
* * variations.0.taxable |
116
|
|
|
* * variations.0.tax_status |
117
|
|
|
* * variations.0.tax_class |
118
|
|
|
* * variations.0.managing_stock |
119
|
|
|
* * variations.0.stock_quantity |
120
|
|
|
* * variations.0.in_stock |
121
|
|
|
* * variations.0.backordered |
122
|
|
|
* * variations.0.purchaseable |
123
|
|
|
* * variations.0.visible |
124
|
|
|
* * variations.0.on_sale |
125
|
|
|
* * variations.0.weight |
126
|
|
|
* * variations.0.dimensions -- See dimensions fields |
127
|
|
|
* * variations.0.shipping_class |
128
|
|
|
* * variations.0.shipping_class_id |
129
|
|
|
* * variations.0.images -- See images fields |
130
|
|
|
* * variations.0.attributes -- See attributes fields |
131
|
|
|
* * variations.0.downloads -- See downloads fields |
132
|
|
|
* * variations.0.download_limit |
133
|
|
|
* * variations.0.download_expiry |
134
|
|
|
* |
135
|
|
|
* ## EXAMPLES |
136
|
|
|
* |
137
|
|
|
* wp wc product create --title="Product Name" |
138
|
|
|
* |
139
|
|
|
* wp wc product create --title="Product Name" -- |
140
|
|
|
* |
141
|
|
|
* @since 2.5.0 |
142
|
|
|
*/ |
143
|
|
|
public function create( $args, $assoc_args ) { |
144
|
|
|
$id = 0; |
145
|
|
|
|
146
|
|
|
try { |
147
|
|
|
$porcelain = isset( $assoc_args['porcelain'] ); |
148
|
|
|
unset( $assoc_args['porcelain'] ); |
149
|
|
|
|
150
|
|
|
$data = apply_filters( 'woocommerce_cli_create_product_data', $this->unflatten_array( $assoc_args ) ); |
151
|
|
|
|
152
|
|
|
// Check if product title is specified |
153
|
|
|
if ( ! isset( $data['title'] ) ) { |
154
|
|
|
throw new WC_CLI_Exception( 'woocommerce_missing_product_title', sprintf( __( 'Missing parameter %s', 'woocommerce' ), 'title' ) ); |
155
|
|
|
} |
156
|
|
|
|
157
|
|
|
// Check product type |
158
|
|
|
if ( ! isset( $data['type'] ) ) { |
159
|
|
|
$data['type'] = 'simple'; |
160
|
|
|
} |
161
|
|
|
|
162
|
|
|
// Set visible visibility when not sent |
163
|
|
|
if ( ! isset( $data['catalog_visibility'] ) ) { |
164
|
|
|
$data['catalog_visibility'] = 'visible'; |
165
|
|
|
} |
166
|
|
|
|
167
|
|
|
// Validate the product type |
168
|
|
|
if ( ! in_array( wc_clean( $data['type'] ), array_keys( wc_get_product_types() ) ) ) { |
169
|
|
|
throw new WC_CLI_Exception( 'woocommerce_cli_invalid_product_type', sprintf( __( 'Invalid product type - the product type must be any of these: %s', 'woocommerce' ), implode( ', ', array_keys( wc_get_product_types() ) ) ) ); |
170
|
|
|
} |
171
|
|
|
|
172
|
|
|
// Enable description html tags. |
173
|
|
|
$post_content = isset( $data['description'] ) ? wc_clean( $data['description'] ) : ''; |
174
|
|
|
if ( $post_content && isset( $data['enable_html_description'] ) && $this->is_true( $data['enable_html_description'] ) ) { |
175
|
|
|
$post_content = $data['description']; |
176
|
|
|
} |
177
|
|
|
|
178
|
|
|
// Enable short description html tags. |
179
|
|
|
$post_excerpt = isset( $data['short_description'] ) ? wc_clean( $data['short_description'] ) : ''; |
180
|
|
|
if ( $post_excerpt && isset( $data['enable_html_short_description'] ) && $this->is_true( $data['enable_html_short_description'] ) ) { |
181
|
|
|
$post_excerpt = $data['short_description']; |
182
|
|
|
} |
183
|
|
|
|
184
|
|
|
$new_product = array( |
185
|
|
|
'post_title' => wc_clean( $data['title'] ), |
186
|
|
|
'post_status' => ( isset( $data['status'] ) ? wc_clean( $data['status'] ) : 'publish' ), |
187
|
|
|
'post_type' => 'product', |
188
|
|
|
'post_excerpt' => ( isset( $data['short_description'] ) ? $post_excerpt : '' ), |
189
|
|
|
'post_content' => ( isset( $data['description'] ) ? $post_content : '' ), |
190
|
|
|
'post_author' => get_current_user_id(), |
191
|
|
|
); |
192
|
|
|
|
193
|
|
|
// Attempts to create the new product |
194
|
|
|
$id = wp_insert_post( $new_product, true ); |
195
|
|
|
|
196
|
|
|
// Checks for an error in the product creation |
197
|
|
|
if ( is_wp_error( $id ) ) { |
198
|
|
|
throw new WC_CLI_Exception( 'woocommerce_cli_cannot_create_product', $id->get_error_message() ); |
199
|
|
|
} |
200
|
|
|
|
201
|
|
|
// Check for featured/gallery images, upload it and set it |
202
|
|
|
if ( isset( $data['images'] ) ) { |
203
|
|
|
$this->save_product_images( $id, $data['images'] ); |
204
|
|
|
} |
205
|
|
|
|
206
|
|
|
// Save product meta fields |
207
|
|
|
$this->save_product_meta( $id, $data ); |
208
|
|
|
|
209
|
|
|
// Save variations |
210
|
|
View Code Duplication |
if ( isset( $data['type'] ) && 'variable' == $data['type'] && isset( $data['variations'] ) && is_array( $data['variations'] ) ) { |
|
|
|
|
211
|
|
|
$this->save_variations( $id, $data ); |
212
|
|
|
} |
213
|
|
|
|
214
|
|
|
do_action( 'woocommerce_cli_create_product', $id, $data ); |
215
|
|
|
|
216
|
|
|
// Clear cache/transients |
217
|
|
|
wc_delete_product_transients( $id ); |
218
|
|
|
|
219
|
|
|
if ( $porcelain ) { |
220
|
|
|
WP_CLI::line( $id ); |
221
|
|
|
} else { |
222
|
|
|
WP_CLI::success( "Created product $id." ); |
223
|
|
|
} |
224
|
|
|
} catch ( WC_CLI_Exception $e ) { |
225
|
|
|
// Remove the product when fails |
226
|
|
|
$this->clear_product( $id ); |
227
|
|
|
|
228
|
|
|
WP_CLI::error( $e->getMessage() ); |
229
|
|
|
} |
230
|
|
|
} |
231
|
|
|
|
232
|
|
|
/** |
233
|
|
|
* Delete products. |
234
|
|
|
* |
235
|
|
|
* ## OPTIONS |
236
|
|
|
* |
237
|
|
|
* <id>... |
238
|
|
|
* : The product ID to delete. |
239
|
|
|
* |
240
|
|
|
* ## EXAMPLES |
241
|
|
|
* |
242
|
|
|
* wp wc product delete 123 |
243
|
|
|
* |
244
|
|
|
* wp wc product delete $(wp wc product list --format=ids) |
245
|
|
|
* |
246
|
|
|
* @since 2.5.0 |
247
|
|
|
*/ |
248
|
|
|
public function delete( $args, $assoc_args ) { |
249
|
|
|
$exit_code = 0; |
250
|
|
|
foreach ( $args as $id ) { |
251
|
|
|
do_action( 'woocommerce_cli_delete_product', $id ); |
252
|
|
|
$r = wp_delete_post( $id, true ); |
253
|
|
|
|
254
|
|
|
if ( $r ) { |
255
|
|
|
WP_CLI::success( "Deleted product $id." ); |
256
|
|
|
} else { |
257
|
|
|
$exit_code += 1; |
258
|
|
|
WP_CLI::warning( "Failed deleting product $id." ); |
259
|
|
|
} |
260
|
|
|
} |
261
|
|
|
exit( $exit_code ? 1 : 0 ); |
262
|
|
|
} |
263
|
|
|
|
264
|
|
|
/** |
265
|
|
|
* Get a product. |
266
|
|
|
* |
267
|
|
|
* ## OPTIONS |
268
|
|
|
* |
269
|
|
|
* <id> |
270
|
|
|
* : Product ID. |
271
|
|
|
* |
272
|
|
|
* [--field=<field>] |
273
|
|
|
* : Instead of returning the whole product fields, returns the value of a single fields. |
274
|
|
|
* |
275
|
|
|
* [--fields=<fields>] |
276
|
|
|
* : Get a specific subset of the product's fields. |
277
|
|
|
* |
278
|
|
|
* [--format=<format>] |
279
|
|
|
* : Accepted values: table, json, csv. Default: table. |
280
|
|
|
* |
281
|
|
|
* ## AVAILABLE FIELDS |
282
|
|
|
* |
283
|
|
|
* For more fields, see: wp wc product list --help |
284
|
|
|
* |
285
|
|
|
* ## EXAMPLES |
286
|
|
|
* |
287
|
|
|
* wp wc product get 123 --fields=id,title,sku |
288
|
|
|
* |
289
|
|
|
* @since 2.5.0 |
290
|
|
|
*/ |
291
|
|
View Code Duplication |
public function get( $args, $assoc_args ) { |
|
|
|
|
292
|
|
|
try { |
293
|
|
|
$product = wc_get_product( $args[0] ); |
294
|
|
|
if ( ! $product ) { |
295
|
|
|
throw new WC_CLI_Exception( 'woocommerce_cli_invalid_product', sprintf( __( 'Invalid product "%s"', 'woocommerce' ), $args[0] ) ); |
296
|
|
|
} |
297
|
|
|
$product_data = $this->get_product_data( $product ); |
298
|
|
|
|
299
|
|
|
$formatter = $this->get_formatter( $assoc_args ); |
300
|
|
|
$formatter->display_item( $product_data ); |
301
|
|
|
} catch ( WC_CLI_Exception $e ) { |
302
|
|
|
WP_CLI::error( $e->getMessage() ); |
303
|
|
|
} |
304
|
|
|
} |
305
|
|
|
|
306
|
|
|
/** |
307
|
|
|
* List products. |
308
|
|
|
* |
309
|
|
|
* ## OPTIONS |
310
|
|
|
* |
311
|
|
|
* [--<field>=<value>] |
312
|
|
|
* : Filter products based on product property. |
313
|
|
|
* |
314
|
|
|
* [--field=<field>] |
315
|
|
|
* : Prints the value of a single field for each product. |
316
|
|
|
* |
317
|
|
|
* [--fields=<fields>] |
318
|
|
|
* : Limit the output to specific product fields. |
319
|
|
|
* |
320
|
|
|
* [--format=<format>] |
321
|
|
|
* : Acceptec values: table, csv, json, count, ids. Default: table. |
322
|
|
|
* |
323
|
|
|
* ## AVAILABLE FIELDS |
324
|
|
|
* |
325
|
|
|
* These fields will be displayed by default for each product: |
326
|
|
|
* |
327
|
|
|
* * id |
328
|
|
|
* * title |
329
|
|
|
* * sku |
330
|
|
|
* * in_stock |
331
|
|
|
* * price |
332
|
|
|
* * sale_price |
333
|
|
|
* * categories |
334
|
|
|
* * tags |
335
|
|
|
* * type |
336
|
|
|
* * created_at |
337
|
|
|
* |
338
|
|
|
* These fields are optionally available: |
339
|
|
|
* |
340
|
|
|
* * updated_at |
341
|
|
|
* * status |
342
|
|
|
* * downloadable |
343
|
|
|
* * virtual |
344
|
|
|
* * permalink |
345
|
|
|
* * regular_price |
346
|
|
|
* * sale_price_dates_from |
347
|
|
|
* * sale_price_dates_to |
348
|
|
|
* * price_html |
349
|
|
|
* * taxable |
350
|
|
|
* * tax_status |
351
|
|
|
* * tax_class |
352
|
|
|
* * managing_stock |
353
|
|
|
* * stock_quantity |
354
|
|
|
* * backorders_allowed |
355
|
|
|
* * backordered |
356
|
|
|
* * backorders |
357
|
|
|
* * sold_individually |
358
|
|
|
* * purchaseable |
359
|
|
|
* * featured |
360
|
|
|
* * visible |
361
|
|
|
* * catalog_visibility |
362
|
|
|
* * on_sale |
363
|
|
|
* * weight |
364
|
|
|
* * shipping_required |
365
|
|
|
* * shipping_taxable |
366
|
|
|
* * shipping_class |
367
|
|
|
* * shipping_class_id |
368
|
|
|
* * description |
369
|
|
|
* * enable_html_description |
370
|
|
|
* * short_description |
371
|
|
|
* * enable_html_short_description |
372
|
|
|
* * reviews_allowed |
373
|
|
|
* * average_rating |
374
|
|
|
* * rating_count |
375
|
|
|
* * related_ids |
376
|
|
|
* * upsell_ids |
377
|
|
|
* * cross_sell_ids |
378
|
|
|
* * parent_id |
379
|
|
|
* * featured_src |
380
|
|
|
* * download_limit |
381
|
|
|
* * download_expiry |
382
|
|
|
* * download_type |
383
|
|
|
* * purchase_note |
384
|
|
|
* * total_sales |
385
|
|
|
* * parent |
386
|
|
|
* * product_url |
387
|
|
|
* * button_text |
388
|
|
|
* |
389
|
|
|
* There are some properties that are nested array. In such case, if array.size |
390
|
|
|
* is zero then listing the fields with `array.0.some_field` will results |
391
|
|
|
* in error that field `array.0.some_field` does not exists. |
392
|
|
|
* |
393
|
|
|
* Dimensions fields: |
394
|
|
|
* |
395
|
|
|
* * dimensions.length |
396
|
|
|
* * dimensions.width |
397
|
|
|
* * dimensions.height |
398
|
|
|
* * dimensions.unit |
399
|
|
|
* |
400
|
|
|
* Images is an array in which element can be accessed by specifying its index: |
401
|
|
|
* |
402
|
|
|
* * images |
403
|
|
|
* * images.size |
404
|
|
|
* * images.0.id |
405
|
|
|
* * images.0.created_at |
406
|
|
|
* * images.0.updated_at |
407
|
|
|
* * images.0.src |
408
|
|
|
* * images.0.title |
409
|
|
|
* * images.0.alt |
410
|
|
|
* * images.0.position |
411
|
|
|
* |
412
|
|
|
* Attributes is an array in which element can be accessed by specifying its index: |
413
|
|
|
* |
414
|
|
|
* * attributes |
415
|
|
|
* * attributes.size |
416
|
|
|
* * attributes.0.name |
417
|
|
|
* * attributes.0.slug |
418
|
|
|
* * attributes.0.position |
419
|
|
|
* * attributes.0.visible |
420
|
|
|
* * attributes.0.variation |
421
|
|
|
* * attributes.0.options |
422
|
|
|
* |
423
|
|
|
* Downloads is an array in which element can be accessed by specifying its index: |
424
|
|
|
* |
425
|
|
|
* * downloads |
426
|
|
|
* * downloads.size |
427
|
|
|
* * downloads.0.id |
428
|
|
|
* * downloads.0.name |
429
|
|
|
* * downloads.0.file |
430
|
|
|
* |
431
|
|
|
* Variations is an array in which element can be accessed by specifying its index: |
432
|
|
|
* |
433
|
|
|
* * variations |
434
|
|
|
* * variations.size |
435
|
|
|
* * variations.0.id |
436
|
|
|
* * variations.0.created_at |
437
|
|
|
* * variations.0.updated_at |
438
|
|
|
* * variations.0.downloadable |
439
|
|
|
* * variations.0.virtual |
440
|
|
|
* * variations.0.permalink |
441
|
|
|
* * variations.0.sku |
442
|
|
|
* * variations.0.price |
443
|
|
|
* * variations.0.regular_price |
444
|
|
|
* * variations.0.sale_price |
445
|
|
|
* * variations.0.sale_price_dates_from |
446
|
|
|
* * variations.0.sale_price_dates_to |
447
|
|
|
* * variations.0.taxable |
448
|
|
|
* * variations.0.tax_status |
449
|
|
|
* * variations.0.tax_class |
450
|
|
|
* * variations.0.managing_stock |
451
|
|
|
* * variations.0.stock_quantity |
452
|
|
|
* * variations.0.in_stock |
453
|
|
|
* * variations.0.backordered |
454
|
|
|
* * variations.0.purchaseable |
455
|
|
|
* * variations.0.visible |
456
|
|
|
* * variations.0.on_sale |
457
|
|
|
* * variations.0.weight |
458
|
|
|
* * variations.0.dimensions -- See dimensions fields |
459
|
|
|
* * variations.0.shipping_class |
460
|
|
|
* * variations.0.shipping_class_id |
461
|
|
|
* * variations.0.images -- See images fields |
462
|
|
|
* * variations.0.attributes -- See attributes fields |
463
|
|
|
* * variations.0.downloads -- See downloads fields |
464
|
|
|
* * variations.0.download_limit |
465
|
|
|
* * variations.0.download_expiry |
466
|
|
|
* |
467
|
|
|
* Fields for filtering query result also available: |
468
|
|
|
* |
469
|
|
|
* * q Filter products with search query. |
470
|
|
|
* * created_at_min Filter products whose created after this date. |
471
|
|
|
* * created_at_max Filter products whose created before this date. |
472
|
|
|
* * updated_at_min Filter products whose updated after this date. |
473
|
|
|
* * updated_at_max Filter products whose updated before this date. |
474
|
|
|
* * limit The maximum returned number of results. |
475
|
|
|
* * offset Offset the returned results. |
476
|
|
|
* * order Accepted values: ASC and DESC. Default: DESC. |
477
|
|
|
* * orderby Sort retrieved products by parameter. One or more options can be passed. |
478
|
|
|
* |
479
|
|
|
* ## EXAMPLES |
480
|
|
|
* |
481
|
|
|
* wp wc product list |
482
|
|
|
* |
483
|
|
|
* wp wc product list --field=id |
484
|
|
|
* |
485
|
|
|
* wp wc product list --fields=id,title,type --format=json |
486
|
|
|
* |
487
|
|
|
* @subcommand list |
488
|
|
|
* @since 2.5.0 |
489
|
|
|
*/ |
490
|
|
View Code Duplication |
public function list_( $args, $assoc_args ) { |
|
|
|
|
491
|
|
|
$query_args = $this->merge_wp_query_args( $this->get_list_query_args( $assoc_args ), $assoc_args ); |
492
|
|
|
$formatter = $this->get_formatter( $assoc_args ); |
493
|
|
|
|
494
|
|
|
if ( 'ids' === $formatter->format ) { |
495
|
|
|
$query_args['fields'] = 'ids'; |
496
|
|
|
$query = new WP_Query( $query_args ); |
497
|
|
|
echo implode( ' ', $query->posts ); |
498
|
|
|
} else { |
499
|
|
|
$query = new WP_Query( $query_args ); |
500
|
|
|
$items = $this->format_posts_to_items( $query->posts ); |
501
|
|
|
$formatter->display_items( $items ); |
502
|
|
|
} |
503
|
|
|
} |
504
|
|
|
|
505
|
|
|
/** |
506
|
|
|
* List of product reviews. |
507
|
|
|
* |
508
|
|
|
* ## OPTIONS |
509
|
|
|
* |
510
|
|
|
* <id> |
511
|
|
|
* : Product ID. |
512
|
|
|
* |
513
|
|
|
* [--field=<field>] |
514
|
|
|
* : Instead of returning the whole review fields, returns the value of a single fields. |
515
|
|
|
* |
516
|
|
|
* [--fields=<fields>] |
517
|
|
|
* : Get a specific subset of the review's fields. |
518
|
|
|
* |
519
|
|
|
* [--format=<format>] |
520
|
|
|
* : Accepted values: table, json, csv. Default: table. |
521
|
|
|
* |
522
|
|
|
* ## AVAILABLE FIELDS |
523
|
|
|
* |
524
|
|
|
* * id |
525
|
|
|
* * rating |
526
|
|
|
* * reviewer_name |
527
|
|
|
* * reviewer_email |
528
|
|
|
* * verified |
529
|
|
|
* * created_at |
530
|
|
|
* |
531
|
|
|
* ## EXAMPLES |
532
|
|
|
* |
533
|
|
|
* wp wc product reviews 123 |
534
|
|
|
* |
535
|
|
|
* wp wc product reviews 123 --fields=id,rating,reviewer_email |
536
|
|
|
* |
537
|
|
|
* @since 2.5.0 |
538
|
|
|
*/ |
539
|
|
|
public function reviews( $args, $assoc_args ) { |
540
|
|
|
try { |
541
|
|
|
$id = $args[0]; |
542
|
|
|
$product = wc_get_product( $id ); |
543
|
|
|
if ( ! $product ) { |
544
|
|
|
throw new WC_CLI_Exception( 'woocommerce_cli_invalid_product', sprintf( __( 'Invalid product "%s"', 'woocommerce' ), $id ) ); |
545
|
|
|
} |
546
|
|
|
|
547
|
|
|
$comments = get_approved_comments( $id ); |
548
|
|
|
$reviews = array(); |
549
|
|
|
|
550
|
|
|
foreach ( $comments as $comment ) { |
551
|
|
|
$reviews[] = array( |
552
|
|
|
'id' => intval( $comment->comment_ID ), |
553
|
|
|
'created_at' => $this->format_datetime( $comment->comment_date_gmt ), |
554
|
|
|
'review' => $comment->comment_content, |
555
|
|
|
'rating' => get_comment_meta( $comment->comment_ID, 'rating', true ), |
556
|
|
|
'reviewer_name' => $comment->comment_author, |
557
|
|
|
'reviewer_email' => $comment->comment_author_email, |
558
|
|
|
'verified' => (bool) get_comment_meta( $comment->comment_ID, 'verified', true ), |
559
|
|
|
); |
560
|
|
|
} |
561
|
|
|
|
562
|
|
|
if ( empty( $assoc_args['fields'] ) ) { |
563
|
|
|
$assoc_args['fields'] = $this->get_review_fields(); |
564
|
|
|
} |
565
|
|
|
|
566
|
|
|
$formatter = $this->get_formatter( $assoc_args ); |
567
|
|
|
$formatter->display_items( $reviews ); |
568
|
|
|
} catch ( WC_CLI_Exception $e ) { |
569
|
|
|
WP_CLI::error( $e->getMessage() ); |
570
|
|
|
} |
571
|
|
|
} |
572
|
|
|
|
573
|
|
|
/** |
574
|
|
|
* Get product types. |
575
|
|
|
* |
576
|
|
|
* ## EXAMPLES |
577
|
|
|
* |
578
|
|
|
* wp wc product types |
579
|
|
|
* |
580
|
|
|
* @since 2.5.0 |
581
|
|
|
*/ |
582
|
|
|
public function types( $__, $___ ) { |
583
|
|
|
$product_types = wc_get_product_types(); |
584
|
|
|
foreach ( $product_types as $type => $label ) { |
585
|
|
|
WP_CLI::line( sprintf( '%s: %s', $label, $type ) ); |
586
|
|
|
} |
587
|
|
|
} |
588
|
|
|
|
589
|
|
|
/** |
590
|
|
|
* Update one or more products. |
591
|
|
|
* |
592
|
|
|
* ## OPTIONS |
593
|
|
|
* |
594
|
|
|
* <id> |
595
|
|
|
* : Product ID |
596
|
|
|
* |
597
|
|
|
* [--<field>=<value>] |
598
|
|
|
* : One or more fields to update. |
599
|
|
|
* |
600
|
|
|
* ## AVAILABLE_FIELDS |
601
|
|
|
* |
602
|
|
|
* For more fields, see: wp wc product create --help |
603
|
|
|
* |
604
|
|
|
* ## EXAMPLES |
605
|
|
|
* |
606
|
|
|
* wp wc product update 123 --title="New Product Title" --description="New description" |
607
|
|
|
* |
608
|
|
|
* @since 2.5.0 |
609
|
|
|
*/ |
610
|
|
|
public function update( $args, $assoc_args ) { |
611
|
|
|
try { |
612
|
|
|
$id = $args[0]; |
613
|
|
|
$data = apply_filters( 'woocommerce_cli_update_product_data', $this->unflatten_array( $assoc_args ) ); |
614
|
|
|
|
615
|
|
|
// Product title. |
616
|
|
|
if ( isset( $data['title'] ) ) { |
617
|
|
|
wp_update_post( array( 'ID' => $id, 'post_title' => wc_clean( $data['title'] ) ) ); |
618
|
|
|
} |
619
|
|
|
|
620
|
|
|
// Product name (slug). |
621
|
|
|
if ( isset( $data['name'] ) ) { |
622
|
|
|
wp_update_post( array( 'ID' => $id, 'post_name' => sanitize_title( $data['name'] ) ) ); |
623
|
|
|
} |
624
|
|
|
|
625
|
|
|
// Product status. |
626
|
|
|
if ( isset( $data['status'] ) ) { |
627
|
|
|
wp_update_post( array( 'ID' => $id, 'post_status' => wc_clean( $data['status'] ) ) ); |
628
|
|
|
} |
629
|
|
|
|
630
|
|
|
// Product short description. |
631
|
|
View Code Duplication |
if ( isset( $data['short_description'] ) ) { |
|
|
|
|
632
|
|
|
// Enable short description html tags. |
633
|
|
|
$post_excerpt = ( isset( $data['enable_html_short_description'] ) && $this->is_true( $data['enable_html_short_description'] ) ) ? $data['short_description'] : wc_clean( $data['short_description'] ); |
634
|
|
|
|
635
|
|
|
wp_update_post( array( 'ID' => $id, 'post_excerpt' => $post_excerpt ) ); |
636
|
|
|
} |
637
|
|
|
|
638
|
|
|
// Product description. |
639
|
|
View Code Duplication |
if ( isset( $data['description'] ) ) { |
|
|
|
|
640
|
|
|
// Enable description html tags. |
641
|
|
|
$post_content = ( isset( $data['enable_html_description'] ) && $this->is_true( $data['enable_html_description'] ) ) ? $data['description'] : wc_clean( $data['description'] ); |
642
|
|
|
|
643
|
|
|
wp_update_post( array( 'ID' => $id, 'post_content' => $post_content ) ); |
644
|
|
|
} |
645
|
|
|
|
646
|
|
|
// Validate the product type |
647
|
|
|
if ( isset( $data['type'] ) && ! in_array( wc_clean( $data['type'] ), array_keys( wc_get_product_types() ) ) ) { |
648
|
|
|
throw new WC_CLI_Exception( 'woocommerce_cli_invalid_product_type', sprintf( __( 'Invalid product type - the product type must be any of these: %s', 'woocommerce' ), implode( ', ', array_keys( wc_get_product_types() ) ) ) ); |
649
|
|
|
} |
650
|
|
|
|
651
|
|
|
// Check for featured/gallery images, upload it and set it |
652
|
|
|
if ( isset( $data['images'] ) ) { |
653
|
|
|
$this->save_product_images( $id, $data['images'] ); |
654
|
|
|
} |
655
|
|
|
|
656
|
|
|
// Save product meta fields |
657
|
|
|
$this->save_product_meta( $id, $data ); |
658
|
|
|
|
659
|
|
|
// Save variations |
660
|
|
View Code Duplication |
if ( isset( $data['type'] ) && 'variable' == $data['type'] && isset( $data['variations'] ) && is_array( $data['variations'] ) ) { |
|
|
|
|
661
|
|
|
$this->save_variations( $id, $data ); |
662
|
|
|
} |
663
|
|
|
|
664
|
|
|
do_action( 'woocommerce_cli_update_product', $id, $data ); |
665
|
|
|
|
666
|
|
|
// Clear cache/transients |
667
|
|
|
wc_delete_product_transients( $id ); |
668
|
|
|
|
669
|
|
|
WP_CLI::success( "Updated product $id." ); |
670
|
|
|
} catch ( WC_CLI_Exception $e ) { |
671
|
|
|
WP_CLI::error( $e->getMessage() ); |
672
|
|
|
} |
673
|
|
|
} |
674
|
|
|
|
675
|
|
|
/** |
676
|
|
|
* Get query args for list subcommand. |
677
|
|
|
* |
678
|
|
|
* @since 2.5.0 |
679
|
|
|
* @param array $args Args from command line |
680
|
|
|
* @return array |
681
|
|
|
*/ |
682
|
|
|
protected function get_list_query_args( $args ) { |
683
|
|
|
$query_args = array( |
684
|
|
|
'post_type' => 'product', |
685
|
|
|
'post_status' => 'publish', |
686
|
|
|
'posts_per_page' => -1, |
687
|
|
|
'meta_query' => array(), |
688
|
|
|
); |
689
|
|
|
|
690
|
|
View Code Duplication |
if ( ! empty( $args['type'] ) ) { |
|
|
|
|
691
|
|
|
$types = explode( ',', $args['type'] ); |
692
|
|
|
$query_args['tax_query'] = array( |
693
|
|
|
array( |
694
|
|
|
'taxonomy' => 'product_type', |
695
|
|
|
'field' => 'slug', |
696
|
|
|
'terms' => $types, |
697
|
|
|
), |
698
|
|
|
); |
699
|
|
|
} |
700
|
|
|
|
701
|
|
|
// Filter products by category |
702
|
|
|
if ( ! empty( $args['category'] ) ) { |
703
|
|
|
$query_args['product_cat'] = $args['category']; |
704
|
|
|
} |
705
|
|
|
|
706
|
|
|
// Filter by specific sku |
707
|
|
View Code Duplication |
if ( ! empty( $args['sku'] ) ) { |
|
|
|
|
708
|
|
|
if ( ! is_array( $query_args['meta_query'] ) ) { |
709
|
|
|
$query_args['meta_query'] = array(); |
710
|
|
|
} |
711
|
|
|
|
712
|
|
|
$query_args['meta_query'][] = array( |
713
|
|
|
'key' => '_sku', |
714
|
|
|
'value' => $args['sku'], |
715
|
|
|
'compare' => '=' |
716
|
|
|
); |
717
|
|
|
} |
718
|
|
|
|
719
|
|
|
return $query_args; |
720
|
|
|
} |
721
|
|
|
|
722
|
|
|
/** |
723
|
|
|
* Get default format fields that will be used in `list` and `get` subcommands. |
724
|
|
|
* |
725
|
|
|
* @since 2.5.0 |
726
|
|
|
* @return string |
727
|
|
|
*/ |
728
|
|
|
protected function get_default_format_fields() { |
729
|
|
|
return 'id,title,sku,in_stock,price,sale_price,categories,tags,type,created_at'; |
730
|
|
|
} |
731
|
|
|
|
732
|
|
|
/** |
733
|
|
|
* Get review fields for formatting. |
734
|
|
|
* |
735
|
|
|
* @since 2.5.0 |
736
|
|
|
* @return string |
737
|
|
|
*/ |
738
|
|
|
protected function get_review_fields() { |
739
|
|
|
return 'id,rating,reviewer_name,reviewer_email,verified,created_at'; |
740
|
|
|
} |
741
|
|
|
|
742
|
|
|
/** |
743
|
|
|
* Format posts from WP_Query result to items in which each item contain |
744
|
|
|
* common properties of item, for instance `post_title` will be `title`. |
745
|
|
|
* |
746
|
|
|
* @since 2.5.0 |
747
|
|
|
* @param array $posts Array of post |
748
|
|
|
* @return array Items |
749
|
|
|
*/ |
750
|
|
|
protected function format_posts_to_items( $posts ) { |
751
|
|
|
$items = array(); |
752
|
|
|
foreach ( $posts as $post ) { |
753
|
|
|
$product = wc_get_product( $post->ID ); |
754
|
|
|
if ( ! $product ) { |
755
|
|
|
continue; |
756
|
|
|
} |
757
|
|
|
$items[] = $this->get_product_data( $product ); |
758
|
|
|
} |
759
|
|
|
|
760
|
|
|
return $items; |
761
|
|
|
} |
762
|
|
|
|
763
|
|
|
/** |
764
|
|
|
* Get standard product data that applies to every product type. |
765
|
|
|
* |
766
|
|
|
* @since 2.5.0 |
767
|
|
|
* @param WC_Product $product |
768
|
|
|
* @return array |
769
|
|
|
*/ |
770
|
|
|
private function get_product_data( $product ) { |
771
|
|
|
|
772
|
|
|
// Add data that applies to every product type. |
773
|
|
|
$product_data = array( |
774
|
|
|
'title' => $product->get_title(), |
775
|
|
|
'id' => (int) $product->is_type( 'variation' ) ? $product->get_variation_id() : $product->id, |
|
|
|
|
776
|
|
|
'created_at' => $this->format_datetime( $product->get_post_data()->post_date_gmt ), |
777
|
|
|
'updated_at' => $this->format_datetime( $product->get_post_data()->post_modified_gmt ), |
778
|
|
|
'type' => $product->product_type, |
779
|
|
|
'status' => $product->get_post_data()->post_status, |
780
|
|
|
'downloadable' => $product->is_downloadable(), |
781
|
|
|
'virtual' => $product->is_virtual(), |
782
|
|
|
'permalink' => $product->get_permalink(), |
783
|
|
|
'sku' => $product->get_sku(), |
784
|
|
|
'price' => $product->get_price(), |
785
|
|
|
'regular_price' => $product->get_regular_price(), |
786
|
|
|
'sale_price' => $product->get_sale_price() ? $product->get_sale_price() : null, |
787
|
|
|
'price_html' => $product->get_price_html(), |
788
|
|
|
'taxable' => $product->is_taxable(), |
789
|
|
|
'tax_status' => $product->get_tax_status(), |
790
|
|
|
'tax_class' => $product->get_tax_class(), |
791
|
|
|
'managing_stock' => $product->managing_stock(), |
792
|
|
|
'stock_quantity' => $product->get_stock_quantity(), |
793
|
|
|
'in_stock' => $product->is_in_stock() ? 'yes' : 'no', |
794
|
|
|
'backorders_allowed' => $product->backorders_allowed(), |
795
|
|
|
'backordered' => $product->is_on_backorder(), |
796
|
|
|
'sold_individually' => $product->is_sold_individually(), |
797
|
|
|
'purchaseable' => $product->is_purchasable(), |
798
|
|
|
'featured' => $product->is_featured(), |
799
|
|
|
'visible' => $product->is_visible(), |
800
|
|
|
'catalog_visibility' => $product->visibility, |
801
|
|
|
'on_sale' => $product->is_on_sale(), |
802
|
|
|
'product_url' => $product->is_type( 'external' ) ? $product->get_product_url() : '', |
|
|
|
|
803
|
|
|
'button_text' => $product->is_type( 'external' ) ? $product->get_button_text() : '', |
|
|
|
|
804
|
|
|
'weight' => $product->get_weight() ? $product->get_weight() : null, |
805
|
|
|
'dimensions' => array( |
806
|
|
|
'length' => $product->length, |
807
|
|
|
'width' => $product->width, |
808
|
|
|
'height' => $product->height, |
809
|
|
|
'unit' => get_option( 'woocommerce_dimension_unit' ), |
810
|
|
|
), |
811
|
|
|
'shipping_required' => $product->needs_shipping(), |
812
|
|
|
'shipping_taxable' => $product->is_shipping_taxable(), |
813
|
|
|
'shipping_class' => $product->get_shipping_class(), |
814
|
|
|
'shipping_class_id' => ( 0 !== $product->get_shipping_class_id() ) ? $product->get_shipping_class_id() : null, |
815
|
|
|
'description' => wpautop( do_shortcode( $product->get_post_data()->post_content ) ), |
816
|
|
|
'short_description' => apply_filters( 'woocommerce_short_description', $product->get_post_data()->post_excerpt ), |
817
|
|
|
'reviews_allowed' => ( 'open' === $product->get_post_data()->comment_status ), |
818
|
|
|
'average_rating' => wc_format_decimal( $product->get_average_rating(), 2 ), |
819
|
|
|
'rating_count' => (int) $product->get_rating_count(), |
820
|
|
|
'related_ids' => implode( ', ', $product->get_related() ), |
821
|
|
|
'upsell_ids' => implode( ', ', $product->get_upsells() ), |
822
|
|
|
'cross_sell_ids' => implode( ', ', $product->get_cross_sells() ), |
823
|
|
|
'parent_id' => $product->post->post_parent, |
824
|
|
|
'categories' => implode( ', ', wp_get_post_terms( $product->id, 'product_cat', array( 'fields' => 'names' ) ) ), |
825
|
|
|
'tags' => implode( ', ', wp_get_post_terms( $product->id, 'product_tag', array( 'fields' => 'names' ) ) ), |
826
|
|
|
'images' => $this->get_images( $product ), |
827
|
|
|
'featured_src' => wp_get_attachment_url( get_post_thumbnail_id( $product->is_type( 'variation' ) ? $product->variation_id : $product->id ) ), |
828
|
|
|
'attributes' => $this->get_attributes( $product ), |
829
|
|
|
'downloads' => $this->get_downloads( $product ), |
830
|
|
|
'download_limit' => (int) $product->download_limit, |
831
|
|
|
'download_expiry' => (int) $product->download_expiry, |
832
|
|
|
'download_type' => $product->download_type, |
833
|
|
|
'purchase_note' => wpautop( do_shortcode( wp_kses_post( $product->purchase_note ) ) ), |
834
|
|
|
'total_sales' => metadata_exists( 'post', $product->id, 'total_sales' ) ? (int) get_post_meta( $product->id, 'total_sales', true ) : 0, |
835
|
|
|
'variations' => array(), |
836
|
|
|
'parent' => array(), |
837
|
|
|
); |
838
|
|
|
|
839
|
|
|
// add variations to variable products |
840
|
|
|
if ( $product->is_type( 'variable' ) && $product->has_child() ) { |
841
|
|
|
$product_data['variations'] = $this->get_variation_data( $product ); |
842
|
|
|
} |
843
|
|
|
|
844
|
|
|
// add the parent product data to an individual variation |
845
|
|
|
if ( $product->is_type( 'variation' ) ) { |
846
|
|
|
$product_data['parent'] = $this->get_product_data( $product->parent ); |
847
|
|
|
} |
848
|
|
|
|
849
|
|
|
return $this->flatten_array( $product_data ); |
850
|
|
|
} |
851
|
|
|
|
852
|
|
|
/** |
853
|
|
|
* Get the images for a product or product variation |
854
|
|
|
* |
855
|
|
|
* @since 2.5.0 |
856
|
|
|
* @param WC_Product|WC_Product_Variation $product |
857
|
|
|
* @return array |
858
|
|
|
*/ |
859
|
|
|
private function get_images( $product ) { |
860
|
|
|
|
861
|
|
|
$images = $attachment_ids = array(); |
862
|
|
|
|
863
|
|
|
if ( $product->is_type( 'variation' ) ) { |
864
|
|
|
|
865
|
|
|
if ( has_post_thumbnail( $product->get_variation_id() ) ) { |
|
|
|
|
866
|
|
|
|
867
|
|
|
// Add variation image if set |
868
|
|
|
$attachment_ids[] = get_post_thumbnail_id( $product->get_variation_id() ); |
|
|
|
|
869
|
|
|
|
870
|
|
|
} elseif ( has_post_thumbnail( $product->id ) ) { |
871
|
|
|
|
872
|
|
|
// Otherwise use the parent product featured image if set |
873
|
|
|
$attachment_ids[] = get_post_thumbnail_id( $product->id ); |
874
|
|
|
} |
875
|
|
|
|
876
|
|
|
} else { |
877
|
|
|
|
878
|
|
|
// Add featured image |
879
|
|
|
if ( has_post_thumbnail( $product->id ) ) { |
880
|
|
|
$attachment_ids[] = get_post_thumbnail_id( $product->id ); |
881
|
|
|
} |
882
|
|
|
|
883
|
|
|
// Add gallery images |
884
|
|
|
$attachment_ids = array_merge( $attachment_ids, $product->get_gallery_attachment_ids() ); |
885
|
|
|
} |
886
|
|
|
|
887
|
|
|
// Build image data |
888
|
|
|
foreach ( $attachment_ids as $position => $attachment_id ) { |
889
|
|
|
|
890
|
|
|
$attachment_post = get_post( $attachment_id ); |
891
|
|
|
|
892
|
|
|
if ( is_null( $attachment_post ) ) { |
893
|
|
|
continue; |
894
|
|
|
} |
895
|
|
|
|
896
|
|
|
$attachment = wp_get_attachment_image_src( $attachment_id, 'full' ); |
897
|
|
|
|
898
|
|
|
if ( ! is_array( $attachment ) ) { |
899
|
|
|
continue; |
900
|
|
|
} |
901
|
|
|
|
902
|
|
|
$images[] = array( |
903
|
|
|
'id' => (int) $attachment_id, |
904
|
|
|
'created_at' => $this->format_datetime( $attachment_post->post_date_gmt ), |
905
|
|
|
'updated_at' => $this->format_datetime( $attachment_post->post_modified_gmt ), |
906
|
|
|
'src' => current( $attachment ), |
907
|
|
|
'title' => get_the_title( $attachment_id ), |
908
|
|
|
'alt' => get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ), |
909
|
|
|
'position' => (int) $position, |
910
|
|
|
); |
911
|
|
|
} |
912
|
|
|
|
913
|
|
|
// Set a placeholder image if the product has no images set |
914
|
|
|
if ( empty( $images ) ) { |
915
|
|
|
|
916
|
|
|
$images[] = array( |
917
|
|
|
'id' => 0, |
918
|
|
|
'created_at' => $this->format_datetime( time() ), // Default to now |
919
|
|
|
'updated_at' => $this->format_datetime( time() ), |
920
|
|
|
'src' => wc_placeholder_img_src(), |
921
|
|
|
'title' => __( 'Placeholder', 'woocommerce' ), |
922
|
|
|
'alt' => __( 'Placeholder', 'woocommerce' ), |
923
|
|
|
'position' => 0, |
924
|
|
|
); |
925
|
|
|
} |
926
|
|
|
|
927
|
|
|
return $images; |
928
|
|
|
} |
929
|
|
|
|
930
|
|
|
/** |
931
|
|
|
* Get the attributes for a product or product variation |
932
|
|
|
* |
933
|
|
|
* @since 2.5.0 |
934
|
|
|
* @param WC_Product|WC_Product_Variation $product |
935
|
|
|
* @return array |
936
|
|
|
*/ |
937
|
|
|
private function get_attributes( $product ) { |
938
|
|
|
|
939
|
|
|
$attributes = array(); |
940
|
|
|
|
941
|
|
|
if ( $product->is_type( 'variation' ) ) { |
942
|
|
|
|
943
|
|
|
// variation attributes |
944
|
|
|
foreach ( $product->get_variation_attributes() as $attribute_name => $attribute ) { |
|
|
|
|
945
|
|
|
|
946
|
|
|
// taxonomy-based attributes are prefixed with `pa_`, otherwise simply `attribute_` |
947
|
|
|
$attributes[] = array( |
948
|
|
|
'name' => wc_attribute_label( str_replace( 'attribute_', '', $attribute_name ) ), |
949
|
|
|
'slug' => str_replace( 'attribute_', '', str_replace( 'pa_', '', $attribute_name ) ), |
950
|
|
|
'option' => $attribute, |
951
|
|
|
); |
952
|
|
|
} |
953
|
|
|
|
954
|
|
|
} else { |
955
|
|
|
|
956
|
|
|
foreach ( $product->get_attributes() as $attribute ) { |
957
|
|
|
|
958
|
|
|
// taxonomy-based attributes are comma-separated, others are pipe (|) separated |
959
|
|
|
if ( $attribute['is_taxonomy'] ) { |
960
|
|
|
$options = explode( ',', $product->get_attribute( $attribute['name'] ) ); |
961
|
|
|
} else { |
962
|
|
|
$options = explode( '|', $product->get_attribute( $attribute['name'] ) ); |
963
|
|
|
} |
964
|
|
|
|
965
|
|
|
$attributes[] = array( |
966
|
|
|
'name' => wc_attribute_label( $attribute['name'] ), |
967
|
|
|
'slug' => str_replace( 'pa_', '', $attribute['name'] ), |
968
|
|
|
'position' => (int) $attribute['position'], |
969
|
|
|
'visible' => (bool) $attribute['is_visible'], |
970
|
|
|
'variation' => (bool) $attribute['is_variation'], |
971
|
|
|
'options' => array_map( 'trim', $options ), |
972
|
|
|
); |
973
|
|
|
} |
974
|
|
|
} |
975
|
|
|
|
976
|
|
|
return $attributes; |
977
|
|
|
} |
978
|
|
|
|
979
|
|
|
/** |
980
|
|
|
* Get the downloads for a product or product variation |
981
|
|
|
* |
982
|
|
|
* @since 2.5.0 |
983
|
|
|
* @param WC_Product|WC_Product_Variation $product |
984
|
|
|
* @return array |
985
|
|
|
*/ |
986
|
|
View Code Duplication |
private function get_downloads( $product ) { |
|
|
|
|
987
|
|
|
|
988
|
|
|
$downloads = array(); |
989
|
|
|
|
990
|
|
|
if ( $product->is_downloadable() ) { |
991
|
|
|
|
992
|
|
|
foreach ( $product->get_files() as $file_id => $file ) { |
993
|
|
|
|
994
|
|
|
$downloads[] = array( |
995
|
|
|
'id' => $file_id, // do not cast as int as this is a hash |
996
|
|
|
'name' => $file['name'], |
997
|
|
|
'file' => $file['file'], |
998
|
|
|
); |
999
|
|
|
} |
1000
|
|
|
} |
1001
|
|
|
|
1002
|
|
|
return $downloads; |
1003
|
|
|
} |
1004
|
|
|
|
1005
|
|
|
/** |
1006
|
|
|
* Get an individual variation's data |
1007
|
|
|
* |
1008
|
|
|
* @since 2.5.0 |
1009
|
|
|
* @param WC_Product $product |
1010
|
|
|
* @return array |
1011
|
|
|
*/ |
1012
|
|
|
private function get_variation_data( $product ) { |
1013
|
|
|
$variations = array(); |
1014
|
|
|
|
1015
|
|
|
foreach ( $product->get_children() as $child_id ) { |
1016
|
|
|
|
1017
|
|
|
$variation = $product->get_child( $child_id ); |
1018
|
|
|
|
1019
|
|
|
if ( ! $variation->exists() ) { |
1020
|
|
|
continue; |
1021
|
|
|
} |
1022
|
|
|
|
1023
|
|
|
$variations[] = array( |
1024
|
|
|
'id' => $variation->get_variation_id(), |
1025
|
|
|
'created_at' => $this->format_datetime( $variation->get_post_data()->post_date_gmt ), |
1026
|
|
|
'updated_at' => $this->format_datetime( $variation->get_post_data()->post_modified_gmt ), |
1027
|
|
|
'downloadable' => $variation->is_downloadable(), |
1028
|
|
|
'virtual' => $variation->is_virtual(), |
1029
|
|
|
'permalink' => $variation->get_permalink(), |
1030
|
|
|
'sku' => $variation->get_sku(), |
1031
|
|
|
'price' => $variation->get_price(), |
1032
|
|
|
'regular_price' => $variation->get_regular_price(), |
1033
|
|
|
'sale_price' => $variation->get_sale_price() ? $variation->get_sale_price() : null, |
1034
|
|
|
'taxable' => $variation->is_taxable(), |
1035
|
|
|
'tax_status' => $variation->get_tax_status(), |
1036
|
|
|
'tax_class' => $variation->get_tax_class(), |
1037
|
|
|
'managing_stock' => $variation->managing_stock(), |
1038
|
|
|
'stock_quantity' => $variation->get_stock_quantity(), |
1039
|
|
|
'in_stock' => $variation->is_in_stock(), |
1040
|
|
|
'backordered' => $variation->is_on_backorder(), |
1041
|
|
|
'purchaseable' => $variation->is_purchasable(), |
1042
|
|
|
'visible' => $variation->variation_is_visible(), |
1043
|
|
|
'on_sale' => $variation->is_on_sale(), |
1044
|
|
|
'weight' => $variation->get_weight() ? $variation->get_weight() : null, |
1045
|
|
|
'dimensions' => array( |
1046
|
|
|
'length' => $variation->length, |
1047
|
|
|
'width' => $variation->width, |
1048
|
|
|
'height' => $variation->height, |
1049
|
|
|
'unit' => get_option( 'woocommerce_dimension_unit' ), |
1050
|
|
|
), |
1051
|
|
|
'shipping_class' => $variation->get_shipping_class(), |
1052
|
|
|
'shipping_class_id' => ( 0 !== $variation->get_shipping_class_id() ) ? $variation->get_shipping_class_id() : null, |
1053
|
|
|
'image' => $this->get_images( $variation ), |
1054
|
|
|
'attributes' => $this->get_attributes( $variation ), |
1055
|
|
|
'downloads' => $this->get_downloads( $variation ), |
1056
|
|
|
'download_limit' => (int) $product->download_limit, |
1057
|
|
|
'download_expiry' => (int) $product->download_expiry, |
1058
|
|
|
); |
1059
|
|
|
} |
1060
|
|
|
|
1061
|
|
|
return $variations; |
1062
|
|
|
} |
1063
|
|
|
|
1064
|
|
|
/** |
1065
|
|
|
* Save product meta |
1066
|
|
|
* |
1067
|
|
|
* @since 2.5.0 |
1068
|
|
|
* @param int $product_id |
1069
|
|
|
* @param array $data |
1070
|
|
|
* @return bool |
1071
|
|
|
* @throws WC_CLI_Exception |
1072
|
|
|
*/ |
1073
|
|
|
private function save_product_meta( $product_id, $data ) { |
1074
|
|
|
global $wpdb; |
1075
|
|
|
|
1076
|
|
|
// Product Type |
1077
|
|
|
$product_type = null; |
1078
|
|
View Code Duplication |
if ( isset( $data['type'] ) ) { |
|
|
|
|
1079
|
|
|
$product_type = wc_clean( $data['type'] ); |
1080
|
|
|
wp_set_object_terms( $product_id, $product_type, 'product_type' ); |
1081
|
|
|
} else { |
1082
|
|
|
$_product_type = get_the_terms( $product_id, 'product_type' ); |
1083
|
|
|
if ( is_array( $_product_type ) ) { |
1084
|
|
|
$_product_type = current( $_product_type ); |
1085
|
|
|
$product_type = $_product_type->slug; |
1086
|
|
|
} |
1087
|
|
|
} |
1088
|
|
|
|
1089
|
|
|
// Virtual |
1090
|
|
View Code Duplication |
if ( isset( $data['virtual'] ) ) { |
|
|
|
|
1091
|
|
|
update_post_meta( $product_id, '_virtual', ( $this->is_true( $data['virtual'] ) ) ? 'yes' : 'no' ); |
1092
|
|
|
} |
1093
|
|
|
|
1094
|
|
|
// Tax status |
1095
|
|
|
if ( isset( $data['tax_status'] ) ) { |
1096
|
|
|
update_post_meta( $product_id, '_tax_status', wc_clean( $data['tax_status'] ) ); |
1097
|
|
|
} |
1098
|
|
|
|
1099
|
|
|
// Tax Class |
1100
|
|
|
if ( isset( $data['tax_class'] ) ) { |
1101
|
|
|
update_post_meta( $product_id, '_tax_class', wc_clean( $data['tax_class'] ) ); |
1102
|
|
|
} |
1103
|
|
|
|
1104
|
|
|
// Catalog Visibility |
1105
|
|
|
if ( isset( $data['catalog_visibility'] ) ) { |
1106
|
|
|
update_post_meta( $product_id, '_visibility', wc_clean( $data['catalog_visibility'] ) ); |
1107
|
|
|
} |
1108
|
|
|
|
1109
|
|
|
// Purchase Note |
1110
|
|
|
if ( isset( $data['purchase_note'] ) ) { |
1111
|
|
|
update_post_meta( $product_id, '_purchase_note', wc_clean( $data['purchase_note'] ) ); |
1112
|
|
|
} |
1113
|
|
|
|
1114
|
|
|
// Featured Product |
1115
|
|
View Code Duplication |
if ( isset( $data['featured'] ) ) { |
|
|
|
|
1116
|
|
|
update_post_meta( $product_id, '_featured', ( $this->is_true( $data['featured'] ) ) ? 'yes' : 'no' ); |
1117
|
|
|
} |
1118
|
|
|
|
1119
|
|
|
// Shipping data |
1120
|
|
|
$this->save_product_shipping_data( $product_id, $data ); |
1121
|
|
|
|
1122
|
|
|
// SKU |
1123
|
|
View Code Duplication |
if ( isset( $data['sku'] ) ) { |
|
|
|
|
1124
|
|
|
$sku = get_post_meta( $product_id, '_sku', true ); |
1125
|
|
|
$new_sku = wc_clean( $data['sku'] ); |
1126
|
|
|
|
1127
|
|
|
if ( '' == $new_sku ) { |
1128
|
|
|
update_post_meta( $product_id, '_sku', '' ); |
1129
|
|
|
} elseif ( $new_sku !== $sku ) { |
1130
|
|
|
if ( ! empty( $new_sku ) ) { |
1131
|
|
|
$unique_sku = wc_product_has_unique_sku( $product_id, $new_sku ); |
1132
|
|
|
if ( ! $unique_sku ) { |
1133
|
|
|
throw new WC_CLI_Exception( 'woocommerce_cli_product_sku_already_exists', __( 'The SKU already exists on another product', 'woocommerce' ) ); |
1134
|
|
|
} else { |
1135
|
|
|
update_post_meta( $product_id, '_sku', $new_sku ); |
1136
|
|
|
} |
1137
|
|
|
} else { |
1138
|
|
|
update_post_meta( $product_id, '_sku', '' ); |
1139
|
|
|
} |
1140
|
|
|
} |
1141
|
|
|
} |
1142
|
|
|
|
1143
|
|
|
// Attributes |
1144
|
|
|
if ( isset( $data['attributes'] ) ) { |
1145
|
|
|
$attributes = array(); |
1146
|
|
|
|
1147
|
|
|
foreach ( $data['attributes'] as $attribute ) { |
1148
|
|
|
$is_taxonomy = 0; |
1149
|
|
|
$taxonomy = 0; |
1150
|
|
|
|
1151
|
|
|
if ( ! isset( $attribute['name'] ) ) { |
1152
|
|
|
continue; |
1153
|
|
|
} |
1154
|
|
|
|
1155
|
|
|
$attribute_slug = sanitize_title( $attribute['name'] ); |
1156
|
|
|
|
1157
|
|
|
if ( isset( $attribute['slug'] ) ) { |
1158
|
|
|
$taxonomy = $this->get_attribute_taxonomy_by_slug( $attribute['slug'] ); |
1159
|
|
|
$attribute_slug = sanitize_title( $attribute['slug'] ); |
1160
|
|
|
} |
1161
|
|
|
|
1162
|
|
|
if ( $taxonomy ) { |
1163
|
|
|
$is_taxonomy = 1; |
1164
|
|
|
} |
1165
|
|
|
|
1166
|
|
|
if ( $is_taxonomy ) { |
1167
|
|
|
|
1168
|
|
|
if ( isset( $attribute['options'] ) ) { |
1169
|
|
|
$options = $attribute['options']; |
1170
|
|
|
|
1171
|
|
|
if ( ! is_array( $attribute['options'] ) ) { |
1172
|
|
|
// Text based attributes - Posted values are term names |
1173
|
|
|
$options = explode( WC_DELIMITER, $options ); |
1174
|
|
|
} |
1175
|
|
|
|
1176
|
|
|
$values = array_map( 'wc_sanitize_term_text_based', $options ); |
1177
|
|
|
$values = array_filter( $values, 'strlen' ); |
1178
|
|
|
} else { |
1179
|
|
|
$values = array(); |
1180
|
|
|
} |
1181
|
|
|
|
1182
|
|
|
// Update post terms |
1183
|
|
|
if ( taxonomy_exists( $taxonomy ) ) { |
1184
|
|
|
wp_set_object_terms( $product_id, $values, $taxonomy ); |
1185
|
|
|
} |
1186
|
|
|
|
1187
|
|
View Code Duplication |
if ( $values ) { |
|
|
|
|
1188
|
|
|
// Add attribute to array, but don't set values |
1189
|
|
|
$attributes[ $taxonomy ] = array( |
1190
|
|
|
'name' => $taxonomy, |
1191
|
|
|
'value' => '', |
1192
|
|
|
'position' => isset( $attribute['position'] ) ? absint( $attribute['position'] ) : 0, |
1193
|
|
|
'is_visible' => ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0, |
1194
|
|
|
'is_variation' => ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0, |
1195
|
|
|
'is_taxonomy' => $is_taxonomy |
1196
|
|
|
); |
1197
|
|
|
} |
1198
|
|
|
|
1199
|
|
View Code Duplication |
} elseif ( isset( $attribute['options'] ) ) { |
|
|
|
|
1200
|
|
|
// Array based |
1201
|
|
|
if ( is_array( $attribute['options'] ) ) { |
1202
|
|
|
$values = implode( ' ' . WC_DELIMITER . ' ', array_map( 'wc_clean', $attribute['options'] ) ); |
1203
|
|
|
|
1204
|
|
|
// Text based, separate by pipe |
1205
|
|
|
} else { |
1206
|
|
|
$values = implode( ' ' . WC_DELIMITER . ' ', array_map( 'wc_clean', explode( WC_DELIMITER, $attribute['options'] ) ) ); |
1207
|
|
|
} |
1208
|
|
|
|
1209
|
|
|
// Custom attribute - Add attribute to array and set the values |
1210
|
|
|
$attributes[ $attribute_slug ] = array( |
1211
|
|
|
'name' => wc_clean( $attribute['name'] ), |
1212
|
|
|
'value' => $values, |
1213
|
|
|
'position' => isset( $attribute['position'] ) ? absint( $attribute['position'] ) : 0, |
1214
|
|
|
'is_visible' => ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0, |
1215
|
|
|
'is_variation' => ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0, |
1216
|
|
|
'is_taxonomy' => $is_taxonomy |
1217
|
|
|
); |
1218
|
|
|
} |
1219
|
|
|
} |
1220
|
|
|
|
1221
|
|
|
uasort( $attributes, 'wc_product_attribute_uasort_comparison' ); |
1222
|
|
|
|
1223
|
|
|
update_post_meta( $product_id, '_product_attributes', $attributes ); |
1224
|
|
|
} |
1225
|
|
|
|
1226
|
|
|
// Sales and prices |
1227
|
|
|
if ( in_array( $product_type, array( 'variable', 'grouped' ) ) ) { |
1228
|
|
|
|
1229
|
|
|
// Variable and grouped products have no prices |
1230
|
|
|
update_post_meta( $product_id, '_regular_price', '' ); |
1231
|
|
|
update_post_meta( $product_id, '_sale_price', '' ); |
1232
|
|
|
update_post_meta( $product_id, '_sale_price_dates_from', '' ); |
1233
|
|
|
update_post_meta( $product_id, '_sale_price_dates_to', '' ); |
1234
|
|
|
update_post_meta( $product_id, '_price', '' ); |
1235
|
|
|
|
1236
|
|
|
} else { |
1237
|
|
|
|
1238
|
|
|
// Regular Price |
1239
|
|
|
if ( isset( $data['regular_price'] ) ) { |
1240
|
|
|
$regular_price = ( '' === $data['regular_price'] ) ? '' : wc_format_decimal( $data['regular_price'] ); |
1241
|
|
|
update_post_meta( $product_id, '_regular_price', $regular_price ); |
1242
|
|
|
} else { |
1243
|
|
|
$regular_price = get_post_meta( $product_id, '_regular_price', true ); |
1244
|
|
|
} |
1245
|
|
|
|
1246
|
|
|
// Sale Price |
1247
|
|
|
if ( isset( $data['sale_price'] ) ) { |
1248
|
|
|
$sale_price = ( '' === $data['sale_price'] ) ? '' : wc_format_decimal( $data['sale_price'] ); |
1249
|
|
|
update_post_meta( $product_id, '_sale_price', $sale_price ); |
1250
|
|
|
} else { |
1251
|
|
|
$sale_price = get_post_meta( $product_id, '_sale_price', true ); |
1252
|
|
|
} |
1253
|
|
|
|
1254
|
|
|
$date_from = isset( $data['sale_price_dates_from'] ) ? strtotime( $data['sale_price_dates_from'] ) : get_post_meta( $product_id, '_sale_price_dates_from', true ); |
1255
|
|
|
$date_to = isset( $data['sale_price_dates_to'] ) ? strtotime( $data['sale_price_dates_to'] ) : get_post_meta( $product_id, '_sale_price_dates_to', true ); |
1256
|
|
|
|
1257
|
|
|
// Dates |
1258
|
|
|
if ( $date_from ) { |
1259
|
|
|
update_post_meta( $product_id, '_sale_price_dates_from', $date_from ); |
1260
|
|
|
} else { |
1261
|
|
|
update_post_meta( $product_id, '_sale_price_dates_from', '' ); |
1262
|
|
|
} |
1263
|
|
|
|
1264
|
|
|
if ( $date_to ) { |
1265
|
|
|
update_post_meta( $product_id, '_sale_price_dates_to', $date_to ); |
1266
|
|
|
} else { |
1267
|
|
|
update_post_meta( $product_id, '_sale_price_dates_to', '' ); |
1268
|
|
|
} |
1269
|
|
|
|
1270
|
|
|
if ( $date_to && ! $date_from ) { |
1271
|
|
|
$date_from = strtotime( 'NOW', current_time( 'timestamp' ) ); |
1272
|
|
|
update_post_meta( $product_id, '_sale_price_dates_from', $date_from ); |
1273
|
|
|
} |
1274
|
|
|
|
1275
|
|
|
// Update price if on sale |
1276
|
|
|
if ( '' !== $sale_price && '' == $date_to && '' == $date_from ) { |
1277
|
|
|
update_post_meta( $product_id, '_price', wc_format_decimal( $sale_price ) ); |
1278
|
|
|
} else { |
1279
|
|
|
update_post_meta( $product_id, '_price', $regular_price ); |
1280
|
|
|
} |
1281
|
|
|
|
1282
|
|
View Code Duplication |
if ( '' !== $sale_price && $date_from && $date_from <= strtotime( 'NOW', current_time( 'timestamp' ) ) ) { |
|
|
|
|
1283
|
|
|
update_post_meta( $product_id, '_price', wc_format_decimal( $sale_price ) ); |
1284
|
|
|
} |
1285
|
|
|
|
1286
|
|
|
if ( $date_to && $date_to < strtotime( 'NOW', current_time( 'timestamp' ) ) ) { |
1287
|
|
|
update_post_meta( $product_id, '_price', $regular_price ); |
1288
|
|
|
update_post_meta( $product_id, '_sale_price_dates_from', '' ); |
1289
|
|
|
update_post_meta( $product_id, '_sale_price_dates_to', '' ); |
1290
|
|
|
} |
1291
|
|
|
} |
1292
|
|
|
|
1293
|
|
|
// Product parent ID for groups |
1294
|
|
View Code Duplication |
if ( isset( $data['parent_id'] ) ) { |
|
|
|
|
1295
|
|
|
wp_update_post( array( 'ID' => $product_id, 'post_parent' => absint( $data['parent_id'] ) ) ); |
1296
|
|
|
} |
1297
|
|
|
|
1298
|
|
|
// Update parent if grouped so price sorting works and stays in sync with the cheapest child |
1299
|
|
|
$_product = wc_get_product( $product_id ); |
1300
|
|
|
if ( $_product && $_product->post->post_parent > 0 || $product_type == 'grouped' ) { |
1301
|
|
|
|
1302
|
|
|
$clear_parent_ids = array(); |
1303
|
|
|
|
1304
|
|
|
if ( $_product->post->post_parent > 0 ) { |
1305
|
|
|
$clear_parent_ids[] = $_product->post->post_parent; |
1306
|
|
|
} |
1307
|
|
|
|
1308
|
|
|
if ( $product_type == 'grouped' ) { |
1309
|
|
|
$clear_parent_ids[] = $product_id; |
1310
|
|
|
} |
1311
|
|
|
|
1312
|
|
|
if ( $clear_parent_ids ) { |
|
|
|
|
1313
|
|
|
foreach ( $clear_parent_ids as $clear_id ) { |
1314
|
|
|
|
1315
|
|
|
$children_by_price = get_posts( array( |
1316
|
|
|
'post_parent' => $clear_id, |
1317
|
|
|
'orderby' => 'meta_value_num', |
1318
|
|
|
'order' => 'asc', |
1319
|
|
|
'meta_key' => '_price', |
1320
|
|
|
'posts_per_page' => 1, |
1321
|
|
|
'post_type' => 'product', |
1322
|
|
|
'fields' => 'ids' |
1323
|
|
|
) ); |
1324
|
|
|
|
1325
|
|
|
if ( $children_by_price ) { |
1326
|
|
|
foreach ( $children_by_price as $child ) { |
1327
|
|
|
$child_price = get_post_meta( $child, '_price', true ); |
1328
|
|
|
update_post_meta( $clear_id, '_price', $child_price ); |
1329
|
|
|
} |
1330
|
|
|
} |
1331
|
|
|
} |
1332
|
|
|
} |
1333
|
|
|
} |
1334
|
|
|
|
1335
|
|
|
// Sold Individually |
1336
|
|
|
if ( isset( $data['sold_individually'] ) ) { |
1337
|
|
|
update_post_meta( $product_id, '_sold_individually', ( $this->is_true( $data['sold_individually'] ) ) ? 'yes' : '' ); |
1338
|
|
|
} |
1339
|
|
|
|
1340
|
|
|
// Stock status |
1341
|
|
View Code Duplication |
if ( isset( $data['in_stock'] ) ) { |
|
|
|
|
1342
|
|
|
$stock_status = ( $this->is_true( $data['in_stock'] ) ) ? 'instock' : 'outofstock'; |
1343
|
|
|
} else { |
1344
|
|
|
$stock_status = get_post_meta( $product_id, '_stock_status', true ); |
1345
|
|
|
|
1346
|
|
|
if ( '' === $stock_status ) { |
1347
|
|
|
$stock_status = 'instock'; |
1348
|
|
|
} |
1349
|
|
|
} |
1350
|
|
|
|
1351
|
|
|
// Stock Data |
1352
|
|
|
if ( 'yes' == get_option( 'woocommerce_manage_stock' ) ) { |
1353
|
|
|
// Manage stock |
1354
|
|
View Code Duplication |
if ( isset( $data['managing_stock'] ) ) { |
|
|
|
|
1355
|
|
|
$managing_stock = ( $this->is_true( $data['managing_stock'] ) ) ? 'yes' : 'no'; |
1356
|
|
|
update_post_meta( $product_id, '_manage_stock', $managing_stock ); |
1357
|
|
|
} else { |
1358
|
|
|
$managing_stock = get_post_meta( $product_id, '_manage_stock', true ); |
1359
|
|
|
} |
1360
|
|
|
|
1361
|
|
|
// Backorders |
1362
|
|
|
if ( isset( $data['backorders'] ) ) { |
1363
|
|
View Code Duplication |
if ( 'notify' == $data['backorders'] ) { |
|
|
|
|
1364
|
|
|
$backorders = 'notify'; |
1365
|
|
|
} else { |
1366
|
|
|
$backorders = ( $this->is_true( $data['backorders'] ) ) ? 'yes' : 'no'; |
1367
|
|
|
} |
1368
|
|
|
|
1369
|
|
|
update_post_meta( $product_id, '_backorders', $backorders ); |
1370
|
|
|
} else { |
1371
|
|
|
$backorders = get_post_meta( $product_id, '_backorders', true ); |
1372
|
|
|
} |
1373
|
|
|
|
1374
|
|
|
if ( 'grouped' == $product_type ) { |
1375
|
|
|
|
1376
|
|
|
update_post_meta( $product_id, '_manage_stock', 'no' ); |
1377
|
|
|
update_post_meta( $product_id, '_backorders', 'no' ); |
1378
|
|
|
update_post_meta( $product_id, '_stock', '' ); |
1379
|
|
|
|
1380
|
|
|
wc_update_product_stock_status( $product_id, $stock_status ); |
1381
|
|
|
|
1382
|
|
|
} elseif ( 'external' == $product_type ) { |
1383
|
|
|
|
1384
|
|
|
update_post_meta( $product_id, '_manage_stock', 'no' ); |
1385
|
|
|
update_post_meta( $product_id, '_backorders', 'no' ); |
1386
|
|
|
update_post_meta( $product_id, '_stock', '' ); |
1387
|
|
|
|
1388
|
|
|
wc_update_product_stock_status( $product_id, 'instock' ); |
1389
|
|
|
|
1390
|
|
|
} elseif ( 'yes' == $managing_stock ) { |
1391
|
|
|
update_post_meta( $product_id, '_backorders', $backorders ); |
1392
|
|
|
|
1393
|
|
|
wc_update_product_stock_status( $product_id, $stock_status ); |
1394
|
|
|
|
1395
|
|
|
// Stock quantity |
1396
|
|
|
if ( isset( $data['stock_quantity'] ) ) { |
1397
|
|
|
wc_update_product_stock( $product_id, intval( $data['stock_quantity'] ) ); |
1398
|
|
|
} |
1399
|
|
|
} else { |
1400
|
|
|
|
1401
|
|
|
// Don't manage stock |
1402
|
|
|
update_post_meta( $product_id, '_manage_stock', 'no' ); |
1403
|
|
|
update_post_meta( $product_id, '_backorders', $backorders ); |
1404
|
|
|
update_post_meta( $product_id, '_stock', '' ); |
1405
|
|
|
|
1406
|
|
|
wc_update_product_stock_status( $product_id, $stock_status ); |
1407
|
|
|
} |
1408
|
|
|
|
1409
|
|
|
} else { |
1410
|
|
|
wc_update_product_stock_status( $product_id, $stock_status ); |
1411
|
|
|
} |
1412
|
|
|
|
1413
|
|
|
// Upsells |
1414
|
|
View Code Duplication |
if ( isset( $data['upsell_ids'] ) ) { |
|
|
|
|
1415
|
|
|
$upsells = array(); |
1416
|
|
|
$ids = $data['upsell_ids']; |
1417
|
|
|
|
1418
|
|
|
if ( ! empty( $ids ) ) { |
1419
|
|
|
foreach ( $ids as $id ) { |
1420
|
|
|
if ( $id && $id > 0 ) { |
1421
|
|
|
$upsells[] = $id; |
1422
|
|
|
} |
1423
|
|
|
} |
1424
|
|
|
|
1425
|
|
|
update_post_meta( $product_id, '_upsell_ids', $upsells ); |
1426
|
|
|
} else { |
1427
|
|
|
delete_post_meta( $product_id, '_upsell_ids' ); |
1428
|
|
|
} |
1429
|
|
|
} |
1430
|
|
|
|
1431
|
|
|
// Cross sells |
1432
|
|
View Code Duplication |
if ( isset( $data['cross_sell_ids'] ) ) { |
|
|
|
|
1433
|
|
|
$crosssells = array(); |
1434
|
|
|
$ids = $data['cross_sell_ids']; |
1435
|
|
|
|
1436
|
|
|
if ( ! empty( $ids ) ) { |
1437
|
|
|
foreach ( $ids as $id ) { |
1438
|
|
|
if ( $id && $id > 0 ) { |
1439
|
|
|
$crosssells[] = $id; |
1440
|
|
|
} |
1441
|
|
|
} |
1442
|
|
|
|
1443
|
|
|
update_post_meta( $product_id, '_crosssell_ids', $crosssells ); |
1444
|
|
|
} else { |
1445
|
|
|
delete_post_meta( $product_id, '_crosssell_ids' ); |
1446
|
|
|
} |
1447
|
|
|
} |
1448
|
|
|
|
1449
|
|
|
// Product categories |
1450
|
|
View Code Duplication |
if ( isset( $data['categories'] ) ) { |
|
|
|
|
1451
|
|
|
$term_ids = array_unique( array_map( 'intval', (array) $data['categories'] ) ); |
1452
|
|
|
wp_set_object_terms( $product_id, $term_ids, 'product_cat' ); |
1453
|
|
|
} |
1454
|
|
|
|
1455
|
|
|
// Product tags |
1456
|
|
View Code Duplication |
if ( isset( $data['tags'] ) ) { |
|
|
|
|
1457
|
|
|
$term_ids = array_unique( array_map( 'intval', (array) $data['tags'] ) ); |
1458
|
|
|
wp_set_object_terms( $product_id, $term_ids, 'product_tag' ); |
1459
|
|
|
} |
1460
|
|
|
|
1461
|
|
|
// Downloadable |
1462
|
|
View Code Duplication |
if ( isset( $data['downloadable'] ) ) { |
|
|
|
|
1463
|
|
|
$is_downloadable = ( $this->is_true( $data['downloadable'] ) ) ? 'yes' : 'no'; |
1464
|
|
|
update_post_meta( $product_id, '_downloadable', $is_downloadable ); |
1465
|
|
|
} else { |
1466
|
|
|
$is_downloadable = get_post_meta( $product_id, '_downloadable', true ); |
1467
|
|
|
} |
1468
|
|
|
|
1469
|
|
|
// Downloadable options |
1470
|
|
|
if ( 'yes' == $is_downloadable ) { |
1471
|
|
|
|
1472
|
|
|
// Downloadable files |
1473
|
|
|
if ( isset( $data['downloads'] ) && is_array( $data['downloads'] ) ) { |
1474
|
|
|
$this->save_downloadable_files( $product_id, $data['downloads'] ); |
1475
|
|
|
} |
1476
|
|
|
|
1477
|
|
|
// Download limit |
1478
|
|
View Code Duplication |
if ( isset( $data['download_limit'] ) ) { |
|
|
|
|
1479
|
|
|
update_post_meta( $product_id, '_download_limit', ( '' === $data['download_limit'] ) ? '' : absint( $data['download_limit'] ) ); |
1480
|
|
|
} |
1481
|
|
|
|
1482
|
|
|
// Download expiry |
1483
|
|
View Code Duplication |
if ( isset( $data['download_expiry'] ) ) { |
|
|
|
|
1484
|
|
|
update_post_meta( $product_id, '_download_expiry', ( '' === $data['download_expiry'] ) ? '' : absint( $data['download_expiry'] ) ); |
1485
|
|
|
} |
1486
|
|
|
|
1487
|
|
|
// Download type |
1488
|
|
|
if ( isset( $data['download_type'] ) ) { |
1489
|
|
|
update_post_meta( $product_id, '_download_type', wc_clean( $data['download_type'] ) ); |
1490
|
|
|
} |
1491
|
|
|
} |
1492
|
|
|
|
1493
|
|
|
// Product url |
1494
|
|
View Code Duplication |
if ( $product_type == 'external' ) { |
|
|
|
|
1495
|
|
|
if ( isset( $data['product_url'] ) ) { |
1496
|
|
|
update_post_meta( $product_id, '_product_url', wc_clean( $data['product_url'] ) ); |
1497
|
|
|
} |
1498
|
|
|
|
1499
|
|
|
if ( isset( $data['button_text'] ) ) { |
1500
|
|
|
update_post_meta( $product_id, '_button_text', wc_clean( $data['button_text'] ) ); |
1501
|
|
|
} |
1502
|
|
|
} |
1503
|
|
|
|
1504
|
|
|
// Reviews allowed |
1505
|
|
|
if ( isset( $data['reviews_allowed'] ) ) { |
1506
|
|
|
$reviews_allowed = ( $this->is_true( $data['reviews_allowed'] ) ) ? 'open' : 'closed'; |
1507
|
|
|
|
1508
|
|
|
$wpdb->update( $wpdb->posts, array( 'comment_status' => $reviews_allowed ), array( 'ID' => $product_id ) ); |
1509
|
|
|
} |
1510
|
|
|
|
1511
|
|
|
// Do action for product type |
1512
|
|
|
do_action( 'woocommerce_cli_process_product_meta_' . $product_type, $product_id, $data ); |
1513
|
|
|
|
1514
|
|
|
return true; |
1515
|
|
|
} |
1516
|
|
|
|
1517
|
|
|
/** |
1518
|
|
|
* Save variations. |
1519
|
|
|
* |
1520
|
|
|
* @since 2.5.0 |
1521
|
|
|
* @param int $id |
1522
|
|
|
* @param array $data |
1523
|
|
|
* @return bool |
1524
|
|
|
* @throws WC_CLI_Exception |
1525
|
|
|
*/ |
1526
|
|
|
private function save_variations( $id, $data ) { |
1527
|
|
|
global $wpdb; |
1528
|
|
|
|
1529
|
|
|
$variations = $data['variations']; |
1530
|
|
|
$attributes = (array) maybe_unserialize( get_post_meta( $id, '_product_attributes', true ) ); |
1531
|
|
|
|
1532
|
|
|
foreach ( $variations as $menu_order => $variation ) { |
1533
|
|
|
$variation_id = isset( $variation['id'] ) ? absint( $variation['id'] ) : 0; |
1534
|
|
|
|
1535
|
|
|
// Generate a useful post title |
1536
|
|
|
$variation_post_title = sprintf( __( 'Variation #%s of %s', 'woocommerce' ), $variation_id, esc_html( get_the_title( $id ) ) ); |
1537
|
|
|
|
1538
|
|
|
// Update or Add post |
1539
|
|
View Code Duplication |
if ( ! $variation_id ) { |
|
|
|
|
1540
|
|
|
$post_status = ( isset( $variation['visible'] ) && false === $variation['visible'] ) ? 'private' : 'publish'; |
1541
|
|
|
|
1542
|
|
|
$new_variation = array( |
1543
|
|
|
'post_title' => $variation_post_title, |
1544
|
|
|
'post_content' => '', |
1545
|
|
|
'post_status' => $post_status, |
1546
|
|
|
'post_author' => get_current_user_id(), |
1547
|
|
|
'post_parent' => $id, |
1548
|
|
|
'post_type' => 'product_variation', |
1549
|
|
|
'menu_order' => $menu_order |
1550
|
|
|
); |
1551
|
|
|
|
1552
|
|
|
$variation_id = wp_insert_post( $new_variation ); |
1553
|
|
|
|
1554
|
|
|
do_action( 'woocommerce_create_product_variation', $variation_id ); |
1555
|
|
|
} else { |
1556
|
|
|
$update_variation = array( 'post_title' => $variation_post_title, 'menu_order' => $menu_order ); |
1557
|
|
|
if ( isset( $variation['visible'] ) ) { |
1558
|
|
|
$post_status = ( false === $variation['visible'] ) ? 'private' : 'publish'; |
1559
|
|
|
$update_variation['post_status'] = $post_status; |
1560
|
|
|
} |
1561
|
|
|
|
1562
|
|
|
$wpdb->update( $wpdb->posts, $update_variation, array( 'ID' => $variation_id ) ); |
1563
|
|
|
|
1564
|
|
|
do_action( 'woocommerce_update_product_variation', $variation_id ); |
1565
|
|
|
} |
1566
|
|
|
|
1567
|
|
|
// Stop with we don't have a variation ID |
1568
|
|
|
if ( is_wp_error( $variation_id ) ) { |
1569
|
|
|
throw new WC_CLI_Exception( 'woocommerce_cli_cannot_save_product_variation', $variation_id->get_error_message() ); |
1570
|
|
|
} |
1571
|
|
|
|
1572
|
|
|
// SKU |
1573
|
|
View Code Duplication |
if ( isset( $variation['sku'] ) ) { |
|
|
|
|
1574
|
|
|
$sku = get_post_meta( $variation_id, '_sku', true ); |
1575
|
|
|
$new_sku = wc_clean( $variation['sku'] ); |
1576
|
|
|
|
1577
|
|
|
if ( '' == $new_sku ) { |
1578
|
|
|
update_post_meta( $variation_id, '_sku', '' ); |
1579
|
|
|
} elseif ( $new_sku !== $sku ) { |
1580
|
|
|
if ( ! empty( $new_sku ) ) { |
1581
|
|
|
$unique_sku = wc_product_has_unique_sku( $variation_id, $new_sku ); |
1582
|
|
|
if ( ! $unique_sku ) { |
1583
|
|
|
throw new WC_CLI_Exception( 'woocommerce_cli_product_sku_already_exists', __( 'The SKU already exists on another product', 'woocommerce' ) ); |
1584
|
|
|
} else { |
1585
|
|
|
update_post_meta( $variation_id, '_sku', $new_sku ); |
1586
|
|
|
} |
1587
|
|
|
} else { |
1588
|
|
|
update_post_meta( $variation_id, '_sku', '' ); |
1589
|
|
|
} |
1590
|
|
|
} |
1591
|
|
|
} |
1592
|
|
|
|
1593
|
|
|
// Thumbnail |
1594
|
|
|
if ( isset( $variation['image'] ) && is_array( $variation['image'] ) ) { |
1595
|
|
|
$image = current( $variation['image'] ); |
1596
|
|
|
if ( $image && is_array( $image ) ) { |
1597
|
|
|
if ( isset( $image['position'] ) && isset( $image['src'] ) && $image['position'] == 0 ) { |
1598
|
|
|
$upload = $this->upload_product_image( wc_clean( $image['src'] ) ); |
1599
|
|
|
if ( is_wp_error( $upload ) ) { |
1600
|
|
|
throw new WC_CLI_Exception( 'woocommerce_cli_cannot_upload_product_image', $upload->get_error_message() ); |
1601
|
|
|
} |
1602
|
|
|
$attachment_id = $this->set_product_image_as_attachment( $upload, $id ); |
1603
|
|
|
update_post_meta( $variation_id, '_thumbnail_id', $attachment_id ); |
1604
|
|
|
} |
1605
|
|
|
} else { |
1606
|
|
|
delete_post_meta( $variation_id, '_thumbnail_id' ); |
1607
|
|
|
} |
1608
|
|
|
} |
1609
|
|
|
|
1610
|
|
|
// Virtual variation |
1611
|
|
View Code Duplication |
if ( isset( $variation['virtual'] ) ) { |
|
|
|
|
1612
|
|
|
$is_virtual = ( $this->is_true( $variation['virtual'] ) ) ? 'yes' : 'no'; |
1613
|
|
|
update_post_meta( $variation_id, '_virtual', $is_virtual ); |
1614
|
|
|
} |
1615
|
|
|
|
1616
|
|
|
// Downloadable variation |
1617
|
|
View Code Duplication |
if ( isset( $variation['downloadable'] ) ) { |
|
|
|
|
1618
|
|
|
$is_downloadable = ( $this->is_true( $variation['downloadable'] ) ) ? 'yes' : 'no'; |
1619
|
|
|
update_post_meta( $variation_id, '_downloadable', $is_downloadable ); |
1620
|
|
|
} else { |
1621
|
|
|
$is_downloadable = get_post_meta( $variation_id, '_downloadable', true ); |
1622
|
|
|
} |
1623
|
|
|
|
1624
|
|
|
// Shipping data |
1625
|
|
|
$this->save_product_shipping_data( $variation_id, $variation ); |
1626
|
|
|
|
1627
|
|
|
// Stock handling |
1628
|
|
View Code Duplication |
if ( isset( $variation['managing_stock'] ) ) { |
|
|
|
|
1629
|
|
|
$managing_stock = ( $this->is_true( $variation['managing_stock'] ) ) ? 'yes' : 'no'; |
1630
|
|
|
update_post_meta( $variation_id, '_manage_stock', $managing_stock ); |
1631
|
|
|
} else { |
1632
|
|
|
$managing_stock = get_post_meta( $variation_id, '_manage_stock', true ); |
1633
|
|
|
} |
1634
|
|
|
|
1635
|
|
|
// Only update stock status to user setting if changed by the user, but do so before looking at stock levels at variation level |
1636
|
|
|
if ( isset( $variation['in_stock'] ) ) { |
1637
|
|
|
$stock_status = ( $this->is_true( $variation['in_stock'] ) ) ? 'instock' : 'outofstock'; |
1638
|
|
|
wc_update_product_stock_status( $variation_id, $stock_status ); |
1639
|
|
|
} |
1640
|
|
|
|
1641
|
|
|
if ( 'yes' === $managing_stock ) { |
1642
|
|
|
if ( isset( $variation['backorders'] ) ) { |
1643
|
|
View Code Duplication |
if ( 'notify' === $variation['backorders'] ) { |
|
|
|
|
1644
|
|
|
$backorders = 'notify'; |
1645
|
|
|
} else { |
1646
|
|
|
$backorders = ( $this->is_true( $variation['backorders'] ) ) ? 'yes' : 'no'; |
1647
|
|
|
} |
1648
|
|
|
} else { |
1649
|
|
|
$backorders = 'no'; |
1650
|
|
|
} |
1651
|
|
|
|
1652
|
|
|
update_post_meta( $variation_id, '_backorders', $backorders ); |
1653
|
|
|
|
1654
|
|
|
if ( isset( $variation['stock_quantity'] ) ) { |
1655
|
|
|
wc_update_product_stock( $variation_id, wc_stock_amount( $variation['stock_quantity'] ) ); |
1656
|
|
|
} |
1657
|
|
|
} else { |
1658
|
|
|
delete_post_meta( $variation_id, '_backorders' ); |
1659
|
|
|
delete_post_meta( $variation_id, '_stock' ); |
1660
|
|
|
} |
1661
|
|
|
|
1662
|
|
|
// Regular Price |
1663
|
|
|
if ( isset( $variation['regular_price'] ) ) { |
1664
|
|
|
$regular_price = ( '' === $variation['regular_price'] ) ? '' : wc_format_decimal( $variation['regular_price'] ); |
1665
|
|
|
update_post_meta( $variation_id, '_regular_price', $regular_price ); |
1666
|
|
|
} else { |
1667
|
|
|
$regular_price = get_post_meta( $variation_id, '_regular_price', true ); |
1668
|
|
|
} |
1669
|
|
|
|
1670
|
|
|
// Sale Price |
1671
|
|
|
if ( isset( $variation['sale_price'] ) ) { |
1672
|
|
|
$sale_price = ( '' === $variation['sale_price'] ) ? '' : wc_format_decimal( $variation['sale_price'] ); |
1673
|
|
|
update_post_meta( $variation_id, '_sale_price', $sale_price ); |
1674
|
|
|
} else { |
1675
|
|
|
$sale_price = get_post_meta( $variation_id, '_sale_price', true ); |
1676
|
|
|
} |
1677
|
|
|
|
1678
|
|
|
$date_from = isset( $variation['sale_price_dates_from'] ) ? strtotime( $variation['sale_price_dates_from'] ) : get_post_meta( $variation_id, '_sale_price_dates_from', true ); |
1679
|
|
|
$date_to = isset( $variation['sale_price_dates_to'] ) ? strtotime( $variation['sale_price_dates_to'] ) : get_post_meta( $variation_id, '_sale_price_dates_to', true ); |
1680
|
|
|
|
1681
|
|
|
// Save Dates |
1682
|
|
|
if ( $date_from ) { |
1683
|
|
|
update_post_meta( $variation_id, '_sale_price_dates_from', $date_from ); |
1684
|
|
|
} else { |
1685
|
|
|
update_post_meta( $variation_id, '_sale_price_dates_from', '' ); |
1686
|
|
|
} |
1687
|
|
|
|
1688
|
|
|
if ( $date_to ) { |
1689
|
|
|
update_post_meta( $variation_id, '_sale_price_dates_to', $date_to ); |
1690
|
|
|
} else { |
1691
|
|
|
update_post_meta( $variation_id, '_sale_price_dates_to', '' ); |
1692
|
|
|
} |
1693
|
|
|
|
1694
|
|
|
if ( $date_to && ! $date_from ) { |
1695
|
|
|
update_post_meta( $variation_id, '_sale_price_dates_from', strtotime( 'NOW', current_time( 'timestamp' ) ) ); |
1696
|
|
|
} |
1697
|
|
|
|
1698
|
|
|
// Update price if on sale |
1699
|
|
|
if ( '' != $sale_price && '' == $date_to && '' == $date_from ) { |
1700
|
|
|
update_post_meta( $variation_id, '_price', $sale_price ); |
1701
|
|
|
} else { |
1702
|
|
|
update_post_meta( $variation_id, '_price', $regular_price ); |
1703
|
|
|
} |
1704
|
|
|
|
1705
|
|
|
if ( '' != $sale_price && $date_from && $date_from < strtotime( 'NOW', current_time( 'timestamp' ) ) ) { |
1706
|
|
|
update_post_meta( $variation_id, '_price', $sale_price ); |
1707
|
|
|
} |
1708
|
|
|
|
1709
|
|
|
if ( $date_to && $date_to < strtotime( 'NOW', current_time( 'timestamp' ) ) ) { |
1710
|
|
|
update_post_meta( $variation_id, '_price', $regular_price ); |
1711
|
|
|
update_post_meta( $variation_id, '_sale_price_dates_from', '' ); |
1712
|
|
|
update_post_meta( $variation_id, '_sale_price_dates_to', '' ); |
1713
|
|
|
} |
1714
|
|
|
|
1715
|
|
|
// Tax class |
1716
|
|
View Code Duplication |
if ( isset( $variation['tax_class'] ) ) { |
|
|
|
|
1717
|
|
|
if ( $variation['tax_class'] !== 'parent' ) { |
1718
|
|
|
update_post_meta( $variation_id, '_tax_class', wc_clean( $variation['tax_class'] ) ); |
1719
|
|
|
} else { |
1720
|
|
|
delete_post_meta( $variation_id, '_tax_class' ); |
1721
|
|
|
} |
1722
|
|
|
} |
1723
|
|
|
|
1724
|
|
|
// Downloads |
1725
|
|
|
if ( 'yes' == $is_downloadable ) { |
1726
|
|
|
// Downloadable files |
1727
|
|
View Code Duplication |
if ( isset( $variation['downloads'] ) && is_array( $variation['downloads'] ) ) { |
|
|
|
|
1728
|
|
|
$this->save_downloadable_files( $id, $variation['downloads'], $variation_id ); |
1729
|
|
|
} |
1730
|
|
|
|
1731
|
|
|
// Download limit |
1732
|
|
|
if ( isset( $variation['download_limit'] ) ) { |
1733
|
|
|
$download_limit = absint( $variation['download_limit'] ); |
1734
|
|
|
update_post_meta( $variation_id, '_download_limit', ( ! $download_limit ) ? '' : $download_limit ); |
1735
|
|
|
} |
1736
|
|
|
|
1737
|
|
|
// Download expiry |
1738
|
|
|
if ( isset( $variation['download_expiry'] ) ) { |
1739
|
|
|
$download_expiry = absint( $variation['download_expiry'] ); |
1740
|
|
|
update_post_meta( $variation_id, '_download_expiry', ( ! $download_expiry ) ? '' : $download_expiry ); |
1741
|
|
|
} |
1742
|
|
|
} else { |
1743
|
|
|
update_post_meta( $variation_id, '_download_limit', '' ); |
1744
|
|
|
update_post_meta( $variation_id, '_download_expiry', '' ); |
1745
|
|
|
update_post_meta( $variation_id, '_downloadable_files', '' ); |
1746
|
|
|
} |
1747
|
|
|
|
1748
|
|
|
// Update taxonomies |
1749
|
|
|
if ( isset( $variation['attributes'] ) ) { |
1750
|
|
|
$updated_attribute_keys = array(); |
1751
|
|
|
|
1752
|
|
|
foreach ( $variation['attributes'] as $slug => $value ) { |
1753
|
|
|
$taxonomy = sanitize_title( $slug ); |
1754
|
|
|
$_attribute = array(); |
1755
|
|
|
|
1756
|
|
|
if ( $this->get_attribute_taxonomy_by_slug( $slug ) !== null ) { |
1757
|
|
|
$taxonomy = $this->get_attribute_taxonomy_by_slug( $slug ); |
1758
|
|
|
} |
1759
|
|
|
|
1760
|
|
|
if ( isset( $attributes[ $taxonomy ] ) ) { |
1761
|
|
|
$_attribute = $attributes[ $taxonomy ]; |
1762
|
|
|
} |
1763
|
|
|
|
1764
|
|
|
if ( isset( $_attribute['is_variation'] ) && $_attribute['is_variation'] ) { |
1765
|
|
|
$attribute_key = 'attribute_' . sanitize_title( $_attribute['name'] ); |
1766
|
|
|
$attribute_value = ! empty( $value ) ? sanitize_title( stripslashes( $value ) ) : ''; |
1767
|
|
|
$updated_attribute_keys[] = $attribute_key; |
1768
|
|
|
|
1769
|
|
|
update_post_meta( $variation_id, $attribute_key, $attribute_value ); |
1770
|
|
|
} |
1771
|
|
|
} |
1772
|
|
|
|
1773
|
|
|
// Remove old taxonomies attributes so data is kept up to date - first get attribute key names |
1774
|
|
|
$delete_attribute_keys = $wpdb->get_col( $wpdb->prepare( "SELECT meta_key FROM {$wpdb->postmeta} WHERE meta_key LIKE 'attribute_%%' AND meta_key NOT IN ( '" . implode( "','", $updated_attribute_keys ) . "' ) AND post_id = %d;", $variation_id ) ); |
1775
|
|
|
|
1776
|
|
|
foreach ( $delete_attribute_keys as $key ) { |
1777
|
|
|
delete_post_meta( $variation_id, $key ); |
1778
|
|
|
} |
1779
|
|
|
} |
1780
|
|
|
|
1781
|
|
|
do_action( 'woocommerce_cli_save_product_variation', $variation_id, $menu_order, $variation ); |
1782
|
|
|
} |
1783
|
|
|
|
1784
|
|
|
// Update parent if variable so price sorting works and stays in sync with the cheapest child |
1785
|
|
|
WC_Product_Variable::sync( $id ); |
1786
|
|
|
|
1787
|
|
|
// Update default attributes options setting |
1788
|
|
|
if ( isset( $data['default_attribute'] ) ) { |
1789
|
|
|
$data['default_attributes'] = $data['default_attribute']; |
1790
|
|
|
} |
1791
|
|
|
|
1792
|
|
|
if ( isset( $data['default_attributes'] ) && is_array( $data['default_attributes'] ) ) { |
1793
|
|
|
$default_attributes = array(); |
1794
|
|
|
|
1795
|
|
|
foreach ( $data['default_attributes'] as $default_attr_key => $default_attr ) { |
1796
|
|
|
if ( ! isset( $default_attr['name'] ) ) { |
1797
|
|
|
continue; |
1798
|
|
|
} |
1799
|
|
|
|
1800
|
|
|
$taxonomy = sanitize_title( $default_attr['name'] ); |
1801
|
|
|
|
1802
|
|
|
if ( isset( $default_attr['slug'] ) ) { |
1803
|
|
|
$taxonomy = $this->get_attribute_taxonomy_by_slug( $default_attr['slug'] ); |
1804
|
|
|
} |
1805
|
|
|
|
1806
|
|
|
if ( isset( $attributes[ $taxonomy ] ) ) { |
1807
|
|
|
$_attribute = $attributes[ $taxonomy ]; |
1808
|
|
|
|
1809
|
|
|
if ( $_attribute['is_variation'] ) { |
1810
|
|
|
// Don't use wc_clean as it destroys sanitized characters |
1811
|
|
|
if ( isset( $default_attr['option'] ) ) { |
1812
|
|
|
$value = sanitize_title( trim( stripslashes( $default_attr['option'] ) ) ); |
1813
|
|
|
} else { |
1814
|
|
|
$value = ''; |
1815
|
|
|
} |
1816
|
|
|
|
1817
|
|
|
if ( $value ) { |
1818
|
|
|
$default_attributes[ $taxonomy ] = $value; |
1819
|
|
|
} |
1820
|
|
|
} |
1821
|
|
|
} |
1822
|
|
|
} |
1823
|
|
|
|
1824
|
|
|
update_post_meta( $id, '_default_attributes', $default_attributes ); |
1825
|
|
|
} |
1826
|
|
|
|
1827
|
|
|
return true; |
1828
|
|
|
} |
1829
|
|
|
|
1830
|
|
|
/** |
1831
|
|
|
* Save product images. |
1832
|
|
|
* |
1833
|
|
|
* @since 2.5.0 |
1834
|
|
|
* @param array $images |
1835
|
|
|
* @param int $id |
1836
|
|
|
* @throws WC_CLI_Exception |
1837
|
|
|
*/ |
1838
|
|
|
private function save_product_images( $id, $images ) { |
1839
|
|
|
if ( is_array( $images ) ) { |
1840
|
|
|
$gallery = array(); |
1841
|
|
|
|
1842
|
|
|
foreach ( $images as $image ) { |
1843
|
|
|
if ( isset( $image['position'] ) && $image['position'] == 0 ) { |
1844
|
|
|
$attachment_id = isset( $image['id'] ) ? absint( $image['id'] ) : 0; |
1845
|
|
|
|
1846
|
|
|
if ( 0 === $attachment_id && isset( $image['src'] ) ) { |
1847
|
|
|
$upload = $this->upload_product_image( esc_url_raw( $image['src'] ) ); |
1848
|
|
|
|
1849
|
|
|
if ( is_wp_error( $upload ) ) { |
1850
|
|
|
throw new WC_CLI_Exception( 'woocommerce_cli_cannot_upload_product_image', $upload->get_error_message() ); |
1851
|
|
|
} |
1852
|
|
|
|
1853
|
|
|
$attachment_id = $this->set_product_image_as_attachment( $upload, $id ); |
1854
|
|
|
} |
1855
|
|
|
|
1856
|
|
|
set_post_thumbnail( $id, $attachment_id ); |
1857
|
|
|
} else { |
1858
|
|
|
$attachment_id = isset( $image['id'] ) ? absint( $image['id'] ) : 0; |
1859
|
|
|
|
1860
|
|
|
if ( 0 === $attachment_id && isset( $image['src'] ) ) { |
1861
|
|
|
$upload = $this->upload_product_image( esc_url_raw( $image['src'] ) ); |
1862
|
|
|
|
1863
|
|
|
if ( is_wp_error( $upload ) ) { |
1864
|
|
|
throw new WC_CLI_Exception( 'woocommerce_cli_cannot_upload_product_image', $upload->get_error_message() ); |
1865
|
|
|
} |
1866
|
|
|
|
1867
|
|
|
$gallery[] = $this->set_product_image_as_attachment( $upload, $id ); |
1868
|
|
|
} else { |
1869
|
|
|
$gallery[] = $attachment_id; |
1870
|
|
|
} |
1871
|
|
|
} |
1872
|
|
|
} |
1873
|
|
|
|
1874
|
|
|
if ( ! empty( $gallery ) ) { |
1875
|
|
|
update_post_meta( $id, '_product_image_gallery', implode( ',', $gallery ) ); |
1876
|
|
|
} |
1877
|
|
|
} else { |
1878
|
|
|
delete_post_thumbnail( $id ); |
1879
|
|
|
update_post_meta( $id, '_product_image_gallery', '' ); |
1880
|
|
|
} |
1881
|
|
|
} |
1882
|
|
|
|
1883
|
|
|
/** |
1884
|
|
|
* Save product shipping data. |
1885
|
|
|
* |
1886
|
|
|
* @since 2.5.0 |
1887
|
|
|
* @param int $id |
1888
|
|
|
* @param array $data |
1889
|
|
|
*/ |
1890
|
|
|
private function save_product_shipping_data( $id, $data ) { |
1891
|
|
|
if ( isset( $data['weight'] ) ) { |
1892
|
|
|
update_post_meta( $id, '_weight', ( '' === $data['weight'] ) ? '' : wc_format_decimal( $data['weight'] ) ); |
1893
|
|
|
} |
1894
|
|
|
|
1895
|
|
|
// Product dimensions |
1896
|
|
|
if ( isset( $data['dimensions'] ) ) { |
1897
|
|
|
// Height |
1898
|
|
View Code Duplication |
if ( isset( $data['dimensions']['height'] ) ) { |
|
|
|
|
1899
|
|
|
update_post_meta( $id, '_height', ( '' === $data['dimensions']['height'] ) ? '' : wc_format_decimal( $data['dimensions']['height'] ) ); |
1900
|
|
|
} |
1901
|
|
|
|
1902
|
|
|
// Width |
1903
|
|
View Code Duplication |
if ( isset( $data['dimensions']['width'] ) ) { |
|
|
|
|
1904
|
|
|
update_post_meta( $id, '_width', ( '' === $data['dimensions']['width'] ) ? '' : wc_format_decimal($data['dimensions']['width'] ) ); |
1905
|
|
|
} |
1906
|
|
|
|
1907
|
|
|
// Length |
1908
|
|
View Code Duplication |
if ( isset( $data['dimensions']['length'] ) ) { |
|
|
|
|
1909
|
|
|
update_post_meta( $id, '_length', ( '' === $data['dimensions']['length'] ) ? '' : wc_format_decimal( $data['dimensions']['length'] ) ); |
1910
|
|
|
} |
1911
|
|
|
} |
1912
|
|
|
|
1913
|
|
|
// Virtual |
1914
|
|
|
if ( isset( $data['virtual'] ) ) { |
1915
|
|
|
$virtual = ( $this->is_true( $data['virtual'] ) ) ? 'yes' : 'no'; |
1916
|
|
|
|
1917
|
|
|
if ( 'yes' == $virtual ) { |
1918
|
|
|
update_post_meta( $id, '_weight', '' ); |
1919
|
|
|
update_post_meta( $id, '_length', '' ); |
1920
|
|
|
update_post_meta( $id, '_width', '' ); |
1921
|
|
|
update_post_meta( $id, '_height', '' ); |
1922
|
|
|
} |
1923
|
|
|
} |
1924
|
|
|
|
1925
|
|
|
// Shipping class |
1926
|
|
|
if ( isset( $data['shipping_class'] ) ) { |
1927
|
|
|
wp_set_object_terms( $id, wc_clean( $data['shipping_class'] ), 'product_shipping_class' ); |
1928
|
|
|
} |
1929
|
|
|
} |
1930
|
|
|
|
1931
|
|
|
/** |
1932
|
|
|
* Save downloadable files. |
1933
|
|
|
* |
1934
|
|
|
* @since 2.5.0 |
1935
|
|
|
* @param int $product_id |
1936
|
|
|
* @param array $downloads |
1937
|
|
|
* @param int $variation_id |
1938
|
|
|
*/ |
1939
|
|
View Code Duplication |
private function save_downloadable_files( $product_id, $downloads, $variation_id = 0 ) { |
|
|
|
|
1940
|
|
|
$files = array(); |
1941
|
|
|
|
1942
|
|
|
// File paths will be stored in an array keyed off md5(file path) |
1943
|
|
|
foreach ( $downloads as $key => $file ) { |
1944
|
|
|
if ( isset( $file['url'] ) ) { |
1945
|
|
|
$file['file'] = $file['url']; |
1946
|
|
|
} |
1947
|
|
|
|
1948
|
|
|
if ( ! isset( $file['file'] ) ) { |
1949
|
|
|
continue; |
1950
|
|
|
} |
1951
|
|
|
|
1952
|
|
|
$file_name = isset( $file['name'] ) ? wc_clean( $file['name'] ) : ''; |
1953
|
|
|
|
1954
|
|
|
if ( 0 === strpos( $file['file'], 'http' ) ) { |
1955
|
|
|
$file_url = esc_url_raw( $file['file'] ); |
1956
|
|
|
} else { |
1957
|
|
|
$file_url = wc_clean( $file['file'] ); |
1958
|
|
|
} |
1959
|
|
|
|
1960
|
|
|
$files[ md5( $file_url ) ] = array( |
1961
|
|
|
'name' => $file_name, |
1962
|
|
|
'file' => $file_url |
1963
|
|
|
); |
1964
|
|
|
} |
1965
|
|
|
|
1966
|
|
|
// Grant permission to any newly added files on any existing orders for this product prior to saving |
1967
|
|
|
do_action( 'woocommerce_process_product_file_download_paths', $product_id, $variation_id, $files ); |
1968
|
|
|
|
1969
|
|
|
$id = ( 0 === $variation_id ) ? $product_id : $variation_id; |
1970
|
|
|
update_post_meta( $id, '_downloadable_files', $files ); |
1971
|
|
|
} |
1972
|
|
|
|
1973
|
|
|
/** |
1974
|
|
|
* Get attribute taxonomy by slug. |
1975
|
|
|
* |
1976
|
|
|
* @since 2.5.0 |
1977
|
|
|
* @param string $slug |
1978
|
|
|
* @return string|null |
1979
|
|
|
*/ |
1980
|
|
|
private function get_attribute_taxonomy_by_slug( $slug ) { |
1981
|
|
|
$taxonomy = null; |
1982
|
|
|
$attribute_taxonomies = wc_get_attribute_taxonomies(); |
1983
|
|
|
|
1984
|
|
|
foreach ( $attribute_taxonomies as $key => $tax ) { |
1985
|
|
|
if ( $slug == $tax->attribute_name ) { |
1986
|
|
|
$taxonomy = 'pa_' . $tax->attribute_name; |
1987
|
|
|
|
1988
|
|
|
break; |
1989
|
|
|
} |
1990
|
|
|
} |
1991
|
|
|
|
1992
|
|
|
return $taxonomy; |
1993
|
|
|
} |
1994
|
|
|
|
1995
|
|
|
/** |
1996
|
|
|
* Upload image from URL |
1997
|
|
|
* |
1998
|
|
|
* @since 2.5.0 |
1999
|
|
|
* @param string $image_url |
2000
|
|
|
* @return int|WP_Error attachment id |
2001
|
|
|
* @throws WC_CLI_Exception |
2002
|
|
|
*/ |
2003
|
|
|
private function upload_product_image( $image_url ) { |
2004
|
|
|
$file_name = basename( current( explode( '?', $image_url ) ) ); |
2005
|
|
|
$wp_filetype = wp_check_filetype( $file_name, null ); |
2006
|
|
|
$parsed_url = @parse_url( $image_url ); |
2007
|
|
|
|
2008
|
|
|
// Check parsed URL |
2009
|
|
|
if ( ! $parsed_url || ! is_array( $parsed_url ) ) { |
2010
|
|
|
throw new WC_CLI_Exception( 'woocommerce_cli_invalid_product_image', sprintf( __( 'Invalid URL %s', 'woocommerce' ), $image_url ) ); |
2011
|
|
|
} |
2012
|
|
|
|
2013
|
|
|
// Ensure url is valid |
2014
|
|
|
$image_url = str_replace( ' ', '%20', $image_url ); |
2015
|
|
|
|
2016
|
|
|
// Get the file |
2017
|
|
|
$response = wp_safe_remote_get( $image_url, array( |
2018
|
|
|
'timeout' => 10 |
2019
|
|
|
) ); |
2020
|
|
|
|
2021
|
|
View Code Duplication |
if ( is_wp_error( $response ) || 200 !== wp_remote_retrieve_response_code( $response ) ) { |
|
|
|
|
2022
|
|
|
throw new WC_CLI_Exception( 'woocommerce_cli_invalid_remote_product_image', sprintf( __( 'Error getting remote image %s', 'woocommerce' ), $image_url ) ); |
2023
|
|
|
} |
2024
|
|
|
|
2025
|
|
|
// Ensure we have a file name and type |
2026
|
|
View Code Duplication |
if ( ! $wp_filetype['type'] ) { |
|
|
|
|
2027
|
|
|
$headers = wp_remote_retrieve_headers( $response ); |
2028
|
|
|
if ( isset( $headers['content-disposition'] ) && strstr( $headers['content-disposition'], 'filename=' ) ) { |
2029
|
|
|
$disposition = end( explode( 'filename=', $headers['content-disposition'] ) ); |
2030
|
|
|
$disposition = sanitize_file_name( $disposition ); |
2031
|
|
|
$file_name = $disposition; |
2032
|
|
|
} elseif ( isset( $headers['content-type'] ) && strstr( $headers['content-type'], 'image/' ) ) { |
2033
|
|
|
$file_name = 'image.' . str_replace( 'image/', '', $headers['content-type'] ); |
2034
|
|
|
} |
2035
|
|
|
unset( $headers ); |
2036
|
|
|
} |
2037
|
|
|
|
2038
|
|
|
// Upload the file. |
2039
|
|
|
$upload = wp_upload_bits( $file_name, '', wp_remote_retrieve_body( $response ) ); |
2040
|
|
|
|
2041
|
|
|
if ( $upload['error'] ) { |
2042
|
|
|
throw new WC_CLI_Exception( 'woocommerce_cli_product_image_upload_error', $upload['error'] ); |
2043
|
|
|
} |
2044
|
|
|
|
2045
|
|
|
// Get filesize |
2046
|
|
|
$filesize = filesize( $upload['file'] ); |
2047
|
|
|
|
2048
|
|
|
if ( 0 == $filesize ) { |
2049
|
|
|
@unlink( $upload['file'] ); |
2050
|
|
|
unset( $upload ); |
2051
|
|
|
throw new WC_CLI_Exception( 'woocommerce_cli_product_image_upload_file_error', __( 'Zero size file downloaded', 'woocommerce' ) ); |
2052
|
|
|
} |
2053
|
|
|
|
2054
|
|
|
unset( $response ); |
2055
|
|
|
|
2056
|
|
|
return $upload; |
2057
|
|
|
} |
2058
|
|
|
|
2059
|
|
|
/** |
2060
|
|
|
* Get product image as attachment |
2061
|
|
|
* |
2062
|
|
|
* @since 2.5.0 |
2063
|
|
|
* @param int $upload |
2064
|
|
|
* @param int $id |
2065
|
|
|
* @return int |
2066
|
|
|
*/ |
2067
|
|
|
private function set_product_image_as_attachment( $upload, $id ) { |
2068
|
|
|
$info = wp_check_filetype( $upload['file'] ); |
2069
|
|
|
$title = ''; |
2070
|
|
|
$content = ''; |
2071
|
|
|
|
2072
|
|
View Code Duplication |
if ( $image_meta = @wp_read_image_metadata( $upload['file'] ) ) { |
|
|
|
|
2073
|
|
|
if ( trim( $image_meta['title'] ) && ! is_numeric( sanitize_title( $image_meta['title'] ) ) ) { |
2074
|
|
|
$title = $image_meta['title']; |
2075
|
|
|
} |
2076
|
|
|
if ( trim( $image_meta['caption'] ) ) { |
2077
|
|
|
$content = $image_meta['caption']; |
2078
|
|
|
} |
2079
|
|
|
} |
2080
|
|
|
|
2081
|
|
|
$attachment = array( |
2082
|
|
|
'post_mime_type' => $info['type'], |
2083
|
|
|
'guid' => $upload['url'], |
2084
|
|
|
'post_parent' => $id, |
2085
|
|
|
'post_title' => $title, |
2086
|
|
|
'post_content' => $content |
2087
|
|
|
); |
2088
|
|
|
|
2089
|
|
|
$attachment_id = wp_insert_attachment( $attachment, $upload['file'], $id ); |
2090
|
|
|
if ( ! is_wp_error( $attachment_id ) ) { |
2091
|
|
|
wp_update_attachment_metadata( $attachment_id, wp_generate_attachment_metadata( $attachment_id, $upload['file'] ) ); |
2092
|
|
|
} |
2093
|
|
|
|
2094
|
|
|
return $attachment_id; |
2095
|
|
|
} |
2096
|
|
|
|
2097
|
|
|
/** |
2098
|
|
|
* Clear product |
2099
|
|
|
* |
2100
|
|
|
* @since 2.5.0 |
2101
|
|
|
* @param int $product_id Product ID |
2102
|
|
|
*/ |
2103
|
|
|
private function clear_product( $product_id ) { |
2104
|
|
|
if ( ! is_numeric( $product_id ) || 0 >= $product_id ) { |
2105
|
|
|
return; |
2106
|
|
|
} |
2107
|
|
|
|
2108
|
|
|
// Delete product attachments |
2109
|
|
|
$attachments = get_children( array( |
2110
|
|
|
'post_parent' => $product_id, |
2111
|
|
|
'post_status' => 'any', |
2112
|
|
|
'post_type' => 'attachment', |
2113
|
|
|
) ); |
2114
|
|
|
|
2115
|
|
|
foreach ( (array) $attachments as $attachment ) { |
2116
|
|
|
wp_delete_attachment( $attachment->ID, true ); |
2117
|
|
|
} |
2118
|
|
|
|
2119
|
|
|
// Delete product |
2120
|
|
|
wp_delete_post( $product_id, true ); |
2121
|
|
|
} |
2122
|
|
|
} |
2123
|
|
|
|
Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.
You can also find more detailed suggestions in the “Code” section of your repository.