class-wc-rest-customers-controller.php 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924
  1. <?php
  2. /**
  3. * REST API Customers controller
  4. *
  5. * Handles requests to the /customers endpoint.
  6. *
  7. * @author WooThemes
  8. * @category API
  9. * @package WooCommerce/API
  10. * @since 3.0.0
  11. */
  12. if ( ! defined( 'ABSPATH' ) ) {
  13. exit;
  14. }
  15. /**
  16. * REST API Customers controller class.
  17. *
  18. * @package WooCommerce/API
  19. * @extends WC_REST_Controller
  20. */
  21. class WC_REST_Customers_V1_Controller extends WC_REST_Controller {
  22. /**
  23. * Endpoint namespace.
  24. *
  25. * @var string
  26. */
  27. protected $namespace = 'wc/v1';
  28. /**
  29. * Route base.
  30. *
  31. * @var string
  32. */
  33. protected $rest_base = 'customers';
  34. /**
  35. * Register the routes for customers.
  36. */
  37. public function register_routes() {
  38. register_rest_route( $this->namespace, '/' . $this->rest_base, array(
  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' => array_merge( $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ), array(
  50. 'email' => array(
  51. 'required' => true,
  52. 'type' => 'string',
  53. 'description' => __( 'New user email address.', 'woocommerce' ),
  54. ),
  55. 'username' => array(
  56. 'required' => 'no' === get_option( 'woocommerce_registration_generate_username', 'yes' ),
  57. 'description' => __( 'New user username.', 'woocommerce' ),
  58. 'type' => 'string',
  59. ),
  60. 'password' => array(
  61. 'required' => 'no' === get_option( 'woocommerce_registration_generate_password', 'no' ),
  62. 'description' => __( 'New user password.', 'woocommerce' ),
  63. 'type' => 'string',
  64. ),
  65. ) ),
  66. ),
  67. 'schema' => array( $this, 'get_public_item_schema' ),
  68. ) );
  69. register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
  70. 'args' => array(
  71. 'id' => array(
  72. 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
  73. 'type' => 'integer',
  74. ),
  75. ),
  76. array(
  77. 'methods' => WP_REST_Server::READABLE,
  78. 'callback' => array( $this, 'get_item' ),
  79. 'permission_callback' => array( $this, 'get_item_permissions_check' ),
  80. 'args' => array(
  81. 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
  82. ),
  83. ),
  84. array(
  85. 'methods' => WP_REST_Server::EDITABLE,
  86. 'callback' => array( $this, 'update_item' ),
  87. 'permission_callback' => array( $this, 'update_item_permissions_check' ),
  88. 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
  89. ),
  90. array(
  91. 'methods' => WP_REST_Server::DELETABLE,
  92. 'callback' => array( $this, 'delete_item' ),
  93. 'permission_callback' => array( $this, 'delete_item_permissions_check' ),
  94. 'args' => array(
  95. 'force' => array(
  96. 'default' => false,
  97. 'type' => 'boolean',
  98. 'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce' ),
  99. ),
  100. 'reassign' => array(
  101. 'default' => 0,
  102. 'type' => 'integer',
  103. 'description' => __( 'ID to reassign posts to.', 'woocommerce' ),
  104. ),
  105. ),
  106. ),
  107. 'schema' => array( $this, 'get_public_item_schema' ),
  108. ) );
  109. register_rest_route( $this->namespace, '/' . $this->rest_base . '/batch', array(
  110. array(
  111. 'methods' => WP_REST_Server::EDITABLE,
  112. 'callback' => array( $this, 'batch_items' ),
  113. 'permission_callback' => array( $this, 'batch_items_permissions_check' ),
  114. 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
  115. ),
  116. 'schema' => array( $this, 'get_public_batch_schema' ),
  117. ) );
  118. }
  119. /**
  120. * Check whether a given request has permission to read customers.
  121. *
  122. * @param WP_REST_Request $request Full details about the request.
  123. * @return WP_Error|boolean
  124. */
  125. public function get_items_permissions_check( $request ) {
  126. if ( ! wc_rest_check_user_permissions( 'read' ) ) {
  127. return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
  128. }
  129. return true;
  130. }
  131. /**
  132. * Check if a given request has access create customers.
  133. *
  134. * @param WP_REST_Request $request Full details about the request.
  135. *
  136. * @return bool|WP_Error
  137. */
  138. public function create_item_permissions_check( $request ) {
  139. if ( ! wc_rest_check_user_permissions( 'create' ) ) {
  140. return new WP_Error( 'woocommerce_rest_cannot_create', __( 'Sorry, you are not allowed to create resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
  141. }
  142. return true;
  143. }
  144. /**
  145. * Check if a given request has access to read a customer.
  146. *
  147. * @param WP_REST_Request $request Full details about the request.
  148. * @return WP_Error|boolean
  149. */
  150. public function get_item_permissions_check( $request ) {
  151. $id = (int) $request['id'];
  152. if ( ! wc_rest_check_user_permissions( 'read', $id ) ) {
  153. return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
  154. }
  155. return true;
  156. }
  157. /**
  158. * Check if a given request has access update a customer.
  159. *
  160. * @param WP_REST_Request $request Full details about the request.
  161. *
  162. * @return bool|WP_Error
  163. */
  164. public function update_item_permissions_check( $request ) {
  165. $id = (int) $request['id'];
  166. if ( ! wc_rest_check_user_permissions( 'edit', $id ) ) {
  167. return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you are not allowed to edit this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
  168. }
  169. return true;
  170. }
  171. /**
  172. * Check if a given request has access delete a customer.
  173. *
  174. * @param WP_REST_Request $request Full details about the request.
  175. *
  176. * @return bool|WP_Error
  177. */
  178. public function delete_item_permissions_check( $request ) {
  179. $id = (int) $request['id'];
  180. if ( ! wc_rest_check_user_permissions( 'delete', $id ) ) {
  181. return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'Sorry, you are not allowed to delete this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
  182. }
  183. return true;
  184. }
  185. /**
  186. * Check if a given request has access batch create, update and delete items.
  187. *
  188. * @param WP_REST_Request $request Full details about the request.
  189. *
  190. * @return bool|WP_Error
  191. */
  192. public function batch_items_permissions_check( $request ) {
  193. if ( ! wc_rest_check_user_permissions( 'batch' ) ) {
  194. return new WP_Error( 'woocommerce_rest_cannot_batch', __( 'Sorry, you are not allowed to batch manipulate this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
  195. }
  196. return true;
  197. }
  198. /**
  199. * Get all customers.
  200. *
  201. * @param WP_REST_Request $request Full details about the request.
  202. * @return WP_Error|WP_REST_Response
  203. */
  204. public function get_items( $request ) {
  205. $prepared_args = array();
  206. $prepared_args['exclude'] = $request['exclude'];
  207. $prepared_args['include'] = $request['include'];
  208. $prepared_args['order'] = $request['order'];
  209. $prepared_args['number'] = $request['per_page'];
  210. if ( ! empty( $request['offset'] ) ) {
  211. $prepared_args['offset'] = $request['offset'];
  212. } else {
  213. $prepared_args['offset'] = ( $request['page'] - 1 ) * $prepared_args['number'];
  214. }
  215. $orderby_possibles = array(
  216. 'id' => 'ID',
  217. 'include' => 'include',
  218. 'name' => 'display_name',
  219. 'registered_date' => 'registered',
  220. );
  221. $prepared_args['orderby'] = $orderby_possibles[ $request['orderby'] ];
  222. $prepared_args['search'] = $request['search'];
  223. if ( '' !== $prepared_args['search'] ) {
  224. $prepared_args['search'] = '*' . $prepared_args['search'] . '*';
  225. }
  226. // Filter by email.
  227. if ( ! empty( $request['email'] ) ) {
  228. $prepared_args['search'] = $request['email'];
  229. $prepared_args['search_columns'] = array( 'user_email' );
  230. }
  231. // Filter by role.
  232. if ( 'all' !== $request['role'] ) {
  233. $prepared_args['role'] = $request['role'];
  234. }
  235. /**
  236. * Filter arguments, before passing to WP_User_Query, when querying users via the REST API.
  237. *
  238. * @see https://developer.wordpress.org/reference/classes/wp_user_query/
  239. *
  240. * @param array $prepared_args Array of arguments for WP_User_Query.
  241. * @param WP_REST_Request $request The current request.
  242. */
  243. $prepared_args = apply_filters( 'woocommerce_rest_customer_query', $prepared_args, $request );
  244. $query = new WP_User_Query( $prepared_args );
  245. $users = array();
  246. foreach ( $query->results as $user ) {
  247. $data = $this->prepare_item_for_response( $user, $request );
  248. $users[] = $this->prepare_response_for_collection( $data );
  249. }
  250. $response = rest_ensure_response( $users );
  251. // Store pagination values for headers then unset for count query.
  252. $per_page = (int) $prepared_args['number'];
  253. $page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 );
  254. $prepared_args['fields'] = 'ID';
  255. $total_users = $query->get_total();
  256. if ( $total_users < 1 ) {
  257. // Out-of-bounds, run the query again without LIMIT for total count.
  258. unset( $prepared_args['number'] );
  259. unset( $prepared_args['offset'] );
  260. $count_query = new WP_User_Query( $prepared_args );
  261. $total_users = $count_query->get_total();
  262. }
  263. $response->header( 'X-WP-Total', (int) $total_users );
  264. $max_pages = ceil( $total_users / $per_page );
  265. $response->header( 'X-WP-TotalPages', (int) $max_pages );
  266. $base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
  267. if ( $page > 1 ) {
  268. $prev_page = $page - 1;
  269. if ( $prev_page > $max_pages ) {
  270. $prev_page = $max_pages;
  271. }
  272. $prev_link = add_query_arg( 'page', $prev_page, $base );
  273. $response->link_header( 'prev', $prev_link );
  274. }
  275. if ( $max_pages > $page ) {
  276. $next_page = $page + 1;
  277. $next_link = add_query_arg( 'page', $next_page, $base );
  278. $response->link_header( 'next', $next_link );
  279. }
  280. return $response;
  281. }
  282. /**
  283. * Create a single customer.
  284. *
  285. * @param WP_REST_Request $request Full details about the request.
  286. * @return WP_Error|WP_REST_Response
  287. */
  288. public function create_item( $request ) {
  289. try {
  290. if ( ! empty( $request['id'] ) ) {
  291. throw new WC_REST_Exception( 'woocommerce_rest_customer_exists', __( 'Cannot create existing resource.', 'woocommerce' ), 400 );
  292. }
  293. // Sets the username.
  294. $request['username'] = ! empty( $request['username'] ) ? $request['username'] : '';
  295. // Sets the password.
  296. $request['password'] = ! empty( $request['password'] ) ? $request['password'] : '';
  297. // Create customer.
  298. $customer = new WC_Customer;
  299. $customer->set_username( $request['username'] );
  300. $customer->set_password( $request['password'] );
  301. $customer->set_email( $request['email'] );
  302. $this->update_customer_meta_fields( $customer, $request );
  303. $customer->save();
  304. if ( ! $customer->get_id() ) {
  305. throw new WC_REST_Exception( 'woocommerce_rest_cannot_create', __( 'This resource cannot be created.', 'woocommerce' ), 400 );
  306. }
  307. $user_data = get_userdata( $customer->get_id() );
  308. $this->update_additional_fields_for_object( $user_data, $request );
  309. /**
  310. * Fires after a customer is created or updated via the REST API.
  311. *
  312. * @param WP_User $user_data Data used to create the customer.
  313. * @param WP_REST_Request $request Request object.
  314. * @param boolean $creating True when creating customer, false when updating customer.
  315. */
  316. do_action( 'woocommerce_rest_insert_customer', $user_data, $request, true );
  317. $request->set_param( 'context', 'edit' );
  318. $response = $this->prepare_item_for_response( $user_data, $request );
  319. $response = rest_ensure_response( $response );
  320. $response->set_status( 201 );
  321. $response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $customer->get_id() ) ) );
  322. return $response;
  323. } catch ( Exception $e ) {
  324. return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
  325. }
  326. }
  327. /**
  328. * Get a single customer.
  329. *
  330. * @param WP_REST_Request $request Full details about the request.
  331. * @return WP_Error|WP_REST_Response
  332. */
  333. public function get_item( $request ) {
  334. $id = (int) $request['id'];
  335. $user_data = get_userdata( $id );
  336. if ( empty( $id ) || empty( $user_data->ID ) ) {
  337. return new WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource ID.', 'woocommerce' ), array( 'status' => 404 ) );
  338. }
  339. $customer = $this->prepare_item_for_response( $user_data, $request );
  340. $response = rest_ensure_response( $customer );
  341. return $response;
  342. }
  343. /**
  344. * Update a single user.
  345. *
  346. * @param WP_REST_Request $request Full details about the request.
  347. * @return WP_Error|WP_REST_Response
  348. */
  349. public function update_item( $request ) {
  350. try {
  351. $id = (int) $request['id'];
  352. $customer = new WC_Customer( $id );
  353. if ( ! $customer->get_id() ) {
  354. throw new WC_REST_Exception( 'woocommerce_rest_invalid_id', __( 'Invalid resource ID.', 'woocommerce' ), 400 );
  355. }
  356. if ( ! empty( $request['email'] ) && email_exists( $request['email'] ) && $request['email'] !== $customer->get_email() ) {
  357. throw new WC_REST_Exception( 'woocommerce_rest_customer_invalid_email', __( 'Email address is invalid.', 'woocommerce' ), 400 );
  358. }
  359. if ( ! empty( $request['username'] ) && $request['username'] !== $customer->get_username() ) {
  360. throw new WC_REST_Exception( 'woocommerce_rest_customer_invalid_argument', __( "Username isn't editable.", 'woocommerce' ), 400 );
  361. }
  362. // Customer email.
  363. if ( isset( $request['email'] ) ) {
  364. $customer->set_email( sanitize_email( $request['email'] ) );
  365. }
  366. // Customer password.
  367. if ( isset( $request['password'] ) ) {
  368. $customer->set_password( wc_clean( $request['password'] ) );
  369. }
  370. $this->update_customer_meta_fields( $customer, $request );
  371. $customer->save();
  372. $user_data = get_userdata( $customer->get_id() );
  373. $this->update_additional_fields_for_object( $user_data, $request );
  374. if ( ! is_user_member_of_blog( $user_data->ID ) ) {
  375. $user_data->add_role( 'customer' );
  376. }
  377. /**
  378. * Fires after a customer is created or updated via the REST API.
  379. *
  380. * @param WP_User $customer Data used to create the customer.
  381. * @param WP_REST_Request $request Request object.
  382. * @param boolean $creating True when creating customer, false when updating customer.
  383. */
  384. do_action( 'woocommerce_rest_insert_customer', $user_data, $request, false );
  385. $request->set_param( 'context', 'edit' );
  386. $response = $this->prepare_item_for_response( $user_data, $request );
  387. $response = rest_ensure_response( $response );
  388. return $response;
  389. } catch ( Exception $e ) {
  390. return new WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
  391. }
  392. }
  393. /**
  394. * Delete a single customer.
  395. *
  396. * @param WP_REST_Request $request Full details about the request.
  397. * @return WP_Error|WP_REST_Response
  398. */
  399. public function delete_item( $request ) {
  400. $id = (int) $request['id'];
  401. $reassign = isset( $request['reassign'] ) ? absint( $request['reassign'] ) : null;
  402. $force = isset( $request['force'] ) ? (bool) $request['force'] : false;
  403. // We don't support trashing for this type, error out.
  404. if ( ! $force ) {
  405. return new WP_Error( 'woocommerce_rest_trash_not_supported', __( 'Customers do not support trashing.', 'woocommerce' ), array( 'status' => 501 ) );
  406. }
  407. $user_data = get_userdata( $id );
  408. if ( ! $user_data ) {
  409. return new WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource id.', 'woocommerce' ), array( 'status' => 400 ) );
  410. }
  411. if ( ! empty( $reassign ) ) {
  412. if ( $reassign === $id || ! get_userdata( $reassign ) ) {
  413. return new WP_Error( 'woocommerce_rest_customer_invalid_reassign', __( 'Invalid resource id for reassignment.', 'woocommerce' ), array( 'status' => 400 ) );
  414. }
  415. }
  416. $request->set_param( 'context', 'edit' );
  417. $response = $this->prepare_item_for_response( $user_data, $request );
  418. /** Include admin customer functions to get access to wp_delete_user() */
  419. require_once ABSPATH . 'wp-admin/includes/user.php';
  420. $customer = new WC_Customer( $id );
  421. if ( ! is_null( $reassign ) ) {
  422. $result = $customer->delete_and_reassign( $reassign );
  423. } else {
  424. $result = $customer->delete();
  425. }
  426. if ( ! $result ) {
  427. return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'The resource cannot be deleted.', 'woocommerce' ), array( 'status' => 500 ) );
  428. }
  429. /**
  430. * Fires after a customer is deleted via the REST API.
  431. *
  432. * @param WP_User $user_data User data.
  433. * @param WP_REST_Response $response The response returned from the API.
  434. * @param WP_REST_Request $request The request sent to the API.
  435. */
  436. do_action( 'woocommerce_rest_delete_customer', $user_data, $response, $request );
  437. return $response;
  438. }
  439. /**
  440. * Prepare a single customer output for response.
  441. *
  442. * @param WP_User $user_data User object.
  443. * @param WP_REST_Request $request Request object.
  444. * @return WP_REST_Response $response Response data.
  445. */
  446. public function prepare_item_for_response( $user_data, $request ) {
  447. $customer = new WC_Customer( $user_data->ID );
  448. $_data = $customer->get_data();
  449. $last_order = wc_get_customer_last_order( $customer->get_id() );
  450. $format_date = array( 'date_created', 'date_modified' );
  451. // Format date values.
  452. foreach ( $format_date as $key ) {
  453. $_data[ $key ] = $_data[ $key ] ? wc_rest_prepare_date_response( $_data[ $key ] ) : null; // v1 API used UTC.
  454. }
  455. $data = array(
  456. 'id' => $_data['id'],
  457. 'date_created' => $_data['date_created'],
  458. 'date_modified' => $_data['date_modified'],
  459. 'email' => $_data['email'],
  460. 'first_name' => $_data['first_name'],
  461. 'last_name' => $_data['last_name'],
  462. 'username' => $_data['username'],
  463. 'last_order' => array(
  464. 'id' => is_object( $last_order ) ? $last_order->get_id() : null,
  465. 'date' => is_object( $last_order ) ? wc_rest_prepare_date_response( $last_order->get_date_created() ) : null, // v1 API used UTC.
  466. ),
  467. 'orders_count' => $customer->get_order_count(),
  468. 'total_spent' => $customer->get_total_spent(),
  469. 'avatar_url' => $customer->get_avatar_url(),
  470. 'billing' => $_data['billing'],
  471. 'shipping' => $_data['shipping'],
  472. );
  473. $context = ! empty( $request['context'] ) ? $request['context'] : 'view';
  474. $data = $this->add_additional_fields_to_object( $data, $request );
  475. $data = $this->filter_response_by_context( $data, $context );
  476. $response = rest_ensure_response( $data );
  477. $response->add_links( $this->prepare_links( $user_data ) );
  478. /**
  479. * Filter customer data returned from the REST API.
  480. *
  481. * @param WP_REST_Response $response The response object.
  482. * @param WP_User $user_data User object used to create response.
  483. * @param WP_REST_Request $request Request object.
  484. */
  485. return apply_filters( 'woocommerce_rest_prepare_customer', $response, $user_data, $request );
  486. }
  487. /**
  488. * Update customer meta fields.
  489. *
  490. * @param WC_Customer $customer
  491. * @param WP_REST_Request $request
  492. */
  493. protected function update_customer_meta_fields( $customer, $request ) {
  494. $schema = $this->get_item_schema();
  495. // Customer first name.
  496. if ( isset( $request['first_name'] ) ) {
  497. $customer->set_first_name( wc_clean( $request['first_name'] ) );
  498. }
  499. // Customer last name.
  500. if ( isset( $request['last_name'] ) ) {
  501. $customer->set_last_name( wc_clean( $request['last_name'] ) );
  502. }
  503. // Customer billing address.
  504. if ( isset( $request['billing'] ) ) {
  505. foreach ( array_keys( $schema['properties']['billing']['properties'] ) as $field ) {
  506. if ( isset( $request['billing'][ $field ] ) && is_callable( array( $customer, "set_billing_{$field}" ) ) ) {
  507. $customer->{"set_billing_{$field}"}( $request['billing'][ $field ] );
  508. }
  509. }
  510. }
  511. // Customer shipping address.
  512. if ( isset( $request['shipping'] ) ) {
  513. foreach ( array_keys( $schema['properties']['shipping']['properties'] ) as $field ) {
  514. if ( isset( $request['shipping'][ $field ] ) && is_callable( array( $customer, "set_shipping_{$field}" ) ) ) {
  515. $customer->{"set_shipping_{$field}"}( $request['shipping'][ $field ] );
  516. }
  517. }
  518. }
  519. }
  520. /**
  521. * Prepare links for the request.
  522. *
  523. * @param WP_User $customer Customer object.
  524. * @return array Links for the given customer.
  525. */
  526. protected function prepare_links( $customer ) {
  527. $links = array(
  528. 'self' => array(
  529. 'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $customer->ID ) ),
  530. ),
  531. 'collection' => array(
  532. 'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ),
  533. ),
  534. );
  535. return $links;
  536. }
  537. /**
  538. * Get the Customer's schema, conforming to JSON Schema.
  539. *
  540. * @return array
  541. */
  542. public function get_item_schema() {
  543. $schema = array(
  544. '$schema' => 'http://json-schema.org/draft-04/schema#',
  545. 'title' => 'customer',
  546. 'type' => 'object',
  547. 'properties' => array(
  548. 'id' => array(
  549. 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
  550. 'type' => 'integer',
  551. 'context' => array( 'view', 'edit' ),
  552. 'readonly' => true,
  553. ),
  554. 'date_created' => array(
  555. 'description' => __( 'The date the customer was created, as GMT.', 'woocommerce' ),
  556. 'type' => 'date-time',
  557. 'context' => array( 'view', 'edit' ),
  558. 'readonly' => true,
  559. ),
  560. 'date_modified' => array(
  561. 'description' => __( 'The date the customer was last modified, as GMT.', 'woocommerce' ),
  562. 'type' => 'date-time',
  563. 'context' => array( 'view', 'edit' ),
  564. 'readonly' => true,
  565. ),
  566. 'email' => array(
  567. 'description' => __( 'The email address for the customer.', 'woocommerce' ),
  568. 'type' => 'string',
  569. 'format' => 'email',
  570. 'context' => array( 'view', 'edit' ),
  571. ),
  572. 'first_name' => array(
  573. 'description' => __( 'Customer first name.', 'woocommerce' ),
  574. 'type' => 'string',
  575. 'context' => array( 'view', 'edit' ),
  576. 'arg_options' => array(
  577. 'sanitize_callback' => 'sanitize_text_field',
  578. ),
  579. ),
  580. 'last_name' => array(
  581. 'description' => __( 'Customer last name.', 'woocommerce' ),
  582. 'type' => 'string',
  583. 'context' => array( 'view', 'edit' ),
  584. 'arg_options' => array(
  585. 'sanitize_callback' => 'sanitize_text_field',
  586. ),
  587. ),
  588. 'username' => array(
  589. 'description' => __( 'Customer login name.', 'woocommerce' ),
  590. 'type' => 'string',
  591. 'context' => array( 'view', 'edit' ),
  592. 'arg_options' => array(
  593. 'sanitize_callback' => 'sanitize_user',
  594. ),
  595. ),
  596. 'password' => array(
  597. 'description' => __( 'Customer password.', 'woocommerce' ),
  598. 'type' => 'string',
  599. 'context' => array( 'edit' ),
  600. ),
  601. 'last_order' => array(
  602. 'description' => __( 'Last order data.', 'woocommerce' ),
  603. 'type' => 'object',
  604. 'context' => array( 'view', 'edit' ),
  605. 'readonly' => true,
  606. 'properties' => array(
  607. 'id' => array(
  608. 'description' => __( 'Last order ID.', 'woocommerce' ),
  609. 'type' => 'integer',
  610. 'context' => array( 'view', 'edit' ),
  611. 'readonly' => true,
  612. ),
  613. 'date' => array(
  614. 'description' => __( 'The date of the customer last order, as GMT.', 'woocommerce' ),
  615. 'type' => 'date-time',
  616. 'context' => array( 'view', 'edit' ),
  617. 'readonly' => true,
  618. ),
  619. ),
  620. ),
  621. 'orders_count' => array(
  622. 'description' => __( 'Quantity of orders made by the customer.', 'woocommerce' ),
  623. 'type' => 'integer',
  624. 'context' => array( 'view', 'edit' ),
  625. 'readonly' => true,
  626. ),
  627. 'total_spent' => array(
  628. 'description' => __( 'Total amount spent.', 'woocommerce' ),
  629. 'type' => 'string',
  630. 'context' => array( 'view', 'edit' ),
  631. 'readonly' => true,
  632. ),
  633. 'avatar_url' => array(
  634. 'description' => __( 'Avatar URL.', 'woocommerce' ),
  635. 'type' => 'string',
  636. 'context' => array( 'view', 'edit' ),
  637. 'readonly' => true,
  638. ),
  639. 'billing' => array(
  640. 'description' => __( 'List of billing address data.', 'woocommerce' ),
  641. 'type' => 'object',
  642. 'context' => array( 'view', 'edit' ),
  643. 'properties' => array(
  644. 'first_name' => array(
  645. 'description' => __( 'First name.', 'woocommerce' ),
  646. 'type' => 'string',
  647. 'context' => array( 'view', 'edit' ),
  648. ),
  649. 'last_name' => array(
  650. 'description' => __( 'Last name.', 'woocommerce' ),
  651. 'type' => 'string',
  652. 'context' => array( 'view', 'edit' ),
  653. ),
  654. 'company' => array(
  655. 'description' => __( 'Company name.', 'woocommerce' ),
  656. 'type' => 'string',
  657. 'context' => array( 'view', 'edit' ),
  658. ),
  659. 'address_1' => array(
  660. 'description' => __( 'Address line 1.', 'woocommerce' ),
  661. 'type' => 'string',
  662. 'context' => array( 'view', 'edit' ),
  663. ),
  664. 'address_2' => array(
  665. 'description' => __( 'Address line 2.', 'woocommerce' ),
  666. 'type' => 'string',
  667. 'context' => array( 'view', 'edit' ),
  668. ),
  669. 'city' => array(
  670. 'description' => __( 'City name.', 'woocommerce' ),
  671. 'type' => 'string',
  672. 'context' => array( 'view', 'edit' ),
  673. ),
  674. 'state' => array(
  675. 'description' => __( 'ISO code or name of the state, province or district.', 'woocommerce' ),
  676. 'type' => 'string',
  677. 'context' => array( 'view', 'edit' ),
  678. ),
  679. 'postcode' => array(
  680. 'description' => __( 'Postal code.', 'woocommerce' ),
  681. 'type' => 'string',
  682. 'context' => array( 'view', 'edit' ),
  683. ),
  684. 'country' => array(
  685. 'description' => __( 'ISO code of the country.', 'woocommerce' ),
  686. 'type' => 'string',
  687. 'context' => array( 'view', 'edit' ),
  688. ),
  689. 'email' => array(
  690. 'description' => __( 'Email address.', 'woocommerce' ),
  691. 'type' => 'string',
  692. 'format' => 'email',
  693. 'context' => array( 'view', 'edit' ),
  694. ),
  695. 'phone' => array(
  696. 'description' => __( 'Phone number.', 'woocommerce' ),
  697. 'type' => 'string',
  698. 'context' => array( 'view', 'edit' ),
  699. ),
  700. ),
  701. ),
  702. 'shipping' => array(
  703. 'description' => __( 'List of shipping address data.', 'woocommerce' ),
  704. 'type' => 'object',
  705. 'context' => array( 'view', 'edit' ),
  706. 'properties' => array(
  707. 'first_name' => array(
  708. 'description' => __( 'First name.', 'woocommerce' ),
  709. 'type' => 'string',
  710. 'context' => array( 'view', 'edit' ),
  711. ),
  712. 'last_name' => array(
  713. 'description' => __( 'Last name.', 'woocommerce' ),
  714. 'type' => 'string',
  715. 'context' => array( 'view', 'edit' ),
  716. ),
  717. 'company' => array(
  718. 'description' => __( 'Company name.', 'woocommerce' ),
  719. 'type' => 'string',
  720. 'context' => array( 'view', 'edit' ),
  721. ),
  722. 'address_1' => array(
  723. 'description' => __( 'Address line 1.', 'woocommerce' ),
  724. 'type' => 'string',
  725. 'context' => array( 'view', 'edit' ),
  726. ),
  727. 'address_2' => array(
  728. 'description' => __( 'Address line 2.', 'woocommerce' ),
  729. 'type' => 'string',
  730. 'context' => array( 'view', 'edit' ),
  731. ),
  732. 'city' => array(
  733. 'description' => __( 'City name.', 'woocommerce' ),
  734. 'type' => 'string',
  735. 'context' => array( 'view', 'edit' ),
  736. ),
  737. 'state' => array(
  738. 'description' => __( 'ISO code or name of the state, province or district.', 'woocommerce' ),
  739. 'type' => 'string',
  740. 'context' => array( 'view', 'edit' ),
  741. ),
  742. 'postcode' => array(
  743. 'description' => __( 'Postal code.', 'woocommerce' ),
  744. 'type' => 'string',
  745. 'context' => array( 'view', 'edit' ),
  746. ),
  747. 'country' => array(
  748. 'description' => __( 'ISO code of the country.', 'woocommerce' ),
  749. 'type' => 'string',
  750. 'context' => array( 'view', 'edit' ),
  751. ),
  752. ),
  753. ),
  754. ),
  755. );
  756. return $this->add_additional_fields_schema( $schema );
  757. }
  758. /**
  759. * Get role names.
  760. *
  761. * @return array
  762. */
  763. protected function get_role_names() {
  764. global $wp_roles;
  765. return array_keys( $wp_roles->role_names );
  766. }
  767. /**
  768. * Get the query params for collections.
  769. *
  770. * @return array
  771. */
  772. public function get_collection_params() {
  773. $params = parent::get_collection_params();
  774. $params['context']['default'] = 'view';
  775. $params['exclude'] = array(
  776. 'description' => __( 'Ensure result set excludes specific IDs.', 'woocommerce' ),
  777. 'type' => 'array',
  778. 'items' => array(
  779. 'type' => 'integer',
  780. ),
  781. 'default' => array(),
  782. 'sanitize_callback' => 'wp_parse_id_list',
  783. );
  784. $params['include'] = array(
  785. 'description' => __( 'Limit result set to specific IDs.', 'woocommerce' ),
  786. 'type' => 'array',
  787. 'items' => array(
  788. 'type' => 'integer',
  789. ),
  790. 'default' => array(),
  791. 'sanitize_callback' => 'wp_parse_id_list',
  792. );
  793. $params['offset'] = array(
  794. 'description' => __( 'Offset the result set by a specific number of items.', 'woocommerce' ),
  795. 'type' => 'integer',
  796. 'sanitize_callback' => 'absint',
  797. 'validate_callback' => 'rest_validate_request_arg',
  798. );
  799. $params['order'] = array(
  800. 'default' => 'asc',
  801. 'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ),
  802. 'enum' => array( 'asc', 'desc' ),
  803. 'sanitize_callback' => 'sanitize_key',
  804. 'type' => 'string',
  805. 'validate_callback' => 'rest_validate_request_arg',
  806. );
  807. $params['orderby'] = array(
  808. 'default' => 'name',
  809. 'description' => __( 'Sort collection by object attribute.', 'woocommerce' ),
  810. 'enum' => array(
  811. 'id',
  812. 'include',
  813. 'name',
  814. 'registered_date',
  815. ),
  816. 'sanitize_callback' => 'sanitize_key',
  817. 'type' => 'string',
  818. 'validate_callback' => 'rest_validate_request_arg',
  819. );
  820. $params['email'] = array(
  821. 'description' => __( 'Limit result set to resources with a specific email.', 'woocommerce' ),
  822. 'type' => 'string',
  823. 'format' => 'email',
  824. 'validate_callback' => 'rest_validate_request_arg',
  825. );
  826. $params['role'] = array(
  827. 'description' => __( 'Limit result set to resources with a specific role.', 'woocommerce' ),
  828. 'type' => 'string',
  829. 'default' => 'customer',
  830. 'enum' => array_merge( array( 'all' ), $this->get_role_names() ),
  831. 'validate_callback' => 'rest_validate_request_arg',
  832. );
  833. return $params;
  834. }
  835. }