Completed
Push — master ( 5630d2...f1b8f5 )
by Mike
14:37
created

ProductSchema::object_to_schema()   B

Complexity

Conditions 6
Paths 32

Size

Total Lines 97
Code Lines 82

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 6
eloc 82
nc 32
nop 2
dl 0
loc 97
rs 7.7705
c 0
b 0
f 0

How to fix   Long Method   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

1
<?php
2
/**
3
 * Product schema.
4
 *
5
 * @package WooCommerce/RestApi
6
 */
7
8
namespace WooCommerce\RestApi\Controllers\Version4\Schema;
9
10
defined( 'ABSPATH' ) || exit;
11
12
/**
13
 * ProductSchema class.
14
 */
15
class ProductSchema {
16
17
	/**
18
	 * Return schema for products.
19
	 *
20
	 * @return array
21
	 */
22
	public static function get_schema() {
23
		$weight_unit    = get_option( 'woocommerce_weight_unit' );
24
		$dimension_unit = get_option( 'woocommerce_dimension_unit' );
25
		$schema         = array(
26
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
27
			'title'      => 'product',
28
			'type'       => 'object',
29
			'properties' => array(
30
				'id'                    => array(
31
					'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
32
					'type'        => 'integer',
33
					'context'     => array( 'view', 'edit', 'embed' ),
34
					'readonly'    => true,
35
				),
36
				'name'                  => array(
37
					'description' => __( 'Product name.', 'woocommerce' ),
38
					'type'        => 'string',
39
					'context'     => array( 'view', 'edit', 'embed' ),
40
					'arg_options' => array(
41
						'sanitize_callback' => 'wp_filter_post_kses',
42
					),
43
				),
44
				'slug'                  => array(
45
					'description' => __( 'Product slug.', 'woocommerce' ),
46
					'type'        => 'string',
47
					'context'     => array( 'view', 'edit', 'embed' ),
48
				),
49
				'permalink'             => array(
50
					'description' => __( 'Product URL.', 'woocommerce' ),
51
					'type'        => 'string',
52
					'format'      => 'uri',
53
					'context'     => array( 'view', 'edit', 'embed' ),
54
					'readonly'    => true,
55
				),
56
				'date_created'          => array(
57
					'description' => __( "The date the product was created, in the site's timezone.", 'woocommerce' ),
58
					'type'        => 'date-time',
59
					'context'     => array( 'view', 'edit' ),
60
				),
61
				'date_created_gmt'      => array(
62
					'description' => __( 'The date the product was created, as GMT.', 'woocommerce' ),
63
					'type'        => 'date-time',
64
					'context'     => array( 'view', 'edit' ),
65
				),
66
				'date_modified'         => array(
67
					'description' => __( "The date the product was last modified, in the site's timezone.", 'woocommerce' ),
68
					'type'        => 'date-time',
69
					'context'     => array( 'view', 'edit' ),
70
					'readonly'    => true,
71
				),
72
				'date_modified_gmt'     => array(
73
					'description' => __( 'The date the product was last modified, as GMT.', 'woocommerce' ),
74
					'type'        => 'date-time',
75
					'context'     => array( 'view', 'edit' ),
76
					'readonly'    => true,
77
				),
78
				'type'                  => array(
79
					'description' => __( 'Product type.', 'woocommerce' ),
80
					'type'        => 'string',
81
					'default'     => 'simple',
82
					'enum'        => array_keys( wc_get_product_types() ),
83
					'context'     => array( 'view', 'edit' ),
84
				),
85
				'status'                => array(
86
					'description' => __( 'Product status (post status).', 'woocommerce' ),
87
					'type'        => 'string',
88
					'default'     => 'publish',
89
					'enum'        => array_merge( array_keys( get_post_statuses() ), array( 'future' ) ),
90
					'context'     => array( 'view', 'edit' ),
91
				),
92
				'featured'              => array(
93
					'description' => __( 'Featured product.', 'woocommerce' ),
94
					'type'        => 'boolean',
95
					'default'     => false,
96
					'context'     => array( 'view', 'edit' ),
97
				),
98
				'catalog_visibility'    => array(
99
					'description' => __( 'Catalog visibility.', 'woocommerce' ),
100
					'type'        => 'string',
101
					'default'     => 'visible',
102
					'enum'        => array( 'visible', 'catalog', 'search', 'hidden' ),
103
					'context'     => array( 'view', 'edit' ),
104
				),
105
				'description'           => array(
106
					'description' => __( 'Product description.', 'woocommerce' ),
107
					'type'        => 'string',
108
					'context'     => array( 'view', 'edit', 'embed' ),
109
					'arg_options' => array(
110
						'sanitize_callback' => 'wp_filter_post_kses',
111
					),
112
				),
113
				'short_description'     => array(
114
					'description' => __( 'Product short description.', 'woocommerce' ),
115
					'type'        => 'string',
116
					'context'     => array( 'view', 'edit', 'embed' ),
117
					'arg_options' => array(
118
						'sanitize_callback' => 'wp_filter_post_kses',
119
					),
120
				),
121
				'sku'                   => array(
122
					'description' => __( 'Unique identifier.', 'woocommerce' ),
123
					'type'        => 'string',
124
					'context'     => array( 'view', 'edit' ),
125
					'arg_options' => array(
126
						'sanitize_callback' => 'wc_clean',
127
					),
128
				),
129
				'price'                 => array(
130
					'description' => __( 'Current product price.', 'woocommerce' ),
131
					'type'        => 'string',
132
					'context'     => array( 'view', 'edit' ),
133
					'readonly'    => true,
134
				),
135
				'regular_price'         => array(
136
					'description' => __( 'Product regular price.', 'woocommerce' ),
137
					'type'        => 'string',
138
					'context'     => array( 'view', 'edit' ),
139
				),
140
				'sale_price'            => array(
141
					'description' => __( 'Product sale price.', 'woocommerce' ),
142
					'type'        => 'string',
143
					'context'     => array( 'view', 'edit' ),
144
				),
145
				'date_on_sale_from'     => array(
146
					'description' => __( "Start date of sale price, in the site's timezone.", 'woocommerce' ),
147
					'type'        => 'date-time',
148
					'context'     => array( 'view', 'edit' ),
149
				),
150
				'date_on_sale_from_gmt' => array(
151
					'description' => __( 'Start date of sale price, as GMT.', 'woocommerce' ),
152
					'type'        => 'date-time',
153
					'context'     => array( 'view', 'edit' ),
154
				),
155
				'date_on_sale_to'       => array(
156
					'description' => __( "End date of sale price, in the site's timezone.", 'woocommerce' ),
157
					'type'        => 'date-time',
158
					'context'     => array( 'view', 'edit' ),
159
				),
160
				'date_on_sale_to_gmt'   => array(
161
					'description' => __( "End date of sale price, in the site's timezone.", 'woocommerce' ),
162
					'type'        => 'date-time',
163
					'context'     => array( 'view', 'edit' ),
164
				),
165
				'price_html'            => array(
166
					'description' => __( 'Price formatted in HTML.', 'woocommerce' ),
167
					'type'        => 'string',
168
					'context'     => array( 'view', 'edit' ),
169
					'readonly'    => true,
170
				),
171
				'on_sale'               => array(
172
					'description' => __( 'Shows if the product is on sale.', 'woocommerce' ),
173
					'type'        => 'boolean',
174
					'context'     => array( 'view', 'edit' ),
175
					'readonly'    => true,
176
				),
177
				'purchasable'           => array(
178
					'description' => __( 'Shows if the product can be bought.', 'woocommerce' ),
179
					'type'        => 'boolean',
180
					'context'     => array( 'view', 'edit' ),
181
					'readonly'    => true,
182
				),
183
				'total_sales'           => array(
184
					'description' => __( 'Amount of sales.', 'woocommerce' ),
185
					'type'        => 'integer',
186
					'context'     => array( 'view', 'edit' ),
187
					'readonly'    => true,
188
				),
189
				'virtual'               => array(
190
					'description' => __( 'If the product is virtual.', 'woocommerce' ),
191
					'type'        => 'boolean',
192
					'default'     => false,
193
					'context'     => array( 'view', 'edit' ),
194
				),
195
				'downloadable'          => array(
196
					'description' => __( 'If the product is downloadable.', 'woocommerce' ),
197
					'type'        => 'boolean',
198
					'default'     => false,
199
					'context'     => array( 'view', 'edit' ),
200
				),
201
				'downloads'             => array(
202
					'description' => __( 'List of downloadable files.', 'woocommerce' ),
203
					'type'        => 'array',
204
					'context'     => array( 'view', 'edit' ),
205
					'items'       => array(
206
						'type'       => 'object',
207
						'properties' => array(
208
							'id'   => array(
209
								'description' => __( 'File ID.', 'woocommerce' ),
210
								'type'        => 'string',
211
								'context'     => array( 'view', 'edit' ),
212
							),
213
							'name' => array(
214
								'description' => __( 'File name.', 'woocommerce' ),
215
								'type'        => 'string',
216
								'context'     => array( 'view', 'edit' ),
217
							),
218
							'file' => array(
219
								'description' => __( 'File URL.', 'woocommerce' ),
220
								'type'        => 'string',
221
								'context'     => array( 'view', 'edit' ),
222
							),
223
						),
224
					),
225
				),
226
				'download_limit'        => array(
227
					'description' => __( 'Number of times downloadable files can be downloaded after purchase.', 'woocommerce' ),
228
					'type'        => 'integer',
229
					'default'     => -1,
230
					'context'     => array( 'view', 'edit' ),
231
				),
232
				'download_expiry'       => array(
233
					'description' => __( 'Number of days until access to downloadable files expires.', 'woocommerce' ),
234
					'type'        => 'integer',
235
					'default'     => -1,
236
					'context'     => array( 'view', 'edit' ),
237
				),
238
				'external_url'          => array(
239
					'description' => __( 'Product external URL. Only for external products.', 'woocommerce' ),
240
					'type'        => 'string',
241
					'format'      => 'uri',
242
					'context'     => array( 'view', 'edit' ),
243
				),
244
				'button_text'           => array(
245
					'description' => __( 'Product external button text. Only for external products.', 'woocommerce' ),
246
					'type'        => 'string',
247
					'context'     => array( 'view', 'edit' ),
248
				),
249
				'tax_status'            => array(
250
					'description' => __( 'Tax status.', 'woocommerce' ),
251
					'type'        => 'string',
252
					'default'     => 'taxable',
253
					'enum'        => array( 'taxable', 'shipping', 'none' ),
254
					'context'     => array( 'view', 'edit' ),
255
				),
256
				'tax_class'             => array(
257
					'description' => __( 'Tax class.', 'woocommerce' ),
258
					'type'        => 'string',
259
					'context'     => array( 'view', 'edit' ),
260
				),
261
				'manage_stock'          => array(
262
					'description' => __( 'Stock management at product level.', 'woocommerce' ),
263
					'type'        => 'boolean',
264
					'default'     => false,
265
					'context'     => array( 'view', 'edit' ),
266
				),
267
				'stock_quantity'        => array(
268
					'description' => __( 'Stock quantity.', 'woocommerce' ),
269
					'type'        => 'integer',
270
					'context'     => array( 'view', 'edit' ),
271
				),
272
				'stock_status'          => array(
273
					'description' => __( 'Controls the stock status of the product.', 'woocommerce' ),
274
					'type'        => 'string',
275
					'default'     => 'instock',
276
					'enum'        => array_keys( wc_get_product_stock_status_options() ),
277
					'context'     => array( 'view', 'edit' ),
278
				),
279
				'backorders'            => array(
280
					'description' => __( 'If managing stock, this controls if backorders are allowed.', 'woocommerce' ),
281
					'type'        => 'string',
282
					'default'     => 'no',
283
					'enum'        => array( 'no', 'notify', 'yes' ),
284
					'context'     => array( 'view', 'edit' ),
285
				),
286
				'backorders_allowed'    => array(
287
					'description' => __( 'Shows if backorders are allowed.', 'woocommerce' ),
288
					'type'        => 'boolean',
289
					'context'     => array( 'view', 'edit' ),
290
					'readonly'    => true,
291
				),
292
				'backordered'           => array(
293
					'description' => __( 'Shows if the product is on backordered.', 'woocommerce' ),
294
					'type'        => 'boolean',
295
					'context'     => array( 'view', 'edit' ),
296
					'readonly'    => true,
297
				),
298
				'sold_individually'     => array(
299
					'description' => __( 'Allow one item to be bought in a single order.', 'woocommerce' ),
300
					'type'        => 'boolean',
301
					'default'     => false,
302
					'context'     => array( 'view', 'edit' ),
303
				),
304
				'weight'                => array(
305
					/* translators: %s: weight unit */
306
					'description' => sprintf( __( 'Product weight (%s).', 'woocommerce' ), $weight_unit ),
0 ignored issues
show
Bug introduced by
It seems like $weight_unit can also be of type false; however, parameter $args of sprintf() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

306
					'description' => sprintf( __( 'Product weight (%s).', 'woocommerce' ), /** @scrutinizer ignore-type */ $weight_unit ),
Loading history...
307
					'type'        => 'string',
308
					'context'     => array( 'view', 'edit' ),
309
				),
310
				'dimensions'            => array(
311
					'description' => __( 'Product dimensions.', 'woocommerce' ),
312
					'type'        => 'object',
313
					'context'     => array( 'view', 'edit' ),
314
					'properties'  => array(
315
						'length' => array(
316
							/* translators: %s: dimension unit */
317
							'description' => sprintf( __( 'Product length (%s).', 'woocommerce' ), $dimension_unit ),
318
							'type'        => 'string',
319
							'context'     => array( 'view', 'edit' ),
320
						),
321
						'width'  => array(
322
							/* translators: %s: dimension unit */
323
							'description' => sprintf( __( 'Product width (%s).', 'woocommerce' ), $dimension_unit ),
324
							'type'        => 'string',
325
							'context'     => array( 'view', 'edit' ),
326
						),
327
						'height' => array(
328
							/* translators: %s: dimension unit */
329
							'description' => sprintf( __( 'Product height (%s).', 'woocommerce' ), $dimension_unit ),
330
							'type'        => 'string',
331
							'context'     => array( 'view', 'edit' ),
332
						),
333
					),
334
				),
335
				'shipping_required'     => array(
336
					'description' => __( 'Shows if the product need to be shipped.', 'woocommerce' ),
337
					'type'        => 'boolean',
338
					'context'     => array( 'view', 'edit' ),
339
					'readonly'    => true,
340
				),
341
				'shipping_taxable'      => array(
342
					'description' => __( 'Shows whether or not the product shipping is taxable.', 'woocommerce' ),
343
					'type'        => 'boolean',
344
					'context'     => array( 'view', 'edit' ),
345
					'readonly'    => true,
346
				),
347
				'shipping_class'        => array(
348
					'description' => __( 'Shipping class slug.', 'woocommerce' ),
349
					'type'        => 'string',
350
					'context'     => array( 'view', 'edit' ),
351
				),
352
				'shipping_class_id'     => array(
353
					'description' => __( 'Shipping class ID.', 'woocommerce' ),
354
					'type'        => 'string',
355
					'context'     => array( 'view', 'edit' ),
356
					'readonly'    => true,
357
				),
358
				'reviews_allowed'       => array(
359
					'description' => __( 'Allow reviews.', 'woocommerce' ),
360
					'type'        => 'boolean',
361
					'default'     => true,
362
					'context'     => array( 'view', 'edit' ),
363
				),
364
				'average_rating'        => array(
365
					'description' => __( 'Reviews average rating.', 'woocommerce' ),
366
					'type'        => 'string',
367
					'context'     => array( 'view', 'edit' ),
368
					'readonly'    => true,
369
				),
370
				'rating_count'          => array(
371
					'description' => __( 'Amount of reviews that the product have.', 'woocommerce' ),
372
					'type'        => 'integer',
373
					'context'     => array( 'view', 'edit' ),
374
					'readonly'    => true,
375
				),
376
				'related_ids'           => array(
377
					'description' => __( 'List of related products IDs.', 'woocommerce' ),
378
					'type'        => 'array',
379
					'items'       => array(
380
						'type' => 'integer',
381
					),
382
					'context'     => array( 'view', 'edit' ),
383
					'readonly'    => true,
384
				),
385
				'upsell_ids'            => array(
386
					'description' => __( 'List of up-sell products IDs.', 'woocommerce' ),
387
					'type'        => 'array',
388
					'items'       => array(
389
						'type' => 'integer',
390
					),
391
					'context'     => array( 'view', 'edit' ),
392
				),
393
				'cross_sell_ids'        => array(
394
					'description' => __( 'List of cross-sell products IDs.', 'woocommerce' ),
395
					'type'        => 'array',
396
					'items'       => array(
397
						'type' => 'integer',
398
					),
399
					'context'     => array( 'view', 'edit' ),
400
				),
401
				'parent_id'             => array(
402
					'description' => __( 'Product parent ID.', 'woocommerce' ),
403
					'type'        => 'integer',
404
					'context'     => array( 'view', 'edit' ),
405
				),
406
				'purchase_note'         => array(
407
					'description' => __( 'Optional note to send the customer after purchase.', 'woocommerce' ),
408
					'type'        => 'string',
409
					'context'     => array( 'view', 'edit' ),
410
					'arg_options' => array(
411
						'sanitize_callback' => 'wp_kses_post',
412
					),
413
				),
414
				'categories'            => array(
415
					'description' => __( 'List of categories.', 'woocommerce' ),
416
					'type'        => 'array',
417
					'context'     => array( 'view', 'edit' ),
418
					'items'       => array(
419
						'type'       => 'object',
420
						'properties' => array(
421
							'id'   => array(
422
								'description' => __( 'Category ID.', 'woocommerce' ),
423
								'type'        => 'integer',
424
								'context'     => array( 'view', 'edit' ),
425
							),
426
							'name' => array(
427
								'description' => __( 'Category name.', 'woocommerce' ),
428
								'type'        => 'string',
429
								'context'     => array( 'view', 'edit' ),
430
								'readonly'    => true,
431
							),
432
							'slug' => array(
433
								'description' => __( 'Category slug.', 'woocommerce' ),
434
								'type'        => 'string',
435
								'context'     => array( 'view', 'edit' ),
436
								'readonly'    => true,
437
							),
438
						),
439
					),
440
				),
441
				'tags'                  => array(
442
					'description' => __( 'List of tags.', 'woocommerce' ),
443
					'type'        => 'array',
444
					'context'     => array( 'view', 'edit' ),
445
					'items'       => array(
446
						'type'       => 'object',
447
						'properties' => array(
448
							'id'   => array(
449
								'description' => __( 'Tag ID.', 'woocommerce' ),
450
								'type'        => 'integer',
451
								'context'     => array( 'view', 'edit' ),
452
							),
453
							'name' => array(
454
								'description' => __( 'Tag name.', 'woocommerce' ),
455
								'type'        => 'string',
456
								'context'     => array( 'view', 'edit' ),
457
								'readonly'    => true,
458
							),
459
							'slug' => array(
460
								'description' => __( 'Tag slug.', 'woocommerce' ),
461
								'type'        => 'string',
462
								'context'     => array( 'view', 'edit' ),
463
								'readonly'    => true,
464
							),
465
						),
466
					),
467
				),
468
				'images'                => array(
469
					'description' => __( 'List of images.', 'woocommerce' ),
470
					'type'        => 'object',
471
					'context'     => array( 'view', 'edit', 'embed' ),
472
					'items'       => array(
473
						'type'       => 'object',
474
						'properties' => array(
475
							'id'                => array(
476
								'description' => __( 'Image ID.', 'woocommerce' ),
477
								'type'        => 'integer',
478
								'context'     => array( 'view', 'edit' ),
479
							),
480
							'date_created'      => array(
481
								'description' => __( "The date the image was created, in the site's timezone.", 'woocommerce' ),
482
								'type'        => 'date-time',
483
								'context'     => array( 'view', 'edit' ),
484
								'readonly'    => true,
485
							),
486
							'date_created_gmt'  => array(
487
								'description' => __( 'The date the image was created, as GMT.', 'woocommerce' ),
488
								'type'        => 'date-time',
489
								'context'     => array( 'view', 'edit' ),
490
								'readonly'    => true,
491
							),
492
							'date_modified'     => array(
493
								'description' => __( "The date the image was last modified, in the site's timezone.", 'woocommerce' ),
494
								'type'        => 'date-time',
495
								'context'     => array( 'view', 'edit' ),
496
								'readonly'    => true,
497
							),
498
							'date_modified_gmt' => array(
499
								'description' => __( 'The date the image was last modified, as GMT.', 'woocommerce' ),
500
								'type'        => 'date-time',
501
								'context'     => array( 'view', 'edit' ),
502
								'readonly'    => true,
503
							),
504
							'src'               => array(
505
								'description' => __( 'Image URL.', 'woocommerce' ),
506
								'type'        => 'string',
507
								'format'      => 'uri',
508
								'context'     => array( 'view', 'edit' ),
509
							),
510
							'name'              => array(
511
								'description' => __( 'Image name.', 'woocommerce' ),
512
								'type'        => 'string',
513
								'context'     => array( 'view', 'edit' ),
514
							),
515
							'alt'               => array(
516
								'description' => __( 'Image alternative text.', 'woocommerce' ),
517
								'type'        => 'string',
518
								'context'     => array( 'view', 'edit' ),
519
							),
520
						),
521
					),
522
				),
523
				'attributes'            => array(
524
					'description' => __( 'List of attributes.', 'woocommerce' ),
525
					'type'        => 'array',
526
					'context'     => array( 'view', 'edit' ),
527
					'items'       => array(
528
						'type'       => 'object',
529
						'properties' => array(
530
							'id'        => array(
531
								'description' => __( 'Attribute ID.', 'woocommerce' ),
532
								'type'        => 'integer',
533
								'context'     => array( 'view', 'edit' ),
534
							),
535
							'name'      => array(
536
								'description' => __( 'Attribute name.', 'woocommerce' ),
537
								'type'        => 'string',
538
								'context'     => array( 'view', 'edit' ),
539
							),
540
							'position'  => array(
541
								'description' => __( 'Attribute position.', 'woocommerce' ),
542
								'type'        => 'integer',
543
								'context'     => array( 'view', 'edit' ),
544
							),
545
							'visible'   => array(
546
								'description' => __( "Define if the attribute is visible on the \"Additional information\" tab in the product's page.", 'woocommerce' ),
547
								'type'        => 'boolean',
548
								'default'     => false,
549
								'context'     => array( 'view', 'edit' ),
550
							),
551
							'variation' => array(
552
								'description' => __( 'Define if the attribute can be used as variation.', 'woocommerce' ),
553
								'type'        => 'boolean',
554
								'default'     => false,
555
								'context'     => array( 'view', 'edit' ),
556
							),
557
							'options'   => array(
558
								'description' => __( 'List of available term names of the attribute.', 'woocommerce' ),
559
								'type'        => 'array',
560
								'items'       => array(
561
									'type' => 'string',
562
								),
563
								'context'     => array( 'view', 'edit' ),
564
							),
565
						),
566
					),
567
				),
568
				'default_attributes'    => array(
569
					'description' => __( 'Defaults variation attributes.', 'woocommerce' ),
570
					'type'        => 'array',
571
					'context'     => array( 'view', 'edit' ),
572
					'items'       => array(
573
						'type'       => 'object',
574
						'properties' => array(
575
							'id'     => array(
576
								'description' => __( 'Attribute ID.', 'woocommerce' ),
577
								'type'        => 'integer',
578
								'context'     => array( 'view', 'edit' ),
579
							),
580
							'name'   => array(
581
								'description' => __( 'Attribute name.', 'woocommerce' ),
582
								'type'        => 'string',
583
								'context'     => array( 'view', 'edit' ),
584
							),
585
							'option' => array(
586
								'description' => __( 'Selected attribute term name.', 'woocommerce' ),
587
								'type'        => 'string',
588
								'context'     => array( 'view', 'edit' ),
589
							),
590
						),
591
					),
592
				),
593
				'variations'            => array(
594
					'description' => __( 'List of variations IDs.', 'woocommerce' ),
595
					'type'        => 'array',
596
					'context'     => array( 'view', 'edit' ),
597
					'items'       => array(
598
						'type' => 'integer',
599
					),
600
					'readonly'    => true,
601
				),
602
				'grouped_products'      => array(
603
					'description' => __( 'List of grouped products ID.', 'woocommerce' ),
604
					'type'        => 'array',
605
					'items'       => array(
606
						'type' => 'integer',
607
					),
608
					'context'     => array( 'view', 'edit' ),
609
					'readonly'    => true,
610
				),
611
				'menu_order'            => array(
612
					'description' => __( 'Menu order, used to custom sort products.', 'woocommerce' ),
613
					'type'        => 'integer',
614
					'context'     => array( 'view', 'edit' ),
615
				),
616
				'meta_data'             => array(
617
					'description' => __( 'Meta data.', 'woocommerce' ),
618
					'type'        => 'array',
619
					'context'     => array( 'view', 'edit' ),
620
					'items'       => array(
621
						'type'       => 'object',
622
						'properties' => array(
623
							'id'    => array(
624
								'description' => __( 'Meta ID.', 'woocommerce' ),
625
								'type'        => 'integer',
626
								'context'     => array( 'view', 'edit' ),
627
								'readonly'    => true,
628
							),
629
							'key'   => array(
630
								'description' => __( 'Meta key.', 'woocommerce' ),
631
								'type'        => 'string',
632
								'context'     => array( 'view', 'edit' ),
633
							),
634
							'value' => array(
635
								'description' => __( 'Meta value.', 'woocommerce' ),
636
								'type'        => 'mixed',
637
								'context'     => array( 'view', 'edit' ),
638
							),
639
						),
640
					),
641
				),
642
			),
643
		);
644
		return $schema;
645
	}
646
647
	/**
648
	 * Convert object to match data in the schema.
649
	 *
650
	 * @param \WC_Product $object Product instance.
651
	 * @param string      $context Request context. Options: 'view' and 'edit'.
652
	 * @return array
653
	 */
654
	public static function object_to_schema( $object, $context ) {
655
		$data = array(
656
			'id'                    => $object->get_id(),
657
			'name'                  => $object->get_name( $context ),
658
			'slug'                  => $object->get_slug( $context ),
659
			'permalink'             => $object->get_permalink(),
660
			'date_created'          => wc_rest_prepare_date_response( $object->get_date_created( $context ), false ),
661
			'date_created_gmt'      => wc_rest_prepare_date_response( $object->get_date_created( $context ) ),
662
			'date_modified'         => wc_rest_prepare_date_response( $object->get_date_modified( $context ), false ),
663
			'date_modified_gmt'     => wc_rest_prepare_date_response( $object->get_date_modified( $context ) ),
664
			'type'                  => $object->get_type(),
665
			'status'                => $object->get_status( $context ),
666
			'featured'              => $object->is_featured(),
667
			'catalog_visibility'    => $object->get_catalog_visibility( $context ),
668
			'description'           => $object->get_description( $context ),
669
			'short_description'     => $object->get_short_description( $context ),
670
			'sku'                   => $object->get_sku( $context ),
671
			'price'                 => $object->get_price( $context ),
672
			'regular_price'         => $object->get_regular_price( $context ),
673
			'sale_price'            => $object->get_sale_price( $context ) ? $object->get_sale_price( $context ) : '',
674
			'date_on_sale_from'     => wc_rest_prepare_date_response( $object->get_date_on_sale_from( $context ), false ),
675
			'date_on_sale_from_gmt' => wc_rest_prepare_date_response( $object->get_date_on_sale_from( $context ) ),
676
			'date_on_sale_to'       => wc_rest_prepare_date_response( $object->get_date_on_sale_to( $context ), false ),
677
			'date_on_sale_to_gmt'   => wc_rest_prepare_date_response( $object->get_date_on_sale_to( $context ) ),
678
			'price_html'            => $object->get_price_html(),
679
			'on_sale'               => $object->is_on_sale( $context ),
680
			'purchasable'           => $object->is_purchasable(),
681
			'total_sales'           => $object->get_total_sales( $context ),
682
			'virtual'               => $object->is_virtual(),
683
			'downloadable'          => $object->is_downloadable(),
684
			'downloads'             => self::get_downloads( $object ),
685
			'download_limit'        => $object->get_download_limit( $context ),
686
			'download_expiry'       => $object->get_download_expiry( $context ),
687
			'external_url'          => '',
688
			'button_text'           => '',
689
			'tax_status'            => $object->get_tax_status( $context ),
690
			'tax_class'             => $object->get_tax_class( $context ),
691
			'manage_stock'          => $object->managing_stock(),
692
			'stock_quantity'        => $object->get_stock_quantity( $context ),
693
			'stock_status'          => $object->get_stock_status( $context ),
694
			'backorders'            => $object->get_backorders( $context ),
695
			'backorders_allowed'    => $object->backorders_allowed(),
696
			'backordered'           => $object->is_on_backorder(),
697
			'sold_individually'     => $object->is_sold_individually(),
698
			'weight'                => $object->get_weight( $context ),
699
			'dimensions'            => array(
700
				'length' => $object->get_length( $context ),
701
				'width'  => $object->get_width( $context ),
702
				'height' => $object->get_height( $context ),
703
			),
704
			'shipping_required'     => $object->needs_shipping(),
705
			'shipping_taxable'      => $object->is_shipping_taxable(),
706
			'shipping_class'        => $object->get_shipping_class(),
707
			'shipping_class_id'     => $object->get_shipping_class_id( $context ),
708
			'reviews_allowed'       => $object->get_reviews_allowed( $context ),
709
			'average_rating'        => $object->get_average_rating( $context ),
710
			'rating_count'          => $object->get_rating_count(),
711
			'related_ids'           => wp_parse_id_list( wc_get_related_products( $object->get_id() ) ),
712
			'upsell_ids'            => wp_parse_id_list( $object->get_upsell_ids( $context ) ),
713
			'cross_sell_ids'        => wp_parse_id_list( $object->get_cross_sell_ids( $context ) ),
714
			'parent_id'             => $object->get_parent_id( $context ),
715
			'purchase_note'         => $object->get_purchase_note( $context ),
716
			'categories'            => self::get_taxonomy_terms( $object ),
717
			'tags'                  => self::get_taxonomy_terms( $object, 'tag' ),
718
			'images'                => self::get_images( $object ),
719
			'attributes'            => self::get_attributes( $object ),
720
			'default_attributes'    => self::get_default_attributes( $object ),
721
			'variations'            => array(),
722
			'grouped_products'      => array(),
723
			'menu_order'            => $object->get_menu_order( $context ),
724
			'meta_data'             => $object->get_meta_data(),
725
		);
726
727
		// Add variations to variable products.
728
		if ( $object->is_type( 'variable' ) ) {
729
			$data['variations'] = $object->get_children();
730
		}
731
732
		// Add grouped products data.
733
		if ( $object->is_type( 'grouped' ) ) {
734
			$data['grouped_products'] = $object->get_children();
735
		}
736
737
		// Add external product data.
738
		if ( $object->is_type( 'external' ) ) {
739
			$data['external_url'] = $object->get_product_url( $context );
0 ignored issues
show
Bug introduced by
The method get_product_url() does not exist on WC_Product. It seems like you code against a sub-type of WC_Product such as WC_Product_External. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

739
			/** @scrutinizer ignore-call */ 
740
   $data['external_url'] = $object->get_product_url( $context );
Loading history...
740
			$data['button_text']  = $object->get_button_text( $context );
0 ignored issues
show
Bug introduced by
The method get_button_text() does not exist on WC_Product. It seems like you code against a sub-type of WC_Product such as WC_Product_External. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

740
			/** @scrutinizer ignore-call */ 
741
   $data['button_text']  = $object->get_button_text( $context );
Loading history...
741
		}
742
743
		if ( 'view' === $context ) {
744
			$data['description']       = wpautop( do_shortcode( $data['description'] ) );
745
			$data['short_description'] = apply_filters( 'woocommerce_short_description', $data['short_description'] );
746
			$data['average_rating']    = wc_format_decimal( $data['average_rating'], 2 );
747
			$data['purchase_note']     = wpautop( do_shortcode( $data['purchase_note'] ) );
748
		}
749
750
		return $data;
751
	}
752
753
	/**
754
	 * Take data in the format of the schema and convert to a product object.
755
	 *
756
	 * @param  \WP_REST_Request $request Request object.
757
	 * @return \WP_Error|\WC_Product
758
	 */
759
	public static function schema_to_object( $request ) {
760
		$id = isset( $request['id'] ) ? (int) $request['id'] : 0;
761
762
		if ( isset( $request['type'] ) ) {
763
			$classname = '\\' . \WC_Product_Factory::get_classname_from_product_type( $request['type'] );
0 ignored issues
show
Bug introduced by
Are you sure WC_Product_Factory::get_..._type($request['type']) of type false|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

763
			$classname = '\\' . /** @scrutinizer ignore-type */ \WC_Product_Factory::get_classname_from_product_type( $request['type'] );
Loading history...
764
765
			if ( ! class_exists( $classname ) ) {
766
				$classname = '\\WC_Product_Simple';
767
			}
768
769
			$object = new $classname( $id );
770
		} elseif ( isset( $request['id'] ) ) {
771
			$object = wc_get_product( $id );
772
		} else {
773
			$object = new \WC_Product_Simple();
774
		}
775
776
		if ( $object->is_type( 'variation' ) ) {
777
			return new \WP_Error(
778
				'woocommerce_rest_invalid_product_id',
779
				__( 'To manipulate product variations you should use the /products/&lt;product_id&gt;/variations/&lt;id&gt; endpoint.', 'woocommerce' ),
780
				array(
781
					'status' => 404,
782
				)
783
			);
784
		}
785
786
		self::set_object_data( $object, $request );
787
788
		return $object;
789
	}
790
791
	/**
792
	 * Set object data from a request.
793
	 *
794
	 * @param \WC_Product      $object Product object.
795
	 * @param \WP_REST_Request $request Request object.
796
	 */
797
	protected static function set_object_data( &$object, $request ) {
798
		$props_to_set = [
799
			'name',
800
			'sku',
801
			'description',
802
			'short_description',
803
			'slug',
804
			'menu_order',
805
			'reviews_allowed',
806
			'virtual',
807
			'tax_status',
808
			'tax_class',
809
			'catalog_visibility',
810
			'purchase_note',
811
			'status',
812
			'featured',
813
			'regular_price',
814
			'sale_price',
815
			'date_on_sale_from',
816
			'date_on_sale_from_gmt',
817
			'date_on_sale_to',
818
			'date_on_sale_to_gmt',
819
			'parent_id',
820
			'sold_individually',
821
			'manage_stock',
822
			'backorders',
823
			'stock_status',
824
			'stock_quantity',
825
			'downloadable',
826
			'button_text',
827
			'download_limit',
828
			'download_expiry',
829
		];
830
831
		foreach ( $props_to_set as $prop ) {
832
			if ( isset( $request[ $prop ] ) && is_callable( array( $object, "set_$prop" ) ) ) {
833
				$object->{"set_$prop"}( $request[ $prop ] );
834
			}
835
		}
836
837
		if ( isset( $request['external_url'] ) && is_callable( array( $object, 'set_product_url' ) ) ) {
838
			$object->set_product_url( $request['external_url'] );
0 ignored issues
show
Bug introduced by
The method set_product_url() does not exist on WC_Product. It seems like you code against a sub-type of WC_Product such as WC_Product_External. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

838
			$object->/** @scrutinizer ignore-call */ 
839
            set_product_url( $request['external_url'] );
Loading history...
839
		}
840
841
		if ( ! empty( $request['date_created'] ) ) {
842
			$date = rest_parse_date( $request['date_created'] );
843
844
			if ( $date ) {
845
				$object->set_date_created( $date );
846
			}
847
		}
848
849
		if ( ! empty( $request['date_created_gmt'] ) ) {
850
			$date = rest_parse_date( $request['date_created_gmt'], true );
851
852
			if ( $date ) {
853
				$object->set_date_created( $date );
854
			}
855
		}
856
857
		if ( isset( $request['upsell_ids'] ) ) {
858
			$object->set_upsell_ids( wp_parse_id_list( $request['upsell_ids'] ) );
859
		}
860
861
		if ( isset( $request['cross_sell_ids'] ) ) {
862
			$object->set_cross_sell_ids( wp_parse_id_list( $request['cross_sell_ids'] ) );
863
		}
864
865
		// Set children for a grouped product.
866
		if ( $object->is_type( 'grouped' ) && isset( $request['grouped_products'] ) ) {
867
			$object->set_children( $request['grouped_products'] );
0 ignored issues
show
Bug introduced by
The method set_children() does not exist on WC_Product. It seems like you code against a sub-type of WC_Product such as WC_Product_Grouped or WC_Product_Variable. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

867
			$object->/** @scrutinizer ignore-call */ 
868
            set_children( $request['grouped_products'] );
Loading history...
868
		}
869
870
		// Allow set meta_data.
871
		if ( isset( $request['meta_data'] ) ) {
872
			foreach ( $request['meta_data'] as $meta ) {
873
				$object->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' );
0 ignored issues
show
Bug introduced by
It seems like IssetNode ? $meta['id'] : '' can also be of type string; however, parameter $meta_id of WC_Data::update_meta_data() does only seem to accept integer, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

873
				$object->update_meta_data( $meta['key'], $meta['value'], /** @scrutinizer ignore-type */ isset( $meta['id'] ) ? $meta['id'] : '' );
Loading history...
874
			}
875
		}
876
877
		// Save default attributes for variable products.
878
		if ( $object->is_type( 'variable' ) && isset( $request['default_attributes'] ) ) {
879
			self::set_default_attributes( $object, $request['default_attributes'] );
880
		}
881
882
		// Check for featured/gallery images, upload it and set it.
883
		if ( isset( $request['images'] ) ) {
884
			self::set_images( $object, $request['images'] );
885
		}
886
887
		// Product categories.
888
		if ( isset( $request['categories'] ) ) {
889
			self::set_taxonomy_terms( $object, $request['categories'] );
890
		}
891
892
		// Product tags.
893
		if ( isset( $request['tags'] ) ) {
894
			self::set_taxonomy_terms( $object, $request['tags'], 'tag' );
895
		}
896
897
		// Downloadable files.
898
		if ( isset( $request['downloads'] ) && is_array( $request['downloads'] ) ) {
899
			self::set_downloadable_files( $object, $request['downloads'] );
900
		}
901
902
		if ( isset( $request['attributes'] ) ) {
903
			self::set_attributes( $object, $request['attributes'] );
904
		}
905
906
		self::set_shipping_data( $object, $request );
907
908
		return $object;
909
	}
910
911
	/**
912
	 * Get the downloads for a product or product variation.
913
	 *
914
	 * @param \WC_Product|\WC_Product_Variation $object Product instance.
915
	 *
916
	 * @return array
917
	 */
918
	protected static function get_downloads( $object ) {
919
		$downloads = array();
920
921
		if ( $object->is_downloadable() ) {
922
			foreach ( $object->get_downloads() as $file_id => $file ) {
923
				$downloads[] = array(
924
					'id'   => $file_id, // MD5 hash.
925
					'name' => $file['name'],
926
					'file' => $file['file'],
927
				);
928
			}
929
		}
930
931
		return $downloads;
932
	}
933
934
	/**
935
	 * Get taxonomy terms.
936
	 *
937
	 * @param \WC_Product $object  Product instance.
938
	 * @param string      $taxonomy Taxonomy slug.
939
	 *
940
	 * @return array
941
	 */
942
	protected static function get_taxonomy_terms( $object, $taxonomy = 'cat' ) {
943
		$terms = array();
944
945
		foreach ( wc_get_object_terms( $object->get_id(), 'product_' . $taxonomy ) as $term ) {
946
			$terms[] = array(
947
				'id'   => $term->term_id,
948
				'name' => $term->name,
949
				'slug' => $term->slug,
950
			);
951
		}
952
953
		return $terms;
954
	}
955
956
	/**
957
	 * Get the images for a product or product variation.
958
	 *
959
	 * @param \WC_Product|\WC_Product_Variation $object Product instance.
960
	 * @return array
961
	 */
962
	protected static function get_images( $object ) {
963
		$images         = array();
964
		$attachment_ids = array();
965
966
		// Add featured image.
967
		if ( $object->get_image_id() ) {
968
			$attachment_ids[] = $object->get_image_id();
969
		}
970
971
		// Add gallery images.
972
		$attachment_ids = array_merge( $attachment_ids, $object->get_gallery_image_ids() );
973
974
		// Build image data.
975
		foreach ( $attachment_ids as $attachment_id ) {
976
			$attachment_post = get_post( $attachment_id );
977
			if ( is_null( $attachment_post ) ) {
978
				continue;
979
			}
980
981
			$attachment = wp_get_attachment_image_src( $attachment_id, 'full' );
982
			if ( ! is_array( $attachment ) ) {
983
				continue;
984
			}
985
986
			$images[] = array(
987
				'id'                => (int) $attachment_id,
988
				'date_created'      => wc_rest_prepare_date_response( $attachment_post->post_date, false ),
989
				'date_created_gmt'  => wc_rest_prepare_date_response( strtotime( $attachment_post->post_date_gmt ) ),
990
				'date_modified'     => wc_rest_prepare_date_response( $attachment_post->post_modified, false ),
991
				'date_modified_gmt' => wc_rest_prepare_date_response( strtotime( $attachment_post->post_modified_gmt ) ),
992
				'src'               => current( $attachment ),
993
				'name'              => get_the_title( $attachment_id ),
994
				'alt'               => get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ),
995
			);
996
		}
997
998
		return $images;
999
	}
1000
1001
	/**
1002
	 * Get product attribute taxonomy name.
1003
	 *
1004
	 * @param string      $slug   Taxonomy name.
1005
	 * @param \WC_Product $object Product data.
1006
	 *
1007
	 * @since  3.0.0
1008
	 * @return string
1009
	 */
1010
	protected static function get_attribute_taxonomy_name( $slug, $object ) {
1011
		// Format slug so it matches attributes of the product.
1012
		$slug       = wc_attribute_taxonomy_slug( $slug );
1013
		$attributes = $object->get_attributes();
1014
		$attribute  = false;
1015
1016
		// pa_ attributes.
1017
		if ( isset( $attributes[ wc_attribute_taxonomy_name( $slug ) ] ) ) {
1018
			$attribute = $attributes[ wc_attribute_taxonomy_name( $slug ) ];
1019
		} elseif ( isset( $attributes[ $slug ] ) ) {
1020
			$attribute = $attributes[ $slug ];
1021
		}
1022
1023
		if ( ! $attribute ) {
1024
			return $slug;
1025
		}
1026
1027
		// Taxonomy attribute name.
1028
		if ( $attribute->is_taxonomy() ) {
1029
			$taxonomy = $attribute->get_taxonomy_object();
1030
			return $taxonomy->attribute_label;
1031
		}
1032
1033
		// Custom product attribute name.
1034
		return $attribute->get_name();
1035
	}
1036
1037
	/**
1038
	 * Get default attributes.
1039
	 *
1040
	 * @param \WC_Product $object Product instance.
1041
	 *
1042
	 * @return array
1043
	 */
1044
	protected static function get_default_attributes( $object ) {
1045
		$default = array();
1046
1047
		if ( $object->is_type( 'variable' ) ) {
1048
			foreach ( array_filter( (array) $object->get_default_attributes(), 'strlen' ) as $key => $value ) {
1049
				if ( 0 === strpos( $key, 'pa_' ) ) {
1050
					$default[] = array(
1051
						'id'     => wc_attribute_taxonomy_id_by_name( $key ),
1052
						'name'   => self::get_attribute_taxonomy_name( $key, $object ),
1053
						'option' => $value,
1054
					);
1055
				} else {
1056
					$default[] = array(
1057
						'id'     => 0,
1058
						'name'   => self::get_attribute_taxonomy_name( $key, $object ),
1059
						'option' => $value,
1060
					);
1061
				}
1062
			}
1063
		}
1064
1065
		return $default;
1066
	}
1067
1068
	/**
1069
	 * Get attribute options.
1070
	 *
1071
	 * @param int   $object_id Product ID.
1072
	 * @param array $attribute  Attribute data.
1073
	 *
1074
	 * @return array
1075
	 */
1076
	protected static function get_attribute_options( $object_id, $attribute ) {
1077
		if ( isset( $attribute['is_taxonomy'] ) && $attribute['is_taxonomy'] ) {
1078
			return wc_get_product_terms(
1079
				$object_id,
1080
				$attribute['name'],
1081
				array(
1082
					'fields' => 'names',
1083
				)
1084
			);
1085
		} elseif ( isset( $attribute['value'] ) ) {
1086
			return array_map( 'trim', explode( '|', $attribute['value'] ) );
1087
		}
1088
1089
		return array();
1090
	}
1091
1092
	/**
1093
	 * Get the attributes for a product or product variation.
1094
	 *
1095
	 * @param \WC_Product|\WC_Product_Variation $object Product instance.
1096
	 *
1097
	 * @return array
1098
	 */
1099
	protected static function get_attributes( $object ) {
1100
		$attributes = array();
1101
1102
		if ( $object->is_type( 'variation' ) ) {
1103
			$_product = wc_get_product( $object->get_parent_id() );
1104
			foreach ( $object->get_variation_attributes() as $attribute_name => $attribute ) {
0 ignored issues
show
Bug introduced by
The method get_variation_attributes() does not exist on WC_Product. Did you maybe mean get_variation_id()? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1104
			foreach ( $object->/** @scrutinizer ignore-call */ get_variation_attributes() as $attribute_name => $attribute ) {

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
1105
				$name = str_replace( 'attribute_', '', $attribute_name );
1106
1107
				if ( empty( $attribute ) && '0' !== $attribute ) {
1108
					continue;
1109
				}
1110
1111
				// Taxonomy-based attributes are prefixed with `pa_`, otherwise simply `attribute_`.
1112
				if ( 0 === strpos( $attribute_name, 'attribute_pa_' ) ) {
1113
					$option_term  = get_term_by( 'slug', $attribute, $name );
1114
					$attributes[] = array(
1115
						'id'     => wc_attribute_taxonomy_id_by_name( $name ),
1116
						'name'   => self::get_attribute_taxonomy_name( $name, $_product ),
1117
						'option' => $option_term && ! is_wp_error( $option_term ) ? $option_term->name : $attribute,
1118
					);
1119
				} else {
1120
					$attributes[] = array(
1121
						'id'     => 0,
1122
						'name'   => self::get_attribute_taxonomy_name( $name, $_product ),
1123
						'option' => $attribute,
1124
					);
1125
				}
1126
			}
1127
		} else {
1128
			foreach ( $object->get_attributes() as $attribute ) {
1129
				$attributes[] = array(
1130
					'id'        => $attribute['is_taxonomy'] ? wc_attribute_taxonomy_id_by_name( $attribute['name'] ) : 0,
1131
					'name'      => self::get_attribute_taxonomy_name( $attribute['name'], $object ),
1132
					'position'  => (int) $attribute['position'],
1133
					'visible'   => (bool) $attribute['is_visible'],
1134
					'variation' => (bool) $attribute['is_variation'],
1135
					'options'   => self::get_attribute_options( $object->get_id(), $attribute ),
1136
				);
1137
			}
1138
		}
1139
1140
		return $attributes;
1141
	}
1142
1143
	/**
1144
	 * Set product object's attributes.
1145
	 *
1146
	 * @param \WC_Product $object Product object.
1147
	 * @param array       $raw_attributes Attribute data from request.
1148
	 */
1149
	protected static function set_attributes( &$object, $raw_attributes ) {
1150
		$attributes = array();
1151
1152
		foreach ( $raw_attributes as $attribute ) {
1153
			$attribute_id   = 0;
1154
			$attribute_name = '';
1155
1156
			// Check ID for global attributes or name for product attributes.
1157
			if ( ! empty( $attribute['id'] ) ) {
1158
				$attribute_id   = absint( $attribute['id'] );
1159
				$attribute_name = wc_attribute_taxonomy_name_by_id( $attribute_id );
1160
			} elseif ( ! empty( $attribute['name'] ) ) {
1161
				$attribute_name = wc_clean( $attribute['name'] );
1162
			}
1163
1164
			if ( ! $attribute_id && ! $attribute_name ) {
1165
				continue;
1166
			}
1167
1168
			if ( $attribute_id ) {
1169
1170
				if ( isset( $attribute['options'] ) ) {
1171
					$options = $attribute['options'];
1172
1173
					if ( ! is_array( $attribute['options'] ) ) {
1174
						// Text based attributes - Posted values are term names.
1175
						$options = explode( WC_DELIMITER, $options );
0 ignored issues
show
Bug introduced by
The constant WooCommerce\RestApi\Cont...on4\Schema\WC_DELIMITER was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1176
					}
1177
1178
					$values = array_map( 'wc_sanitize_term_text_based', $options );
1179
					$values = array_filter( $values, 'strlen' );
1180
				} else {
1181
					$values = array();
1182
				}
1183
1184
				if ( ! empty( $values ) ) {
1185
					// Add attribute to array, but don't set values.
1186
					$attribute_object = new \WC_Product_Attribute();
1187
					$attribute_object->set_id( $attribute_id );
1188
					$attribute_object->set_name( $attribute_name );
0 ignored issues
show
Bug introduced by
$attribute_name of type array|string is incompatible with the type integer expected by parameter $value of WC_Product_Attribute::set_name(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1188
					$attribute_object->set_name( /** @scrutinizer ignore-type */ $attribute_name );
Loading history...
1189
					$attribute_object->set_options( $values );
1190
					$attribute_object->set_position( isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0' );
0 ignored issues
show
Bug introduced by
IssetNode ? (string)absi...bute['position']) : '0' of type string is incompatible with the type integer expected by parameter $value of WC_Product_Attribute::set_position(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1190
					$attribute_object->set_position( /** @scrutinizer ignore-type */ isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0' );
Loading history...
1191
					$attribute_object->set_visible( ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0 );
0 ignored issues
show
Bug introduced by
IssetNode && $attribute['visible'] ? 1 : 0 of type integer is incompatible with the type boolean expected by parameter $value of WC_Product_Attribute::set_visible(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1191
					$attribute_object->set_visible( /** @scrutinizer ignore-type */ ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0 );
Loading history...
1192
					$attribute_object->set_variation( ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0 );
0 ignored issues
show
Bug introduced by
IssetNode && $attribute['variation'] ? 1 : 0 of type integer is incompatible with the type boolean expected by parameter $value of WC_Product_Attribute::set_variation(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1192
					$attribute_object->set_variation( /** @scrutinizer ignore-type */ ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0 );
Loading history...
1193
					$attributes[] = $attribute_object;
1194
				}
1195
			} elseif ( isset( $attribute['options'] ) ) {
1196
				// Custom attribute - Add attribute to array and set the values.
1197
				if ( is_array( $attribute['options'] ) ) {
1198
					$values = $attribute['options'];
1199
				} else {
1200
					$values = explode( WC_DELIMITER, $attribute['options'] );
1201
				}
1202
				$attribute_object = new \WC_Product_Attribute();
1203
				$attribute_object->set_name( $attribute_name );
1204
				$attribute_object->set_options( $values );
1205
				$attribute_object->set_position( isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0' );
1206
				$attribute_object->set_visible( ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0 );
1207
				$attribute_object->set_variation( ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0 );
1208
				$attributes[] = $attribute_object;
1209
			}
1210
		}
1211
		$object->set_attributes( $attributes );
1212
	}
1213
1214
	/**
1215
	 * Set product images.
1216
	 *
1217
	 * @throws \WC_REST_Exception REST API exceptions.
1218
	 *
1219
	 * @param \WC_Product $object Product instance.
1220
	 * @param array       $images  Images data.
1221
	 */
1222
	protected static function set_images( &$object, $images ) {
1223
		$images = is_array( $images ) ? array_filter( $images ) : array();
0 ignored issues
show
introduced by
The condition is_array($images) is always true.
Loading history...
1224
1225
		if ( ! empty( $images ) ) {
1226
			$gallery = array();
1227
1228
			foreach ( $images as $index => $image ) {
1229
				$attachment_id = isset( $image['id'] ) ? absint( $image['id'] ) : 0;
1230
1231
				if ( 0 === $attachment_id && isset( $image['src'] ) ) {
1232
					$upload = wc_rest_upload_image_from_url( esc_url_raw( $image['src'] ) );
1233
1234
					if ( is_wp_error( $upload ) ) {
1235
						if ( ! apply_filters( 'woocommerce_rest_suppress_image_upload_error', false, $upload, $object->get_id(), $images ) ) {
1236
							throw new \WC_REST_Exception( 'woocommerce_product_image_upload_error', $upload->get_error_message(), 400 );
1237
						} else {
1238
							continue;
1239
						}
1240
					}
1241
1242
					$attachment_id = wc_rest_set_uploaded_image_as_attachment( $upload, $object->get_id() );
0 ignored issues
show
Bug introduced by
It seems like $upload can also be of type WP_Error; however, parameter $upload of wc_rest_set_uploaded_image_as_attachment() does only seem to accept array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1242
					$attachment_id = wc_rest_set_uploaded_image_as_attachment( /** @scrutinizer ignore-type */ $upload, $object->get_id() );
Loading history...
1243
				}
1244
1245
				if ( ! wp_attachment_is_image( $attachment_id ) ) {
1246
					/* translators: %s: image ID */
1247
					throw new \WC_REST_Exception( 'woocommerce_product_invalid_image_id', sprintf( __( '#%s is an invalid image ID.', 'woocommerce' ), $attachment_id ), 400 );
1248
				}
1249
1250
				$featured_image = $object->get_image_id();
1251
1252
				if ( 0 === $index ) {
1253
					$object->set_image_id( $attachment_id );
1254
				} else {
1255
					$gallery[] = $attachment_id;
1256
				}
1257
1258
				// Set the image alt if present.
1259
				if ( ! empty( $image['alt'] ) ) {
1260
					update_post_meta( $attachment_id, '_wp_attachment_image_alt', wc_clean( $image['alt'] ) );
1261
				}
1262
1263
				// Set the image name if present.
1264
				if ( ! empty( $image['name'] ) ) {
1265
					wp_update_post(
1266
						array(
1267
							'ID'         => $attachment_id,
1268
							'post_title' => $image['name'],
1269
						)
1270
					);
1271
				}
1272
			}
1273
1274
			$object->set_gallery_image_ids( $gallery );
1275
		} else {
1276
			$object->set_image_id( '' );
1277
			$object->set_gallery_image_ids( array() );
1278
		}
1279
	}
1280
1281
	/**
1282
	 * Set product shipping data.
1283
	 *
1284
	 * @param \WC_Product $object Product instance.
1285
	 * @param array       $data    Shipping data.
1286
	 */
1287
	protected static function set_shipping_data( &$object, $data ) {
1288
		if ( $object->get_virtual() ) {
1289
			$object->set_weight( '' );
1290
			$object->set_height( '' );
1291
			$object->set_length( '' );
1292
			$object->set_width( '' );
1293
		} else {
1294
			if ( isset( $data['weight'] ) ) {
1295
				$object->set_weight( $data['weight'] );
1296
			}
1297
1298
			// Height.
1299
			if ( isset( $data['dimensions']['height'] ) ) {
1300
				$object->set_height( $data['dimensions']['height'] );
1301
			}
1302
1303
			// Width.
1304
			if ( isset( $data['dimensions']['width'] ) ) {
1305
				$object->set_width( $data['dimensions']['width'] );
1306
			}
1307
1308
			// Length.
1309
			if ( isset( $data['dimensions']['length'] ) ) {
1310
				$object->set_length( $data['dimensions']['length'] );
1311
			}
1312
		}
1313
1314
		// Shipping class.
1315
		if ( isset( $data['shipping_class'] ) ) {
1316
			$data_store        = $object->get_data_store();
1317
			$shipping_class_id = $data_store->get_shipping_class_id_by_slug( wc_clean( $data['shipping_class'] ) );
1318
			$object->set_shipping_class_id( $shipping_class_id );
1319
		}
1320
	}
1321
1322
	/**
1323
	 * Save downloadable files.
1324
	 *
1325
	 * @param \WC_Product $object    Product instance.
1326
	 * @param array       $downloads  Downloads data.
1327
	 */
1328
	protected static function set_downloadable_files( &$object, $downloads ) {
1329
		$files = array();
1330
		foreach ( $downloads as $key => $file ) {
1331
			if ( empty( $file['file'] ) ) {
1332
				continue;
1333
			}
1334
1335
			$download = new \WC_Product_Download();
1336
			$download->set_id( ! empty( $file['id'] ) ? $file['id'] : wp_generate_uuid4() );
1337
			$download->set_name( $file['name'] ? $file['name'] : wc_get_filename_from_url( $file['file'] ) );
1338
			$download->set_file( apply_filters( 'woocommerce_file_download_path', $file['file'], $object, $key ) );
1339
			$files[] = $download;
1340
		}
1341
		$object->set_downloads( $files );
1342
	}
1343
1344
	/**
1345
	 * Save taxonomy terms.
1346
	 *
1347
	 * @param \WC_Product $object  Product instance.
1348
	 * @param array       $terms    Terms data.
1349
	 * @param string      $taxonomy Taxonomy name.
1350
	 */
1351
	protected static function set_taxonomy_terms( &$object, $terms, $taxonomy = 'cat' ) {
1352
		$term_ids = wp_list_pluck( $terms, 'id' );
1353
1354
		if ( 'cat' === $taxonomy ) {
1355
			$object->set_category_ids( $term_ids );
1356
		} elseif ( 'tag' === $taxonomy ) {
1357
			$object->set_tag_ids( $term_ids );
1358
		}
1359
	}
1360
1361
	/**
1362
	 * Save default attributes.
1363
	 *
1364
	 * @param \WC_Product $object Product instance.
1365
	 * @param array       $raw_default_attributes Default attributes.
1366
	 */
1367
	protected static function set_default_attributes( &$object, $raw_default_attributes ) {
1368
		$attributes         = $object->get_attributes();
1369
		$default_attributes = array();
1370
1371
		foreach ( $raw_default_attributes as $attribute ) {
1372
			$attribute_id   = 0;
1373
			$attribute_name = '';
1374
1375
			// Check ID for global attributes or name for product attributes.
1376
			if ( ! empty( $attribute['id'] ) ) {
1377
				$attribute_id   = absint( $attribute['id'] );
1378
				$attribute_name = wc_attribute_taxonomy_name_by_id( $attribute_id );
1379
			} elseif ( ! empty( $attribute['name'] ) ) {
1380
				$attribute_name = sanitize_title( $attribute['name'] );
1381
			}
1382
1383
			if ( ! $attribute_id && ! $attribute_name ) {
1384
				continue;
1385
			}
1386
1387
			if ( isset( $attributes[ $attribute_name ] ) ) {
1388
				$_attribute = $attributes[ $attribute_name ];
1389
1390
				if ( $_attribute['is_variation'] ) {
1391
					$value = isset( $attribute['option'] ) ? wc_clean( stripslashes( $attribute['option'] ) ) : '';
1392
1393
					if ( ! empty( $_attribute['is_taxonomy'] ) ) {
1394
						// If dealing with a taxonomy, we need to get the slug from the name posted to the API.
1395
						$term = get_term_by( 'name', $value, $attribute_name );
1396
1397
						if ( $term && ! is_wp_error( $term ) ) {
1398
							$value = $term->slug;
1399
						} else {
1400
							$value = sanitize_title( $value );
1401
						}
1402
					}
1403
1404
					if ( $value ) {
1405
						$default_attributes[ $attribute_name ] = $value;
1406
					}
1407
				}
1408
			}
1409
		}
1410
1411
		$object->set_default_attributes( $default_attributes );
1412
	}
1413
}
1414