WC_REST_Product_Variations_Controller

REST API variations controller class.

Defined (1)

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

/includes/api/class-wc-rest-product-variations-controller.php  
  1. class WC_REST_Product_Variations_Controller extends WC_REST_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/(?P<product_id>[\d]+)/variations'; 
  14.  
  15. /** 
  16. * Post type. 
  17. * @var string 
  18. */ 
  19. protected $post_type = 'product_variation'; 
  20.  
  21. /** 
  22. * Initialize product actions (parent). 
  23. */ 
  24. public function __construct() { 
  25. add_filter( "woocommerce_rest_{$this->post_type}_query", array( $this, 'add_product_id' ), 9, 2 ); 
  26. parent::__construct(); 
  27.  
  28. /** 
  29. * Register the routes for products. 
  30. */ 
  31. public function register_routes() { 
  32. register_rest_route( $this->namespace, '/' . $this->rest_base, array( 
  33. 'args' => array( 
  34. 'product_id' => array( 
  35. 'description' => __( 'Unique identifier for the variable product.', 'woocommerce' ),  
  36. 'type' => 'integer',  
  37. ),  
  38. ),  
  39. array( 
  40. 'methods' => WP_REST_Server::READABLE,  
  41. 'callback' => array( $this, 'get_items' ),  
  42. 'permission_callback' => array( $this, 'get_items_permissions_check' ),  
  43. 'args' => $this->get_collection_params(),  
  44. ),  
  45. array( 
  46. 'methods' => WP_REST_Server::CREATABLE,  
  47. 'callback' => array( $this, 'create_item' ),  
  48. 'permission_callback' => array( $this, 'create_item_permissions_check' ),  
  49. 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),  
  50. ),  
  51. 'schema' => array( $this, 'get_public_item_schema' ),  
  52. ) ); 
  53. register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array( 
  54. 'args' => array( 
  55. 'product_id' => array( 
  56. 'description' => __( 'Unique identifier for the variable product.', 'woocommerce' ),  
  57. 'type' => 'integer',  
  58. ),  
  59. 'id' => array( 
  60. 'description' => __( 'Unique identifier for the variation.', 'woocommerce' ),  
  61. 'type' => 'integer',  
  62. ),  
  63. ),  
  64. array( 
  65. 'methods' => WP_REST_Server::READABLE,  
  66. 'callback' => array( $this, 'get_item' ),  
  67. 'permission_callback' => array( $this, 'get_item_permissions_check' ),  
  68. 'args' => array( 
  69. 'context' => $this->get_context_param( array( 'default' => 'view' ) ),  
  70. ),  
  71. ),  
  72. array( 
  73. 'methods' => WP_REST_Server::EDITABLE,  
  74. 'callback' => array( $this, 'update_item' ),  
  75. 'permission_callback' => array( $this, 'update_item_permissions_check' ),  
  76. 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),  
  77. ),  
  78. array( 
  79. 'methods' => WP_REST_Server::DELETABLE,  
  80. 'callback' => array( $this, 'delete_item' ),  
  81. 'permission_callback' => array( $this, 'delete_item_permissions_check' ),  
  82. 'args' => array( 
  83. 'force' => array( 
  84. 'default' => false,  
  85. 'type' => 'boolean',  
  86. 'description' => __( 'Whether to bypass trash and force deletion.', 'woocommerce' ),  
  87. ),  
  88. ),  
  89. ),  
  90. 'schema' => array( $this, 'get_public_item_schema' ),  
  91. ) ); 
  92. register_rest_route( $this->namespace, '/' . $this->rest_base . '/batch', array( 
  93. 'args' => array( 
  94. 'product_id' => array( 
  95. 'description' => __( 'Unique identifier for the variable product.', 'woocommerce' ),  
  96. 'type' => 'integer',  
  97. ),  
  98. ),  
  99. array( 
  100. 'methods' => WP_REST_Server::EDITABLE,  
  101. 'callback' => array( $this, 'batch_items' ),  
  102. 'permission_callback' => array( $this, 'batch_items_permissions_check' ),  
  103. 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),  
  104. ),  
  105. 'schema' => array( $this, 'get_public_batch_schema' ),  
  106. ) ); 
  107.  
  108. /** 
  109. * Get object. 
  110. * @since 3.0.0 
  111. * @param int $id Object ID. 
  112. * @return WC_Data 
  113. */ 
  114. protected function get_object( $id ) { 
  115. return wc_get_product( $id ); 
  116.  
  117. /** 
  118. * Prepare a single variation output for response. 
  119. * @since 3.0.0 
  120. * @param WC_Data $object Object data. 
  121. * @param WP_REST_Request $request Request object. 
  122. * @return WP_REST_Response 
  123. */ 
  124. public function prepare_object_for_response( $object, $request ) { 
  125. $data = array( 
  126. 'id' => $object->get_id(),  
  127. 'date_created' => wc_rest_prepare_date_response( $object->get_date_created(), false ),  
  128. 'date_created_gmt' => wc_rest_prepare_date_response( $object->get_date_created() ),  
  129. 'date_modified' => wc_rest_prepare_date_response( $object->get_date_modified(), false ),  
  130. 'date_modified_gmt' => wc_rest_prepare_date_response( $object->get_date_modified() ),  
  131. 'description' => wc_format_content( $object->get_description() ),  
  132. 'permalink' => $object->get_permalink(),  
  133. 'sku' => $object->get_sku(),  
  134. 'price' => $object->get_price(),  
  135. 'regular_price' => $object->get_regular_price(),  
  136. 'sale_price' => $object->get_sale_price(),  
  137. 'date_on_sale_from' => wc_rest_prepare_date_response( $object->get_date_on_sale_from(), false ),  
  138. 'date_on_sale_from_gmt' => wc_rest_prepare_date_response( $object->get_date_on_sale_from() ),  
  139. 'date_on_sale_to' => wc_rest_prepare_date_response( $object->get_date_on_sale_to(), false ),  
  140. 'date_on_sale_to_gmt' => wc_rest_prepare_date_response( $object->get_date_on_sale_to() ),  
  141. 'on_sale' => $object->is_on_sale(),  
  142. 'visible' => $object->is_visible(),  
  143. 'purchasable' => $object->is_purchasable(),  
  144. 'virtual' => $object->is_virtual(),  
  145. 'downloadable' => $object->is_downloadable(),  
  146. 'downloads' => $this->get_downloads( $object ),  
  147. 'download_limit' => '' !== $object->get_download_limit() ? (int) $object->get_download_limit() : -1,  
  148. 'download_expiry' => '' !== $object->get_download_expiry() ? (int) $object->get_download_expiry() : -1,  
  149. 'tax_status' => $object->get_tax_status(),  
  150. 'tax_class' => $object->get_tax_class(),  
  151. 'manage_stock' => $object->managing_stock(),  
  152. 'stock_quantity' => $object->get_stock_quantity(),  
  153. 'in_stock' => $object->is_in_stock(),  
  154. 'backorders' => $object->get_backorders(),  
  155. 'backorders_allowed' => $object->backorders_allowed(),  
  156. 'backordered' => $object->is_on_backorder(),  
  157. 'weight' => $object->get_weight(),  
  158. 'dimensions' => array( 
  159. 'length' => $object->get_length(),  
  160. 'width' => $object->get_width(),  
  161. 'height' => $object->get_height(),  
  162. ),  
  163. 'shipping_class' => $object->get_shipping_class(),  
  164. 'shipping_class_id' => $object->get_shipping_class_id(),  
  165. 'image' => current( $this->get_images( $object ) ),  
  166. 'attributes' => $this->get_attributes( $object ),  
  167. 'menu_order' => $object->get_menu_order(),  
  168. 'meta_data' => $object->get_meta_data(),  
  169. ); 
  170.  
  171. $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; 
  172. $data = $this->add_additional_fields_to_object( $data, $request ); 
  173. $data = $this->filter_response_by_context( $data, $context ); 
  174. $response = rest_ensure_response( $data ); 
  175. $response->add_links( $this->prepare_links( $object, $request ) ); 
  176.  
  177. /** 
  178. * Filter the data for a response. 
  179. * The dynamic portion of the hook name, $this->post_type,  
  180. * refers to object type being prepared for the response. 
  181. * @param WP_REST_Response $response The response object. 
  182. * @param WC_Data $object Object data. 
  183. * @param WP_REST_Request $request Request object. 
  184. */ 
  185. return apply_filters( "woocommerce_rest_prepare_{$this->post_type}_object", $response, $object, $request ); 
  186.  
  187. /** 
  188. * Prepare objects query. 
  189. * @since 3.0.0 
  190. * @param WP_REST_Request $request Full details about the request. 
  191. * @return array 
  192. */ 
  193. protected function prepare_objects_query( $request ) { 
  194. $args = parent::prepare_objects_query( $request ); 
  195.  
  196. $args['post_parent'] = $request['product_id']; 
  197.  
  198. return $args; 
  199.  
  200. /** 
  201. * Prepare a single variation for create or update. 
  202. * @param WP_REST_Request $request Request object. 
  203. * @param bool $creating If is creating a new object. 
  204. * @return WP_Error|WC_Data 
  205. */ 
  206. protected function prepare_object_for_database( $request, $creating = false ) { 
  207. if ( isset( $request['id'] ) ) { 
  208. $variation = wc_get_product( absint( $request['id'] ) ); 
  209. } else { 
  210. $variation = new WC_Product_Variation(); 
  211.  
  212. $variation->set_parent_id( absint( $request['product_id'] ) ); 
  213.  
  214. // Status. 
  215. if ( isset( $request['visible'] ) ) { 
  216. $variation->set_status( false === $request['visible'] ? 'private' : 'publish' ); 
  217.  
  218. // SKU. 
  219. if ( isset( $request['sku'] ) ) { 
  220. $variation->set_sku( wc_clean( $request['sku'] ) ); 
  221.  
  222. // Thumbnail. 
  223. if ( isset( $request['image'] ) ) { 
  224. if ( is_array( $request['image'] ) ) { 
  225. $image = $request['image']; 
  226. if ( is_array( $image ) ) { 
  227. $image['position'] = 0; 
  228.  
  229. $variation = $this->set_product_images( $variation, array( $image ) ); 
  230. } else { 
  231. $variation->set_image_id( '' ); 
  232.  
  233. // Virtual variation. 
  234. if ( isset( $request['virtual'] ) ) { 
  235. $variation->set_virtual( $request['virtual'] ); 
  236.  
  237. // Downloadable variation. 
  238. if ( isset( $request['downloadable'] ) ) { 
  239. $variation->set_downloadable( $request['downloadable'] ); 
  240.  
  241. // Downloads. 
  242. if ( $variation->get_downloadable() ) { 
  243. // Downloadable files. 
  244. if ( isset( $request['downloads'] ) && is_array( $request['downloads'] ) ) { 
  245. $variation = $this->save_downloadable_files( $variation, $request['downloads'] ); 
  246.  
  247. // Download limit. 
  248. if ( isset( $request['download_limit'] ) ) { 
  249. $variation->set_download_limit( $request['download_limit'] ); 
  250.  
  251. // Download expiry. 
  252. if ( isset( $request['download_expiry'] ) ) { 
  253. $variation->set_download_expiry( $request['download_expiry'] ); 
  254.  
  255. // Shipping data. 
  256. $variation = $this->save_product_shipping_data( $variation, $request ); 
  257.  
  258. // Stock handling. 
  259. if ( isset( $request['manage_stock'] ) ) { 
  260. $variation->set_manage_stock( $request['manage_stock'] ); 
  261.  
  262. if ( isset( $request['in_stock'] ) ) { 
  263. $variation->set_stock_status( true === $request['in_stock'] ? 'instock' : 'outofstock' ); 
  264.  
  265. if ( isset( $request['backorders'] ) ) { 
  266. $variation->set_backorders( $request['backorders'] ); 
  267.  
  268. if ( $variation->get_manage_stock() ) { 
  269. if ( isset( $request['stock_quantity'] ) ) { 
  270. $variation->set_stock_quantity( $request['stock_quantity'] ); 
  271. } elseif ( isset( $request['inventory_delta'] ) ) { 
  272. $stock_quantity = wc_stock_amount( $variation->get_stock_quantity() ); 
  273. $stock_quantity += wc_stock_amount( $request['inventory_delta'] ); 
  274. $variation->set_stock_quantity( $stock_quantity ); 
  275. } else { 
  276. $variation->set_backorders( 'no' ); 
  277. $variation->set_stock_quantity( '' ); 
  278.  
  279. // Regular Price. 
  280. if ( isset( $request['regular_price'] ) ) { 
  281. $variation->set_regular_price( $request['regular_price'] ); 
  282.  
  283. // Sale Price. 
  284. if ( isset( $request['sale_price'] ) ) { 
  285. $variation->set_sale_price( $request['sale_price'] ); 
  286.  
  287. if ( isset( $request['date_on_sale_from'] ) ) { 
  288. $variation->set_date_on_sale_from( $request['date_on_sale_from'] ); 
  289.  
  290. if ( isset( $request['date_on_sale_from_gmt'] ) ) { 
  291. $variation->set_date_on_sale_from( $request['date_on_sale_from_gmt'] ? strtotime( $request['date_on_sale_from_gmt'] ) : null ); 
  292.  
  293. if ( isset( $request['date_on_sale_to'] ) ) { 
  294. $variation->set_date_on_sale_to( $request['date_on_sale_to'] ); 
  295.  
  296. if ( isset( $request['date_on_sale_to_gmt'] ) ) { 
  297. $variation->set_date_on_sale_to( $request['date_on_sale_to_gmt'] ? strtotime( $request['date_on_sale_to_gmt'] ) : null ); 
  298.  
  299. // Tax class. 
  300. if ( isset( $request['tax_class'] ) ) { 
  301. $variation->set_tax_class( $request['tax_class'] ); 
  302.  
  303. // Description. 
  304. if ( isset( $request['description'] ) ) { 
  305. $variation->set_description( wp_kses_post( $request['description'] ) ); 
  306.  
  307. // Update taxonomies. 
  308. if ( isset( $request['attributes'] ) ) { 
  309. $attributes = array(); 
  310. $parent = wc_get_product( $variation->get_parent_id() ); 
  311. $parent_attributes = $parent->get_attributes(); 
  312.  
  313. foreach ( $request['attributes'] as $attribute ) { 
  314. $attribute_id = 0; 
  315. $attribute_name = ''; 
  316.  
  317. // Check ID for global attributes or name for product attributes. 
  318. if ( ! empty( $attribute['id'] ) ) { 
  319. $attribute_id = absint( $attribute['id'] ); 
  320. $attribute_name = wc_attribute_taxonomy_name_by_id( $attribute_id ); 
  321. } elseif ( ! empty( $attribute['name'] ) ) { 
  322. $attribute_name = sanitize_title( $attribute['name'] ); 
  323.  
  324. if ( ! $attribute_id && ! $attribute_name ) { 
  325. continue; 
  326.  
  327. if ( ! isset( $parent_attributes[ $attribute_name ] ) || ! $parent_attributes[ $attribute_name ]->get_variation() ) { 
  328. continue; 
  329.  
  330. $attribute_key = sanitize_title( $parent_attributes[ $attribute_name ]->get_name() ); 
  331. $attribute_value = isset( $attribute['option'] ) ? wc_clean( stripslashes( $attribute['option'] ) ) : ''; 
  332.  
  333. if ( $parent_attributes[ $attribute_name ]->is_taxonomy() ) { 
  334. // If dealing with a taxonomy, we need to get the slug from the name posted to the API. 
  335. $term = get_term_by( 'name', $attribute_value, $attribute_name ); 
  336.  
  337. if ( $term && ! is_wp_error( $term ) ) { 
  338. $attribute_value = $term->slug; 
  339. } else { 
  340. $attribute_value = sanitize_title( $attribute_value ); 
  341.  
  342. $attributes[ $attribute_key ] = $attribute_value; 
  343.  
  344. $variation->set_attributes( $attributes ); 
  345.  
  346. // Menu order. 
  347. if ( $request['menu_order'] ) { 
  348. $variation->set_menu_order( $request['menu_order'] ); 
  349.  
  350. // Meta data. 
  351. if ( is_array( $request['meta_data'] ) ) { 
  352. foreach ( $request['meta_data'] as $meta ) { 
  353. $variation->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' ); 
  354.  
  355. /** 
  356. * Filters an object before it is inserted via the REST API. 
  357. * The dynamic portion of the hook name, `$this->post_type`,  
  358. * refers to the object type slug. 
  359. * @param WC_Data $variation Object object. 
  360. * @param WP_REST_Request $request Request object. 
  361. * @param bool $creating If is creating a new object. 
  362. */ 
  363. return apply_filters( "woocommerce_rest_pre_insert_{$this->post_type}_object", $variation, $request, $creating ); 
  364.  
  365. /** 
  366. * Clear caches here so in sync with any new variations. 
  367. * @param WC_Data $object Object data. 
  368. */ 
  369. public function clear_transients( $object ) { 
  370. wc_delete_product_transients( $object->get_parent_id() ); 
  371. wp_cache_delete( 'product-' . $object->get_parent_id(), 'products' ); 
  372.  
  373. /** 
  374. * Delete a variation. 
  375. * @param WP_REST_Request $request Full details about the request 
  376. * @return WP_Error|boolean 
  377. */ 
  378. public function delete_item( $request ) { 
  379. $id = absint( is_array( $request['id'] ) ? $request['id']['id'] : $request['id'] ); 
  380. $force = (bool) $request['force']; 
  381. $object = $this->get_object( (int) $request['id'] ); 
  382. $result = false; 
  383.  
  384. if ( ! $object || 0 === $object->get_id() ) { 
  385. return new WP_Error( "woocommerce_rest_{$this->post_type}_invalid_id", __( 'Invalid ID.', 'woocommerce' ), array( 'status' => 404 ) ); 
  386.  
  387. $supports_trash = EMPTY_TRASH_DAYS > 0 && is_callable( array( $object, 'get_status' ) ); 
  388.  
  389. /** 
  390. * Filter whether an object is trashable. 
  391. * Return false to disable trash support for the object. 
  392. * @param boolean $supports_trash Whether the object type support trashing. 
  393. * @param WC_Data $object The object being considered for trashing support. 
  394. */ 
  395. $supports_trash = apply_filters( "woocommerce_rest_{$this->post_type}_object_trashable", $supports_trash, $object ); 
  396.  
  397. if ( ! wc_rest_check_post_permissions( $this->post_type, 'delete', $object->get_id() ) ) { 
  398. /** translators: %s: post type */ 
  399. 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() ) ); 
  400.  
  401. $request->set_param( 'context', 'edit' ); 
  402. $response = $this->prepare_object_for_response( $object, $request ); 
  403.  
  404. // If we're forcing, then delete permanently. 
  405. if ( $force ) { 
  406. $object->delete( true ); 
  407. $result = 0 === $object->get_id(); 
  408. } else { 
  409. // If we don't support trashing for this type, error out. 
  410. if ( ! $supports_trash ) { 
  411. /** translators: %s: post type */ 
  412. return new WP_Error( 'woocommerce_rest_trash_not_supported', sprintf( __( 'The %s does not support trashing.', 'woocommerce' ), $this->post_type ), array( 'status' => 501 ) ); 
  413.  
  414. // Otherwise, only trash if we haven't already. 
  415. if ( is_callable( array( $object, 'get_status' ) ) ) { 
  416. if ( 'trash' === $object->get_status() ) { 
  417. /** translators: %s: post type */ 
  418. return new WP_Error( 'woocommerce_rest_already_trashed', sprintf( __( 'The %s has already been deleted.', 'woocommerce' ), $this->post_type ), array( 'status' => 410 ) ); 
  419.  
  420. $object->delete(); 
  421. $result = 'trash' === $object->get_status(); 
  422.  
  423. if ( ! $result ) { 
  424. /** translators: %s: post type */ 
  425. return new WP_Error( 'woocommerce_rest_cannot_delete', sprintf( __( 'The %s cannot be deleted.', 'woocommerce' ), $this->post_type ), array( 'status' => 500 ) ); 
  426.  
  427. // Delete parent product transients. 
  428. if ( 0 !== $object->get_parent_id() ) { 
  429. wc_delete_product_transients( $object->get_parent_id() ); 
  430.  
  431. /** 
  432. * Fires after a single object is deleted or trashed via the REST API. 
  433. * @param WC_Data $object The deleted or trashed object. 
  434. * @param WP_REST_Response $response The response data. 
  435. * @param WP_REST_Request $request The request sent to the API. 
  436. */ 
  437. do_action( "woocommerce_rest_delete_{$this->post_type}_object", $object, $response, $request ); 
  438.  
  439. return $response; 
  440.  
  441. /** 
  442. * Bulk create, update and delete items. 
  443. * @since 3.0.0 
  444. * @param WP_REST_Request $request Full details about the request. 
  445. * @return array Of WP_Error or WP_REST_Response. 
  446. */ 
  447. public function batch_items( $request ) { 
  448. $items = array_filter( $request->get_params() ); 
  449. $params = $request->get_url_params(); 
  450. $product_id = $params['product_id']; 
  451. $body_params = array(); 
  452.  
  453. foreach ( array( 'update', 'create', 'delete' ) as $batch_type ) { 
  454. if ( ! empty( $items[ $batch_type ] ) ) { 
  455. $injected_items = array(); 
  456. foreach ( $items[ $batch_type ] as $item ) { 
  457. $injected_items[] = is_array( $item ) ? array_merge( array( 'product_id' => $product_id ), $item ) : $item; 
  458. $body_params[ $batch_type ] = $injected_items; 
  459.  
  460. $request = new WP_REST_Request( $request->get_method() ); 
  461. $request->set_body_params( $body_params ); 
  462.  
  463. return parent::batch_items( $request ); 
  464.  
  465. /** 
  466. * Prepare links for the request. 
  467. * @param WC_Data $object Object data. 
  468. * @param WP_REST_Request $request Request object. 
  469. * @return array Links for the given post. 
  470. */ 
  471. protected function prepare_links( $object, $request ) { 
  472. $product_id = (int) $request['product_id']; 
  473. $base = str_replace( '(?P<product_id>[\d]+)', $product_id, $this->rest_base ); 
  474. $links = array( 
  475. 'self' => array( 
  476. 'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $base, $object->get_id() ) ),  
  477. ),  
  478. 'collection' => array( 
  479. 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $base ) ),  
  480. ),  
  481. 'up' => array( 
  482. 'href' => rest_url( sprintf( '/%s/products/%d', $this->namespace, $product_id ) ),  
  483. ),  
  484. ); 
  485. return $links; 
  486.  
  487. /** 
  488. * Get the Variation's schema, conforming to JSON Schema. 
  489. * @return array 
  490. */ 
  491. public function get_item_schema() { 
  492. $weight_unit = get_option( 'woocommerce_weight_unit' ); 
  493. $dimension_unit = get_option( 'woocommerce_dimension_unit' ); 
  494. $schema = array( 
  495. '$schema' => 'http://json-schema.org/draft-04/schema#',  
  496. 'title' => $this->post_type,  
  497. 'type' => 'object',  
  498. 'properties' => array( 
  499. 'id' => array( 
  500. 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),  
  501. 'type' => 'integer',  
  502. 'context' => array( 'view', 'edit' ),  
  503. 'readonly' => true,  
  504. ),  
  505. 'date_created' => array( 
  506. 'description' => __( "The date the variation was created, in the site's timezone.", 'woocommerce' ),  
  507. 'type' => 'date-time',  
  508. 'context' => array( 'view', 'edit' ),  
  509. 'readonly' => true,  
  510. ),  
  511. 'date_modified' => array( 
  512. 'description' => __( "The date the variation was last modified, in the site's timezone.", 'woocommerce' ),  
  513. 'type' => 'date-time',  
  514. 'context' => array( 'view', 'edit' ),  
  515. 'readonly' => true,  
  516. ),  
  517. 'description' => array( 
  518. 'description' => __( 'Variation description.', 'woocommerce' ),  
  519. 'type' => 'string',  
  520. 'context' => array( 'view', 'edit' ),  
  521. ),  
  522. 'permalink' => array( 
  523. 'description' => __( 'Variation URL.', 'woocommerce' ),  
  524. 'type' => 'string',  
  525. 'format' => 'uri',  
  526. 'context' => array( 'view', 'edit' ),  
  527. 'readonly' => true,  
  528. ),  
  529. 'sku' => array( 
  530. 'description' => __( 'Unique identifier.', 'woocommerce' ),  
  531. 'type' => 'string',  
  532. 'context' => array( 'view', 'edit' ),  
  533. ),  
  534. 'price' => array( 
  535. 'description' => __( 'Current variation price.', 'woocommerce' ),  
  536. 'type' => 'string',  
  537. 'context' => array( 'view', 'edit' ),  
  538. 'readonly' => true,  
  539. ),  
  540. 'regular_price' => array( 
  541. 'description' => __( 'Variation regular price.', 'woocommerce' ),  
  542. 'type' => 'string',  
  543. 'context' => array( 'view', 'edit' ),  
  544. ),  
  545. 'sale_price' => array( 
  546. 'description' => __( 'Variation sale price.', 'woocommerce' ),  
  547. 'type' => 'string',  
  548. 'context' => array( 'view', 'edit' ),  
  549. ),  
  550. 'date_on_sale_from' => array( 
  551. 'description' => __( "Start date of sale price, in the site's timezone.", 'woocommerce' ),  
  552. 'type' => 'date-time',  
  553. 'context' => array( 'view', 'edit' ),  
  554. ),  
  555. 'date_on_sale_from_gmt' => array( 
  556. 'description' => __( 'Start date of sale price, as GMT.', 'woocommerce' ),  
  557. 'type' => 'date-time',  
  558. 'context' => array( 'view', 'edit' ),  
  559. ),  
  560. 'date_on_sale_to' => array( 
  561. 'description' => __( "End date of sale price, in the site's timezone.", 'woocommerce' ),  
  562. 'type' => 'date-time',  
  563. 'context' => array( 'view', 'edit' ),  
  564. ),  
  565. 'date_on_sale_to_gmt' => array( 
  566. 'description' => __( "End date of sale price, in the site's timezone.", 'woocommerce' ),  
  567. 'type' => 'date-time',  
  568. 'context' => array( 'view', 'edit' ),  
  569. ),  
  570. 'on_sale' => array( 
  571. 'description' => __( 'Shows if the variation is on sale.', 'woocommerce' ),  
  572. 'type' => 'boolean',  
  573. 'context' => array( 'view', 'edit' ),  
  574. 'readonly' => true,  
  575. ),  
  576. 'visible' => array( 
  577. 'description' => __( "Define if the attribute is visible on the \"Additional information\" tab in the product's page.", 'woocommerce' ),  
  578. 'type' => 'boolean',  
  579. 'default' => true,  
  580. 'context' => array( 'view', 'edit' ),  
  581. ),  
  582. 'purchasable' => array( 
  583. 'description' => __( 'Shows if the variation can be bought.', 'woocommerce' ),  
  584. 'type' => 'boolean',  
  585. 'context' => array( 'view', 'edit' ),  
  586. 'readonly' => true,  
  587. ),  
  588. 'virtual' => array( 
  589. 'description' => __( 'If the variation is virtual.', 'woocommerce' ),  
  590. 'type' => 'boolean',  
  591. 'default' => false,  
  592. 'context' => array( 'view', 'edit' ),  
  593. ),  
  594. 'downloadable' => array( 
  595. 'description' => __( 'If the variation is downloadable.', 'woocommerce' ),  
  596. 'type' => 'boolean',  
  597. 'default' => false,  
  598. 'context' => array( 'view', 'edit' ),  
  599. ),  
  600. 'downloads' => array( 
  601. 'description' => __( 'List of downloadable files.', 'woocommerce' ),  
  602. 'type' => 'array',  
  603. 'context' => array( 'view', 'edit' ),  
  604. 'items' => array( 
  605. 'type' => 'object',  
  606. 'properties' => array( 
  607. 'id' => array( 
  608. 'description' => __( 'File MD5 hash.', 'woocommerce' ),  
  609. 'type' => 'string',  
  610. 'context' => array( 'view', 'edit' ),  
  611. 'readonly' => true,  
  612. ),  
  613. 'name' => array( 
  614. 'description' => __( 'File name.', 'woocommerce' ),  
  615. 'type' => 'string',  
  616. 'context' => array( 'view', 'edit' ),  
  617. ),  
  618. 'file' => array( 
  619. 'description' => __( 'File URL.', 'woocommerce' ),  
  620. 'type' => 'string',  
  621. 'context' => array( 'view', 'edit' ),  
  622. ),  
  623. ),  
  624. ),  
  625. ),  
  626. 'download_limit' => array( 
  627. 'description' => __( 'Number of times downloadable files can be downloaded after purchase.', 'woocommerce' ),  
  628. 'type' => 'integer',  
  629. 'default' => -1,  
  630. 'context' => array( 'view', 'edit' ),  
  631. ),  
  632. 'download_expiry' => array( 
  633. 'description' => __( 'Number of days until access to downloadable files expires.', 'woocommerce' ),  
  634. 'type' => 'integer',  
  635. 'default' => -1,  
  636. 'context' => array( 'view', 'edit' ),  
  637. ),  
  638. 'tax_status' => array( 
  639. 'description' => __( 'Tax status.', 'woocommerce' ),  
  640. 'type' => 'string',  
  641. 'default' => 'taxable',  
  642. 'enum' => array( 'taxable', 'shipping', 'none' ),  
  643. 'context' => array( 'view', 'edit' ),  
  644. ),  
  645. 'tax_class' => array( 
  646. 'description' => __( 'Tax class.', 'woocommerce' ),  
  647. 'type' => 'string',  
  648. 'context' => array( 'view', 'edit' ),  
  649. ),  
  650. 'manage_stock' => array( 
  651. 'description' => __( 'Stock management at variation level.', 'woocommerce' ),  
  652. 'type' => 'boolean',  
  653. 'default' => false,  
  654. 'context' => array( 'view', 'edit' ),  
  655. ),  
  656. 'stock_quantity' => array( 
  657. 'description' => __( 'Stock quantity.', 'woocommerce' ),  
  658. 'type' => 'integer',  
  659. 'context' => array( 'view', 'edit' ),  
  660. ),  
  661. 'in_stock' => array( 
  662. 'description' => __( 'Controls whether or not the variation is listed as "in stock" or "out of stock" on the frontend.', 'woocommerce' ),  
  663. 'type' => 'boolean',  
  664. 'default' => true,  
  665. 'context' => array( 'view', 'edit' ),  
  666. ),  
  667. 'backorders' => array( 
  668. 'description' => __( 'If managing stock, this controls if backorders are allowed.', 'woocommerce' ),  
  669. 'type' => 'string',  
  670. 'default' => 'no',  
  671. 'enum' => array( 'no', 'notify', 'yes' ),  
  672. 'context' => array( 'view', 'edit' ),  
  673. ),  
  674. 'backorders_allowed' => array( 
  675. 'description' => __( 'Shows if backorders are allowed.', 'woocommerce' ),  
  676. 'type' => 'boolean',  
  677. 'context' => array( 'view', 'edit' ),  
  678. 'readonly' => true,  
  679. ),  
  680. 'backordered' => array( 
  681. 'description' => __( 'Shows if the variation is on backordered.', 'woocommerce' ),  
  682. 'type' => 'boolean',  
  683. 'context' => array( 'view', 'edit' ),  
  684. 'readonly' => true,  
  685. ),  
  686. 'weight' => array( 
  687. /** translators: %s: weight unit */ 
  688. 'description' => sprintf( __( 'Variation weight (%s).', 'woocommerce' ), $weight_unit ),  
  689. 'type' => 'string',  
  690. 'context' => array( 'view', 'edit' ),  
  691. ),  
  692. 'dimensions' => array( 
  693. 'description' => __( 'Variation dimensions.', 'woocommerce' ),  
  694. 'type' => 'object',  
  695. 'context' => array( 'view', 'edit' ),  
  696. 'properties' => array( 
  697. 'length' => array( 
  698. /** translators: %s: dimension unit */ 
  699. 'description' => sprintf( __( 'Variation length (%s).', 'woocommerce' ), $dimension_unit ),  
  700. 'type' => 'string',  
  701. 'context' => array( 'view', 'edit' ),  
  702. ),  
  703. 'width' => array( 
  704. /** translators: %s: dimension unit */ 
  705. 'description' => sprintf( __( 'Variation width (%s).', 'woocommerce' ), $dimension_unit ),  
  706. 'type' => 'string',  
  707. 'context' => array( 'view', 'edit' ),  
  708. ),  
  709. 'height' => array( 
  710. /** translators: %s: dimension unit */ 
  711. 'description' => sprintf( __( 'Variation height (%s).', 'woocommerce' ), $dimension_unit ),  
  712. 'type' => 'string',  
  713. 'context' => array( 'view', 'edit' ),  
  714. ),  
  715. ),  
  716. ),  
  717. 'shipping_class' => array( 
  718. 'description' => __( 'Shipping class slug.', 'woocommerce' ),  
  719. 'type' => 'string',  
  720. 'context' => array( 'view', 'edit' ),  
  721. ),  
  722. 'shipping_class_id' => array( 
  723. 'description' => __( 'Shipping class ID.', 'woocommerce' ),  
  724. 'type' => 'string',  
  725. 'context' => array( 'view', 'edit' ),  
  726. 'readonly' => true,  
  727. ),  
  728. 'image' => array( 
  729. 'description' => __( 'Variation image data.', 'woocommerce' ),  
  730. 'type' => 'object',  
  731. 'context' => array( 'view', 'edit' ),  
  732. 'properties' => array( 
  733. 'id' => array( 
  734. 'description' => __( 'Image ID.', 'woocommerce' ),  
  735. 'type' => 'integer',  
  736. 'context' => array( 'view', 'edit' ),  
  737. ),  
  738. 'date_created' => array( 
  739. 'description' => __( "The date the image was created, in the site's timezone.", 'woocommerce' ),  
  740. 'type' => 'date-time',  
  741. 'context' => array( 'view', 'edit' ),  
  742. 'readonly' => true,  
  743. ),  
  744. 'date_created_gmt' => array( 
  745. 'description' => __( 'The date the image was created, as GMT.', 'woocommerce' ),  
  746. 'type' => 'date-time',  
  747. 'context' => array( 'view', 'edit' ),  
  748. 'readonly' => true,  
  749. ),  
  750. 'date_modified' => array( 
  751. 'description' => __( "The date the image was last modified, in the site's timezone.", 'woocommerce' ),  
  752. 'type' => 'date-time',  
  753. 'context' => array( 'view', 'edit' ),  
  754. 'readonly' => true,  
  755. ),  
  756. 'date_modified_gmt' => array( 
  757. 'description' => __( 'The date the image was last modified, as GMT.', 'woocommerce' ),  
  758. 'type' => 'date-time',  
  759. 'context' => array( 'view', 'edit' ),  
  760. 'readonly' => true,  
  761. ),  
  762. 'src' => array( 
  763. 'description' => __( 'Image URL.', 'woocommerce' ),  
  764. 'type' => 'string',  
  765. 'format' => 'uri',  
  766. 'context' => array( 'view', 'edit' ),  
  767. ),  
  768. 'name' => array( 
  769. 'description' => __( 'Image name.', 'woocommerce' ),  
  770. 'type' => 'string',  
  771. 'context' => array( 'view', 'edit' ),  
  772. ),  
  773. 'alt' => array( 
  774. 'description' => __( 'Image alternative text.', 'woocommerce' ),  
  775. 'type' => 'string',  
  776. 'context' => array( 'view', 'edit' ),  
  777. ),  
  778. 'position' => array( 
  779. 'description' => __( 'Image position. 0 means that the image is featured.', 'woocommerce' ),  
  780. 'type' => 'integer',  
  781. 'context' => array( 'view', 'edit' ),  
  782. ),  
  783. ),  
  784. ),  
  785. 'attributes' => array( 
  786. 'description' => __( 'List of attributes.', 'woocommerce' ),  
  787. 'type' => 'array',  
  788. 'context' => array( 'view', 'edit' ),  
  789. 'items' => array( 
  790. 'type' => 'object',  
  791. 'properties' => array( 
  792. 'id' => array( 
  793. 'description' => __( 'Attribute ID.', 'woocommerce' ),  
  794. 'type' => 'integer',  
  795. 'context' => array( 'view', 'edit' ),  
  796. ),  
  797. 'name' => array( 
  798. 'description' => __( 'Attribute name.', 'woocommerce' ),  
  799. 'type' => 'string',  
  800. 'context' => array( 'view', 'edit' ),  
  801. ),  
  802. 'option' => array( 
  803. 'description' => __( 'Selected attribute term name.', 'woocommerce' ),  
  804. 'type' => 'string',  
  805. 'context' => array( 'view', 'edit' ),  
  806. ),  
  807. ),  
  808. ),  
  809. ),  
  810. 'menu_order' => array( 
  811. 'description' => __( 'Menu order, used to custom sort products.', 'woocommerce' ),  
  812. 'type' => 'integer',  
  813. 'context' => array( 'view', 'edit' ),  
  814. ),  
  815. 'meta_data' => array( 
  816. 'description' => __( 'Meta data.', 'woocommerce' ),  
  817. 'type' => 'array',  
  818. 'context' => array( 'view', 'edit' ),  
  819. 'items' => array( 
  820. 'type' => 'object',  
  821. 'properties' => array( 
  822. 'id' => array( 
  823. 'description' => __( 'Meta ID.', 'woocommerce' ),  
  824. 'type' => 'integer',  
  825. 'context' => array( 'view', 'edit' ),  
  826. 'readonly' => true,  
  827. ),  
  828. 'key' => array( 
  829. 'description' => __( 'Meta key.', 'woocommerce' ),  
  830. 'type' => 'string',  
  831. 'context' => array( 'view', 'edit' ),  
  832. ),  
  833. 'value' => array( 
  834. 'description' => __( 'Meta value.', 'woocommerce' ),  
  835. 'type' => 'string',  
  836. 'context' => array( 'view', 'edit' ),  
  837. ),  
  838. ),  
  839. ),  
  840. ),  
  841. ),  
  842. ); 
  843.  
  844. return $this->add_additional_fields_schema( $schema );