WC_REST_Products_Controller

REST API Products controller class.

Defined (1)

The class is defined in the following location(s).

/includes/api/class-wc-rest-products-controller.php  
  1. class WC_REST_Products_Controller extends WC_REST_Legacy_Products_Controller { 
  2.  
  3. /** 
  4. * Endpoint namespace. 
  5. * @var string 
  6. */ 
  7. protected $namespace = 'wc/v2'; 
  8.  
  9. /** 
  10. * Route base. 
  11. * @var string 
  12. */ 
  13. protected $rest_base = 'products'; 
  14.  
  15. /** 
  16. * Post type. 
  17. * @var string 
  18. */ 
  19. protected $post_type = 'product'; 
  20.  
  21. /** 
  22. * If object is hierarchical. 
  23. * @var bool 
  24. */ 
  25. protected $hierarchical = true; 
  26.  
  27. /** 
  28. * Initialize product actions. 
  29. */ 
  30. public function __construct() { 
  31. add_action( "woocommerce_rest_insert_{$this->post_type}_object", array( $this, 'clear_transients' ) ); 
  32.  
  33. /** 
  34. * Register the routes for products. 
  35. */ 
  36. public function register_routes() { 
  37. register_rest_route( $this->namespace, '/' . $this->rest_base, array( 
  38. array( 
  39. 'methods' => WP_REST_Server::READABLE,  
  40. 'callback' => array( $this, 'get_items' ),  
  41. 'permission_callback' => array( $this, 'get_items_permissions_check' ),  
  42. 'args' => $this->get_collection_params(),  
  43. ),  
  44. array( 
  45. 'methods' => WP_REST_Server::CREATABLE,  
  46. 'callback' => array( $this, 'create_item' ),  
  47. 'permission_callback' => array( $this, 'create_item_permissions_check' ),  
  48. 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),  
  49. ),  
  50. 'schema' => array( $this, 'get_public_item_schema' ),  
  51. ) ); 
  52.  
  53. register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array( 
  54. 'args' => array( 
  55. 'id' => array( 
  56. 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),  
  57. 'type' => 'integer',  
  58. ),  
  59. ),  
  60. array( 
  61. 'methods' => WP_REST_Server::READABLE,  
  62. 'callback' => array( $this, 'get_item' ),  
  63. 'permission_callback' => array( $this, 'get_item_permissions_check' ),  
  64. 'args' => array( 
  65. 'context' => $this->get_context_param( array( 'default' => 'view' ) ),  
  66. ),  
  67. ),  
  68. array( 
  69. 'methods' => WP_REST_Server::EDITABLE,  
  70. 'callback' => array( $this, 'update_item' ),  
  71. 'permission_callback' => array( $this, 'update_item_permissions_check' ),  
  72. 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),  
  73. ),  
  74. array( 
  75. 'methods' => WP_REST_Server::DELETABLE,  
  76. 'callback' => array( $this, 'delete_item' ),  
  77. 'permission_callback' => array( $this, 'delete_item_permissions_check' ),  
  78. 'args' => array( 
  79. 'force' => array( 
  80. 'default' => false,  
  81. 'description' => __( 'Whether to bypass trash and force deletion.', 'woocommerce' ),  
  82. 'type' => 'boolean',  
  83. ),  
  84. ),  
  85. ),  
  86. 'schema' => array( $this, 'get_public_item_schema' ),  
  87. ) ); 
  88.  
  89. register_rest_route( $this->namespace, '/' . $this->rest_base . '/batch', array( 
  90. array( 
  91. 'methods' => WP_REST_Server::EDITABLE,  
  92. 'callback' => array( $this, 'batch_items' ),  
  93. 'permission_callback' => array( $this, 'batch_items_permissions_check' ),  
  94. 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),  
  95. ),  
  96. 'schema' => array( $this, 'get_public_batch_schema' ),  
  97. ) ); 
  98.  
  99. /** 
  100. * Get object. 
  101. * @since 3.0.0 
  102. * @param int $id Object ID. 
  103. * @return WC_Data 
  104. */ 
  105. protected function get_object( $id ) { 
  106. return wc_get_product( $id ); 
  107.  
  108. /** 
  109. * Prepare a single product output for response. 
  110. * @since 3.0.0 
  111. * @param WC_Data $object Object data. 
  112. * @param WP_REST_Request $request Request object. 
  113. * @return WP_REST_Response 
  114. */ 
  115. public function prepare_object_for_response( $object, $request ) { 
  116. $data = $this->get_product_data( $object ); 
  117.  
  118. // Add variations to variable products. 
  119. if ( $object->is_type( 'variable' ) && $object->has_child() ) { 
  120. $data['variations'] = $object->get_children(); 
  121.  
  122. // Add grouped products data. 
  123. if ( $object->is_type( 'grouped' ) && $object->has_child() ) { 
  124. $data['grouped_products'] = $object->get_children(); 
  125.  
  126. $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; 
  127. $data = $this->add_additional_fields_to_object( $data, $request ); 
  128. $data = $this->filter_response_by_context( $data, $context ); 
  129. $response = rest_ensure_response( $data ); 
  130. $response->add_links( $this->prepare_links( $object, $request ) ); 
  131.  
  132. /** 
  133. * Filter the data for a response. 
  134. * The dynamic portion of the hook name, $this->post_type,  
  135. * refers to object type being prepared for the response. 
  136. * @param WP_REST_Response $response The response object. 
  137. * @param WC_Data $object Object data. 
  138. * @param WP_REST_Request $request Request object. 
  139. */ 
  140. return apply_filters( "woocommerce_rest_prepare_{$this->post_type}_object", $response, $object, $request ); 
  141.  
  142. /** 
  143. * Prepare objects query. 
  144. * @since 3.0.0 
  145. * @param WP_REST_Request $request Full details about the request. 
  146. * @return array 
  147. */ 
  148. protected function prepare_objects_query( $request ) { 
  149. $args = parent::prepare_objects_query( $request ); 
  150.  
  151. // Set post_status. 
  152. $args['post_status'] = $request['status']; 
  153.  
  154. // Taxonomy query to filter products by type, category,  
  155. // tag, shipping class, and attribute. 
  156. $tax_query = array(); 
  157.  
  158. // Map between taxonomy name and arg's key. 
  159. $taxonomies = array( 
  160. 'product_cat' => 'category',  
  161. 'product_tag' => 'tag',  
  162. 'product_shipping_class' => 'shipping_class',  
  163. ); 
  164.  
  165. // Set tax_query for each passed arg. 
  166. foreach ( $taxonomies as $taxonomy => $key ) { 
  167. if ( ! empty( $request[ $key ] ) ) { 
  168. $tax_query[] = array( 
  169. 'taxonomy' => $taxonomy,  
  170. 'field' => 'term_id',  
  171. 'terms' => $request[ $key ],  
  172. ); 
  173.  
  174. // Filter product type by slug. 
  175. if ( ! empty( $request['type'] ) ) { 
  176. $tax_query[] = array( 
  177. 'taxonomy' => 'product_type',  
  178. 'field' => 'slug',  
  179. 'terms' => $request['type'],  
  180. ); 
  181.  
  182. // Filter by attribute and term. 
  183. if ( ! empty( $request['attribute'] ) && ! empty( $request['attribute_term'] ) ) { 
  184. if ( in_array( $request['attribute'], wc_get_attribute_taxonomy_names(), true ) ) { 
  185. $tax_query[] = array( 
  186. 'taxonomy' => $request['attribute'],  
  187. 'field' => 'term_id',  
  188. 'terms' => $request['attribute_term'],  
  189. ); 
  190.  
  191. if ( ! empty( $tax_query ) ) { 
  192. $args['tax_query'] = $tax_query; 
  193.  
  194. // Filter featured. 
  195. if ( is_bool( $request['featured'] ) ) { 
  196. $args['tax_query'][] = array( 
  197. 'taxonomy' => 'product_visibility',  
  198. 'field' => 'name',  
  199. 'terms' => 'featured',  
  200. ); 
  201.  
  202. // Filter by sku. 
  203. if ( ! empty( $request['sku'] ) ) { 
  204. $skus = explode( ', ', $request['sku'] ); 
  205. // Include the current string as a SKU too. 
  206. if ( 1 < count( $skus ) ) { 
  207. $skus[] = $request['sku']; 
  208.  
  209. $args['meta_query'] = $this->add_meta_query( $args, array( 
  210. 'key' => '_sku',  
  211. 'value' => $skus,  
  212. 'compare' => 'IN',  
  213. ) ); 
  214.  
  215. // Filter by tax class. 
  216. if ( ! empty( $request['tax_class'] ) ) { 
  217. $args['meta_query'] = $this->add_meta_query( $args, array( 
  218. 'key' => '_tax_class',  
  219. 'value' => 'standard' !== $request['tax_class'] ? $request['tax_class'] : '',  
  220. ) ); 
  221.  
  222. // Price filter. 
  223. if ( ! empty( $request['min_price'] ) || ! empty( $request['max_price'] ) ) { 
  224. $args['meta_query'] = $this->add_meta_query( $args, wc_get_min_max_price_meta_query( $request ) ); 
  225.  
  226. // Filter product in stock or out of stock. 
  227. if ( is_bool( $request['in_stock'] ) ) { 
  228. $args['meta_query'] = $this->add_meta_query( $args, array( 
  229. 'key' => '_stock_status',  
  230. 'value' => true === $request['in_stock'] ? 'instock' : 'outofstock',  
  231. ) ); 
  232.  
  233. // Filter by on sale products. 
  234. if ( is_bool( $request['on_sale'] ) ) { 
  235. $on_sale_key = $request['on_sale'] ? 'post__in' : 'post__not_in'; 
  236. $args[ $on_sale_key ] += wc_get_product_ids_on_sale(); 
  237.  
  238. // Force the post_type argument, since it's not a user input variable. 
  239. if ( ! empty( $request['sku'] ) ) { 
  240. $args['post_type'] = array( 'product', 'product_variation' ); 
  241. } else { 
  242. $args['post_type'] = $this->post_type; 
  243.  
  244. return $args; 
  245.  
  246. /** 
  247. * Get the downloads for a product or product variation. 
  248. * @param WC_Product|WC_Product_Variation $product Product instance. 
  249. * @return array 
  250. */ 
  251. protected function get_downloads( $product ) { 
  252. $downloads = array(); 
  253.  
  254. if ( $product->is_downloadable() ) { 
  255. foreach ( $product->get_downloads() as $file_id => $file ) { 
  256. $downloads[] = array( 
  257. 'id' => $file_id, // MD5 hash. 
  258. 'name' => $file['name'],  
  259. 'file' => $file['file'],  
  260. ); 
  261.  
  262. return $downloads; 
  263.  
  264. /** 
  265. * Get taxonomy terms. 
  266. * @param WC_Product $product Product instance. 
  267. * @param string $taxonomy Taxonomy slug. 
  268. * @return array 
  269. */ 
  270. protected function get_taxonomy_terms( $product, $taxonomy = 'cat' ) { 
  271. $terms = array(); 
  272.  
  273. foreach ( wc_get_object_terms( $product->get_id(), 'product_' . $taxonomy ) as $term ) { 
  274. $terms[] = array( 
  275. 'id' => $term->term_id,  
  276. 'name' => $term->name,  
  277. 'slug' => $term->slug,  
  278. ); 
  279.  
  280. return $terms; 
  281.  
  282. /** 
  283. * Get the images for a product or product variation. 
  284. * @param WC_Product|WC_Product_Variation $product Product instance. 
  285. * @return array 
  286. */ 
  287. protected function get_images( $product ) { 
  288. $images = array(); 
  289. $attachment_ids = array(); 
  290.  
  291. // Add featured image. 
  292. if ( has_post_thumbnail( $product->get_id() ) ) { 
  293. $attachment_ids[] = $product->get_image_id(); 
  294.  
  295. // Add gallery images. 
  296. $attachment_ids = array_merge( $attachment_ids, $product->get_gallery_image_ids() ); 
  297.  
  298. // Build image data. 
  299. foreach ( $attachment_ids as $position => $attachment_id ) { 
  300. $attachment_post = get_post( $attachment_id ); 
  301. if ( is_null( $attachment_post ) ) { 
  302. continue; 
  303.  
  304. $attachment = wp_get_attachment_image_src( $attachment_id, 'full' ); 
  305. if ( ! is_array( $attachment ) ) { 
  306. continue; 
  307.  
  308. $images[] = array( 
  309. 'id' => (int) $attachment_id,  
  310. 'date_created' => wc_rest_prepare_date_response( $attachment_post->post_date, false ),  
  311. 'date_created_gmt' => wc_rest_prepare_date_response( strtotime( $attachment_post->post_date_gmt ) ),  
  312. 'date_modified' => wc_rest_prepare_date_response( $attachment_post->post_modified, false ),  
  313. 'date_modified_gmt' => wc_rest_prepare_date_response( strtotime( $attachment_post->post_modified_gmt ) ),  
  314. 'src' => current( $attachment ),  
  315. 'name' => get_the_title( $attachment_id ),  
  316. 'alt' => get_post_meta( $attachment_id, '_wp_attachment_image_alt', true ),  
  317. 'position' => (int) $position,  
  318. ); 
  319.  
  320. // Set a placeholder image if the product has no images set. 
  321. if ( empty( $images ) ) { 
  322. $images[] = array( 
  323. 'id' => 0,  
  324. 'date_created' => wc_rest_prepare_date_response( current_time( 'mysql' ), false ), // Default to now. 
  325. 'date_created_gmt' => wc_rest_prepare_date_response( current_time( 'timestamp', true ) ), // Default to now. 
  326. 'date_modified' => wc_rest_prepare_date_response( current_time( 'mysql' ), false ),  
  327. 'date_modified_gmt' => wc_rest_prepare_date_response( current_time( 'timestamp', true ) ),  
  328. 'src' => wc_placeholder_img_src(),  
  329. 'name' => __( 'Placeholder', 'woocommerce' ),  
  330. 'alt' => __( 'Placeholder', 'woocommerce' ),  
  331. 'position' => 0,  
  332. ); 
  333.  
  334. return $images; 
  335.  
  336. /** 
  337. * Get attribute taxonomy label. 
  338. * @deprecated 3.0.0 
  339. * @param string $name Taxonomy name. 
  340. * @return string 
  341. */ 
  342. protected function get_attribute_taxonomy_label( $name ) { 
  343. $tax = get_taxonomy( $name ); 
  344. $labels = get_taxonomy_labels( $tax ); 
  345.  
  346. return $labels->singular_name; 
  347.  
  348. /** 
  349. * Get product attribute taxonomy name. 
  350. * @since 3.0.0 
  351. * @param string $slug Taxonomy name. 
  352. * @param WC_Product $product Product data. 
  353. * @return string 
  354. */ 
  355. protected function get_attribute_taxonomy_name( $slug, $product ) { 
  356. $attributes = $product->get_attributes(); 
  357.  
  358. if ( ! isset( $attributes[ $slug ] ) ) { 
  359. return str_replace( 'pa_', '', $slug ); 
  360.  
  361. $attribute = $attributes[ $slug ]; 
  362.  
  363. // Taxonomy attribute name. 
  364. if ( $attribute->is_taxonomy() ) { 
  365. $taxonomy = $attribute->get_taxonomy_object(); 
  366. return $taxonomy->attribute_label; 
  367.  
  368. // Custom product attribute name. 
  369. return $attribute->get_name(); 
  370.  
  371. /** 
  372. * Get default attributes. 
  373. * @param WC_Product $product Product instance. 
  374. * @return array 
  375. */ 
  376. protected function get_default_attributes( $product ) { 
  377. $default = array(); 
  378.  
  379. if ( $product->is_type( 'variable' ) ) { 
  380. foreach ( array_filter( (array) $product->get_default_attributes(), 'strlen' ) as $key => $value ) { 
  381. if ( 0 === strpos( $key, 'pa_' ) ) { 
  382. $default[] = array( 
  383. 'id' => wc_attribute_taxonomy_id_by_name( $key ),  
  384. 'name' => $this->get_attribute_taxonomy_name( $key, $product ),  
  385. 'option' => $value,  
  386. ); 
  387. } else { 
  388. $default[] = array( 
  389. 'id' => 0,  
  390. 'name' => $this->get_attribute_taxonomy_name( $key, $product ),  
  391. 'option' => $value,  
  392. ); 
  393.  
  394. return $default; 
  395.  
  396. /** 
  397. * Get attribute options. 
  398. * @param int $product_id Product ID. 
  399. * @param array $attribute Attribute data. 
  400. * @return array 
  401. */ 
  402. protected function get_attribute_options( $product_id, $attribute ) { 
  403. if ( isset( $attribute['is_taxonomy'] ) && $attribute['is_taxonomy'] ) { 
  404. return wc_get_product_terms( $product_id, $attribute['name'], array( 'fields' => 'names' ) ); 
  405. } elseif ( isset( $attribute['value'] ) ) { 
  406. return array_map( 'trim', explode( '|', $attribute['value'] ) ); 
  407.  
  408. return array(); 
  409.  
  410. /** 
  411. * Get the attributes for a product or product variation. 
  412. * @param WC_Product|WC_Product_Variation $product Product instance. 
  413. * @return array 
  414. */ 
  415. protected function get_attributes( $product ) { 
  416. $attributes = array(); 
  417.  
  418. if ( $product->is_type( 'variation' ) ) { 
  419. $_product = wc_get_product( $product->get_parent_id() ); 
  420. foreach ( $product->get_variation_attributes() as $attribute_name => $attribute ) { 
  421. $name = str_replace( 'attribute_', '', $attribute_name ); 
  422.  
  423. if ( ! $attribute ) { 
  424. continue; 
  425.  
  426. // Taxonomy-based attributes are prefixed with `pa_`, otherwise simply `attribute_`. 
  427. if ( 0 === strpos( $attribute_name, 'attribute_pa_' ) ) { 
  428. $option_term = get_term_by( 'slug', $attribute, $name ); 
  429. $attributes[] = array( 
  430. 'id' => wc_attribute_taxonomy_id_by_name( $name ),  
  431. 'name' => $this->get_attribute_taxonomy_name( $name, $_product ),  
  432. 'option' => $option_term && ! is_wp_error( $option_term ) ? $option_term->name : $attribute,  
  433. ); 
  434. } else { 
  435. $attributes[] = array( 
  436. 'id' => 0,  
  437. 'name' => $this->get_attribute_taxonomy_name( $name, $_product ),  
  438. 'option' => $attribute,  
  439. ); 
  440. } else { 
  441. foreach ( $product->get_attributes() as $attribute ) { 
  442. $attributes[] = array( 
  443. 'id' => $attribute['is_taxonomy'] ? wc_attribute_taxonomy_id_by_name( $attribute['name'] ) : 0,  
  444. 'name' => $this->get_attribute_taxonomy_name( $attribute['name'], $product ),  
  445. 'position' => (int) $attribute['position'],  
  446. 'visible' => (bool) $attribute['is_visible'],  
  447. 'variation' => (bool) $attribute['is_variation'],  
  448. 'options' => $this->get_attribute_options( $product->get_id(), $attribute ),  
  449. ); 
  450.  
  451. return $attributes; 
  452.  
  453. /** 
  454. * Get product data. 
  455. * @param WC_Product $product Product instance. 
  456. * @return array 
  457. */ 
  458. protected function get_product_data( $product ) { 
  459. $data = array( 
  460. 'id' => $product->get_id(),  
  461. 'name' => $product->get_name(),  
  462. 'slug' => $product->get_slug(),  
  463. 'permalink' => $product->get_permalink(),  
  464. 'date_created' => wc_rest_prepare_date_response( $product->get_date_created(), false ),  
  465. 'date_created_gmt' => wc_rest_prepare_date_response( $product->get_date_created() ),  
  466. 'date_modified' => wc_rest_prepare_date_response( $product->get_date_modified(), false ),  
  467. 'date_modified_gmt' => wc_rest_prepare_date_response( $product->get_date_modified() ),  
  468. 'type' => $product->get_type(),  
  469. 'status' => $product->get_status(),  
  470. 'featured' => $product->is_featured(),  
  471. 'catalog_visibility' => $product->get_catalog_visibility(),  
  472. 'description' => wpautop( do_shortcode( $product->get_description() ) ),  
  473. 'short_description' => apply_filters( 'woocommerce_short_description', $product->get_short_description() ),  
  474. 'sku' => $product->get_sku(),  
  475. 'price' => $product->get_price(),  
  476. 'regular_price' => $product->get_regular_price(),  
  477. 'sale_price' => $product->get_sale_price() ? $product->get_sale_price() : '',  
  478. 'date_on_sale_from' => wc_rest_prepare_date_response( $product->get_date_on_sale_from(), false ),  
  479. 'date_on_sale_from_gmt' => wc_rest_prepare_date_response( $product->get_date_on_sale_from() ),  
  480. 'date_on_sale_to' => wc_rest_prepare_date_response( $product->get_date_on_sale_to(), false ),  
  481. 'date_on_sale_to_gmt' => wc_rest_prepare_date_response( $product->get_date_on_sale_to() ),  
  482. 'price_html' => $product->get_price_html(),  
  483. 'on_sale' => $product->is_on_sale(),  
  484. 'purchasable' => $product->is_purchasable(),  
  485. 'total_sales' => $product->get_total_sales(),  
  486. 'virtual' => $product->is_virtual(),  
  487. 'downloadable' => $product->is_downloadable(),  
  488. 'downloads' => $this->get_downloads( $product ),  
  489. 'download_limit' => $product->get_download_limit(),  
  490. 'download_expiry' => $product->get_download_expiry(),  
  491. 'external_url' => $product->is_type( 'external' ) ? $product->get_product_url() : '',  
  492. 'button_text' => $product->is_type( 'external' ) ? $product->get_button_text() : '',  
  493. 'tax_status' => $product->get_tax_status(),  
  494. 'tax_class' => $product->get_tax_class(),  
  495. 'manage_stock' => $product->managing_stock(),  
  496. 'stock_quantity' => $product->get_stock_quantity(),  
  497. 'in_stock' => $product->is_in_stock(),  
  498. 'backorders' => $product->get_backorders(),  
  499. 'backorders_allowed' => $product->backorders_allowed(),  
  500. 'backordered' => $product->is_on_backorder(),  
  501. 'sold_individually' => $product->is_sold_individually(),  
  502. 'weight' => $product->get_weight(),  
  503. 'dimensions' => array( 
  504. 'length' => $product->get_length(),  
  505. 'width' => $product->get_width(),  
  506. 'height' => $product->get_height(),  
  507. ),  
  508. 'shipping_required' => $product->needs_shipping(),  
  509. 'shipping_taxable' => $product->is_shipping_taxable(),  
  510. 'shipping_class' => $product->get_shipping_class(),  
  511. 'shipping_class_id' => $product->get_shipping_class_id(),  
  512. 'reviews_allowed' => $product->get_reviews_allowed(),  
  513. 'average_rating' => wc_format_decimal( $product->get_average_rating(), 2 ),  
  514. 'rating_count' => $product->get_rating_count(),  
  515. 'related_ids' => array_map( 'absint', array_values( wc_get_related_products( $product->get_id() ) ) ),  
  516. 'upsell_ids' => array_map( 'absint', $product->get_upsell_ids() ),  
  517. 'cross_sell_ids' => array_map( 'absint', $product->get_cross_sell_ids() ),  
  518. 'parent_id' => $product->get_parent_id(),  
  519. 'purchase_note' => wpautop( do_shortcode( wp_kses_post( $product->get_purchase_note() ) ) ),  
  520. 'categories' => $this->get_taxonomy_terms( $product ),  
  521. 'tags' => $this->get_taxonomy_terms( $product, 'tag' ),  
  522. 'images' => $this->get_images( $product ),  
  523. 'attributes' => $this->get_attributes( $product ),  
  524. 'default_attributes' => $this->get_default_attributes( $product ),  
  525. 'variations' => array(),  
  526. 'grouped_products' => array(),  
  527. 'menu_order' => $product->get_menu_order(),  
  528. 'meta_data' => $product->get_meta_data(),  
  529. ); 
  530.  
  531. return $data; 
  532.  
  533. /** 
  534. * Prepare links for the request. 
  535. * @param WC_Data $object Object data. 
  536. * @param WP_REST_Request $request Request object. 
  537. * @return array Links for the given post. 
  538. */ 
  539. protected function prepare_links( $object, $request ) { 
  540. $links = array( 
  541. 'self' => array( 
  542. 'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $object->get_id() ) ),  
  543. ),  
  544. 'collection' => array( 
  545. 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ),  
  546. ),  
  547. ); 
  548.  
  549. if ( $object->get_parent_id() ) { 
  550. $links['up'] = array( 
  551. 'href' => rest_url( sprintf( '/%s/products/%d', $this->namespace, $object->get_parent_id() ) ),  
  552. ); 
  553.  
  554. return $links; 
  555.  
  556. /** 
  557. * Prepare a single product for create or update. 
  558. * @param WP_REST_Request $request Request object. 
  559. * @param bool $creating If is creating a new object. 
  560. * @return WP_Error|WC_Data 
  561. */ 
  562. protected function prepare_object_for_database( $request, $creating = false ) { 
  563. $id = isset( $request['id'] ) ? absint( $request['id'] ) : 0; 
  564.  
  565. // Type is the most important part here because we need to be using the correct class and methods. 
  566. if ( isset( $request['type'] ) ) { 
  567. $classname = WC_Product_Factory::get_classname_from_product_type( $request['type'] ); 
  568.  
  569. if ( ! class_exists( $classname ) ) { 
  570. $classname = 'WC_Product_Simple'; 
  571.  
  572. $product = new $classname( $id ); 
  573. } elseif ( isset( $request['id'] ) ) { 
  574. $product = wc_get_product( $id ); 
  575. } else { 
  576. $product = new WC_Product_Simple(); 
  577.  
  578. if ( 'variation' === $product->get_type() ) { 
  579. return new WP_Error( "woocommerce_rest_invalid_{$this->post_type}_id", __( 'To manipulate product variations you should use the /products/<product_id>/variations/<id> endpoint.', 'woocommerce' ), array( 'status' => 404 ) ); 
  580.  
  581. // Post title. 
  582. if ( isset( $request['name'] ) ) { 
  583. $product->set_name( wp_filter_post_kses( $request['name'] ) ); 
  584.  
  585. // Post content. 
  586. if ( isset( $request['description'] ) ) { 
  587. $product->set_description( wp_filter_post_kses( $request['description'] ) ); 
  588.  
  589. // Post excerpt. 
  590. if ( isset( $request['short_description'] ) ) { 
  591. $product->set_short_description( wp_filter_post_kses( $request['short_description'] ) ); 
  592.  
  593. // Post status. 
  594. if ( isset( $request['status'] ) ) { 
  595. $product->set_status( get_post_status_object( $request['status'] ) ? $request['status'] : 'draft' ); 
  596.  
  597. // Post slug. 
  598. if ( isset( $request['slug'] ) ) { 
  599. $product->set_slug( $request['slug'] ); 
  600.  
  601. // Menu order. 
  602. if ( isset( $request['menu_order'] ) ) { 
  603. $product->set_menu_order( $request['menu_order'] ); 
  604.  
  605. // Comment status. 
  606. if ( isset( $request['reviews_allowed'] ) ) { 
  607. $product->set_reviews_allowed( $request['reviews_allowed'] ); 
  608.  
  609. // Virtual. 
  610. if ( isset( $request['virtual'] ) ) { 
  611. $product->set_virtual( $request['virtual'] ); 
  612.  
  613. // Tax status. 
  614. if ( isset( $request['tax_status'] ) ) { 
  615. $product->set_tax_status( $request['tax_status'] ); 
  616.  
  617. // Tax Class. 
  618. if ( isset( $request['tax_class'] ) ) { 
  619. $product->set_tax_class( $request['tax_class'] ); 
  620.  
  621. // Catalog Visibility. 
  622. if ( isset( $request['catalog_visibility'] ) ) { 
  623. $product->set_catalog_visibility( $request['catalog_visibility'] ); 
  624.  
  625. // Purchase Note. 
  626. if ( isset( $request['purchase_note'] ) ) { 
  627. $product->set_purchase_note( wc_clean( $request['purchase_note'] ) ); 
  628.  
  629. // Featured Product. 
  630. if ( isset( $request['featured'] ) ) { 
  631. $product->set_featured( $request['featured'] ); 
  632.  
  633. // Shipping data. 
  634. $product = $this->save_product_shipping_data( $product, $request ); 
  635.  
  636. // SKU. 
  637. if ( isset( $request['sku'] ) ) { 
  638. $product->set_sku( wc_clean( $request['sku'] ) ); 
  639.  
  640. // Attributes. 
  641. if ( isset( $request['attributes'] ) ) { 
  642. $attributes = array(); 
  643.  
  644. foreach ( $request['attributes'] as $attribute ) { 
  645. $attribute_id = 0; 
  646. $attribute_name = ''; 
  647.  
  648. // Check ID for global attributes or name for product attributes. 
  649. if ( ! empty( $attribute['id'] ) ) { 
  650. $attribute_id = absint( $attribute['id'] ); 
  651. $attribute_name = wc_attribute_taxonomy_name_by_id( $attribute_id ); 
  652. } elseif ( ! empty( $attribute['name'] ) ) { 
  653. $attribute_name = wc_clean( $attribute['name'] ); 
  654.  
  655. if ( ! $attribute_id && ! $attribute_name ) { 
  656. continue; 
  657.  
  658. if ( $attribute_id ) { 
  659.  
  660. if ( isset( $attribute['options'] ) ) { 
  661. $options = $attribute['options']; 
  662.  
  663. if ( ! is_array( $attribute['options'] ) ) { 
  664. // Text based attributes - Posted values are term names. 
  665. $options = explode( WC_DELIMITER, $options ); 
  666.  
  667. $values = array_map( 'wc_sanitize_term_text_based', $options ); 
  668. $values = array_filter( $values, 'strlen' ); 
  669. } else { 
  670. $values = array(); 
  671.  
  672. if ( ! empty( $values ) ) { 
  673. // Add attribute to array, but don't set values. 
  674. $attribute_object = new WC_Product_Attribute(); 
  675. $attribute_object->set_id( $attribute_id ); 
  676. $attribute_object->set_name( $attribute_name ); 
  677. $attribute_object->set_options( $values ); 
  678. $attribute_object->set_position( isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0' ); 
  679. $attribute_object->set_visible( ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0 ); 
  680. $attribute_object->set_variation( ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0 ); 
  681. $attributes[] = $attribute_object; 
  682. } elseif ( isset( $attribute['options'] ) ) { 
  683. // Custom attribute - Add attribute to array and set the values. 
  684. if ( is_array( $attribute['options'] ) ) { 
  685. $values = $attribute['options']; 
  686. } else { 
  687. $values = explode( WC_DELIMITER, $attribute['options'] ); 
  688. $attribute_object = new WC_Product_Attribute(); 
  689. $attribute_object->set_name( $attribute_name ); 
  690. $attribute_object->set_options( $values ); 
  691. $attribute_object->set_position( isset( $attribute['position'] ) ? (string) absint( $attribute['position'] ) : '0' ); 
  692. $attribute_object->set_visible( ( isset( $attribute['visible'] ) && $attribute['visible'] ) ? 1 : 0 ); 
  693. $attribute_object->set_variation( ( isset( $attribute['variation'] ) && $attribute['variation'] ) ? 1 : 0 ); 
  694. $attributes[] = $attribute_object; 
  695. $product->set_attributes( $attributes ); 
  696.  
  697. // Sales and prices. 
  698. if ( in_array( $product->get_type(), array( 'variable', 'grouped' ), true ) ) { 
  699. $product->set_regular_price( '' ); 
  700. $product->set_sale_price( '' ); 
  701. $product->set_date_on_sale_to( '' ); 
  702. $product->set_date_on_sale_from( '' ); 
  703. $product->set_price( '' ); 
  704. } else { 
  705. // Regular Price. 
  706. if ( isset( $request['regular_price'] ) ) { 
  707. $product->set_regular_price( $request['regular_price'] ); 
  708.  
  709. // Sale Price. 
  710. if ( isset( $request['sale_price'] ) ) { 
  711. $product->set_sale_price( $request['sale_price'] ); 
  712.  
  713. if ( isset( $request['date_on_sale_from'] ) ) { 
  714. $product->set_date_on_sale_from( $request['date_on_sale_from'] ); 
  715.  
  716. if ( isset( $request['date_on_sale_from_gmt'] ) ) { 
  717. $product->set_date_on_sale_from( $request['date_on_sale_from_gmt'] ? strtotime( $request['date_on_sale_from_gmt'] ) : null ); 
  718.  
  719. if ( isset( $request['date_on_sale_to'] ) ) { 
  720. $product->set_date_on_sale_to( $request['date_on_sale_to'] ); 
  721.  
  722. if ( isset( $request['date_on_sale_to_gmt'] ) ) { 
  723. $product->set_date_on_sale_to( $request['date_on_sale_to_gmt'] ? strtotime( $request['date_on_sale_to_gmt'] ) : null ); 
  724.  
  725. // Product parent ID for groups. 
  726. if ( isset( $request['parent_id'] ) ) { 
  727. $product->set_parent_id( $request['parent_id'] ); 
  728.  
  729. // Sold individually. 
  730. if ( isset( $request['sold_individually'] ) ) { 
  731. $product->set_sold_individually( $request['sold_individually'] ); 
  732.  
  733. // Stock status. 
  734. if ( isset( $request['in_stock'] ) ) { 
  735. $stock_status = true === $request['in_stock'] ? 'instock' : 'outofstock'; 
  736. } else { 
  737. $stock_status = $product->get_stock_status(); 
  738.  
  739. // Stock data. 
  740. if ( 'yes' === get_option( 'woocommerce_manage_stock' ) ) { 
  741. // Manage stock. 
  742. if ( isset( $request['manage_stock'] ) ) { 
  743. $product->set_manage_stock( $request['manage_stock'] ); 
  744.  
  745. // Backorders. 
  746. if ( isset( $request['backorders'] ) ) { 
  747. $product->set_backorders( $request['backorders'] ); 
  748.  
  749. if ( $product->is_type( 'grouped' ) ) { 
  750. $product->set_manage_stock( 'no' ); 
  751. $product->set_backorders( 'no' ); 
  752. $product->set_stock_quantity( '' ); 
  753. $product->set_stock_status( $stock_status ); 
  754. } elseif ( $product->is_type( 'external' ) ) { 
  755. $product->set_manage_stock( 'no' ); 
  756. $product->set_backorders( 'no' ); 
  757. $product->set_stock_quantity( '' ); 
  758. $product->set_stock_status( 'instock' ); 
  759. } elseif ( $product->get_manage_stock() ) { 
  760. // Stock status is always determined by children so sync later. 
  761. if ( ! $product->is_type( 'variable' ) ) { 
  762. $product->set_stock_status( $stock_status ); 
  763.  
  764. // Stock quantity. 
  765. if ( isset( $request['stock_quantity'] ) ) { 
  766. $product->set_stock_quantity( wc_stock_amount( $request['stock_quantity'] ) ); 
  767. } elseif ( isset( $request['inventory_delta'] ) ) { 
  768. $stock_quantity = wc_stock_amount( $product->get_stock_quantity() ); 
  769. $stock_quantity += wc_stock_amount( $request['inventory_delta'] ); 
  770. $product->set_stock_quantity( wc_stock_amount( $stock_quantity ) ); 
  771. } else { 
  772. // Don't manage stock. 
  773. $product->set_manage_stock( 'no' ); 
  774. $product->set_stock_quantity( '' ); 
  775. $product->set_stock_status( $stock_status ); 
  776. } elseif ( ! $product->is_type( 'variable' ) ) { 
  777. $product->set_stock_status( $stock_status ); 
  778.  
  779. // Upsells. 
  780. if ( isset( $request['upsell_ids'] ) ) { 
  781. $upsells = array(); 
  782. $ids = $request['upsell_ids']; 
  783.  
  784. if ( ! empty( $ids ) ) { 
  785. foreach ( $ids as $id ) { 
  786. if ( $id && $id > 0 ) { 
  787. $upsells[] = $id; 
  788.  
  789. $product->set_upsell_ids( $upsells ); 
  790.  
  791. // Cross sells. 
  792. if ( isset( $request['cross_sell_ids'] ) ) { 
  793. $crosssells = array(); 
  794. $ids = $request['cross_sell_ids']; 
  795.  
  796. if ( ! empty( $ids ) ) { 
  797. foreach ( $ids as $id ) { 
  798. if ( $id && $id > 0 ) { 
  799. $crosssells[] = $id; 
  800.  
  801. $product->set_cross_sell_ids( $crosssells ); 
  802.  
  803. // Product categories. 
  804. if ( isset( $request['categories'] ) && is_array( $request['categories'] ) ) { 
  805. $product = $this->save_taxonomy_terms( $product, $request['categories'] ); 
  806.  
  807. // Product tags. 
  808. if ( isset( $request['tags'] ) && is_array( $request['tags'] ) ) { 
  809. $product = $this->save_taxonomy_terms( $product, $request['tags'], 'tag' ); 
  810.  
  811. // Downloadable. 
  812. if ( isset( $request['downloadable'] ) ) { 
  813. $product->set_downloadable( $request['downloadable'] ); 
  814.  
  815. // Downloadable options. 
  816. if ( $product->get_downloadable() ) { 
  817.  
  818. // Downloadable files. 
  819. if ( isset( $request['downloads'] ) && is_array( $request['downloads'] ) ) { 
  820. $product = $this->save_downloadable_files( $product, $request['downloads'] ); 
  821.  
  822. // Download limit. 
  823. if ( isset( $request['download_limit'] ) ) { 
  824. $product->set_download_limit( $request['download_limit'] ); 
  825.  
  826. // Download expiry. 
  827. if ( isset( $request['download_expiry'] ) ) { 
  828. $product->set_download_expiry( $request['download_expiry'] ); 
  829.  
  830. // Product url and button text for external products. 
  831. if ( $product->is_type( 'external' ) ) { 
  832. if ( isset( $request['external_url'] ) ) { 
  833. $product->set_product_url( $request['external_url'] ); 
  834.  
  835. if ( isset( $request['button_text'] ) ) { 
  836. $product->set_button_text( $request['button_text'] ); 
  837.  
  838. // Save default attributes for variable products. 
  839. if ( $product->is_type( 'variable' ) ) { 
  840. $product = $this->save_default_attributes( $product, $request ); 
  841.  
  842. // Check for featured/gallery images, upload it and set it. 
  843. if ( isset( $request['images'] ) ) { 
  844. $product = $this->set_product_images( $product, $request['images'] ); 
  845.  
  846. // Allow set meta_data. 
  847. if ( is_array( $request['meta_data'] ) ) { 
  848. foreach ( $request['meta_data'] as $meta ) { 
  849. $product->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' ); 
  850.  
  851. /** 
  852. * Filters an object before it is inserted via the REST API. 
  853. * The dynamic portion of the hook name, `$this->post_type`,  
  854. * refers to the object type slug. 
  855. * @param WC_Data $product Object object. 
  856. * @param WP_REST_Request $request Request object. 
  857. * @param bool $creating If is creating a new object. 
  858. */ 
  859. return apply_filters( "woocommerce_rest_pre_insert_{$this->post_type}_object", $product, $request, $creating ); 
  860.  
  861. /** 
  862. * Set product images. 
  863. * @throws WC_REST_Exception REST API exceptions. 
  864. * @param WC_Product $product Product instance. 
  865. * @param array $images Images data. 
  866. * @return WC_Product 
  867. */ 
  868. protected function set_product_images( $product, $images ) { 
  869. if ( is_array( $images ) ) { 
  870. $gallery = array(); 
  871.  
  872. foreach ( $images as $image ) { 
  873. $attachment_id = isset( $image['id'] ) ? absint( $image['id'] ) : 0; 
  874.  
  875. if ( 0 === $attachment_id && isset( $image['src'] ) ) { 
  876. $upload = wc_rest_upload_image_from_url( esc_url_raw( $image['src'] ) ); 
  877.  
  878. if ( is_wp_error( $upload ) ) { 
  879. if ( ! apply_filters( 'woocommerce_rest_suppress_image_upload_error', false, $upload, $product->get_id(), $images ) ) { 
  880. throw new WC_REST_Exception( 'woocommerce_product_image_upload_error', $upload->get_error_message(), 400 ); 
  881. } else { 
  882. continue; 
  883.  
  884. $attachment_id = wc_rest_set_uploaded_image_as_attachment( $upload, $product->get_id() ); 
  885.  
  886. if ( ! wp_attachment_is_image( $attachment_id ) ) { 
  887. throw new WC_REST_Exception( 'woocommerce_product_invalid_image_id', sprintf( __( '#%s is an invalid image ID.', 'woocommerce' ), $attachment_id ), 400 ); 
  888.  
  889. if ( isset( $image['position'] ) && 0 === absint( $image['position'] ) ) { 
  890. $product->set_image_id( $attachment_id ); 
  891. } else { 
  892. $gallery[] = $attachment_id; 
  893.  
  894. // Set the image alt if present. 
  895. if ( ! empty( $image['alt'] ) ) { 
  896. update_post_meta( $attachment_id, '_wp_attachment_image_alt', wc_clean( $image['alt'] ) ); 
  897.  
  898. // Set the image name if present. 
  899. if ( ! empty( $image['name'] ) ) { 
  900. wp_update_post( array( 'ID' => $attachment_id, 'post_title' => $image['name'] ) ); 
  901.  
  902. if ( ! empty( $gallery ) ) { 
  903. $product->set_gallery_image_ids( $gallery ); 
  904. } else { 
  905. $product->set_image_id( '' ); 
  906. $product->set_gallery_image_ids( array() ); 
  907.  
  908. return $product; 
  909.  
  910. /** 
  911. * Save product shipping data. 
  912. * @param WC_Product $product Product instance. 
  913. * @param array $data Shipping data. 
  914. * @return WC_Product 
  915. */ 
  916. protected function save_product_shipping_data( $product, $data ) { 
  917. // Virtual. 
  918. if ( isset( $data['virtual'] ) && true === $data['virtual'] ) { 
  919. $product->set_weight( '' ); 
  920. $product->set_height( '' ); 
  921. $product->set_length( '' ); 
  922. $product->set_width( '' ); 
  923. } else { 
  924. if ( isset( $data['weight'] ) ) { 
  925. $product->set_weight( $data['weight'] ); 
  926.  
  927. // Height. 
  928. if ( isset( $data['dimensions']['height'] ) ) { 
  929. $product->set_height( $data['dimensions']['height'] ); 
  930.  
  931. // Width. 
  932. if ( isset( $data['dimensions']['width'] ) ) { 
  933. $product->set_width( $data['dimensions']['width'] ); 
  934.  
  935. // Length. 
  936. if ( isset( $data['dimensions']['length'] ) ) { 
  937. $product->set_length( $data['dimensions']['length'] ); 
  938.  
  939. // Shipping class. 
  940. if ( isset( $data['shipping_class'] ) ) { 
  941. $shipping_class_term = get_term_by( 'slug', wc_clean( $data['shipping_class'] ), 'product_shipping_class' ); 
  942.  
  943. if ( $shipping_class_term ) { 
  944. $product->set_shipping_class_id( $shipping_class_term->term_id ); 
  945.  
  946. return $product; 
  947.  
  948. /** 
  949. * Save downloadable files. 
  950. * @param WC_Product $product Product instance. 
  951. * @param array $downloads Downloads data. 
  952. * @param int $deprecated Deprecated since 3.0. 
  953. * @return WC_Product 
  954. */ 
  955. protected function save_downloadable_files( $product, $downloads, $deprecated = 0 ) { 
  956. if ( $deprecated ) { 
  957. wc_deprecated_argument( 'variation_id', '3.0', 'save_downloadable_files() not requires a variation_id anymore.' ); 
  958.  
  959. $files = array(); 
  960. foreach ( $downloads as $key => $file ) { 
  961. if ( empty( $file['file'] ) ) { 
  962. continue; 
  963.  
  964. $download = new WC_Product_Download(); 
  965. $download->set_id( $key ); 
  966. $download->set_name( $file['name'] ? $file['name'] : wc_get_filename_from_url( $file['file'] ) ); 
  967. $download->set_file( apply_filters( 'woocommerce_file_download_path', $file['file'], $product, $key ) ); 
  968. $files[] = $download; 
  969. $product->set_downloads( $files ); 
  970.  
  971. return $product; 
  972.  
  973. /** 
  974. * Save taxonomy terms. 
  975. * @param WC_Product $product Product instance. 
  976. * @param array $terms Terms data. 
  977. * @param string $taxonomy Taxonomy name. 
  978. * @return WC_Product 
  979. */ 
  980. protected function save_taxonomy_terms( $product, $terms, $taxonomy = 'cat' ) { 
  981. $term_ids = wp_list_pluck( $terms, 'id' ); 
  982.  
  983. if ( 'cat' === $taxonomy ) { 
  984. $product->set_category_ids( $term_ids ); 
  985. } elseif ( 'tag' === $taxonomy ) { 
  986. $product->set_tag_ids( $term_ids ); 
  987.  
  988. return $product; 
  989.  
  990. /** 
  991. * Save default attributes. 
  992. * @since 3.0.0 
  993. * @param WC_Product $product Product instance. 
  994. * @param WP_REST_Request $request Request data. 
  995. * @return WC_Product 
  996. */ 
  997. protected function save_default_attributes( $product, $request ) { 
  998. if ( isset( $request['default_attributes'] ) && is_array( $request['default_attributes'] ) ) { 
  999.  
  1000. $attributes = $product->get_attributes(); 
  1001. $default_attributes = array(); 
  1002.  
  1003. foreach ( $request['default_attributes'] as $attribute ) { 
  1004. $attribute_id = 0; 
  1005. $attribute_name = ''; 
  1006.  
  1007. // Check ID for global attributes or name for product attributes. 
  1008. if ( ! empty( $attribute['id'] ) ) { 
  1009. $attribute_id = absint( $attribute['id'] ); 
  1010. $attribute_name = wc_attribute_taxonomy_name_by_id( $attribute_id ); 
  1011. } elseif ( ! empty( $attribute['name'] ) ) { 
  1012. $attribute_name = sanitize_title( $attribute['name'] ); 
  1013.  
  1014. if ( ! $attribute_id && ! $attribute_name ) { 
  1015. continue; 
  1016.  
  1017. if ( isset( $attributes[ $attribute_name ] ) ) { 
  1018. $_attribute = $attributes[ $attribute_name ]; 
  1019.  
  1020. if ( $_attribute['is_variation'] ) { 
  1021. $value = isset( $attribute['option'] ) ? wc_clean( stripslashes( $attribute['option'] ) ) : ''; 
  1022.  
  1023. if ( ! empty( $_attribute['is_taxonomy'] ) ) { 
  1024. // If dealing with a taxonomy, we need to get the slug from the name posted to the API. 
  1025. $term = get_term_by( 'name', $value, $attribute_name ); 
  1026.  
  1027. if ( $term && ! is_wp_error( $term ) ) { 
  1028. $value = $term->slug; 
  1029. } else { 
  1030. $value = sanitize_title( $value ); 
  1031.  
  1032. if ( $value ) { 
  1033. $default_attributes[ $attribute_name ] = $value; 
  1034.  
  1035. $product->set_default_attributes( $default_attributes ); 
  1036.  
  1037. return $product; 
  1038.  
  1039. /** 
  1040. * Clear caches here so in sync with any new variations/children. 
  1041. * @param WC_Data $object Object data. 
  1042. */ 
  1043. public function clear_transients( $object ) { 
  1044. wc_delete_product_transients( $object->get_id() ); 
  1045. wp_cache_delete( 'product-' . $object->get_id(), 'products' ); 
  1046.  
  1047. /** 
  1048. * Delete a single item. 
  1049. * @param WP_REST_Request $request Full details about the request. 
  1050. * @return WP_REST_Response|WP_Error 
  1051. */ 
  1052. public function delete_item( $request ) { 
  1053. $id = (int) $request['id']; 
  1054. $force = (bool) $request['force']; 
  1055. $object = $this->get_object( (int) $request['id'] ); 
  1056. $result = false; 
  1057.  
  1058. if ( ! $object || 0 === $object->get_id() ) { 
  1059. return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'Invalid ID.', 'woocommerce' ), array( 'status' => 404 ) ); 
  1060.  
  1061. if ( 'variation' === $object->get_type() ) { 
  1062. return new WP_Error( "woocommerce_rest_invalid_{$this->post_type}_id", __( 'To manipulate product variations you should use the /products/<product_id>/variations/<id> endpoint.', 'woocommerce' ), array( 'status' => 404 ) ); 
  1063.  
  1064. $supports_trash = EMPTY_TRASH_DAYS > 0 && is_callable( array( $object, 'get_status' ) ); 
  1065.  
  1066. /** 
  1067. * Filter whether an object is trashable. 
  1068. * Return false to disable trash support for the object. 
  1069. * @param boolean $supports_trash Whether the object type support trashing. 
  1070. * @param WC_Data $object The object being considered for trashing support. 
  1071. */ 
  1072. $supports_trash = apply_filters( "woocommerce_rest_{$this->post_type}_object_trashable", $supports_trash, $object ); 
  1073.  
  1074. if ( ! wc_rest_check_post_permissions( $this->post_type, 'delete', $object->get_id() ) ) { 
  1075. /** translators: %s: post type */ 
  1076. return new WP_Error( "woocommerce_rest_user_cannot_delete_{$this->post_type}", sprintf( __( 'Sorry, you are not allowed to delete %s.', 'woocommerce' ), $this->post_type ), array( 'status' => rest_authorization_required_code() ) ); 
  1077.  
  1078. $request->set_param( 'context', 'edit' ); 
  1079. $response = $this->prepare_object_for_response( $object, $request ); 
  1080.  
  1081. // If we're forcing, then delete permanently. 
  1082. if ( $force ) { 
  1083. if ( $object->is_type( 'variable' ) ) { 
  1084. foreach ( $object->get_children() as $child_id ) { 
  1085. $child = wc_get_product( $child_id ); 
  1086. $child->delete( true ); 
  1087. } elseif ( $object->is_type( 'grouped' ) ) { 
  1088. foreach ( $object->get_children() as $child_id ) { 
  1089. $child = wc_get_product( $child_id ); 
  1090. $child->set_parent_id( 0 ); 
  1091. $child->save(); 
  1092.  
  1093. $object->delete( true ); 
  1094. $result = 0 === $object->get_id(); 
  1095. } else { 
  1096. // If we don't support trashing for this type, error out. 
  1097. if ( ! $supports_trash ) { 
  1098. /** translators: %s: post type */ 
  1099. return new WP_Error( 'woocommerce_rest_trash_not_supported', sprintf( __( 'The %s does not support trashing.', 'woocommerce' ), $this->post_type ), array( 'status' => 501 ) ); 
  1100.  
  1101. // Otherwise, only trash if we haven't already. 
  1102. if ( is_callable( array( $object, 'get_status' ) ) ) { 
  1103. if ( 'trash' === $object->get_status() ) { 
  1104. /** translators: %s: post type */ 
  1105. return new WP_Error( 'woocommerce_rest_already_trashed', sprintf( __( 'The %s has already been deleted.', 'woocommerce' ), $this->post_type ), array( 'status' => 410 ) ); 
  1106.  
  1107. $object->delete(); 
  1108. $result = 'trash' === $object->get_status(); 
  1109.  
  1110. if ( ! $result ) { 
  1111. /** translators: %s: post type */ 
  1112. return new WP_Error( 'woocommerce_rest_cannot_delete', sprintf( __( 'The %s cannot be deleted.', 'woocommerce' ), $this->post_type ), array( 'status' => 500 ) ); 
  1113.  
  1114. // Delete parent product transients. 
  1115. if ( 0 !== $object->get_parent_id() ) { 
  1116. wc_delete_product_transients( $object->get_parent_id() ); 
  1117.  
  1118. /** 
  1119. * Fires after a single object is deleted or trashed via the REST API. 
  1120. * @param WC_Data $object The deleted or trashed object. 
  1121. * @param WP_REST_Response $response The response data. 
  1122. * @param WP_REST_Request $request The request sent to the API. 
  1123. */ 
  1124. do_action( "woocommerce_rest_delete_{$this->post_type}_object", $object, $response, $request ); 
  1125.  
  1126. return $response; 
  1127.  
  1128. /** 
  1129. * Get the Product's schema, conforming to JSON Schema. 
  1130. * @return array 
  1131. */ 
  1132. public function get_item_schema() { 
  1133. $weight_unit = get_option( 'woocommerce_weight_unit' ); 
  1134. $dimension_unit = get_option( 'woocommerce_dimension_unit' ); 
  1135. $schema = array( 
  1136. '$schema' => 'http://json-schema.org/draft-04/schema#',  
  1137. 'title' => $this->post_type,  
  1138. 'type' => 'object',  
  1139. 'properties' => array( 
  1140. 'id' => array( 
  1141. 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),  
  1142. 'type' => 'integer',  
  1143. 'context' => array( 'view', 'edit' ),  
  1144. 'readonly' => true,  
  1145. ),  
  1146. 'name' => array( 
  1147. 'description' => __( 'Product name.', 'woocommerce' ),  
  1148. 'type' => 'string',  
  1149. 'context' => array( 'view', 'edit' ),  
  1150. ),  
  1151. 'slug' => array( 
  1152. 'description' => __( 'Product slug.', 'woocommerce' ),  
  1153. 'type' => 'string',  
  1154. 'context' => array( 'view', 'edit' ),  
  1155. ),  
  1156. 'permalink' => array( 
  1157. 'description' => __( 'Product URL.', 'woocommerce' ),  
  1158. 'type' => 'string',  
  1159. 'format' => 'uri',  
  1160. 'context' => array( 'view', 'edit' ),  
  1161. 'readonly' => true,  
  1162. ),  
  1163. 'date_created' => array( 
  1164. 'description' => __( "The date the product was created, in the site's timezone.", 'woocommerce' ),  
  1165. 'type' => 'date-time',  
  1166. 'context' => array( 'view', 'edit' ),  
  1167. 'readonly' => true,  
  1168. ),  
  1169. 'date_created_gmt' => array( 
  1170. 'description' => __( "The date the product was created, as GMT.", 'woocommerce' ),  
  1171. 'type' => 'date-time',  
  1172. 'context' => array( 'view', 'edit' ),  
  1173. 'readonly' => true,  
  1174. ),  
  1175. 'date_modified' => array( 
  1176. 'description' => __( "The date the product was last modified, in the site's timezone.", 'woocommerce' ),  
  1177. 'type' => 'date-time',  
  1178. 'context' => array( 'view', 'edit' ),  
  1179. 'readonly' => true,  
  1180. ),  
  1181. 'date_modified_gmt' => array( 
  1182. 'description' => __( "The date the product was last modified, as GMT.", 'woocommerce' ),  
  1183. 'type' => 'date-time',  
  1184. 'context' => array( 'view', 'edit' ),  
  1185. 'readonly' => true,  
  1186. ),  
  1187. 'type' => array( 
  1188. 'description' => __( 'Product type.', 'woocommerce' ),  
  1189. 'type' => 'string',  
  1190. 'default' => 'simple',  
  1191. 'enum' => array_keys( wc_get_product_types() ),  
  1192. 'context' => array( 'view', 'edit' ),  
  1193. ),  
  1194. 'status' => array( 
  1195. 'description' => __( 'Product status (post status).', 'woocommerce' ),  
  1196. 'type' => 'string',  
  1197. 'default' => 'publish',  
  1198. 'enum' => array_keys( get_post_statuses() ),  
  1199. 'context' => array( 'view', 'edit' ),  
  1200. ),  
  1201. 'featured' => array( 
  1202. 'description' => __( 'Featured product.', 'woocommerce' ),  
  1203. 'type' => 'boolean',  
  1204. 'default' => false,  
  1205. 'context' => array( 'view', 'edit' ),  
  1206. ),  
  1207. 'catalog_visibility' => array( 
  1208. 'description' => __( 'Catalog visibility.', 'woocommerce' ),  
  1209. 'type' => 'string',  
  1210. 'default' => 'visible',  
  1211. 'enum' => array( 'visible', 'catalog', 'search', 'hidden' ),  
  1212. 'context' => array( 'view', 'edit' ),  
  1213. ),  
  1214. 'description' => array( 
  1215. 'description' => __( 'Product description.', 'woocommerce' ),  
  1216. 'type' => 'string',  
  1217. 'context' => array( 'view', 'edit' ),  
  1218. ),  
  1219. 'short_description' => array( 
  1220. 'description' => __( 'Product short description.', 'woocommerce' ),  
  1221. 'type' => 'string',  
  1222. 'context' => array( 'view', 'edit' ),  
  1223. ),  
  1224. 'sku' => array( 
  1225. 'description' => __( 'Unique identifier.', 'woocommerce' ),  
  1226. 'type' => 'string',  
  1227. 'context' => array( 'view', 'edit' ),  
  1228. ),  
  1229. 'price' => array( 
  1230. 'description' => __( 'Current product price.', 'woocommerce' ),  
  1231. 'type' => 'string',  
  1232. 'context' => array( 'view', 'edit' ),  
  1233. 'readonly' => true,  
  1234. ),  
  1235. 'regular_price' => array( 
  1236. 'description' => __( 'Product regular price.', 'woocommerce' ),  
  1237. 'type' => 'string',  
  1238. 'context' => array( 'view', 'edit' ),  
  1239. ),  
  1240. 'sale_price' => array( 
  1241. 'description' => __( 'Product sale price.', 'woocommerce' ),  
  1242. 'type' => 'string',  
  1243. 'context' => array( 'view', 'edit' ),  
  1244. ),  
  1245. 'date_on_sale_from' => array( 
  1246. 'description' => __( "Start date of sale price, in the site's timezone.", 'woocommerce' ),  
  1247. 'type' => 'date-time',  
  1248. 'context' => array( 'view', 'edit' ),  
  1249. ),  
  1250. 'date_on_sale_from_gmt' => array( 
  1251. 'description' => __( 'Start date of sale price, as GMT.', 'woocommerce' ),  
  1252. 'type' => 'date-time',  
  1253. 'context' => array( 'view', 'edit' ),  
  1254. ),  
  1255. 'date_on_sale_to' => array( 
  1256. 'description' => __( "End date of sale price, in the site's timezone.", 'woocommerce' ),  
  1257. 'type' => 'date-time',  
  1258. 'context' => array( 'view', 'edit' ),  
  1259. ),  
  1260. 'date_on_sale_to_gmt' => array( 
  1261. 'description' => __( "End date of sale price, in the site's timezone.", 'woocommerce' ),  
  1262. 'type' => 'date-time',  
  1263. 'context' => array( 'view', 'edit' ),  
  1264. ),  
  1265. 'price_html' => array( 
  1266. 'description' => __( 'Price formatted in HTML.', 'woocommerce' ),  
  1267. 'type' => 'string',  
  1268. 'context' => array( 'view', 'edit' ),  
  1269. 'readonly' => true,  
  1270. ),  
  1271. 'on_sale' => array( 
  1272. 'description' => __( 'Shows if the product is on sale.', 'woocommerce' ),  
  1273. 'type' => 'boolean',  
  1274. 'context' => array( 'view', 'edit' ),  
  1275. 'readonly' => true,  
  1276. ),  
  1277. 'purchasable' => array( 
  1278. 'description' => __( 'Shows if the product can be bought.', 'woocommerce' ),  
  1279. 'type' => 'boolean',  
  1280. 'context' => array( 'view', 'edit' ),  
  1281. 'readonly' => true,  
  1282. ),  
  1283. 'total_sales' => array( 
  1284. 'description' => __( 'Amount of sales.', 'woocommerce' ),  
  1285. 'type' => 'integer',  
  1286. 'context' => array( 'view', 'edit' ),  
  1287. 'readonly' => true,  
  1288. ),  
  1289. 'virtual' => array( 
  1290. 'description' => __( 'If the product is virtual.', 'woocommerce' ),  
  1291. 'type' => 'boolean',  
  1292. 'default' => false,  
  1293. 'context' => array( 'view', 'edit' ),  
  1294. ),  
  1295. 'downloadable' => array( 
  1296. 'description' => __( 'If the product is downloadable.', 'woocommerce' ),  
  1297. 'type' => 'boolean',  
  1298. 'default' => false,  
  1299. 'context' => array( 'view', 'edit' ),  
  1300. ),  
  1301. 'downloads' => array( 
  1302. 'description' => __( 'List of downloadable files.', 'woocommerce' ),  
  1303. 'type' => 'array',  
  1304. 'context' => array( 'view', 'edit' ),  
  1305. 'items' => array( 
  1306. 'type' => 'object',  
  1307. 'properties' => array( 
  1308. 'id' => array( 
  1309. 'description' => __( 'File MD5 hash.', 'woocommerce' ),  
  1310. 'type' => 'string',  
  1311. 'context' => array( 'view', 'edit' ),  
  1312. 'readonly' => true,  
  1313. ),  
  1314. 'name' => array( 
  1315. 'description' => __( 'File name.', 'woocommerce' ),  
  1316. 'type' => 'string',  
  1317. 'context' => array( 'view', 'edit' ),  
  1318. ),  
  1319. 'file' => array( 
  1320. 'description' => __( 'File URL.', 'woocommerce' ),  
  1321. 'type' => 'string',  
  1322. 'context' => array( 'view', 'edit' ),  
  1323. ),  
  1324. ),  
  1325. ),  
  1326. ),  
  1327. 'download_limit' => array( 
  1328. 'description' => __( 'Number of times downloadable files can be downloaded after purchase.', 'woocommerce' ),  
  1329. 'type' => 'integer',  
  1330. 'default' => -1,  
  1331. 'context' => array( 'view', 'edit' ),  
  1332. ),  
  1333. 'download_expiry' => array( 
  1334. 'description' => __( 'Number of days until access to downloadable files expires.', 'woocommerce' ),  
  1335. 'type' => 'integer',  
  1336. 'default' => -1,  
  1337. 'context' => array( 'view', 'edit' ),  
  1338. ),  
  1339. 'external_url' => array( 
  1340. 'description' => __( 'Product external URL. Only for external products.', 'woocommerce' ),  
  1341. 'type' => 'string',  
  1342. 'format' => 'uri',  
  1343. 'context' => array( 'view', 'edit' ),  
  1344. ),  
  1345. 'button_text' => array( 
  1346. 'description' => __( 'Product external button text. Only for external products.', 'woocommerce' ),  
  1347. 'type' => 'string',  
  1348. 'context' => array( 'view', 'edit' ),  
  1349. ),  
  1350. 'tax_status' => array( 
  1351. 'description' => __( 'Tax status.', 'woocommerce' ),  
  1352. 'type' => 'string',  
  1353. 'default' => 'taxable',  
  1354. 'enum' => array( 'taxable', 'shipping', 'none' ),  
  1355. 'context' => array( 'view', 'edit' ),  
  1356. ),  
  1357. 'tax_class' => array( 
  1358. 'description' => __( 'Tax class.', 'woocommerce' ),  
  1359. 'type' => 'string',  
  1360. 'context' => array( 'view', 'edit' ),  
  1361. ),  
  1362. 'manage_stock' => array( 
  1363. 'description' => __( 'Stock management at product level.', 'woocommerce' ),  
  1364. 'type' => 'boolean',  
  1365. 'default' => false,  
  1366. 'context' => array( 'view', 'edit' ),  
  1367. ),  
  1368. 'stock_quantity' => array( 
  1369. 'description' => __( 'Stock quantity.', 'woocommerce' ),  
  1370. 'type' => 'integer',  
  1371. 'context' => array( 'view', 'edit' ),  
  1372. ),  
  1373. 'in_stock' => array( 
  1374. 'description' => __( 'Controls whether or not the product is listed as "in stock" or "out of stock" on the frontend.', 'woocommerce' ),  
  1375. 'type' => 'boolean',  
  1376. 'default' => true,  
  1377. 'context' => array( 'view', 'edit' ),  
  1378. ),  
  1379. 'backorders' => array( 
  1380. 'description' => __( 'If managing stock, this controls if backorders are allowed.', 'woocommerce' ),  
  1381. 'type' => 'string',  
  1382. 'default' => 'no',  
  1383. 'enum' => array( 'no', 'notify', 'yes' ),  
  1384. 'context' => array( 'view', 'edit' ),  
  1385. ),  
  1386. 'backorders_allowed' => array( 
  1387. 'description' => __( 'Shows if backorders are allowed.', 'woocommerce' ),  
  1388. 'type' => 'boolean',  
  1389. 'context' => array( 'view', 'edit' ),  
  1390. 'readonly' => true,  
  1391. ),  
  1392. 'backordered' => array( 
  1393. 'description' => __( 'Shows if the product is on backordered.', 'woocommerce' ),  
  1394. 'type' => 'boolean',  
  1395. 'context' => array( 'view', 'edit' ),  
  1396. 'readonly' => true,  
  1397. ),  
  1398. 'sold_individually' => array( 
  1399. 'description' => __( 'Allow one item to be bought in a single order.', 'woocommerce' ),  
  1400. 'type' => 'boolean',  
  1401. 'default' => false,  
  1402. 'context' => array( 'view', 'edit' ),  
  1403. ),  
  1404. 'weight' => array( 
  1405. /** translators: %s: weight unit */ 
  1406. 'description' => sprintf( __( 'Product weight (%s).', 'woocommerce' ), $weight_unit ),  
  1407. 'type' => 'string',  
  1408. 'context' => array( 'view', 'edit' ),  
  1409. ),  
  1410. 'dimensions' => array( 
  1411. 'description' => __( 'Product dimensions.', 'woocommerce' ),  
  1412. 'type' => 'object',  
  1413. 'context' => array( 'view', 'edit' ),  
  1414. 'properties' => array( 
  1415. 'length' => array( 
  1416. /** translators: %s: dimension unit */ 
  1417. 'description' => sprintf( __( 'Product length (%s).', 'woocommerce' ), $dimension_unit ),  
  1418. 'type' => 'string',  
  1419. 'context' => array( 'view', 'edit' ),  
  1420. ),  
  1421. 'width' => array( 
  1422. /** translators: %s: dimension unit */ 
  1423. 'description' => sprintf( __( 'Product width (%s).', 'woocommerce' ), $dimension_unit ),  
  1424. 'type' => 'string',  
  1425. 'context' => array( 'view', 'edit' ),  
  1426. ),  
  1427. 'height' => array( 
  1428. /** translators: %s: dimension unit */ 
  1429. 'description' => sprintf( __( 'Product height (%s).', 'woocommerce' ), $dimension_unit ),  
  1430. 'type' => 'string',  
  1431. 'context' => array( 'view', 'edit' ),  
  1432. ),  
  1433. ),  
  1434. ),  
  1435. 'shipping_required' => array( 
  1436. 'description' => __( 'Shows if the product need to be shipped.', 'woocommerce' ),  
  1437. 'type' => 'boolean',  
  1438. 'context' => array( 'view', 'edit' ),  
  1439. 'readonly' => true,  
  1440. ),  
  1441. 'shipping_taxable' => array( 
  1442. 'description' => __( 'Shows whether or not the product shipping is taxable.', 'woocommerce' ),  
  1443. 'type' => 'boolean',  
  1444. 'context' => array( 'view', 'edit' ),  
  1445. 'readonly' => true,  
  1446. ),  
  1447. 'shipping_class' => array( 
  1448. 'description' => __( 'Shipping class slug.', 'woocommerce' ),  
  1449. 'type' => 'string',  
  1450. 'context' => array( 'view', 'edit' ),  
  1451. ),  
  1452. 'shipping_class_id' => array( 
  1453. 'description' => __( 'Shipping class ID.', 'woocommerce' ),  
  1454. 'type' => 'string',  
  1455. 'context' => array( 'view', 'edit' ),  
  1456. 'readonly' => true,  
  1457. ),  
  1458. 'reviews_allowed' => array( 
  1459. 'description' => __( 'Allow reviews.', 'woocommerce' ),  
  1460. 'type' => 'boolean',  
  1461. 'default' => true,  
  1462. 'context' => array( 'view', 'edit' ),  
  1463. ),  
  1464. 'average_rating' => array( 
  1465. 'description' => __( 'Reviews average rating.', 'woocommerce' ),  
  1466. 'type' => 'string',  
  1467. 'context' => array( 'view', 'edit' ),  
  1468. 'readonly' => true,  
  1469. ),  
  1470. 'rating_count' => array( 
  1471. 'description' => __( 'Amount of reviews that the product have.', 'woocommerce' ),  
  1472. 'type' => 'integer',  
  1473. 'context' => array( 'view', 'edit' ),  
  1474. 'readonly' => true,  
  1475. ),  
  1476. 'related_ids' => array( 
  1477. 'description' => __( 'List of related products IDs.', 'woocommerce' ),  
  1478. 'type' => 'array',  
  1479. 'items' => array( 
  1480. 'type' => 'integer',  
  1481. ),  
  1482. 'context' => array( 'view', 'edit' ),  
  1483. 'readonly' => true,  
  1484. ),  
  1485. 'upsell_ids' => array( 
  1486. 'description' => __( 'List of up-sell products IDs.', 'woocommerce' ),  
  1487. 'type' => 'array',  
  1488. 'items' => array( 
  1489. 'type' => 'integer',  
  1490. ),  
  1491. 'context' => array( 'view', 'edit' ),  
  1492. ),  
  1493. 'cross_sell_ids' => array( 
  1494. 'description' => __( 'List of cross-sell products IDs.', 'woocommerce' ),  
  1495. 'type' => 'array',  
  1496. 'items' => array( 
  1497. 'type' => 'integer',  
  1498. ),  
  1499. 'context' => array( 'view', 'edit' ),  
  1500. ),  
  1501. 'parent_id' => array( 
  1502. 'description' => __( 'Product parent ID.', 'woocommerce' ),  
  1503. 'type' => 'integer',  
  1504. 'context' => array( 'view', 'edit' ),  
  1505. ),  
  1506. 'purchase_note' => array( 
  1507. 'description' => __( 'Optional note to send the customer after purchase.', 'woocommerce' ),  
  1508. 'type' => 'string',  
  1509. 'context' => array( 'view', 'edit' ),  
  1510. ),  
  1511. 'categories' => array( 
  1512. 'description' => __( 'List of categories.', 'woocommerce' ),  
  1513. 'type' => 'array',  
  1514. 'context' => array( 'view', 'edit' ),  
  1515. 'items' => array( 
  1516. 'type' => 'object',  
  1517. 'properties' => array( 
  1518. 'id' => array( 
  1519. 'description' => __( 'Category ID.', 'woocommerce' ),  
  1520. 'type' => 'integer',  
  1521. 'context' => array( 'view', 'edit' ),  
  1522. ),  
  1523. 'name' => array( 
  1524. 'description' => __( 'Category name.', 'woocommerce' ),  
  1525. 'type' => 'string',  
  1526. 'context' => array( 'view', 'edit' ),  
  1527. 'readonly' => true,  
  1528. ),  
  1529. 'slug' => array( 
  1530. 'description' => __( 'Category slug.', 'woocommerce' ),  
  1531. 'type' => 'string',  
  1532. 'context' => array( 'view', 'edit' ),  
  1533. 'readonly' => true,  
  1534. ),  
  1535. ),  
  1536. ),  
  1537. ),  
  1538. 'tags' => array( 
  1539. 'description' => __( 'List of tags.', 'woocommerce' ),  
  1540. 'type' => 'array',  
  1541. 'context' => array( 'view', 'edit' ),  
  1542. 'items' => array( 
  1543. 'type' => 'object',  
  1544. 'properties' => array( 
  1545. 'id' => array( 
  1546. 'description' => __( 'Tag ID.', 'woocommerce' ),  
  1547. 'type' => 'integer',  
  1548. 'context' => array( 'view', 'edit' ),  
  1549. ),  
  1550. 'name' => array( 
  1551. 'description' => __( 'Tag name.', 'woocommerce' ),  
  1552. 'type' => 'string',  
  1553. 'context' => array( 'view', 'edit' ),  
  1554. 'readonly' => true,  
  1555. ),  
  1556. 'slug' => array( 
  1557. 'description' => __( 'Tag slug.', 'woocommerce' ),  
  1558. 'type' => 'string',  
  1559. 'context' => array( 'view', 'edit' ),  
  1560. 'readonly' => true,  
  1561. ),  
  1562. ),  
  1563. ),  
  1564. ),  
  1565. 'images' => array( 
  1566. 'description' => __( 'List of images.', 'woocommerce' ),  
  1567. 'type' => 'object',  
  1568. 'context' => array( 'view', 'edit' ),  
  1569. 'items' => array( 
  1570. 'type' => 'object',  
  1571. 'properties' => array( 
  1572. 'id' => array( 
  1573. 'description' => __( 'Image ID.', 'woocommerce' ),  
  1574. 'type' => 'integer',  
  1575. 'context' => array( 'view', 'edit' ),  
  1576. ),  
  1577. 'date_created' => array( 
  1578. 'description' => __( "The date the image was created, in the site's timezone.", 'woocommerce' ),  
  1579. 'type' => 'date-time',  
  1580. 'context' => array( 'view', 'edit' ),  
  1581. 'readonly' => true,  
  1582. ),  
  1583. 'date_created_gmt' => array( 
  1584. 'description' => __( "The date the image was created, as GMT.", 'woocommerce' ),  
  1585. 'type' => 'date-time',  
  1586. 'context' => array( 'view', 'edit' ),  
  1587. 'readonly' => true,  
  1588. ),  
  1589. 'date_modified' => array( 
  1590. 'description' => __( "The date the image was last modified, in the site's timezone.", 'woocommerce' ),  
  1591. 'type' => 'date-time',  
  1592. 'context' => array( 'view', 'edit' ),  
  1593. 'readonly' => true,  
  1594. ),  
  1595. 'date_modified_gmt' => array( 
  1596. 'description' => __( "The date the image was last modified, as GMT.", 'woocommerce' ),  
  1597. 'type' => 'date-time',  
  1598. 'context' => array( 'view', 'edit' ),  
  1599. 'readonly' => true,  
  1600. ),  
  1601. 'src' => array( 
  1602. 'description' => __( 'Image URL.', 'woocommerce' ),  
  1603. 'type' => 'string',  
  1604. 'format' => 'uri',  
  1605. 'context' => array( 'view', 'edit' ),  
  1606. ),  
  1607. 'name' => array( 
  1608. 'description' => __( 'Image name.', 'woocommerce' ),  
  1609. 'type' => 'string',  
  1610. 'context' => array( 'view', 'edit' ),  
  1611. ),  
  1612. 'alt' => array( 
  1613. 'description' => __( 'Image alternative text.', 'woocommerce' ),  
  1614. 'type' => 'string',  
  1615. 'context' => array( 'view', 'edit' ),  
  1616. ),  
  1617. 'position' => array( 
  1618. 'description' => __( 'Image position. 0 means that the image is featured.', 'woocommerce' ),  
  1619. 'type' => 'integer',  
  1620. 'context' => array( 'view', 'edit' ),  
  1621. ),  
  1622. ),  
  1623. ),  
  1624. ),  
  1625. 'attributes' => array( 
  1626. 'description' => __( 'List of attributes.', 'woocommerce' ),  
  1627. 'type' => 'array',  
  1628. 'context' => array( 'view', 'edit' ),  
  1629. 'items' => array( 
  1630. 'type' => 'object',  
  1631. 'properties' => array( 
  1632. 'id' => array( 
  1633. 'description' => __( 'Attribute ID.', 'woocommerce' ),  
  1634. 'type' => 'integer',  
  1635. 'context' => array( 'view', 'edit' ),  
  1636. ),  
  1637. 'name' => array( 
  1638. 'description' => __( 'Attribute name.', 'woocommerce' ),  
  1639. 'type' => 'string',  
  1640. 'context' => array( 'view', 'edit' ),  
  1641. ),  
  1642. 'position' => array( 
  1643. 'description' => __( 'Attribute position.', 'woocommerce' ),  
  1644. 'type' => 'integer',  
  1645. 'context' => array( 'view', 'edit' ),  
  1646. ),  
  1647. 'visible' => array( 
  1648. 'description' => __( "Define if the attribute is visible on the \"Additional information\" tab in the product's page.", 'woocommerce' ),  
  1649. 'type' => 'boolean',  
  1650. 'default' => false,  
  1651. 'context' => array( 'view', 'edit' ),  
  1652. ),  
  1653. 'variation' => array( 
  1654. 'description' => __( 'Define if the attribute can be used as variation.', 'woocommerce' ),  
  1655. 'type' => 'boolean',  
  1656. 'default' => false,  
  1657. 'context' => array( 'view', 'edit' ),  
  1658. ),  
  1659. 'options' => array( 
  1660. 'description' => __( 'List of available term names of the attribute.', 'woocommerce' ),  
  1661. 'type' => 'array',  
  1662. 'context' => array( 'view', 'edit' ),  
  1663. ),  
  1664. ),  
  1665. ),  
  1666. ),  
  1667. 'default_attributes' => array( 
  1668. 'description' => __( 'Defaults variation attributes.', 'woocommerce' ),  
  1669. 'type' => 'array',  
  1670. 'context' => array( 'view', 'edit' ),  
  1671. 'items' => array( 
  1672. 'type' => 'object',  
  1673. 'properties' => array( 
  1674. 'id' => array( 
  1675. 'description' => __( 'Attribute ID.', 'woocommerce' ),  
  1676. 'type' => 'integer',  
  1677. 'context' => array( 'view', 'edit' ),  
  1678. ),  
  1679. 'name' => array( 
  1680. 'description' => __( 'Attribute name.', 'woocommerce' ),  
  1681. 'type' => 'string',  
  1682. 'context' => array( 'view', 'edit' ),  
  1683. ),  
  1684. 'option' => array( 
  1685. 'description' => __( 'Selected attribute term name.', 'woocommerce' ),  
  1686. 'type' => 'string',  
  1687. 'context' => array( 'view', 'edit' ),  
  1688. ),  
  1689. ),  
  1690. ),  
  1691. ),  
  1692. 'variations' => array( 
  1693. 'description' => __( 'List of variations IDs.', 'woocommerce' ),  
  1694. 'type' => 'array',  
  1695. 'context' => array( 'view', 'edit' ),  
  1696. 'items' => array( 
  1697. 'type' => 'integer',  
  1698. ),  
  1699. 'readonly' => true,  
  1700. ),  
  1701. 'grouped_products' => array( 
  1702. 'description' => __( 'List of grouped products ID.', 'woocommerce' ),  
  1703. 'type' => 'array',  
  1704. 'items' => array( 
  1705. 'type' => 'integer',  
  1706. ),  
  1707. 'context' => array( 'view', 'edit' ),  
  1708. 'readonly' => true,  
  1709. ),  
  1710. 'menu_order' => array( 
  1711. 'description' => __( 'Menu order, used to custom sort products.', 'woocommerce' ),  
  1712. 'type' => 'integer',  
  1713. 'context' => array( 'view', 'edit' ),  
  1714. ),  
  1715. 'meta_data' => array( 
  1716. 'description' => __( 'Meta data.', 'woocommerce' ),  
  1717. 'type' => 'array',  
  1718. 'context' => array( 'view', 'edit' ),  
  1719. 'items' => array( 
  1720. 'type' => 'object',  
  1721. 'properties' => array( 
  1722. 'id' => array( 
  1723. 'description' => __( 'Meta ID.', 'woocommerce' ),  
  1724. 'type' => 'integer',  
  1725. 'context' => array( 'view', 'edit' ),  
  1726. 'readonly' => true,  
  1727. ),  
  1728. 'key' => array( 
  1729. 'description' => __( 'Meta key.', 'woocommerce' ),  
  1730. 'type' => 'string',  
  1731. 'context' => array( 'view', 'edit' ),  
  1732. ),  
  1733. 'value' => array( 
  1734. 'description' => __( 'Meta value.', 'woocommerce' ),  
  1735. 'type' => 'string',  
  1736. 'context' => array( 'view', 'edit' ),  
  1737. ),  
  1738. ),  
  1739. ),  
  1740. ),  
  1741. ),  
  1742. ); 
  1743.  
  1744. return $this->add_additional_fields_schema( $schema ); 
  1745.  
  1746. /** 
  1747. * Get the query params for collections of attachments. 
  1748. * @return array 
  1749. */ 
  1750. public function get_collection_params() { 
  1751. $params = parent::get_collection_params(); 
  1752.  
  1753. $params['slug'] = array( 
  1754. 'description' => __( 'Limit result set to products with a specific slug.', 'woocommerce' ),  
  1755. 'type' => 'string',  
  1756. 'validate_callback' => 'rest_validate_request_arg',  
  1757. ); 
  1758. $params['status'] = array( 
  1759. 'default' => 'any',  
  1760. 'description' => __( 'Limit result set to products assigned a specific status.', 'woocommerce' ),  
  1761. 'type' => 'string',  
  1762. 'enum' => array_merge( array( 'any' ), array_keys( get_post_statuses() ) ),  
  1763. 'sanitize_callback' => 'sanitize_key',  
  1764. 'validate_callback' => 'rest_validate_request_arg',  
  1765. ); 
  1766. $params['type'] = array( 
  1767. 'description' => __( 'Limit result set to products assigned a specific type.', 'woocommerce' ),  
  1768. 'type' => 'string',  
  1769. 'enum' => array_keys( wc_get_product_types() ),  
  1770. 'sanitize_callback' => 'sanitize_key',  
  1771. 'validate_callback' => 'rest_validate_request_arg',  
  1772. ); 
  1773. $params['sku'] = array( 
  1774. 'description' => __( 'Limit result set to products with a specific SKU.', 'woocommerce' ),  
  1775. 'type' => 'string',  
  1776. 'sanitize_callback' => 'sanitize_text_field',  
  1777. 'validate_callback' => 'rest_validate_request_arg',  
  1778. ); 
  1779. $params['featured'] = array( 
  1780. 'description' => __( 'Limit result set to featured products.', 'woocommerce' ),  
  1781. 'type' => 'boolean',  
  1782. 'sanitize_callback' => 'wc_string_to_bool',  
  1783. 'validate_callback' => 'rest_validate_request_arg',  
  1784. ); 
  1785. $params['category'] = array( 
  1786. 'description' => __( 'Limit result set to products assigned a specific category ID.', 'woocommerce' ),  
  1787. 'type' => 'string',  
  1788. 'sanitize_callback' => 'wp_parse_id_list',  
  1789. 'validate_callback' => 'rest_validate_request_arg',  
  1790. ); 
  1791. $params['tag'] = array( 
  1792. 'description' => __( 'Limit result set to products assigned a specific tag ID.', 'woocommerce' ),  
  1793. 'type' => 'string',  
  1794. 'sanitize_callback' => 'wp_parse_id_list',  
  1795. 'validate_callback' => 'rest_validate_request_arg',  
  1796. ); 
  1797. $params['shipping_class'] = array( 
  1798. 'description' => __( 'Limit result set to products assigned a specific shipping class ID.', 'woocommerce' ),  
  1799. 'type' => 'string',  
  1800. 'sanitize_callback' => 'wp_parse_id_list',  
  1801. 'validate_callback' => 'rest_validate_request_arg',  
  1802. ); 
  1803. $params['attribute'] = array( 
  1804. 'description' => __( 'Limit result set to products with a specific attribute.', 'woocommerce' ),  
  1805. 'type' => 'string',  
  1806. 'sanitize_callback' => 'sanitize_text_field',  
  1807. 'validate_callback' => 'rest_validate_request_arg',  
  1808. ); 
  1809. $params['attribute_term'] = array( 
  1810. 'description' => __( 'Limit result set to products with a specific attribute term ID (required an assigned attribute).', 'woocommerce' ),  
  1811. 'type' => 'string',  
  1812. 'sanitize_callback' => 'wp_parse_id_list',  
  1813. 'validate_callback' => 'rest_validate_request_arg',  
  1814. ); 
  1815.  
  1816. if ( wc_tax_enabled() ) { 
  1817. $params['tax_class'] = array( 
  1818. 'description' => __( 'Limit result set to products with a specific tax class.', 'woocommerce' ),  
  1819. 'type' => 'string',  
  1820. 'enum' => array_merge( array( 'standard' ), WC_Tax::get_tax_class_slugs() ),  
  1821. 'sanitize_callback' => 'sanitize_text_field',  
  1822. 'validate_callback' => 'rest_validate_request_arg',  
  1823. ); 
  1824.  
  1825. $params['in_stock'] = array( 
  1826. 'description' => __( 'Limit result set to products in stock or out of stock.', 'woocommerce' ),  
  1827. 'type' => 'boolean',  
  1828. 'sanitize_callback' => 'wc_string_to_bool',  
  1829. 'validate_callback' => 'rest_validate_request_arg',  
  1830. ); 
  1831. $params['on_sale'] = array( 
  1832. 'description' => __( 'Limit result set to products on sale.', 'woocommerce' ),  
  1833. 'type' => 'boolean',  
  1834. 'sanitize_callback' => 'wc_string_to_bool',  
  1835. 'validate_callback' => 'rest_validate_request_arg',  
  1836. ); 
  1837. $params['min_price'] = array( 
  1838. 'description' => __( 'Limit result set to products based on a minimum price.', 'woocommerce' ),  
  1839. 'type' => 'string',  
  1840. 'sanitize_callback' => 'sanitize_text_field',  
  1841. 'validate_callback' => 'rest_validate_request_arg',  
  1842. ); 
  1843. $params['max_price'] = array( 
  1844. 'description' => __( 'Limit result set to products based on a maximum price.', 'woocommerce' ),  
  1845. 'type' => 'string',  
  1846. 'sanitize_callback' => 'sanitize_text_field',  
  1847. 'validate_callback' => 'rest_validate_request_arg',  
  1848. ); 
  1849.  
  1850. return $params;