Test Failed
Push — master ( a5e4c3...7495fa )
by Mike
43:00
created

Products::delete_item()   C

Complexity

Conditions 11
Paths 20

Size

Total Lines 119
Code Lines 59

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 11
eloc 59
nc 20
nop 1
dl 0
loc 119
rs 6.7478
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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
 * REST API Products controller
4
 *
5
 * Handles requests to the /products endpoint.
6
 *
7
 * @package WooCommerce/RestApi
8
 */
9
10
namespace WooCommerce\RestApi\Controllers\Version4;
11
12
defined( 'ABSPATH' ) || exit;
13
14
/**
15
 * REST API Products controller class.
16
 */
17
class Products extends AbstractObjectsController {
18
19
	/**
20
	 * Route base.
21
	 *
22
	 * @var string
23
	 */
24
	protected $rest_base = 'products';
25
26
	/**
27
	 * Post type.
28
	 *
29
	 * @var string
30
	 */
31
	protected $post_type = 'product';
32
33
	/**
34
	 * If object is hierarchical.
35
	 *
36
	 * @var bool
37
	 */
38
	protected $hierarchical = true;
39
40
	/**
41
	 * Initialize product actions.
42
	 */
43
	public function __construct() {
44
		add_action( "woocommerce_rest_insert_{$this->post_type}_object", array( $this, 'clear_transients' ) );
45
	}
46
47
	/**
48
	 * Register the routes for products.
49
	 */
50
	public function register_routes() {
51
		register_rest_route(
52
			$this->namespace,
53
			'/' . $this->rest_base,
54
			array(
55
				array(
56
					'methods'             => \WP_REST_Server::READABLE,
0 ignored issues
show
Bug introduced by
The type WP_REST_Server was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
57
					'callback'            => array( $this, 'get_items' ),
58
					'permission_callback' => array( $this, 'get_items_permissions_check' ),
59
					'args'                => $this->get_collection_params(),
60
				),
61
				array(
62
					'methods'             => \WP_REST_Server::CREATABLE,
63
					'callback'            => array( $this, 'create_item' ),
64
					'permission_callback' => array( $this, 'create_item_permissions_check' ),
65
					'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ),
66
				),
67
				'schema' => array( $this, 'get_public_item_schema' ),
68
			),
69
			true
70
		);
71
72
		register_rest_route(
73
			$this->namespace,
74
			'/' . $this->rest_base . '/(?P<id>[\d]+)',
75
			array(
76
				'args'   => array(
77
					'id' => array(
78
						'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
79
						'type'        => 'integer',
80
					),
81
				),
82
				array(
83
					'methods'             => \WP_REST_Server::READABLE,
84
					'callback'            => array( $this, 'get_item' ),
85
					'permission_callback' => array( $this, 'get_item_permissions_check' ),
86
					'args'                => array(
87
						'context' => $this->get_context_param(
88
							array(
89
								'default' => 'view',
90
							)
91
						),
92
					),
93
				),
94
				array(
95
					'methods'             => \WP_REST_Server::EDITABLE,
96
					'callback'            => array( $this, 'update_item' ),
97
					'permission_callback' => array( $this, 'update_item_permissions_check' ),
98
					'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::EDITABLE ),
99
				),
100
				array(
101
					'methods'             => \WP_REST_Server::DELETABLE,
102
					'callback'            => array( $this, 'delete_item' ),
103
					'permission_callback' => array( $this, 'delete_item_permissions_check' ),
104
					'args'                => array(
105
						'force' => array(
106
							'default'     => false,
107
							'description' => __( 'Whether to bypass trash and force deletion.', 'woocommerce' ),
108
							'type'        => 'boolean',
109
						),
110
					),
111
				),
112
				'schema' => array( $this, 'get_public_item_schema' ),
113
			),
114
			true
115
		);
116
117
		register_rest_route(
118
			$this->namespace,
119
			'/' . $this->rest_base . '/batch',
120
			array(
121
				array(
122
					'methods'             => \WP_REST_Server::EDITABLE,
123
					'callback'            => array( $this, 'batch_items' ),
124
					'permission_callback' => array( $this, 'batch_items_permissions_check' ),
125
					'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::EDITABLE ),
126
				),
127
				'schema' => array( $this, 'get_public_batch_schema' ),
128
			),
129
			true
130
		);
131
	}
132
133
	/**
134
	 * Get object.
135
	 *
136
	 * @param int $id Object ID.
137
	 *
138
	 * @since  3.0.0
139
	 * @return WC_Data
0 ignored issues
show
Bug introduced by
The type WooCommerce\RestApi\Controllers\Version4\WC_Data was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
140
	 */
141
	protected function get_object( $id ) {
142
		return wc_get_product( $id );
143
	}
144
145
	/**
146
	 * Prepare a single product output for response.
147
	 *
148
	 * @param WC_Data         $object  Object data.
149
	 * @param \WP_REST_Request $request Request object.
0 ignored issues
show
Bug introduced by
The type WP_REST_Request was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
150
	 *
151
	 * @since  3.0.0
152
	 * @return \WP_REST_Response
0 ignored issues
show
Bug introduced by
The type WP_REST_Response was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
153
	 */
154
	public function prepare_object_for_response( $object, $request ) {
155
		$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
156
		$data    = $this->get_product_data( $object, $context );
157
158
		// Add variations to variable products.
159
		if ( $object->is_type( 'variable' ) && $object->has_child() ) {
160
			$data['variations'] = $object->get_children();
161
		}
162
163
		// Add grouped products data.
164
		if ( $object->is_type( 'grouped' ) && $object->has_child() ) {
165
			$data['grouped_products'] = $object->get_children();
166
		}
167
168
		$data     = $this->add_additional_fields_to_object( $data, $request );
169
		$data     = $this->filter_response_by_context( $data, $context );
170
		$response = rest_ensure_response( $data );
171
		$response->add_links( $this->prepare_links( $object, $request ) );
172
173
		/**
174
		 * Filter the data for a response.
175
		 *
176
		 * The dynamic portion of the hook name, $this->post_type,
177
		 * refers to object type being prepared for the response.
178
		 *
179
		 * @param \WP_REST_Response $response The response object.
180
		 * @param WC_Data          $object   Object data.
181
		 * @param \WP_REST_Request  $request  Request object.
182
		 */
183
		return apply_filters( "woocommerce_rest_prepare_{$this->post_type}_object", $response, $object, $request );
184
	}
185
186
	/**
187
	 * Prepare a single product for create or update.
188
	 *
189
	 * @param  \WP_REST_Request $request Request object.
190
	 * @param  bool            $creating If is creating a new object.
191
	 * @return \WP_Error|WC_Data
192
	 */
193
	protected function prepare_object_for_database( $request, $creating = false ) {
194
		$id = isset( $request['id'] ) ? absint( $request['id'] ) : 0;
195
196
		// Type is the most important part here because we need to be using the correct class and methods.
197
		if ( isset( $request['type'] ) ) {
198
			$classname = \WC_Product_Factory::get_classname_from_product_type( $request['type'] );
0 ignored issues
show
Bug introduced by
The type WC_Product_Factory was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
199
200
			if ( ! class_exists( $classname ) ) {
201
				$classname = 'WC_Product_Simple';
202
			}
203
204
			$product = new $classname( $id );
205
		} elseif ( isset( $request['id'] ) ) {
206
			$product = wc_get_product( $id );
207
		} else {
208
			$product = new \WC_Product_Simple();
0 ignored issues
show
Bug introduced by
The type WC_Product_Simple was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
209
		}
210
211
		if ( 'variation' === $product->get_type() ) {
212
			return new \WP_Error(
0 ignored issues
show
Bug introduced by
The type WP_Error was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
213
				"woocommerce_rest_invalid_{$this->post_type}_id",
214
				__( 'To manipulate product variations you should use the /products/&lt;product_id&gt;/variations/&lt;id&gt; endpoint.', 'woocommerce' ),
215
				array(
216
					'status' => 404,
217
				)
218
			);
219
		}
220
221
		// Post title.
222
		if ( isset( $request['name'] ) ) {
223
			$product->set_name( wp_filter_post_kses( $request['name'] ) );
224
		}
225
226
		// Post content.
227
		if ( isset( $request['description'] ) ) {
228
			$product->set_description( wp_filter_post_kses( $request['description'] ) );
229
		}
230
231
		// Post excerpt.
232
		if ( isset( $request['short_description'] ) ) {
233
			$product->set_short_description( wp_filter_post_kses( $request['short_description'] ) );
234
		}
235
236
		// Post status.
237
		if ( isset( $request['status'] ) ) {
238
			$product->set_status( get_post_status_object( $request['status'] ) ? $request['status'] : 'draft' );
239
		}
240
241
		// Post slug.
242
		if ( isset( $request['slug'] ) ) {
243
			$product->set_slug( $request['slug'] );
244
		}
245
246
		// Menu order.
247
		if ( isset( $request['menu_order'] ) ) {
248
			$product->set_menu_order( $request['menu_order'] );
249
		}
250
251
		// Comment status.
252
		if ( isset( $request['reviews_allowed'] ) ) {
253
			$product->set_reviews_allowed( $request['reviews_allowed'] );
254
		}
255
256
		// Virtual.
257
		if ( isset( $request['virtual'] ) ) {
258
			$product->set_virtual( $request['virtual'] );
259
		}
260
261
		// Tax status.
262
		if ( isset( $request['tax_status'] ) ) {
263
			$product->set_tax_status( $request['tax_status'] );
264
		}
265
266
		// Tax Class.
267
		if ( isset( $request['tax_class'] ) ) {
268
			$product->set_tax_class( $request['tax_class'] );
269
		}
270
271
		// Catalog Visibility.
272
		if ( isset( $request['catalog_visibility'] ) ) {
273
			$product->set_catalog_visibility( $request['catalog_visibility'] );
274
		}
275
276
		// Purchase Note.
277
		if ( isset( $request['purchase_note'] ) ) {
278
			$product->set_purchase_note( wp_kses_post( wp_unslash( $request['purchase_note'] ) ) );
279
		}
280
281
		// Featured Product.
282
		if ( isset( $request['featured'] ) ) {
283
			$product->set_featured( $request['featured'] );
284
		}
285
286
		// Shipping data.
287
		$product = $this->save_product_shipping_data( $product, $request );
288
289
		// SKU.
290
		if ( isset( $request['sku'] ) ) {
291
			$product->set_sku( wc_clean( $request['sku'] ) );
292
		}
293
294
		// Attributes.
295
		if ( isset( $request['attributes'] ) ) {
296
			$attributes = array();
297
298
			foreach ( $request['attributes'] as $attribute ) {
299
				$attribute_id   = 0;
300
				$attribute_name = '';
301
302
				// Check ID for global attributes or name for product attributes.
303
				if ( ! empty( $attribute['id'] ) ) {
304
					$attribute_id   = absint( $attribute['id'] );
305
					$attribute_name = wc_attribute_taxonomy_name_by_id( $attribute_id );
306
				} elseif ( ! empty( $attribute['name'] ) ) {
307
					$attribute_name = wc_clean( $attribute['name'] );
308
				}
309
310
				if ( ! $attribute_id && ! $attribute_name ) {
311
					continue;
312
				}
313
314
				if ( $attribute_id ) {
315
316
					if ( isset( $attribute['options'] ) ) {
317
						$options = $attribute['options'];
318
319
						if ( ! is_array( $attribute['options'] ) ) {
320
							// Text based attributes - Posted values are term names.
321
							$options = explode( WC_DELIMITER, $options );
0 ignored issues
show
Bug introduced by
The constant WooCommerce\RestApi\Cont...s\Version4\WC_DELIMITER was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
322
						}
323
324
						$values = array_map( 'wc_sanitize_term_text_based', $options );
325
						$values = array_filter( $values, 'strlen' );
326
					} else {
327
						$values = array();
328
					}
329
330
					if ( ! empty( $values ) ) {
331
						// Add attribute to array, but don't set values.
332
						$attribute_object = new \WC_Product_Attribute();
0 ignored issues
show
Bug introduced by
The type WC_Product_Attribute was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
333
						$attribute_object->set_id( $attribute_id );
334
						$attribute_object->set_name( $attribute_name );
335
						$attribute_object->set_options( $values );
336
						$attribute_object->set_position( isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0' );
337
						$attribute_object->set_visible( ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0 );
338
						$attribute_object->set_variation( ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0 );
339
						$attributes[] = $attribute_object;
340
					}
341
				} elseif ( isset( $attribute['options'] ) ) {
342
					// Custom attribute - Add attribute to array and set the values.
343
					if ( is_array( $attribute['options'] ) ) {
344
						$values = $attribute['options'];
345
					} else {
346
						$values = explode( WC_DELIMITER, $attribute['options'] );
347
					}
348
					$attribute_object = new \WC_Product_Attribute();
349
					$attribute_object->set_name( $attribute_name );
350
					$attribute_object->set_options( $values );
351
					$attribute_object->set_position( isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0' );
352
					$attribute_object->set_visible( ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0 );
353
					$attribute_object->set_variation( ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0 );
354
					$attributes[] = $attribute_object;
355
				}
356
			}
357
			$product->set_attributes( $attributes );
358
		}
359
360
		// Sales and prices.
361
		if ( in_array( $product->get_type(), array( 'variable', 'grouped' ), true ) ) {
362
			$product->set_regular_price( '' );
363
			$product->set_sale_price( '' );
364
			$product->set_date_on_sale_to( '' );
365
			$product->set_date_on_sale_from( '' );
366
			$product->set_price( '' );
367
		} else {
368
			// Regular Price.
369
			if ( isset( $request['regular_price'] ) ) {
370
				$product->set_regular_price( $request['regular_price'] );
371
			}
372
373
			// Sale Price.
374
			if ( isset( $request['sale_price'] ) ) {
375
				$product->set_sale_price( $request['sale_price'] );
376
			}
377
378
			if ( isset( $request['date_on_sale_from'] ) ) {
379
				$product->set_date_on_sale_from( $request['date_on_sale_from'] );
380
			}
381
382
			if ( isset( $request['date_on_sale_from_gmt'] ) ) {
383
				$product->set_date_on_sale_from( $request['date_on_sale_from_gmt'] ? strtotime( $request['date_on_sale_from_gmt'] ) : null );
384
			}
385
386
			if ( isset( $request['date_on_sale_to'] ) ) {
387
				$product->set_date_on_sale_to( $request['date_on_sale_to'] );
388
			}
389
390
			if ( isset( $request['date_on_sale_to_gmt'] ) ) {
391
				$product->set_date_on_sale_to( $request['date_on_sale_to_gmt'] ? strtotime( $request['date_on_sale_to_gmt'] ) : null );
392
			}
393
		}
394
395
		// Product parent ID.
396
		if ( isset( $request['parent_id'] ) ) {
397
			$product->set_parent_id( $request['parent_id'] );
398
		}
399
400
		// Sold individually.
401
		if ( isset( $request['sold_individually'] ) ) {
402
			$product->set_sold_individually( $request['sold_individually'] );
403
		}
404
405
		// Stock status; stock_status has priority over in_stock.
406
		if ( isset( $request['stock_status'] ) ) {
407
			$stock_status = $request['stock_status'];
408
		} else {
409
			$stock_status = $product->get_stock_status();
410
		}
411
412
		// Stock data.
413
		if ( 'yes' === get_option( 'woocommerce_manage_stock' ) ) {
414
			// Manage stock.
415
			if ( isset( $request['manage_stock'] ) ) {
416
				$product->set_manage_stock( $request['manage_stock'] );
417
			}
418
419
			// Backorders.
420
			if ( isset( $request['backorders'] ) ) {
421
				$product->set_backorders( $request['backorders'] );
422
			}
423
424
			if ( $product->is_type( 'grouped' ) ) {
425
				$product->set_manage_stock( 'no' );
426
				$product->set_backorders( 'no' );
427
				$product->set_stock_quantity( '' );
428
				$product->set_stock_status( $stock_status );
429
			} elseif ( $product->is_type( 'external' ) ) {
430
				$product->set_manage_stock( 'no' );
431
				$product->set_backorders( 'no' );
432
				$product->set_stock_quantity( '' );
433
				$product->set_stock_status( 'instock' );
434
			} elseif ( $product->get_manage_stock() ) {
435
				// Stock status is always determined by children so sync later.
436
				if ( ! $product->is_type( 'variable' ) ) {
437
					$product->set_stock_status( $stock_status );
438
				}
439
440
				// Stock quantity.
441
				if ( isset( $request['stock_quantity'] ) ) {
442
					$product->set_stock_quantity( wc_stock_amount( $request['stock_quantity'] ) );
443
				} elseif ( isset( $request['inventory_delta'] ) ) {
444
					$stock_quantity  = wc_stock_amount( $product->get_stock_quantity() );
445
					$stock_quantity += wc_stock_amount( $request['inventory_delta'] );
446
					$product->set_stock_quantity( wc_stock_amount( $stock_quantity ) );
447
				}
448
			} else {
449
				// Don't manage stock.
450
				$product->set_manage_stock( 'no' );
451
				$product->set_stock_quantity( '' );
452
				$product->set_stock_status( $stock_status );
453
			}
454
		} elseif ( ! $product->is_type( 'variable' ) ) {
455
			$product->set_stock_status( $stock_status );
456
		}
457
458
		// Upsells.
459
		if ( isset( $request['upsell_ids'] ) ) {
460
			$upsells = array();
461
			$ids     = $request['upsell_ids'];
462
463
			if ( ! empty( $ids ) ) {
464
				foreach ( $ids as $id ) {
465
					if ( $id && $id > 0 ) {
466
						$upsells[] = $id;
467
					}
468
				}
469
			}
470
471
			$product->set_upsell_ids( $upsells );
472
		}
473
474
		// Cross sells.
475
		if ( isset( $request['cross_sell_ids'] ) ) {
476
			$crosssells = array();
477
			$ids        = $request['cross_sell_ids'];
478
479
			if ( ! empty( $ids ) ) {
480
				foreach ( $ids as $id ) {
481
					if ( $id && $id > 0 ) {
482
						$crosssells[] = $id;
483
					}
484
				}
485
			}
486
487
			$product->set_cross_sell_ids( $crosssells );
488
		}
489
490
		// Product categories.
491
		if ( isset( $request['categories'] ) && is_array( $request['categories'] ) ) {
492
			$product = $this->save_taxonomy_terms( $product, $request['categories'] );
493
		}
494
495
		// Product tags.
496
		if ( isset( $request['tags'] ) && is_array( $request['tags'] ) ) {
497
			$product = $this->save_taxonomy_terms( $product, $request['tags'], 'tag' );
498
		}
499
500
		// Downloadable.
501
		if ( isset( $request['downloadable'] ) ) {
502
			$product->set_downloadable( $request['downloadable'] );
503
		}
504
505
		// Downloadable options.
506
		if ( $product->get_downloadable() ) {
507
508
			// Downloadable files.
509
			if ( isset( $request['downloads'] ) && is_array( $request['downloads'] ) ) {
510
				$product = $this->save_downloadable_files( $product, $request['downloads'] );
511
			}
512
513
			// Download limit.
514
			if ( isset( $request['download_limit'] ) ) {
515
				$product->set_download_limit( $request['download_limit'] );
516
			}
517
518
			// Download expiry.
519
			if ( isset( $request['download_expiry'] ) ) {
520
				$product->set_download_expiry( $request['download_expiry'] );
521
			}
522
		}
523
524
		// Product url and button text for external products.
525
		if ( $product->is_type( 'external' ) ) {
526
			if ( isset( $request['external_url'] ) ) {
527
				$product->set_product_url( $request['external_url'] );
528
			}
529
530
			if ( isset( $request['button_text'] ) ) {
531
				$product->set_button_text( $request['button_text'] );
532
			}
533
		}
534
535
		// Save default attributes for variable products.
536
		if ( $product->is_type( 'variable' ) ) {
537
			$product = $this->save_default_attributes( $product, $request );
538
		}
539
540
		// Set children for a grouped product.
541
		if ( $product->is_type( 'grouped' ) && isset( $request['grouped_products'] ) ) {
542
			$product->set_children( $request['grouped_products'] );
543
		}
544
545
		// Check for featured/gallery images, upload it and set it.
546
		if ( isset( $request['images'] ) ) {
547
			$product = $this->set_product_images( $product, $request['images'] );
548
		}
549
550
		// Allow set meta_data.
551
		if ( is_array( $request['meta_data'] ) ) {
552
			foreach ( $request['meta_data'] as $meta ) {
553
				$product->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' );
554
			}
555
		}
556
557
		if ( ! empty( $request['date_created'] ) ) {
558
			$date = rest_parse_date( $request['date_created'] );
559
560
			if ( $date ) {
561
				$product->set_date_created( $date );
562
			}
563
		}
564
565
		if ( ! empty( $request['date_created_gmt'] ) ) {
566
			$date = rest_parse_date( $request['date_created_gmt'], true );
567
568
			if ( $date ) {
569
				$product->set_date_created( $date );
570
			}
571
		}
572
573
		if ( ! empty( $request['search'] ) ) {
574
			$args['search'] = trim( $request['search'] );
575
			unset( $args['s'] );
576
		}
577
578
		if ( ! empty( $request['low_in_stock'] ) ) {
579
			$args['low_in_stock'] = $request['low_in_stock'];
580
			$args['post_type']    = array( 'product', 'product_variation' );
581
		}
582
583
		/**
584
		 * Filters an object before it is inserted via the REST API.
585
		 *
586
		 * The dynamic portion of the hook name, `$this->post_type`,
587
		 * refers to the object type slug.
588
		 *
589
		 * @param WC_Data         $product  Object object.
590
		 * @param \WP_REST_Request $request  Request object.
591
		 * @param bool            $creating If is creating a new object.
592
		 */
593
		return apply_filters( "woocommerce_rest_pre_insert_{$this->post_type}_object", $product, $request, $creating );
594
	}
595
596
	/**
597
	 * Get a collection of posts and add the post title filter option to \WP_Query.
598
	 *
599
	 * @param \WP_REST_Request $request Full details about the request.
600
	 * @return \WP_Error\WP_REST_Response
0 ignored issues
show
Bug introduced by
The type WP_Error\WP_REST_Response was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
601
	 */
602
	public function get_items( $request ) {
603
		add_filter( 'posts_where', array( __CLASS__, 'add_wp_query_filter' ), 10, 2 );
604
		add_filter( 'posts_join', array( __CLASS__, 'add_wp_query_join' ), 10, 2 );
605
		add_filter( 'posts_groupby', array( __CLASS__, 'add_wp_query_group_by' ), 10, 2 );
606
		$response = parent::get_items( $request );
607
		remove_filter( 'posts_where', array( __CLASS__, 'add_wp_query_filter' ), 10 );
608
		remove_filter( 'posts_join', array( __CLASS__, 'add_wp_query_join' ), 10 );
609
		remove_filter( 'posts_groupby', array( __CLASS__, 'add_wp_query_group_by' ), 10 );
610
		return $response;
611
	}
612
613
	/**
614
	 * Add in conditional search filters for products.
615
	 *
616
	 * @param string $where Where clause used to search posts.
617
	 * @param object $wp_query \WP_Query object.
618
	 * @return string
619
	 */
620
	public static function add_wp_query_filter( $where, $wp_query ) {
621
		global $wpdb;
622
623
		$search = $wp_query->get( 'search' );
624
		if ( $search ) {
625
			$search = $wpdb->esc_like( $search );
626
			$search = "'%" . $search . "%'";
627
			$where .= " AND ({$wpdb->posts}.post_title LIKE {$search}";
628
			$where .= wc_product_sku_enabled() ? ' OR ps_post_meta.meta_key = "_sku" AND ps_post_meta.meta_value LIKE ' . $search . ')' : ')';
629
		}
630
631
		if ( $wp_query->get( 'low_in_stock' ) ) {
632
			$low_stock_amount = absint( max( get_option( 'woocommerce_notify_low_stock_amount' ), 1 ) );
633
			$where           .= " AND lis_postmeta2.meta_key = '_manage_stock'
634
			AND lis_postmeta2.meta_value = 'yes'
635
			AND lis_postmeta.meta_key = '_stock'
636
			AND lis_postmeta.meta_value IS NOT NULL
637
			AND lis_postmeta3.meta_key = '_low_stock_amount'
638
			AND (
639
				lis_postmeta3.meta_value > ''
640
				AND CAST(lis_postmeta.meta_value AS SIGNED) <= CAST(lis_postmeta3.meta_value AS SIGNED)
641
				OR lis_postmeta3.meta_value <= ''
642
				AND CAST(lis_postmeta.meta_value AS SIGNED) <= {$low_stock_amount}
643
			)";
644
		}
645
646
		return $where;
647
	}
648
649
	/**
650
	 * Join posts meta tables when product search or low stock query is present.
651
	 *
652
	 * @param string $join Join clause used to search posts.
653
	 * @param object $wp_query \WP_Query object.
654
	 * @return string
655
	 */
656
	public static function add_wp_query_join( $join, $wp_query ) {
657
		global $wpdb;
658
659
		$search = $wp_query->get( 'search' );
660
		if ( $search && wc_product_sku_enabled() ) {
661
			$join .= " INNER JOIN {$wpdb->postmeta} AS ps_post_meta ON ps_post_meta.post_id = {$wpdb->posts}.ID";
662
		}
663
664
		if ( $wp_query->get( 'low_in_stock' ) ) {
665
			$join .= " INNER JOIN {$wpdb->postmeta} AS lis_postmeta ON {$wpdb->posts}.ID = lis_postmeta.post_id
666
			INNER JOIN {$wpdb->postmeta} AS lis_postmeta2 ON {$wpdb->posts}.ID = lis_postmeta2.post_id
667
			INNER JOIN {$wpdb->postmeta} AS lis_postmeta3 ON {$wpdb->posts}.ID = lis_postmeta3.post_id";
668
		}
669
670
		return $join;
671
	}
672
673
	/**
674
	 * Group by post ID to prevent duplicates.
675
	 *
676
	 * @param string $groupby Group by clause used to organize posts.
677
	 * @param object $wp_query \WP_Query object.
678
	 * @return string
679
	 */
680
	public static function add_wp_query_group_by( $groupby, $wp_query ) {
681
		global $wpdb;
682
683
		$search       = $wp_query->get( 'search' );
684
		$low_in_stock = $wp_query->get( 'low_in_stock' );
685
		if ( empty( $groupby ) && ( $search || $low_in_stock ) ) {
686
			$groupby = $wpdb->posts . '.ID';
687
		}
688
		return $groupby;
689
	}
690
691
	/**
692
	 * Make extra product orderby features supported by WooCommerce available to the WC API.
693
	 * This includes 'price', 'popularity', and 'rating'.
694
	 *
695
	 * @param \WP_REST_Request $request Request data.
696
	 * @return array
697
	 */
698
	protected function prepare_objects_query( $request ) {
699
		$args = parent::prepare_objects_query( $request );
700
701
		// Set post_status.
702
		$args['post_status'] = $request['status'];
703
704
		// Taxonomy query to filter products by type, category,
705
		// tag, shipping class, and attribute.
706
		$tax_query = array();
707
708
		// Map between taxonomy name and arg's key.
709
		$taxonomies = array(
710
			'product_cat'            => 'category',
711
			'product_tag'            => 'tag',
712
			'product_shipping_class' => 'shipping_class',
713
		);
714
715
		// Set tax_query for each passed arg.
716
		foreach ( $taxonomies as $taxonomy => $key ) {
717
			if ( ! empty( $request[ $key ] ) ) {
718
				$tax_query[] = array(
719
					'taxonomy' => $taxonomy,
720
					'field'    => 'term_id',
721
					'terms'    => $request[ $key ],
722
				);
723
			}
724
		}
725
726
		// Filter product type by slug.
727
		if ( ! empty( $request['type'] ) ) {
728
			$tax_query[] = array(
729
				'taxonomy' => 'product_type',
730
				'field'    => 'slug',
731
				'terms'    => $request['type'],
732
			);
733
		}
734
735
		// Filter by attribute and term.
736
		if ( ! empty( $request['attribute'] ) && ! empty( $request['attribute_term'] ) ) {
737
			if ( in_array( $request['attribute'], wc_get_attribute_taxonomy_names(), true ) ) {
738
				$tax_query[] = array(
739
					'taxonomy' => $request['attribute'],
740
					'field'    => 'term_id',
741
					'terms'    => $request['attribute_term'],
742
				);
743
			}
744
		}
745
746
		// Build tax_query if taxonomies are set.
747
		if ( ! empty( $tax_query ) ) {
748
			if ( ! empty( $args['tax_query'] ) ) {
749
				$args['tax_query'] = array_merge( $tax_query, $args['tax_query'] ); // WPCS: slow query ok.
750
			} else {
751
				$args['tax_query'] = $tax_query; // WPCS: slow query ok.
752
			}
753
		}
754
755
		// Filter featured.
756
		if ( is_bool( $request['featured'] ) ) {
757
			$args['tax_query'][] = array(
758
				'taxonomy' => 'product_visibility',
759
				'field'    => 'name',
760
				'terms'    => 'featured',
761
				'operator' => true === $request['featured'] ? 'IN' : 'NOT IN',
762
			);
763
		}
764
765
		// Filter by sku.
766
		if ( ! empty( $request['sku'] ) ) {
767
			$skus = explode( ',', $request['sku'] );
768
			// Include the current string as a SKU too.
769
			if ( 1 < count( $skus ) ) {
770
				$skus[] = $request['sku'];
771
			}
772
773
			$args['meta_query'] = $this->add_meta_query( // WPCS: slow query ok.
774
				$args,
775
				array(
776
					'key'     => '_sku',
777
					'value'   => $skus,
778
					'compare' => 'IN',
779
				)
780
			);
781
		}
782
783
		// Filter by tax class.
784
		if ( ! empty( $request['tax_class'] ) ) {
785
			$args['meta_query'] = $this->add_meta_query( // WPCS: slow query ok.
786
				$args,
787
				array(
788
					'key'   => '_tax_class',
789
					'value' => 'standard' !== $request['tax_class'] ? $request['tax_class'] : '',
790
				)
791
			);
792
		}
793
794
		// Price filter.
795
		if ( ! empty( $request['min_price'] ) || ! empty( $request['max_price'] ) ) {
796
			$args['meta_query'] = $this->add_meta_query( $args, wc_get_min_max_price_meta_query( $request ) );  // WPCS: slow query ok.
797
		}
798
799
		// Filter product by stock_status.
800
		if ( ! empty( $request['stock_status'] ) ) {
801
			$args['meta_query'] = $this->add_meta_query( // WPCS: slow query ok.
802
				$args,
803
				array(
804
					'key'   => '_stock_status',
805
					'value' => $request['stock_status'],
806
				)
807
			);
808
		}
809
810
		// Filter by on sale products.
811
		if ( is_bool( $request['on_sale'] ) ) {
812
			$on_sale_key = $request['on_sale'] ? 'post__in' : 'post__not_in';
813
			$on_sale_ids = wc_get_product_ids_on_sale();
814
815
			// Use 0 when there's no on sale products to avoid return all products.
816
			$on_sale_ids = empty( $on_sale_ids ) ? array( 0 ) : $on_sale_ids;
817
818
			$args[ $on_sale_key ] += $on_sale_ids;
819
		}
820
821
		// Force the post_type argument, since it's not a user input variable.
822
		if ( ! empty( $request['sku'] ) ) {
823
			$args['post_type'] = array( 'product', 'product_variation' );
824
		} else {
825
			$args['post_type'] = $this->post_type;
826
		}
827
828
		$orderby = $request->get_param( 'orderby' );
829
		$order   = $request->get_param( 'order' );
830
831
		$ordering_args   = WC()->query->get_catalog_ordering_args( $orderby, $order );
832
		$args['orderby'] = $ordering_args['orderby'];
833
		$args['order']   = $ordering_args['order'];
834
		if ( $ordering_args['meta_key'] ) {
835
			$args['meta_key'] = $ordering_args['meta_key']; // WPCS: slow query ok.
836
		}
837
838
		return $args;
839
	}
840
841
	/**
842
	 * Get the downloads for a product or product variation.
843
	 *
844
	 * @param WC_Product|WC_Product_Variation $product Product instance.
0 ignored issues
show
Bug introduced by
The type WooCommerce\RestApi\Cont...ers\Version4\WC_Product was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
Bug introduced by
The type WooCommerce\RestApi\Cont...n4\WC_Product_Variation was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
845
	 *
846
	 * @return array
847
	 */
848
	protected function get_downloads( $product ) {
849
		$downloads = array();
850
851
		if ( $product->is_downloadable() ) {
852
			foreach ( $product->get_downloads() as $file_id => $file ) {
853
				$downloads[] = array(
854
					'id'   => $file_id, // MD5 hash.
855
					'name' => $file['name'],
856
					'file' => $file['file'],
857
				);
858
			}
859
		}
860
861
		return $downloads;
862
	}
863
864
	/**
865
	 * Get taxonomy terms.
866
	 *
867
	 * @param WC_Product $product  Product instance.
868
	 * @param string     $taxonomy Taxonomy slug.
869
	 *
870
	 * @return array
871
	 */
872
	protected function get_taxonomy_terms( $product, $taxonomy = 'cat' ) {
873
		$terms = array();
874
875
		foreach ( wc_get_object_terms( $product->get_id(), 'product_' . $taxonomy ) as $term ) {
876
			$terms[] = array(
877
				'id'   => $term->term_id,
878
				'name' => $term->name,
879
				'slug' => $term->slug,
880
			);
881
		}
882
883
		return $terms;
884
	}
885
886
	/**
887
	 * Get the images for a product or product variation.
888
	 *
889
	 * @param WC_Product|WC_Product_Variation $product Product instance.
890
	 * @return array
891
	 */
892
	protected function get_images( $product ) {
893
		$images         = array();
894
		$attachment_ids = array();
895
896
		// Add featured image.
897
		if ( $product->get_image_id() ) {
898
			$attachment_ids[] = $product->get_image_id();
899
		}
900
901
		// Add gallery images.
902
		$attachment_ids = array_merge( $attachment_ids, $product->get_gallery_image_ids() );
903
904
		// Build image data.
905
		foreach ( $attachment_ids as $attachment_id ) {
906
			$attachment_post = get_post( $attachment_id );
907
			if ( is_null( $attachment_post ) ) {
908
				continue;
909
			}
910
911
			$attachment = wp_get_attachment_image_src( $attachment_id, 'full' );
912
			if ( ! is_array( $attachment ) ) {
913
				continue;
914
			}
915
916
			$images[] = array(
917
				'id'                => (int) $attachment_id,
918
				'date_created'      => wc_rest_prepare_date_response( $attachment_post->post_date, false ),
919
				'date_created_gmt'  => wc_rest_prepare_date_response( strtotime( $attachment_post->post_date_gmt ) ),
920
				'date_modified'     => wc_rest_prepare_date_response( $attachment_post->post_modified, false ),
921
				'date_modified_gmt' => wc_rest_prepare_date_response( strtotime( $attachment_post->post_modified_gmt ) ),
922
				'src'               => current( $attachment ),
923
				'name'              => get_the_title( $attachment_id ),
924
				'alt'               => get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ),
925
			);
926
		}
927
928
		return $images;
929
	}
930
931
	/**
932
	 * Get attribute taxonomy label.
933
	 *
934
	 * @param string $name Taxonomy name.
935
	 *
936
	 * @deprecated 3.0.0
937
	 * @return     string
938
	 */
939
	protected function get_attribute_taxonomy_label( $name ) {
940
		$tax    = get_taxonomy( $name );
941
		$labels = get_taxonomy_labels( $tax );
942
943
		return $labels->singular_name;
944
	}
945
946
	/**
947
	 * Get product attribute taxonomy name.
948
	 *
949
	 * @param string     $slug    Taxonomy name.
950
	 * @param WC_Product $product Product data.
951
	 *
952
	 * @since  3.0.0
953
	 * @return string
954
	 */
955
	protected function get_attribute_taxonomy_name( $slug, $product ) {
956
		// Format slug so it matches attributes of the product.
957
		$slug       = wc_attribute_taxonomy_slug( $slug );
958
		$attributes = $product->get_attributes();
959
		$attribute  = false;
960
961
		// pa_ attributes.
962
		if ( isset( $attributes[ wc_attribute_taxonomy_name( $slug ) ] ) ) {
963
			$attribute = $attributes[ wc_attribute_taxonomy_name( $slug ) ];
964
		} elseif ( isset( $attributes[ $slug ] ) ) {
965
			$attribute = $attributes[ $slug ];
966
		}
967
968
		if ( ! $attribute ) {
969
			return $slug;
970
		}
971
972
		// Taxonomy attribute name.
973
		if ( $attribute->is_taxonomy() ) {
974
			$taxonomy = $attribute->get_taxonomy_object();
975
			return $taxonomy->attribute_label;
976
		}
977
978
		// Custom product attribute name.
979
		return $attribute->get_name();
980
	}
981
982
	/**
983
	 * Get default attributes.
984
	 *
985
	 * @param WC_Product $product Product instance.
986
	 *
987
	 * @return array
988
	 */
989
	protected function get_default_attributes( $product ) {
990
		$default = array();
991
992
		if ( $product->is_type( 'variable' ) ) {
993
			foreach ( array_filter( (array) $product->get_default_attributes(), 'strlen' ) as $key => $value ) {
994
				if ( 0 === strpos( $key, 'pa_' ) ) {
995
					$default[] = array(
996
						'id'     => wc_attribute_taxonomy_id_by_name( $key ),
997
						'name'   => $this->get_attribute_taxonomy_name( $key, $product ),
998
						'option' => $value,
999
					);
1000
				} else {
1001
					$default[] = array(
1002
						'id'     => 0,
1003
						'name'   => $this->get_attribute_taxonomy_name( $key, $product ),
1004
						'option' => $value,
1005
					);
1006
				}
1007
			}
1008
		}
1009
1010
		return $default;
1011
	}
1012
1013
	/**
1014
	 * Get attribute options.
1015
	 *
1016
	 * @param int   $product_id Product ID.
1017
	 * @param array $attribute  Attribute data.
1018
	 *
1019
	 * @return array
1020
	 */
1021
	protected function get_attribute_options( $product_id, $attribute ) {
1022
		if ( isset( $attribute['is_taxonomy'] ) && $attribute['is_taxonomy'] ) {
1023
			return wc_get_product_terms(
1024
				$product_id,
1025
				$attribute['name'],
1026
				array(
1027
					'fields' => 'names',
1028
				)
1029
			);
1030
		} elseif ( isset( $attribute['value'] ) ) {
1031
			return array_map( 'trim', explode( '|', $attribute['value'] ) );
1032
		}
1033
1034
		return array();
1035
	}
1036
1037
	/**
1038
	 * Get the attributes for a product or product variation.
1039
	 *
1040
	 * @param WC_Product|WC_Product_Variation $product Product instance.
1041
	 *
1042
	 * @return array
1043
	 */
1044
	protected function get_attributes( $product ) {
1045
		$attributes = array();
1046
1047
		if ( $product->is_type( 'variation' ) ) {
1048
			$_product = wc_get_product( $product->get_parent_id() );
1049
			foreach ( $product->get_variation_attributes() as $attribute_name => $attribute ) {
1050
				$name = str_replace( 'attribute_', '', $attribute_name );
1051
1052
				if ( empty( $attribute ) && '0' !== $attribute ) {
1053
					continue;
1054
				}
1055
1056
				// Taxonomy-based attributes are prefixed with `pa_`, otherwise simply `attribute_`.
1057
				if ( 0 === strpos( $attribute_name, 'attribute_pa_' ) ) {
1058
					$option_term  = get_term_by( 'slug', $attribute, $name );
1059
					$attributes[] = array(
1060
						'id'     => wc_attribute_taxonomy_id_by_name( $name ),
1061
						'name'   => $this->get_attribute_taxonomy_name( $name, $_product ),
1062
						'option' => $option_term && ! is_wp_error( $option_term ) ? $option_term->name : $attribute,
1063
					);
1064
				} else {
1065
					$attributes[] = array(
1066
						'id'     => 0,
1067
						'name'   => $this->get_attribute_taxonomy_name( $name, $_product ),
1068
						'option' => $attribute,
1069
					);
1070
				}
1071
			}
1072
		} else {
1073
			foreach ( $product->get_attributes() as $attribute ) {
1074
				$attributes[] = array(
1075
					'id'        => $attribute['is_taxonomy'] ? wc_attribute_taxonomy_id_by_name( $attribute['name'] ) : 0,
1076
					'name'      => $this->get_attribute_taxonomy_name( $attribute['name'], $product ),
1077
					'position'  => (int) $attribute['position'],
1078
					'visible'   => (bool) $attribute['is_visible'],
1079
					'variation' => (bool) $attribute['is_variation'],
1080
					'options'   => $this->get_attribute_options( $product->get_id(), $attribute ),
1081
				);
1082
			}
1083
		}
1084
1085
		return $attributes;
1086
	}
1087
1088
	/**
1089
	 * Get product data.
1090
	 *
1091
	 * @param WC_Product $product Product instance.
1092
	 * @param string     $context Request context.
1093
	 *                            Options: 'view' and 'edit'.
1094
	 *
1095
	 * @return array
1096
	 */
1097
	protected function get_product_data( $product, $context = 'view' ) {
1098
		$data = array(
1099
			'id'                    => $product->get_id(),
1100
			'name'                  => $product->get_name( $context ),
1101
			'slug'                  => $product->get_slug( $context ),
1102
			'permalink'             => $product->get_permalink(),
1103
			'date_created'          => wc_rest_prepare_date_response( $product->get_date_created( $context ), false ),
1104
			'date_created_gmt'      => wc_rest_prepare_date_response( $product->get_date_created( $context ) ),
1105
			'date_modified'         => wc_rest_prepare_date_response( $product->get_date_modified( $context ), false ),
1106
			'date_modified_gmt'     => wc_rest_prepare_date_response( $product->get_date_modified( $context ) ),
1107
			'type'                  => $product->get_type(),
1108
			'status'                => $product->get_status( $context ),
1109
			'featured'              => $product->is_featured(),
1110
			'catalog_visibility'    => $product->get_catalog_visibility( $context ),
1111
			'description'           => 'view' === $context ? wpautop( do_shortcode( $product->get_description() ) ) : $product->get_description( $context ),
1112
			'short_description'     => 'view' === $context ? apply_filters( 'woocommerce_short_description', $product->get_short_description() ) : $product->get_short_description( $context ),
1113
			'sku'                   => $product->get_sku( $context ),
1114
			'price'                 => $product->get_price( $context ),
1115
			'regular_price'         => $product->get_regular_price( $context ),
1116
			'sale_price'            => $product->get_sale_price( $context ) ? $product->get_sale_price( $context ) : '',
1117
			'date_on_sale_from'     => wc_rest_prepare_date_response( $product->get_date_on_sale_from( $context ), false ),
1118
			'date_on_sale_from_gmt' => wc_rest_prepare_date_response( $product->get_date_on_sale_from( $context ) ),
1119
			'date_on_sale_to'       => wc_rest_prepare_date_response( $product->get_date_on_sale_to( $context ), false ),
1120
			'date_on_sale_to_gmt'   => wc_rest_prepare_date_response( $product->get_date_on_sale_to( $context ) ),
1121
			'price_html'            => $product->get_price_html(),
1122
			'on_sale'               => $product->is_on_sale( $context ),
1123
			'purchasable'           => $product->is_purchasable(),
1124
			'total_sales'           => $product->get_total_sales( $context ),
1125
			'virtual'               => $product->is_virtual(),
1126
			'downloadable'          => $product->is_downloadable(),
1127
			'downloads'             => $this->get_downloads( $product ),
1128
			'download_limit'        => $product->get_download_limit( $context ),
1129
			'download_expiry'       => $product->get_download_expiry( $context ),
1130
			'external_url'          => $product->is_type( 'external' ) ? $product->get_product_url( $context ) : '',
1131
			'button_text'           => $product->is_type( 'external' ) ? $product->get_button_text( $context ) : '',
1132
			'tax_status'            => $product->get_tax_status( $context ),
1133
			'tax_class'             => $product->get_tax_class( $context ),
1134
			'manage_stock'          => $product->managing_stock(),
1135
			'stock_quantity'        => $product->get_stock_quantity( $context ),
1136
			'stock_status'          => $product->get_stock_status( $context ),
1137
			'backorders'            => $product->get_backorders( $context ),
1138
			'backorders_allowed'    => $product->backorders_allowed(),
1139
			'backordered'           => $product->is_on_backorder(),
1140
			'sold_individually'     => $product->is_sold_individually(),
1141
			'weight'                => $product->get_weight( $context ),
1142
			'dimensions'            => array(
1143
				'length' => $product->get_length( $context ),
1144
				'width'  => $product->get_width( $context ),
1145
				'height' => $product->get_height( $context ),
1146
			),
1147
			'shipping_required'     => $product->needs_shipping(),
1148
			'shipping_taxable'      => $product->is_shipping_taxable(),
1149
			'shipping_class'        => $product->get_shipping_class(),
1150
			'shipping_class_id'     => $product->get_shipping_class_id( $context ),
1151
			'reviews_allowed'       => $product->get_reviews_allowed( $context ),
1152
			'average_rating'        => 'view' === $context ? wc_format_decimal( $product->get_average_rating(), 2 ) : $product->get_average_rating( $context ),
1153
			'rating_count'          => $product->get_rating_count(),
1154
			'related_ids'           => array_map( 'absint', array_values( wc_get_related_products( $product->get_id() ) ) ),
1155
			'upsell_ids'            => array_map( 'absint', $product->get_upsell_ids( $context ) ),
1156
			'cross_sell_ids'        => array_map( 'absint', $product->get_cross_sell_ids( $context ) ),
1157
			'parent_id'             => $product->get_parent_id( $context ),
1158
			'purchase_note'         => 'view' === $context ? wpautop( do_shortcode( wp_kses_post( $product->get_purchase_note() ) ) ) : $product->get_purchase_note( $context ),
1159
			'categories'            => $this->get_taxonomy_terms( $product ),
1160
			'tags'                  => $this->get_taxonomy_terms( $product, 'tag' ),
1161
			'images'                => $this->get_images( $product ),
1162
			'attributes'            => $this->get_attributes( $product ),
1163
			'default_attributes'    => $this->get_default_attributes( $product ),
1164
			'variations'            => array(),
1165
			'grouped_products'      => array(),
1166
			'menu_order'            => $product->get_menu_order( $context ),
1167
			'meta_data'             => $product->get_meta_data(),
1168
		);
1169
1170
		return $data;
1171
	}
1172
1173
	/**
1174
	 * Prepare links for the request.
1175
	 *
1176
	 * @param WC_Data         $object  Object data.
1177
	 * @param \WP_REST_Request $request Request object.
1178
	 *
1179
	 * @return array                   Links for the given post.
1180
	 */
1181
	protected function prepare_links( $object, $request ) {
1182
		$links = array(
1183
			'self'       => array(
1184
				'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $object->get_id() ) ),  // @codingStandardsIgnoreLine.
1185
			),
1186
			'collection' => array(
1187
				'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ),  // @codingStandardsIgnoreLine.
1188
			),
1189
		);
1190
1191
		if ( $object->get_parent_id() ) {
1192
			$links['up'] = array(
1193
				'href' => rest_url( sprintf( '/%s/products/%d', $this->namespace, $object->get_parent_id() ) ),  // @codingStandardsIgnoreLine.
1194
			);
1195
		}
1196
1197
		return $links;
1198
	}
1199
1200
	/**
1201
	 * Set product images.
1202
	 *
1203
	 * @throws \WC_REST_Exception REST API exceptions.
1204
	 *
1205
	 * @param WC_Product $product Product instance.
1206
	 * @param array      $images  Images data.
1207
	 * @return WC_Product
1208
	 */
1209
	protected function set_product_images( $product, $images ) {
1210
		$images = is_array( $images ) ? array_filter( $images ) : array();
0 ignored issues
show
introduced by
The condition is_array($images) is always true.
Loading history...
1211
1212
		if ( ! empty( $images ) ) {
1213
			$gallery = array();
1214
1215
			foreach ( $images as $index => $image ) {
1216
				$attachment_id = isset( $image['id'] ) ? absint( $image['id'] ) : 0;
1217
1218
				if ( 0 === $attachment_id && isset( $image['src'] ) ) {
1219
					$upload = wc_rest_upload_image_from_url( esc_url_raw( $image['src'] ) );
1220
1221
					if ( is_wp_error( $upload ) ) {
1222
						if ( ! apply_filters( 'woocommerce_rest_suppress_image_upload_error', false, $upload, $product->get_id(), $images ) ) {
1223
							throw new \WC_REST_Exception( 'woocommerce_product_image_upload_error', $upload->get_error_message(), 400 );
0 ignored issues
show
Bug introduced by
The type WC_REST_Exception was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1224
						} else {
1225
							continue;
1226
						}
1227
					}
1228
1229
					$attachment_id = wc_rest_set_uploaded_image_as_attachment( $upload, $product->get_id() );
1230
				}
1231
1232
				if ( ! wp_attachment_is_image( $attachment_id ) ) {
1233
					/* translators: %s: image ID */
1234
					throw new \WC_REST_Exception( 'woocommerce_product_invalid_image_id', sprintf( __( '#%s is an invalid image ID.', 'woocommerce' ), $attachment_id ), 400 );
1235
				}
1236
1237
				$featured_image = $product->get_image_id();
1238
1239
				if ( 0 === $index ) {
1240
					$product->set_image_id( $attachment_id );
1241
				} else {
1242
					$gallery[] = $attachment_id;
1243
				}
1244
1245
				// Set the image alt if present.
1246
				if ( ! empty( $image['alt'] ) ) {
1247
					update_post_meta( $attachment_id, '_wp_attachment_image_alt', wc_clean( $image['alt'] ) );
1248
				}
1249
1250
				// Set the image name if present.
1251
				if ( ! empty( $image['name'] ) ) {
1252
					wp_update_post(
1253
						array(
1254
							'ID'         => $attachment_id,
1255
							'post_title' => $image['name'],
1256
						)
1257
					);
1258
				}
1259
			}
1260
1261
			$product->set_gallery_image_ids( $gallery );
1262
		} else {
1263
			$product->set_image_id( '' );
1264
			$product->set_gallery_image_ids( array() );
1265
		}
1266
1267
		return $product;
1268
	}
1269
1270
	/**
1271
	 * Save product shipping data.
1272
	 *
1273
	 * @param WC_Product $product Product instance.
1274
	 * @param array      $data    Shipping data.
1275
	 *
1276
	 * @return WC_Product
1277
	 */
1278
	protected function save_product_shipping_data( $product, $data ) {
1279
		// Virtual.
1280
		if ( isset( $data['virtual'] ) && true === $data['virtual'] ) {
1281
			$product->set_weight( '' );
1282
			$product->set_height( '' );
1283
			$product->set_length( '' );
1284
			$product->set_width( '' );
1285
		} else {
1286
			if ( isset( $data['weight'] ) ) {
1287
				$product->set_weight( $data['weight'] );
1288
			}
1289
1290
			// Height.
1291
			if ( isset( $data['dimensions']['height'] ) ) {
1292
				$product->set_height( $data['dimensions']['height'] );
1293
			}
1294
1295
			// Width.
1296
			if ( isset( $data['dimensions']['width'] ) ) {
1297
				$product->set_width( $data['dimensions']['width'] );
1298
			}
1299
1300
			// Length.
1301
			if ( isset( $data['dimensions']['length'] ) ) {
1302
				$product->set_length( $data['dimensions']['length'] );
1303
			}
1304
		}
1305
1306
		// Shipping class.
1307
		if ( isset( $data['shipping_class'] ) ) {
1308
			$data_store        = $product->get_data_store();
1309
			$shipping_class_id = $data_store->get_shipping_class_id_by_slug( wc_clean( $data['shipping_class'] ) );
1310
			$product->set_shipping_class_id( $shipping_class_id );
1311
		}
1312
1313
		return $product;
1314
	}
1315
1316
	/**
1317
	 * Save downloadable files.
1318
	 *
1319
	 * @param WC_Product $product    Product instance.
1320
	 * @param array      $downloads  Downloads data.
1321
	 * @param int        $deprecated Deprecated since 3.0.
1322
	 *
1323
	 * @return WC_Product
1324
	 */
1325
	protected function save_downloadable_files( $product, $downloads, $deprecated = 0 ) {
1326
		if ( $deprecated ) {
1327
			wc_deprecated_argument( 'variation_id', '3.0', 'save_downloadable_files() not requires a variation_id anymore.' );
1328
		}
1329
1330
		$files = array();
1331
		foreach ( $downloads as $key => $file ) {
1332
			if ( empty( $file['file'] ) ) {
1333
				continue;
1334
			}
1335
1336
			$download = new \WC_Product_Download();
0 ignored issues
show
Bug introduced by
The type WC_Product_Download was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
1337
			$download->set_id( ! empty( $file['id'] ) ? $file['id'] : wp_generate_uuid4() );
1338
			$download->set_name( $file['name'] ? $file['name'] : wc_get_filename_from_url( $file['file'] ) );
1339
			$download->set_file( apply_filters( 'woocommerce_file_download_path', $file['file'], $product, $key ) );
1340
			$files[] = $download;
1341
		}
1342
		$product->set_downloads( $files );
1343
1344
		return $product;
1345
	}
1346
1347
	/**
1348
	 * Save taxonomy terms.
1349
	 *
1350
	 * @param WC_Product $product  Product instance.
1351
	 * @param array      $terms    Terms data.
1352
	 * @param string     $taxonomy Taxonomy name.
1353
	 *
1354
	 * @return WC_Product
1355
	 */
1356
	protected function save_taxonomy_terms( $product, $terms, $taxonomy = 'cat' ) {
1357
		$term_ids = wp_list_pluck( $terms, 'id' );
1358
1359
		if ( 'cat' === $taxonomy ) {
1360
			$product->set_category_ids( $term_ids );
1361
		} elseif ( 'tag' === $taxonomy ) {
1362
			$product->set_tag_ids( $term_ids );
1363
		}
1364
1365
		return $product;
1366
	}
1367
1368
	/**
1369
	 * Save default attributes.
1370
	 *
1371
	 * @param WC_Product      $product Product instance.
1372
	 * @param \WP_REST_Request $request Request data.
1373
	 *
1374
	 * @since  3.0.0
1375
	 * @return WC_Product
1376
	 */
1377
	protected function save_default_attributes( $product, $request ) {
1378
		if ( isset( $request['default_attributes'] ) && is_array( $request['default_attributes'] ) ) {
1379
1380
			$attributes         = $product->get_attributes();
1381
			$default_attributes = array();
1382
1383
			foreach ( $request['default_attributes'] as $attribute ) {
1384
				$attribute_id   = 0;
1385
				$attribute_name = '';
1386
1387
				// Check ID for global attributes or name for product attributes.
1388
				if ( ! empty( $attribute['id'] ) ) {
1389
					$attribute_id   = absint( $attribute['id'] );
1390
					$attribute_name = wc_attribute_taxonomy_name_by_id( $attribute_id );
1391
				} elseif ( ! empty( $attribute['name'] ) ) {
1392
					$attribute_name = sanitize_title( $attribute['name'] );
1393
				}
1394
1395
				if ( ! $attribute_id && ! $attribute_name ) {
1396
					continue;
1397
				}
1398
1399
				if ( isset( $attributes[ $attribute_name ] ) ) {
1400
					$_attribute = $attributes[ $attribute_name ];
1401
1402
					if ( $_attribute['is_variation'] ) {
1403
						$value = isset( $attribute['option'] ) ? wc_clean( stripslashes( $attribute['option'] ) ) : '';
1404
1405
						if ( ! empty( $_attribute['is_taxonomy'] ) ) {
1406
							// If dealing with a taxonomy, we need to get the slug from the name posted to the API.
1407
							$term = get_term_by( 'name', $value, $attribute_name );
1408
1409
							if ( $term && ! is_wp_error( $term ) ) {
1410
								$value = $term->slug;
1411
							} else {
1412
								$value = sanitize_title( $value );
1413
							}
1414
						}
1415
1416
						if ( $value ) {
1417
							$default_attributes[ $attribute_name ] = $value;
1418
						}
1419
					}
1420
				}
1421
			}
1422
1423
			$product->set_default_attributes( $default_attributes );
1424
		}
1425
1426
		return $product;
1427
	}
1428
1429
	/**
1430
	 * Clear caches here so in sync with any new variations/children.
1431
	 *
1432
	 * @param WC_Data $object Object data.
1433
	 */
1434
	public function clear_transients( $object ) {
1435
		wc_delete_product_transients( $object->get_id() );
1436
		wp_cache_delete( 'product-' . $object->get_id(), 'products' );
1437
	}
1438
1439
	/**
1440
	 * Delete a single item.
1441
	 *
1442
	 * @param \WP_REST_Request $request Full details about the request.
1443
	 *
1444
	 * @return \WP_REST_Response|\WP_Error
1445
	 */
1446
	public function delete_item( $request ) {
1447
		$id     = (int) $request['id'];
1448
		$force  = (bool) $request['force'];
1449
		$object = $this->get_object( (int) $request['id'] );
1450
		$result = false;
1451
1452
		if ( ! $object || 0 === $object->get_id() ) {
0 ignored issues
show
introduced by
$object is of type WooCommerce\RestApi\Controllers\Version4\WC_Data, thus it always evaluated to true.
Loading history...
1453
			return new \WP_Error(
1454
				"woocommerce_rest_{$this->post_type}_invalid_id",
1455
				__( 'Invalid ID.', 'woocommerce' ),
1456
				array(
1457
					'status' => 404,
1458
				)
1459
			);
1460
		}
1461
1462
		if ( 'variation' === $object->get_type() ) {
1463
			return new \WP_Error(
1464
				"woocommerce_rest_invalid_{$this->post_type}_id",
1465
				__( 'To manipulate product variations you should use the /products/&lt;product_id&gt;/variations/&lt;id&gt; endpoint.', 'woocommerce' ),
1466
				array(
1467
					'status' => 404,
1468
				)
1469
			);
1470
		}
1471
1472
		$supports_trash = EMPTY_TRASH_DAYS > 0 && is_callable( array( $object, 'get_status' ) );
0 ignored issues
show
Bug introduced by
The constant WooCommerce\RestApi\Cont...rsion4\EMPTY_TRASH_DAYS was not found. Maybe you did not declare it correctly or list all dependencies?
Loading history...
1473
1474
		/**
1475
		 * Filter whether an object is trashable.
1476
		 *
1477
		 * Return false to disable trash support for the object.
1478
		 *
1479
		 * @param boolean $supports_trash Whether the object type support trashing.
1480
		 * @param WC_Data $object         The object being considered for trashing support.
1481
		 */
1482
		$supports_trash = apply_filters( "woocommerce_rest_{$this->post_type}_object_trashable", $supports_trash, $object );
1483
1484
		if ( ! wc_rest_check_post_permissions( $this->post_type, 'delete', $object->get_id() ) ) {
1485
			return new \WP_Error(
1486
				"woocommerce_rest_user_cannot_delete_{$this->post_type}",
1487
				/* translators: %s: post type */
1488
				sprintf( __( 'Sorry, you are not allowed to delete %s.', 'woocommerce' ), $this->post_type ),
1489
				array(
1490
					'status' => rest_authorization_required_code(),
1491
				)
1492
			);
1493
		}
1494
1495
		$request->set_param( 'context', 'edit' );
1496
1497
		// If we're forcing, then delete permanently.
1498
		if ( $force ) {
1499
			$previous = $this->prepare_object_for_response( $object, $request );
1500
1501
			$object->delete( true );
1502
			$result = 0 === $object->get_id();
1503
1504
			$response = new \WP_REST_Response();
1505
			$response->set_data(
1506
				array(
1507
					'deleted'  => true,
1508
					'previous' => $previous->get_data(),
1509
				)
1510
			);
1511
		} else {
1512
			// If we don't support trashing for this type, error out.
1513
			if ( ! $supports_trash ) {
1514
				return new \WP_Error(
1515
					'woocommerce_rest_trash_not_supported',
1516
					/* translators: %s: post type */
1517
					sprintf( __( 'The %s does not support trashing.', 'woocommerce' ), $this->post_type ),
1518
					array(
1519
						'status' => 501,
1520
					)
1521
				);
1522
			}
1523
1524
			// Otherwise, only trash if we haven't already.
1525
			if ( is_callable( array( $object, 'get_status' ) ) ) {
1526
				if ( 'trash' === $object->get_status() ) {
1527
					return new \WP_Error(
1528
						'woocommerce_rest_already_trashed',
1529
						/* translators: %s: post type */
1530
						sprintf( __( 'The %s has already been deleted.', 'woocommerce' ), $this->post_type ),
1531
						array(
1532
							'status' => 410,
1533
						)
1534
					);
1535
				}
1536
1537
				$object->delete();
1538
				$result = 'trash' === $object->get_status();
1539
			}
1540
1541
			$response = $this->prepare_object_for_response( $object, $request );
1542
		}
1543
1544
		if ( ! $result ) {
1545
			return new \WP_Error(
1546
				'woocommerce_rest_cannot_delete',
1547
				/* translators: %s: post type */
1548
				sprintf( __( 'The %s cannot be deleted.', 'woocommerce' ), $this->post_type ),
1549
				array(
1550
					'status' => 500,
1551
				)
1552
			);
1553
		}
1554
1555
		/**
1556
		 * Fires after a single object is deleted or trashed via the REST API.
1557
		 *
1558
		 * @param WC_Data          $object   The deleted or trashed object.
1559
		 * @param \WP_REST_Response $response The response data.
1560
		 * @param \WP_REST_Request  $request  The request sent to the API.
1561
		 */
1562
		do_action( "woocommerce_rest_delete_{$this->post_type}_object", $object, $response, $request );
1563
1564
		return $response;
1565
	}
1566
1567
	/**
1568
	 * Get the Product's schema, conforming to JSON Schema.
1569
	 *
1570
	 * @return array
1571
	 */
1572
	public function get_item_schema() {
1573
		$weight_unit    = get_option( 'woocommerce_weight_unit' );
1574
		$dimension_unit = get_option( 'woocommerce_dimension_unit' );
1575
		$schema         = array(
1576
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
1577
			'title'      => $this->post_type,
1578
			'type'       => 'object',
1579
			'properties' => array(
1580
				'id'                    => array(
1581
					'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
1582
					'type'        => 'integer',
1583
					'context'     => array( 'view', 'edit', 'embed' ),
1584
					'readonly'    => true,
1585
				),
1586
				'name'                  => array(
1587
					'description' => __( 'Product name.', 'woocommerce' ),
1588
					'type'        => 'string',
1589
					'context'     => array( 'view', 'edit', 'embed' ),
1590
				),
1591
				'slug'                  => array(
1592
					'description' => __( 'Product slug.', 'woocommerce' ),
1593
					'type'        => 'string',
1594
					'context'     => array( 'view', 'edit', 'embed' ),
1595
				),
1596
				'permalink'             => array(
1597
					'description' => __( 'Product URL.', 'woocommerce' ),
1598
					'type'        => 'string',
1599
					'format'      => 'uri',
1600
					'context'     => array( 'view', 'edit', 'embed' ),
1601
					'readonly'    => true,
1602
				),
1603
				'date_created'          => array(
1604
					'description' => __( "The date the product was created, in the site's timezone.", 'woocommerce' ),
1605
					'type'        => 'date-time',
1606
					'context'     => array( 'view', 'edit' ),
1607
				),
1608
				'date_created_gmt'      => array(
1609
					'description' => __( 'The date the product was created, as GMT.', 'woocommerce' ),
1610
					'type'        => 'date-time',
1611
					'context'     => array( 'view', 'edit' ),
1612
				),
1613
				'date_modified'         => array(
1614
					'description' => __( "The date the product was last modified, in the site's timezone.", 'woocommerce' ),
1615
					'type'        => 'date-time',
1616
					'context'     => array( 'view', 'edit' ),
1617
					'readonly'    => true,
1618
				),
1619
				'date_modified_gmt'     => array(
1620
					'description' => __( 'The date the product was last modified, as GMT.', 'woocommerce' ),
1621
					'type'        => 'date-time',
1622
					'context'     => array( 'view', 'edit' ),
1623
					'readonly'    => true,
1624
				),
1625
				'type'                  => array(
1626
					'description' => __( 'Product type.', 'woocommerce' ),
1627
					'type'        => 'string',
1628
					'default'     => 'simple',
1629
					'enum'        => array_keys( wc_get_product_types() ),
1630
					'context'     => array( 'view', 'edit' ),
1631
				),
1632
				'status'                => array(
1633
					'description' => __( 'Product status (post status).', 'woocommerce' ),
1634
					'type'        => 'string',
1635
					'default'     => 'publish',
1636
					'enum'        => array_merge( array_keys( get_post_statuses() ), array( 'future' ) ),
1637
					'context'     => array( 'view', 'edit' ),
1638
				),
1639
				'featured'              => array(
1640
					'description' => __( 'Featured product.', 'woocommerce' ),
1641
					'type'        => 'boolean',
1642
					'default'     => false,
1643
					'context'     => array( 'view', 'edit' ),
1644
				),
1645
				'catalog_visibility'    => array(
1646
					'description' => __( 'Catalog visibility.', 'woocommerce' ),
1647
					'type'        => 'string',
1648
					'default'     => 'visible',
1649
					'enum'        => array( 'visible', 'catalog', 'search', 'hidden' ),
1650
					'context'     => array( 'view', 'edit' ),
1651
				),
1652
				'description'           => array(
1653
					'description' => __( 'Product description.', 'woocommerce' ),
1654
					'type'        => 'string',
1655
					'context'     => array( 'view', 'edit', 'embed' ),
1656
				),
1657
				'short_description'     => array(
1658
					'description' => __( 'Product short description.', 'woocommerce' ),
1659
					'type'        => 'string',
1660
					'context'     => array( 'view', 'edit', 'embed' ),
1661
				),
1662
				'sku'                   => array(
1663
					'description' => __( 'Unique identifier.', 'woocommerce' ),
1664
					'type'        => 'string',
1665
					'context'     => array( 'view', 'edit' ),
1666
				),
1667
				'price'                 => array(
1668
					'description' => __( 'Current product price.', 'woocommerce' ),
1669
					'type'        => 'string',
1670
					'context'     => array( 'view', 'edit' ),
1671
					'readonly'    => true,
1672
				),
1673
				'regular_price'         => array(
1674
					'description' => __( 'Product regular price.', 'woocommerce' ),
1675
					'type'        => 'string',
1676
					'context'     => array( 'view', 'edit' ),
1677
				),
1678
				'sale_price'            => array(
1679
					'description' => __( 'Product sale price.', 'woocommerce' ),
1680
					'type'        => 'string',
1681
					'context'     => array( 'view', 'edit' ),
1682
				),
1683
				'date_on_sale_from'     => array(
1684
					'description' => __( "Start date of sale price, in the site's timezone.", 'woocommerce' ),
1685
					'type'        => 'date-time',
1686
					'context'     => array( 'view', 'edit' ),
1687
				),
1688
				'date_on_sale_from_gmt' => array(
1689
					'description' => __( 'Start date of sale price, as GMT.', 'woocommerce' ),
1690
					'type'        => 'date-time',
1691
					'context'     => array( 'view', 'edit' ),
1692
				),
1693
				'date_on_sale_to'       => array(
1694
					'description' => __( "End date of sale price, in the site's timezone.", 'woocommerce' ),
1695
					'type'        => 'date-time',
1696
					'context'     => array( 'view', 'edit' ),
1697
				),
1698
				'date_on_sale_to_gmt'   => array(
1699
					'description' => __( "End date of sale price, in the site's timezone.", 'woocommerce' ),
1700
					'type'        => 'date-time',
1701
					'context'     => array( 'view', 'edit' ),
1702
				),
1703
				'price_html'            => array(
1704
					'description' => __( 'Price formatted in HTML.', 'woocommerce' ),
1705
					'type'        => 'string',
1706
					'context'     => array( 'view', 'edit' ),
1707
					'readonly'    => true,
1708
				),
1709
				'on_sale'               => array(
1710
					'description' => __( 'Shows if the product is on sale.', 'woocommerce' ),
1711
					'type'        => 'boolean',
1712
					'context'     => array( 'view', 'edit' ),
1713
					'readonly'    => true,
1714
				),
1715
				'purchasable'           => array(
1716
					'description' => __( 'Shows if the product can be bought.', 'woocommerce' ),
1717
					'type'        => 'boolean',
1718
					'context'     => array( 'view', 'edit' ),
1719
					'readonly'    => true,
1720
				),
1721
				'total_sales'           => array(
1722
					'description' => __( 'Amount of sales.', 'woocommerce' ),
1723
					'type'        => 'integer',
1724
					'context'     => array( 'view', 'edit' ),
1725
					'readonly'    => true,
1726
				),
1727
				'virtual'               => array(
1728
					'description' => __( 'If the product is virtual.', 'woocommerce' ),
1729
					'type'        => 'boolean',
1730
					'default'     => false,
1731
					'context'     => array( 'view', 'edit' ),
1732
				),
1733
				'downloadable'          => array(
1734
					'description' => __( 'If the product is downloadable.', 'woocommerce' ),
1735
					'type'        => 'boolean',
1736
					'default'     => false,
1737
					'context'     => array( 'view', 'edit' ),
1738
				),
1739
				'downloads'             => array(
1740
					'description' => __( 'List of downloadable files.', 'woocommerce' ),
1741
					'type'        => 'array',
1742
					'context'     => array( 'view', 'edit' ),
1743
					'items'       => array(
1744
						'type'       => 'object',
1745
						'properties' => array(
1746
							'id'   => array(
1747
								'description' => __( 'File ID.', 'woocommerce' ),
1748
								'type'        => 'string',
1749
								'context'     => array( 'view', 'edit' ),
1750
							),
1751
							'name' => array(
1752
								'description' => __( 'File name.', 'woocommerce' ),
1753
								'type'        => 'string',
1754
								'context'     => array( 'view', 'edit' ),
1755
							),
1756
							'file' => array(
1757
								'description' => __( 'File URL.', 'woocommerce' ),
1758
								'type'        => 'string',
1759
								'context'     => array( 'view', 'edit' ),
1760
							),
1761
						),
1762
					),
1763
				),
1764
				'download_limit'        => array(
1765
					'description' => __( 'Number of times downloadable files can be downloaded after purchase.', 'woocommerce' ),
1766
					'type'        => 'integer',
1767
					'default'     => -1,
1768
					'context'     => array( 'view', 'edit' ),
1769
				),
1770
				'download_expiry'       => array(
1771
					'description' => __( 'Number of days until access to downloadable files expires.', 'woocommerce' ),
1772
					'type'        => 'integer',
1773
					'default'     => -1,
1774
					'context'     => array( 'view', 'edit' ),
1775
				),
1776
				'external_url'          => array(
1777
					'description' => __( 'Product external URL. Only for external products.', 'woocommerce' ),
1778
					'type'        => 'string',
1779
					'format'      => 'uri',
1780
					'context'     => array( 'view', 'edit' ),
1781
				),
1782
				'button_text'           => array(
1783
					'description' => __( 'Product external button text. Only for external products.', 'woocommerce' ),
1784
					'type'        => 'string',
1785
					'context'     => array( 'view', 'edit' ),
1786
				),
1787
				'tax_status'            => array(
1788
					'description' => __( 'Tax status.', 'woocommerce' ),
1789
					'type'        => 'string',
1790
					'default'     => 'taxable',
1791
					'enum'        => array( 'taxable', 'shipping', 'none' ),
1792
					'context'     => array( 'view', 'edit' ),
1793
				),
1794
				'tax_class'             => array(
1795
					'description' => __( 'Tax class.', 'woocommerce' ),
1796
					'type'        => 'string',
1797
					'context'     => array( 'view', 'edit' ),
1798
				),
1799
				'manage_stock'          => array(
1800
					'description' => __( 'Stock management at product level.', 'woocommerce' ),
1801
					'type'        => 'boolean',
1802
					'default'     => false,
1803
					'context'     => array( 'view', 'edit' ),
1804
				),
1805
				'stock_quantity'        => array(
1806
					'description' => __( 'Stock quantity.', 'woocommerce' ),
1807
					'type'        => 'integer',
1808
					'context'     => array( 'view', 'edit' ),
1809
				),
1810
				'stock_status'          => array(
1811
					'description' => __( 'Controls the stock status of the product.', 'woocommerce' ),
1812
					'type'        => 'string',
1813
					'default'     => 'instock',
1814
					'enum'        => array_keys( wc_get_product_stock_status_options() ),
1815
					'context'     => array( 'view', 'edit' ),
1816
				),
1817
				'backorders'            => array(
1818
					'description' => __( 'If managing stock, this controls if backorders are allowed.', 'woocommerce' ),
1819
					'type'        => 'string',
1820
					'default'     => 'no',
1821
					'enum'        => array( 'no', 'notify', 'yes' ),
1822
					'context'     => array( 'view', 'edit' ),
1823
				),
1824
				'backorders_allowed'    => array(
1825
					'description' => __( 'Shows if backorders are allowed.', 'woocommerce' ),
1826
					'type'        => 'boolean',
1827
					'context'     => array( 'view', 'edit' ),
1828
					'readonly'    => true,
1829
				),
1830
				'backordered'           => array(
1831
					'description' => __( 'Shows if the product is on backordered.', 'woocommerce' ),
1832
					'type'        => 'boolean',
1833
					'context'     => array( 'view', 'edit' ),
1834
					'readonly'    => true,
1835
				),
1836
				'sold_individually'     => array(
1837
					'description' => __( 'Allow one item to be bought in a single order.', 'woocommerce' ),
1838
					'type'        => 'boolean',
1839
					'default'     => false,
1840
					'context'     => array( 'view', 'edit' ),
1841
				),
1842
				'weight'                => array(
1843
					/* translators: %s: weight unit */
1844
					'description' => sprintf( __( 'Product weight (%s).', 'woocommerce' ), $weight_unit ),
1845
					'type'        => 'string',
1846
					'context'     => array( 'view', 'edit' ),
1847
				),
1848
				'dimensions'            => array(
1849
					'description' => __( 'Product dimensions.', 'woocommerce' ),
1850
					'type'        => 'object',
1851
					'context'     => array( 'view', 'edit' ),
1852
					'properties'  => array(
1853
						'length' => array(
1854
							/* translators: %s: dimension unit */
1855
							'description' => sprintf( __( 'Product length (%s).', 'woocommerce' ), $dimension_unit ),
1856
							'type'        => 'string',
1857
							'context'     => array( 'view', 'edit' ),
1858
						),
1859
						'width'  => array(
1860
							/* translators: %s: dimension unit */
1861
							'description' => sprintf( __( 'Product width (%s).', 'woocommerce' ), $dimension_unit ),
1862
							'type'        => 'string',
1863
							'context'     => array( 'view', 'edit' ),
1864
						),
1865
						'height' => array(
1866
							/* translators: %s: dimension unit */
1867
							'description' => sprintf( __( 'Product height (%s).', 'woocommerce' ), $dimension_unit ),
1868
							'type'        => 'string',
1869
							'context'     => array( 'view', 'edit' ),
1870
						),
1871
					),
1872
				),
1873
				'shipping_required'     => array(
1874
					'description' => __( 'Shows if the product need to be shipped.', 'woocommerce' ),
1875
					'type'        => 'boolean',
1876
					'context'     => array( 'view', 'edit' ),
1877
					'readonly'    => true,
1878
				),
1879
				'shipping_taxable'      => array(
1880
					'description' => __( 'Shows whether or not the product shipping is taxable.', 'woocommerce' ),
1881
					'type'        => 'boolean',
1882
					'context'     => array( 'view', 'edit' ),
1883
					'readonly'    => true,
1884
				),
1885
				'shipping_class'        => array(
1886
					'description' => __( 'Shipping class slug.', 'woocommerce' ),
1887
					'type'        => 'string',
1888
					'context'     => array( 'view', 'edit' ),
1889
				),
1890
				'shipping_class_id'     => array(
1891
					'description' => __( 'Shipping class ID.', 'woocommerce' ),
1892
					'type'        => 'string',
1893
					'context'     => array( 'view', 'edit' ),
1894
					'readonly'    => true,
1895
				),
1896
				'reviews_allowed'       => array(
1897
					'description' => __( 'Allow reviews.', 'woocommerce' ),
1898
					'type'        => 'boolean',
1899
					'default'     => true,
1900
					'context'     => array( 'view', 'edit' ),
1901
				),
1902
				'average_rating'        => array(
1903
					'description' => __( 'Reviews average rating.', 'woocommerce' ),
1904
					'type'        => 'string',
1905
					'context'     => array( 'view', 'edit' ),
1906
					'readonly'    => true,
1907
				),
1908
				'rating_count'          => array(
1909
					'description' => __( 'Amount of reviews that the product have.', 'woocommerce' ),
1910
					'type'        => 'integer',
1911
					'context'     => array( 'view', 'edit' ),
1912
					'readonly'    => true,
1913
				),
1914
				'related_ids'           => array(
1915
					'description' => __( 'List of related products IDs.', 'woocommerce' ),
1916
					'type'        => 'array',
1917
					'items'       => array(
1918
						'type' => 'integer',
1919
					),
1920
					'context'     => array( 'view', 'edit' ),
1921
					'readonly'    => true,
1922
				),
1923
				'upsell_ids'            => array(
1924
					'description' => __( 'List of up-sell products IDs.', 'woocommerce' ),
1925
					'type'        => 'array',
1926
					'items'       => array(
1927
						'type' => 'integer',
1928
					),
1929
					'context'     => array( 'view', 'edit' ),
1930
				),
1931
				'cross_sell_ids'        => array(
1932
					'description' => __( 'List of cross-sell products IDs.', 'woocommerce' ),
1933
					'type'        => 'array',
1934
					'items'       => array(
1935
						'type' => 'integer',
1936
					),
1937
					'context'     => array( 'view', 'edit' ),
1938
				),
1939
				'parent_id'             => array(
1940
					'description' => __( 'Product parent ID.', 'woocommerce' ),
1941
					'type'        => 'integer',
1942
					'context'     => array( 'view', 'edit' ),
1943
				),
1944
				'purchase_note'         => array(
1945
					'description' => __( 'Optional note to send the customer after purchase.', 'woocommerce' ),
1946
					'type'        => 'string',
1947
					'context'     => array( 'view', 'edit' ),
1948
				),
1949
				'categories'            => array(
1950
					'description' => __( 'List of categories.', 'woocommerce' ),
1951
					'type'        => 'array',
1952
					'context'     => array( 'view', 'edit' ),
1953
					'items'       => array(
1954
						'type'       => 'object',
1955
						'properties' => array(
1956
							'id'   => array(
1957
								'description' => __( 'Category ID.', 'woocommerce' ),
1958
								'type'        => 'integer',
1959
								'context'     => array( 'view', 'edit' ),
1960
							),
1961
							'name' => array(
1962
								'description' => __( 'Category name.', 'woocommerce' ),
1963
								'type'        => 'string',
1964
								'context'     => array( 'view', 'edit' ),
1965
								'readonly'    => true,
1966
							),
1967
							'slug' => array(
1968
								'description' => __( 'Category slug.', 'woocommerce' ),
1969
								'type'        => 'string',
1970
								'context'     => array( 'view', 'edit' ),
1971
								'readonly'    => true,
1972
							),
1973
						),
1974
					),
1975
				),
1976
				'tags'                  => array(
1977
					'description' => __( 'List of tags.', 'woocommerce' ),
1978
					'type'        => 'array',
1979
					'context'     => array( 'view', 'edit' ),
1980
					'items'       => array(
1981
						'type'       => 'object',
1982
						'properties' => array(
1983
							'id'   => array(
1984
								'description' => __( 'Tag ID.', 'woocommerce' ),
1985
								'type'        => 'integer',
1986
								'context'     => array( 'view', 'edit' ),
1987
							),
1988
							'name' => array(
1989
								'description' => __( 'Tag name.', 'woocommerce' ),
1990
								'type'        => 'string',
1991
								'context'     => array( 'view', 'edit' ),
1992
								'readonly'    => true,
1993
							),
1994
							'slug' => array(
1995
								'description' => __( 'Tag slug.', 'woocommerce' ),
1996
								'type'        => 'string',
1997
								'context'     => array( 'view', 'edit' ),
1998
								'readonly'    => true,
1999
							),
2000
						),
2001
					),
2002
				),
2003
				'images'                => array(
2004
					'description' => __( 'List of images.', 'woocommerce' ),
2005
					'type'        => 'object',
2006
					'context'     => array( 'view', 'edit', 'embed' ),
2007
					'items'       => array(
2008
						'type'       => 'object',
2009
						'properties' => array(
2010
							'id'                => array(
2011
								'description' => __( 'Image ID.', 'woocommerce' ),
2012
								'type'        => 'integer',
2013
								'context'     => array( 'view', 'edit' ),
2014
							),
2015
							'date_created'      => array(
2016
								'description' => __( "The date the image was created, in the site's timezone.", 'woocommerce' ),
2017
								'type'        => 'date-time',
2018
								'context'     => array( 'view', 'edit' ),
2019
								'readonly'    => true,
2020
							),
2021
							'date_created_gmt'  => array(
2022
								'description' => __( 'The date the image was created, as GMT.', 'woocommerce' ),
2023
								'type'        => 'date-time',
2024
								'context'     => array( 'view', 'edit' ),
2025
								'readonly'    => true,
2026
							),
2027
							'date_modified'     => array(
2028
								'description' => __( "The date the image was last modified, in the site's timezone.", 'woocommerce' ),
2029
								'type'        => 'date-time',
2030
								'context'     => array( 'view', 'edit' ),
2031
								'readonly'    => true,
2032
							),
2033
							'date_modified_gmt' => array(
2034
								'description' => __( 'The date the image was last modified, as GMT.', 'woocommerce' ),
2035
								'type'        => 'date-time',
2036
								'context'     => array( 'view', 'edit' ),
2037
								'readonly'    => true,
2038
							),
2039
							'src'               => array(
2040
								'description' => __( 'Image URL.', 'woocommerce' ),
2041
								'type'        => 'string',
2042
								'format'      => 'uri',
2043
								'context'     => array( 'view', 'edit' ),
2044
							),
2045
							'name'              => array(
2046
								'description' => __( 'Image name.', 'woocommerce' ),
2047
								'type'        => 'string',
2048
								'context'     => array( 'view', 'edit' ),
2049
							),
2050
							'alt'               => array(
2051
								'description' => __( 'Image alternative text.', 'woocommerce' ),
2052
								'type'        => 'string',
2053
								'context'     => array( 'view', 'edit' ),
2054
							),
2055
						),
2056
					),
2057
				),
2058
				'attributes'            => array(
2059
					'description' => __( 'List of attributes.', 'woocommerce' ),
2060
					'type'        => 'array',
2061
					'context'     => array( 'view', 'edit' ),
2062
					'items'       => array(
2063
						'type'       => 'object',
2064
						'properties' => array(
2065
							'id'        => array(
2066
								'description' => __( 'Attribute ID.', 'woocommerce' ),
2067
								'type'        => 'integer',
2068
								'context'     => array( 'view', 'edit' ),
2069
							),
2070
							'name'      => array(
2071
								'description' => __( 'Attribute name.', 'woocommerce' ),
2072
								'type'        => 'string',
2073
								'context'     => array( 'view', 'edit' ),
2074
							),
2075
							'position'  => array(
2076
								'description' => __( 'Attribute position.', 'woocommerce' ),
2077
								'type'        => 'integer',
2078
								'context'     => array( 'view', 'edit' ),
2079
							),
2080
							'visible'   => array(
2081
								'description' => __( "Define if the attribute is visible on the \"Additional information\" tab in the product's page.", 'woocommerce' ),
2082
								'type'        => 'boolean',
2083
								'default'     => false,
2084
								'context'     => array( 'view', 'edit' ),
2085
							),
2086
							'variation' => array(
2087
								'description' => __( 'Define if the attribute can be used as variation.', 'woocommerce' ),
2088
								'type'        => 'boolean',
2089
								'default'     => false,
2090
								'context'     => array( 'view', 'edit' ),
2091
							),
2092
							'options'   => array(
2093
								'description' => __( 'List of available term names of the attribute.', 'woocommerce' ),
2094
								'type'        => 'array',
2095
								'items'       => array(
2096
									'type' => 'string',
2097
								),
2098
								'context'     => array( 'view', 'edit' ),
2099
							),
2100
						),
2101
					),
2102
				),
2103
				'default_attributes'    => array(
2104
					'description' => __( 'Defaults variation attributes.', 'woocommerce' ),
2105
					'type'        => 'array',
2106
					'context'     => array( 'view', 'edit' ),
2107
					'items'       => array(
2108
						'type'       => 'object',
2109
						'properties' => array(
2110
							'id'     => array(
2111
								'description' => __( 'Attribute ID.', 'woocommerce' ),
2112
								'type'        => 'integer',
2113
								'context'     => array( 'view', 'edit' ),
2114
							),
2115
							'name'   => array(
2116
								'description' => __( 'Attribute name.', 'woocommerce' ),
2117
								'type'        => 'string',
2118
								'context'     => array( 'view', 'edit' ),
2119
							),
2120
							'option' => array(
2121
								'description' => __( 'Selected attribute term name.', 'woocommerce' ),
2122
								'type'        => 'string',
2123
								'context'     => array( 'view', 'edit' ),
2124
							),
2125
						),
2126
					),
2127
				),
2128
				'variations'            => array(
2129
					'description' => __( 'List of variations IDs.', 'woocommerce' ),
2130
					'type'        => 'array',
2131
					'context'     => array( 'view', 'edit' ),
2132
					'items'       => array(
2133
						'type' => 'integer',
2134
					),
2135
					'readonly'    => true,
2136
				),
2137
				'grouped_products'      => array(
2138
					'description' => __( 'List of grouped products ID.', 'woocommerce' ),
2139
					'type'        => 'array',
2140
					'items'       => array(
2141
						'type' => 'integer',
2142
					),
2143
					'context'     => array( 'view', 'edit' ),
2144
					'readonly'    => true,
2145
				),
2146
				'menu_order'            => array(
2147
					'description' => __( 'Menu order, used to custom sort products.', 'woocommerce' ),
2148
					'type'        => 'integer',
2149
					'context'     => array( 'view', 'edit' ),
2150
				),
2151
				'meta_data'             => array(
2152
					'description' => __( 'Meta data.', 'woocommerce' ),
2153
					'type'        => 'array',
2154
					'context'     => array( 'view', 'edit' ),
2155
					'items'       => array(
2156
						'type'       => 'object',
2157
						'properties' => array(
2158
							'id'    => array(
2159
								'description' => __( 'Meta ID.', 'woocommerce' ),
2160
								'type'        => 'integer',
2161
								'context'     => array( 'view', 'edit' ),
2162
								'readonly'    => true,
2163
							),
2164
							'key'   => array(
2165
								'description' => __( 'Meta key.', 'woocommerce' ),
2166
								'type'        => 'string',
2167
								'context'     => array( 'view', 'edit' ),
2168
							),
2169
							'value' => array(
2170
								'description' => __( 'Meta value.', 'woocommerce' ),
2171
								'type'        => 'mixed',
2172
								'context'     => array( 'view', 'edit' ),
2173
							),
2174
						),
2175
					),
2176
				),
2177
			),
2178
		);
2179
		return $this->add_additional_fields_schema( $schema );
2180
	}
2181
2182
	/**
2183
	 * Get the query params for collections of attachments.
2184
	 *
2185
	 * @return array
2186
	 */
2187
	public function get_collection_params() {
2188
		$params = parent::get_collection_params();
2189
2190
		$params['slug']           = array(
2191
			'description'       => __( 'Limit result set to products with a specific slug.', 'woocommerce' ),
2192
			'type'              => 'string',
2193
			'validate_callback' => 'rest_validate_request_arg',
2194
		);
2195
		$params['status']         = array(
2196
			'default'           => 'any',
2197
			'description'       => __( 'Limit result set to products assigned a specific status.', 'woocommerce' ),
2198
			'type'              => 'string',
2199
			'enum'              => array_merge( array( 'any', 'future' ), array_keys( get_post_statuses() ) ),
2200
			'sanitize_callback' => 'sanitize_key',
2201
			'validate_callback' => 'rest_validate_request_arg',
2202
		);
2203
		$params['type']           = array(
2204
			'description'       => __( 'Limit result set to products assigned a specific type.', 'woocommerce' ),
2205
			'type'              => 'string',
2206
			'enum'              => array_keys( wc_get_product_types() ),
2207
			'sanitize_callback' => 'sanitize_key',
2208
			'validate_callback' => 'rest_validate_request_arg',
2209
		);
2210
		$params['sku']            = array(
2211
			'description'       => __( 'Limit result set to products with specific SKU(s). Use commas to separate.', 'woocommerce' ),
2212
			'type'              => 'string',
2213
			'sanitize_callback' => 'sanitize_text_field',
2214
			'validate_callback' => 'rest_validate_request_arg',
2215
		);
2216
		$params['featured']       = array(
2217
			'description'       => __( 'Limit result set to featured products.', 'woocommerce' ),
2218
			'type'              => 'boolean',
2219
			'sanitize_callback' => 'wc_string_to_bool',
2220
			'validate_callback' => 'rest_validate_request_arg',
2221
		);
2222
		$params['category']       = array(
2223
			'description'       => __( 'Limit result set to products assigned a specific category ID.', 'woocommerce' ),
2224
			'type'              => 'string',
2225
			'sanitize_callback' => 'wp_parse_id_list',
2226
			'validate_callback' => 'rest_validate_request_arg',
2227
		);
2228
		$params['tag']            = array(
2229
			'description'       => __( 'Limit result set to products assigned a specific tag ID.', 'woocommerce' ),
2230
			'type'              => 'string',
2231
			'sanitize_callback' => 'wp_parse_id_list',
2232
			'validate_callback' => 'rest_validate_request_arg',
2233
		);
2234
		$params['shipping_class'] = array(
2235
			'description'       => __( 'Limit result set to products assigned a specific shipping class ID.', 'woocommerce' ),
2236
			'type'              => 'string',
2237
			'sanitize_callback' => 'wp_parse_id_list',
2238
			'validate_callback' => 'rest_validate_request_arg',
2239
		);
2240
		$params['attribute']      = array(
2241
			'description'       => __( 'Limit result set to products with a specific attribute. Use the taxonomy name/attribute slug.', 'woocommerce' ),
2242
			'type'              => 'string',
2243
			'sanitize_callback' => 'sanitize_text_field',
2244
			'validate_callback' => 'rest_validate_request_arg',
2245
		);
2246
		$params['attribute_term'] = array(
2247
			'description'       => __( 'Limit result set to products with a specific attribute term ID (required an assigned attribute).', 'woocommerce' ),
2248
			'type'              => 'string',
2249
			'sanitize_callback' => 'wp_parse_id_list',
2250
			'validate_callback' => 'rest_validate_request_arg',
2251
		);
2252
2253
		if ( wc_tax_enabled() ) {
2254
			$params['tax_class'] = array(
2255
				'description'       => __( 'Limit result set to products with a specific tax class.', 'woocommerce' ),
2256
				'type'              => 'string',
2257
				'enum'              => array_merge( array( 'standard' ), \WC_Tax::get_tax_class_slugs() ),
0 ignored issues
show
Bug introduced by
The type WC_Tax was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
2258
				'sanitize_callback' => 'sanitize_text_field',
2259
				'validate_callback' => 'rest_validate_request_arg',
2260
			);
2261
		}
2262
		$params['on_sale']   = array(
2263
			'description'       => __( 'Limit result set to products on sale.', 'woocommerce' ),
2264
			'type'              => 'boolean',
2265
			'sanitize_callback' => 'wc_string_to_bool',
2266
			'validate_callback' => 'rest_validate_request_arg',
2267
		);
2268
		$params['min_price'] = array(
2269
			'description'       => __( 'Limit result set to products based on a minimum price.', 'woocommerce' ),
2270
			'type'              => 'string',
2271
			'sanitize_callback' => 'sanitize_text_field',
2272
			'validate_callback' => 'rest_validate_request_arg',
2273
		);
2274
		$params['max_price'] = array(
2275
			'description'       => __( 'Limit result set to products based on a maximum price.', 'woocommerce' ),
2276
			'type'              => 'string',
2277
			'sanitize_callback' => 'sanitize_text_field',
2278
			'validate_callback' => 'rest_validate_request_arg',
2279
		);
2280
		$params['stock_status'] = array(
2281
			'description'       => __( 'Limit result set to products with specified stock status.', 'woocommerce' ),
2282
			'type'              => 'string',
2283
			'enum'              => array_keys( wc_get_product_stock_status_options() ),
2284
			'sanitize_callback' => 'sanitize_text_field',
2285
			'validate_callback' => 'rest_validate_request_arg',
2286
		);
2287
		$params['low_in_stock'] = array(
2288
			'description'       => __( 'Limit result set to products that are low or out of stock.', 'woocommerce' ),
2289
			'type'              => 'boolean',
2290
			'default'           => false,
2291
			'sanitize_callback' => 'wc_string_to_bool',
2292
		);
2293
		$params['search']       = array(
2294
			'description'       => __( 'Search by similar product name or sku.', 'woocommerce' ),
2295
			'type'              => 'string',
2296
			'validate_callback' => 'rest_validate_request_arg',
2297
		);
2298
		$params['orderby']['enum'] = array_merge( $params['orderby']['enum'], array( 'price', 'popularity', 'rating' ) );
2299
2300
		return $params;
2301
	}
2302
}
2303