WC_REST_Coupons_Controller

REST API Coupons controller class.

Defined (1)

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

/includes/api/class-wc-rest-coupons-controller.php  
  1. class WC_REST_Coupons_Controller extends WC_REST_Legacy_Coupons_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 = 'coupons'; 
  14.  
  15. /** 
  16. * Post type. 
  17. * @var string 
  18. */ 
  19. protected $post_type = 'shop_coupon'; 
  20.  
  21. /** 
  22. * Register the routes for coupons. 
  23. */ 
  24. public function register_routes() { 
  25. register_rest_route( $this->namespace, '/' . $this->rest_base, array( 
  26. array( 
  27. 'methods' => WP_REST_Server::READABLE,  
  28. 'callback' => array( $this, 'get_items' ),  
  29. 'permission_callback' => array( $this, 'get_items_permissions_check' ),  
  30. 'args' => $this->get_collection_params(),  
  31. ),  
  32. array( 
  33. 'methods' => WP_REST_Server::CREATABLE,  
  34. 'callback' => array( $this, 'create_item' ),  
  35. 'permission_callback' => array( $this, 'create_item_permissions_check' ),  
  36. 'args' => array_merge( $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ), array( 
  37. 'code' => array( 
  38. 'description' => __( 'Coupon code.', 'woocommerce' ),  
  39. 'required' => true,  
  40. 'type' => 'string',  
  41. ),  
  42. ) ),  
  43. ),  
  44. 'schema' => array( $this, 'get_public_item_schema' ),  
  45. ) ); 
  46.  
  47. register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array( 
  48. 'args' => array( 
  49. 'id' => array( 
  50. 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),  
  51. 'type' => 'integer',  
  52. ),  
  53. ),  
  54. array( 
  55. 'methods' => WP_REST_Server::READABLE,  
  56. 'callback' => array( $this, 'get_item' ),  
  57. 'permission_callback' => array( $this, 'get_item_permissions_check' ),  
  58. 'args' => array( 
  59. 'context' => $this->get_context_param( array( 'default' => 'view' ) ),  
  60. ),  
  61. ),  
  62. array( 
  63. 'methods' => WP_REST_Server::EDITABLE,  
  64. 'callback' => array( $this, 'update_item' ),  
  65. 'permission_callback' => array( $this, 'update_item_permissions_check' ),  
  66. 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),  
  67. ),  
  68. array( 
  69. 'methods' => WP_REST_Server::DELETABLE,  
  70. 'callback' => array( $this, 'delete_item' ),  
  71. 'permission_callback' => array( $this, 'delete_item_permissions_check' ),  
  72. 'args' => array( 
  73. 'force' => array( 
  74. 'default' => false,  
  75. 'type' => 'boolean',  
  76. 'description' => __( 'Whether to bypass trash and force deletion.', 'woocommerce' ),  
  77. ),  
  78. ),  
  79. ),  
  80. 'schema' => array( $this, 'get_public_item_schema' ),  
  81. ) ); 
  82.  
  83. register_rest_route( $this->namespace, '/' . $this->rest_base . '/batch', array( 
  84. array( 
  85. 'methods' => WP_REST_Server::EDITABLE,  
  86. 'callback' => array( $this, 'batch_items' ),  
  87. 'permission_callback' => array( $this, 'batch_items_permissions_check' ),  
  88. 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),  
  89. ),  
  90. 'schema' => array( $this, 'get_public_batch_schema' ),  
  91. ) ); 
  92.  
  93. /** 
  94. * Get object. 
  95. * @since 3.0.0 
  96. * @param int $id Object ID. 
  97. * @return WC_Data 
  98. */ 
  99. protected function get_object( $id ) { 
  100. return new WC_Coupon( $id ); 
  101.  
  102. /** 
  103. * Get formatted item data. 
  104. * @since 3.0.0 
  105. * @param WC_Data $object WC_Data instance. 
  106. * @return array 
  107. */ 
  108. protected function get_formatted_item_data( $object ) { 
  109. $data = $object->get_data(); 
  110.  
  111. $format_decimal = array( 'amount', 'minimum_amount', 'maximum_amount' ); 
  112. $format_date = array( 'date_created', 'date_modified', 'date_expires' ); 
  113. $format_null = array( 'usage_limit', 'usage_limit_per_user', 'limit_usage_to_x_items' ); 
  114.  
  115. // Format decimal values. 
  116. foreach ( $format_decimal as $key ) { 
  117. $data[ $key ] = wc_format_decimal( $data[ $key ], 2 ); 
  118.  
  119. // Format date values. 
  120. foreach ( $format_date as $key ) { 
  121. $datetime = $data[ $key ]; 
  122. $data[ $key ] = wc_rest_prepare_date_response( $datetime, false ); 
  123. $data[ $key . '_gmt' ] = wc_rest_prepare_date_response( $datetime ); 
  124.  
  125. // Format null values. 
  126. foreach ( $format_null as $key ) { 
  127. $data[ $key ] = $data[ $key ] ? $data[ $key ] : null; 
  128.  
  129. return array( 
  130. 'id' => $object->get_id(),  
  131. 'code' => $data['code'],  
  132. 'amount' => $data['amount'],  
  133. 'date_created' => $data['date_created'],  
  134. 'date_created_gmt' => $data['date_created_gmt'],  
  135. 'date_modified' => $data['date_modified'],  
  136. 'date_modified_gmt' => $data['date_modified_gmt'],  
  137. 'discount_type' => $data['discount_type'],  
  138. 'description' => $data['description'],  
  139. 'date_expires' => $data['date_expires'],  
  140. 'date_expires_gmt' => $data['date_expires_gmt'],  
  141. 'usage_count' => $data['usage_count'],  
  142. 'individual_use' => $data['individual_use'],  
  143. 'product_ids' => $data['product_ids'],  
  144. 'excluded_product_ids' => $data['excluded_product_ids'],  
  145. 'usage_limit' => $data['usage_limit'],  
  146. 'usage_limit_per_user' => $data['usage_limit_per_user'],  
  147. 'limit_usage_to_x_items' => $data['limit_usage_to_x_items'],  
  148. 'free_shipping' => $data['free_shipping'],  
  149. 'product_categories' => $data['product_categories'],  
  150. 'excluded_product_categories' => $data['excluded_product_categories'],  
  151. 'exclude_sale_items' => $data['exclude_sale_items'],  
  152. 'minimum_amount' => $data['minimum_amount'],  
  153. 'maximum_amount' => $data['maximum_amount'],  
  154. 'email_restrictions' => $data['email_restrictions'],  
  155. 'used_by' => $data['used_by'],  
  156. 'meta_data' => $data['meta_data'],  
  157. ); 
  158.  
  159. /** 
  160. * Prepare a single coupon output for response. 
  161. * @since 3.0.0 
  162. * @param WC_Data $object Object data. 
  163. * @param WP_REST_Request $request Request object. 
  164. * @return WP_REST_Response 
  165. */ 
  166. public function prepare_object_for_response( $object, $request ) { 
  167. $data = $this->get_formatted_item_data( $object ); 
  168. $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; 
  169. $data = $this->add_additional_fields_to_object( $data, $request ); 
  170. $data = $this->filter_response_by_context( $data, $context ); 
  171. $response = rest_ensure_response( $data ); 
  172. $response->add_links( $this->prepare_links( $object, $request ) ); 
  173.  
  174. /** 
  175. * Filter the data for a response. 
  176. * The dynamic portion of the hook name, $this->post_type,  
  177. * refers to object type being prepared for the response. 
  178. * @param WP_REST_Response $response The response object. 
  179. * @param WC_Data $object Object data. 
  180. * @param WP_REST_Request $request Request object. 
  181. */ 
  182. return apply_filters( "woocommerce_rest_prepare_{$this->post_type}_object", $response, $object, $request ); 
  183.  
  184. /** 
  185. * Prepare objects query. 
  186. * @since 3.0.0 
  187. * @param WP_REST_Request $request Full details about the request. 
  188. * @return array 
  189. */ 
  190. protected function prepare_objects_query( $request ) { 
  191. $args = parent::prepare_objects_query( $request ); 
  192.  
  193. if ( ! empty( $request['code'] ) ) { 
  194. $id = wc_get_coupon_id_by_code( $request['code'] ); 
  195. $args['post__in'] = array( $id ); 
  196.  
  197. // Get only ids. 
  198. $args['fields'] = 'ids'; 
  199.  
  200. return $args; 
  201.  
  202. /** 
  203. * Only reutrn writeable props from schema. 
  204. * @param array $schema 
  205. * @return bool 
  206. */ 
  207. protected function filter_writable_props( $schema ) { 
  208. return empty( $schema['readonly'] ); 
  209.  
  210. /** 
  211. * Prepare a single coupon for create or update. 
  212. * @param WP_REST_Request $request Request object. 
  213. * @param bool $creating If is creating a new object. 
  214. * @return WP_Error|WC_Data 
  215. */ 
  216. protected function prepare_object_for_database( $request, $creating = false ) { 
  217. $id = isset( $request['id'] ) ? absint( $request['id'] ) : 0; 
  218. $coupon = new WC_Coupon( $id ); 
  219. $schema = $this->get_item_schema(); 
  220. $data_keys = array_keys( array_filter( $schema['properties'], array( $this, 'filter_writable_props' ) ) ); 
  221.  
  222. // Validate required POST fields. 
  223. if ( $creating && empty( $request['code'] ) ) { 
  224. return new WP_Error( 'woocommerce_rest_empty_coupon_code', sprintf( __( 'The coupon code cannot be empty.', 'woocommerce' ), 'code' ), array( 'status' => 400 ) ); 
  225.  
  226. // Handle all writable props. 
  227. foreach ( $data_keys as $key ) { 
  228. $value = $request[ $key ]; 
  229.  
  230. if ( ! is_null( $value ) ) { 
  231. switch ( $key ) { 
  232. case 'code' : 
  233. $coupon_code = wc_format_coupon_code( $value ); 
  234. $id = $coupon->get_id() ? $coupon->get_id() : 0; 
  235. $id_from_code = wc_get_coupon_id_by_code( $coupon_code, $id ); 
  236.  
  237. if ( $id_from_code ) { 
  238. return new WP_Error( 'woocommerce_rest_coupon_code_already_exists', __( 'The coupon code already exists', 'woocommerce' ), array( 'status' => 400 ) ); 
  239.  
  240. $coupon->set_code( $coupon_code ); 
  241. break; 
  242. case 'meta_data' : 
  243. if ( is_array( $value ) ) { 
  244. foreach ( $value as $meta ) { 
  245. $coupon->update_meta_data( $meta['key'], $meta['value'], isset( $meta['id'] ) ? $meta['id'] : '' ); 
  246. break; 
  247. case 'description' : 
  248. $coupon->set_description( wp_filter_post_kses( $value ) ); 
  249. break; 
  250. default : 
  251. if ( is_callable( array( $coupon, "set_{$key}" ) ) ) { 
  252. $coupon->{"set_{$key}"}( $value ); 
  253. break; 
  254.  
  255. /** 
  256. * Filters an object before it is inserted via the REST API. 
  257. * The dynamic portion of the hook name, `$this->post_type`,  
  258. * refers to the object type slug. 
  259. * @param WC_Data $coupon Object object. 
  260. * @param WP_REST_Request $request Request object. 
  261. * @param bool $creating If is creating a new object. 
  262. */ 
  263. return apply_filters( "woocommerce_rest_pre_insert_{$this->post_type}_object", $coupon, $request, $creating ); 
  264.  
  265. /** 
  266. * Get the Coupon's schema, conforming to JSON Schema. 
  267. * @return array 
  268. */ 
  269. public function get_item_schema() { 
  270. $schema = array( 
  271. '$schema' => 'http://json-schema.org/draft-04/schema#',  
  272. 'title' => $this->post_type,  
  273. 'type' => 'object',  
  274. 'properties' => array( 
  275. 'id' => array( 
  276. 'description' => __( 'Unique identifier for the object.', 'woocommerce' ),  
  277. 'type' => 'integer',  
  278. 'context' => array( 'view', 'edit' ),  
  279. 'readonly' => true,  
  280. ),  
  281. 'code' => array( 
  282. 'description' => __( 'Coupon code.', 'woocommerce' ),  
  283. 'type' => 'string',  
  284. 'context' => array( 'view', 'edit' ),  
  285. ),  
  286. 'amount' => array( 
  287. 'description' => __( 'The amount of discount. Should always be numeric, even if setting a percentage.', 'woocommerce' ),  
  288. 'type' => 'string',  
  289. 'context' => array( 'view', 'edit' ),  
  290. ),  
  291. 'date_created' => array( 
  292. 'description' => __( "The date the coupon was created, in the site's timezone.", 'woocommerce' ),  
  293. 'type' => 'date-time',  
  294. 'context' => array( 'view', 'edit' ),  
  295. 'readonly' => true,  
  296. ),  
  297. 'date_created_gmt' => array( 
  298. 'description' => __( 'The date the coupon was created, as GMT.', 'woocommerce' ),  
  299. 'type' => 'date-time',  
  300. 'context' => array( 'view', 'edit' ),  
  301. 'readonly' => true,  
  302. ),  
  303. 'date_modified' => array( 
  304. 'description' => __( "The date the coupon was last modified, in the site's timezone.", 'woocommerce' ),  
  305. 'type' => 'date-time',  
  306. 'context' => array( 'view', 'edit' ),  
  307. 'readonly' => true,  
  308. ),  
  309. 'date_modified_gmt' => array( 
  310. 'description' => __( 'The date the coupon was last modified, as GMT.', 'woocommerce' ),  
  311. 'type' => 'date-time',  
  312. 'context' => array( 'view', 'edit' ),  
  313. 'readonly' => true,  
  314. ),  
  315. 'discount_type' => array( 
  316. 'description' => __( 'Determines the type of discount that will be applied.', 'woocommerce' ),  
  317. 'type' => 'string',  
  318. 'default' => 'fixed_cart',  
  319. 'enum' => array_keys( wc_get_coupon_types() ),  
  320. 'context' => array( 'view', 'edit' ),  
  321. ),  
  322. 'description' => array( 
  323. 'description' => __( 'Coupon description.', 'woocommerce' ),  
  324. 'type' => 'string',  
  325. 'context' => array( 'view', 'edit' ),  
  326. ),  
  327. 'date_expires' => array( 
  328. 'description' => __( "The date the coupon expires, in the site's timezone.", 'woocommerce' ),  
  329. 'type' => 'string',  
  330. 'context' => array( 'view', 'edit' ),  
  331. ),  
  332. 'date_expires_gmt' => array( 
  333. 'description' => __( "The date the coupon expires, as GMT.", 'woocommerce' ),  
  334. 'type' => 'string',  
  335. 'context' => array( 'view', 'edit' ),  
  336. ),  
  337. 'usage_count' => array( 
  338. 'description' => __( 'Number of times the coupon has been used already.', 'woocommerce' ),  
  339. 'type' => 'integer',  
  340. 'context' => array( 'view', 'edit' ),  
  341. 'readonly' => true,  
  342. ),  
  343. 'individual_use' => array( 
  344. 'description' => __( 'If true, the coupon can only be used individually. Other applied coupons will be removed from the cart.', 'woocommerce' ),  
  345. 'type' => 'boolean',  
  346. 'default' => false,  
  347. 'context' => array( 'view', 'edit' ),  
  348. ),  
  349. 'product_ids' => array( 
  350. 'description' => __( "List of product IDs the coupon can be used on.", 'woocommerce' ),  
  351. 'type' => 'array',  
  352. 'items' => array( 
  353. 'type' => 'integer',  
  354. ),  
  355. 'context' => array( 'view', 'edit' ),  
  356. ),  
  357. 'excluded_product_ids' => array( 
  358. 'description' => __( "List of product IDs the coupon cannot be used on.", 'woocommerce' ),  
  359. 'type' => 'array',  
  360. 'items' => array( 
  361. 'type' => 'integer',  
  362. ),  
  363. 'context' => array( 'view', 'edit' ),  
  364. ),  
  365. 'usage_limit' => array( 
  366. 'description' => __( 'How many times the coupon can be used in total.', 'woocommerce' ),  
  367. 'type' => 'integer',  
  368. 'context' => array( 'view', 'edit' ),  
  369. ),  
  370. 'usage_limit_per_user' => array( 
  371. 'description' => __( 'How many times the coupon can be used per customer.', 'woocommerce' ),  
  372. 'type' => 'integer',  
  373. 'context' => array( 'view', 'edit' ),  
  374. ),  
  375. 'limit_usage_to_x_items' => array( 
  376. 'description' => __( 'Max number of items in the cart the coupon can be applied to.', 'woocommerce' ),  
  377. 'type' => 'integer',  
  378. 'context' => array( 'view', 'edit' ),  
  379. ),  
  380. 'free_shipping' => array( 
  381. 'description' => __( 'If true and if the free shipping method requires a coupon, this coupon will enable free shipping.', 'woocommerce' ),  
  382. 'type' => 'boolean',  
  383. 'default' => false,  
  384. 'context' => array( 'view', 'edit' ),  
  385. ),  
  386. 'product_categories' => array( 
  387. 'description' => __( "List of category IDs the coupon applies to.", 'woocommerce' ),  
  388. 'type' => 'array',  
  389. 'items' => array( 
  390. 'type' => 'integer',  
  391. ),  
  392. 'context' => array( 'view', 'edit' ),  
  393. ),  
  394. 'excluded_product_categories' => array( 
  395. 'description' => __( "List of category IDs the coupon does not apply to.", 'woocommerce' ),  
  396. 'type' => 'array',  
  397. 'items' => array( 
  398. 'type' => 'integer',  
  399. ),  
  400. 'context' => array( 'view', 'edit' ),  
  401. ),  
  402. 'exclude_sale_items' => array( 
  403. 'description' => __( 'If true, this coupon will not be applied to items that have sale prices.', 'woocommerce' ),  
  404. 'type' => 'boolean',  
  405. 'default' => false,  
  406. 'context' => array( 'view', 'edit' ),  
  407. ),  
  408. 'minimum_amount' => array( 
  409. 'description' => __( 'Minimum order amount that needs to be in the cart before coupon applies.', 'woocommerce' ),  
  410. 'type' => 'string',  
  411. 'context' => array( 'view', 'edit' ),  
  412. ),  
  413. 'maximum_amount' => array( 
  414. 'description' => __( 'Maximum order amount allowed when using the coupon.', 'woocommerce' ),  
  415. 'type' => 'string',  
  416. 'context' => array( 'view', 'edit' ),  
  417. ),  
  418. 'email_restrictions' => array( 
  419. 'description' => __( 'List of email addresses that can use this coupon.', 'woocommerce' ),  
  420. 'type' => 'array',  
  421. 'items' => array( 
  422. 'type' => 'string',  
  423. ),  
  424. 'context' => array( 'view', 'edit' ),  
  425. ),  
  426. 'used_by' => array( 
  427. 'description' => __( 'List of user IDs (or guest email addresses) that have used the coupon.', 'woocommerce' ),  
  428. 'type' => 'array',  
  429. 'items' => array( 
  430. 'type' => 'integer',  
  431. ),  
  432. 'context' => array( 'view', 'edit' ),  
  433. 'readonly' => true,  
  434. ),  
  435. 'meta_data' => array( 
  436. 'description' => __( 'Meta data.', 'woocommerce' ),  
  437. 'type' => 'array',  
  438. 'context' => array( 'view', 'edit' ),  
  439. 'items' => array( 
  440. 'type' => 'object',  
  441. 'properties' => array( 
  442. 'id' => array( 
  443. 'description' => __( 'Meta ID.', 'woocommerce' ),  
  444. 'type' => 'integer',  
  445. 'context' => array( 'view', 'edit' ),  
  446. 'readonly' => true,  
  447. ),  
  448. 'key' => array( 
  449. 'description' => __( 'Meta key.', 'woocommerce' ),  
  450. 'type' => 'string',  
  451. 'context' => array( 'view', 'edit' ),  
  452. ),  
  453. 'value' => array( 
  454. 'description' => __( 'Meta value.', 'woocommerce' ),  
  455. 'type' => 'string',  
  456. 'context' => array( 'view', 'edit' ),  
  457. ),  
  458. ),  
  459. ),  
  460. ),  
  461. ),  
  462. ); 
  463. return $this->add_additional_fields_schema( $schema ); 
  464.  
  465. /** 
  466. * Get the query params for collections of attachments. 
  467. * @return array 
  468. */ 
  469. public function get_collection_params() { 
  470. $params = parent::get_collection_params(); 
  471.  
  472. $params['code'] = array( 
  473. 'description' => __( 'Limit result set to resources with a specific code.', 'woocommerce' ),  
  474. 'type' => 'string',  
  475. 'sanitize_callback' => 'sanitize_text_field',  
  476. 'validate_callback' => 'rest_validate_request_arg',  
  477. ); 
  478.  
  479. return $params;