Issues (334)

src/Controllers/Version4/Customers.php (5 issues)

Labels
1
<?php
2
/**
3
 * REST API Customers controller
4
 *
5
 * Handles requests to the /customers endpoint.
6
 *
7
 * @package Automattic/WooCommerce/RestApi
8
 */
9
10
namespace Automattic\WooCommerce\RestApi\Controllers\Version4;
11
12
defined( 'ABSPATH' ) || exit;
13
14
use \WP_REST_Server;
15
use Automattic\WooCommerce\RestApi\Controllers\Version4\Requests\CustomerRequest;
16
use Automattic\WooCommerce\RestApi\Controllers\Version4\Responses\CustomerResponse;
17
use Automattic\WooCommerce\RestApi\Controllers\Version4\Utilities\Pagination;
18
19
/**
20
 * REST API Customers controller class.
21
 */
22
class Customers extends AbstractController {
23
24
	/**
25
	 * Route base.
26
	 *
27
	 * @var string
28
	 */
29
	protected $rest_base = 'customers';
30
31
	/**
32
	 * Permission to check.
33
	 *
34
	 * @var string
35
	 */
36
	protected $resource_type = 'customers';
37
38
	/**
39
	 * Register the routes for customers.
40
	 */
41
	public function register_routes() {
42
		register_rest_route(
43
			$this->namespace,
44
			'/' . $this->rest_base,
45
			array(
46
				array(
47
					'methods'             => \WP_REST_Server::READABLE,
48
					'callback'            => array( $this, 'get_items' ),
49
					'permission_callback' => array( $this, 'get_items_permissions_check' ),
50
					'args'                => $this->get_collection_params(),
51
				),
52
				array(
53
					'methods'             => \WP_REST_Server::CREATABLE,
54
					'callback'            => array( $this, 'create_item' ),
55
					'permission_callback' => array( $this, 'create_item_permissions_check' ),
56
					'args'                => array_merge(
57
						$this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ),
58
						array(
59
							'email' => array(
60
								'required'    => true,
61
								'type'        => 'string',
62
								'description' => __( 'New user email address.', 'woocommerce-rest-api' ),
63
							),
64
							'username' => array(
65
								'required'    => 'no' === get_option( 'woocommerce_registration_generate_username', 'yes' ),
66
								'description' => __( 'New user username.', 'woocommerce-rest-api' ),
67
								'type'        => 'string',
68
							),
69
							'password' => array(
70
								'required'    => 'no' === get_option( 'woocommerce_registration_generate_password', 'no' ),
71
								'description' => __( 'New user password.', 'woocommerce-rest-api' ),
72
								'type'        => 'string',
73
							),
74
						)
75
					),
76
				),
77
				'schema' => array( $this, 'get_public_item_schema' ),
78
			),
79
			true
80
		);
81
82
		register_rest_route(
83
			$this->namespace,
84
			'/' . $this->rest_base . '/(?P<id>[\d]+)',
85
			array(
86
				'args' => array(
87
					'id' => array(
88
						'description' => __( 'Unique identifier for the resource.', 'woocommerce-rest-api' ),
89
						'type'        => 'integer',
90
					),
91
				),
92
				array(
93
					'methods'             => \WP_REST_Server::READABLE,
94
					'callback'            => array( $this, 'get_item' ),
95
					'permission_callback' => array( $this, 'get_item_permissions_check' ),
96
					'args'                => array(
97
						'context' => $this->get_context_param( array( 'default' => 'view' ) ),
98
					),
99
				),
100
				array(
101
					'methods'             => \WP_REST_Server::EDITABLE,
102
					'callback'            => array( $this, 'update_item' ),
103
					'permission_callback' => array( $this, 'update_item_permissions_check' ),
104
					'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::EDITABLE ),
105
				),
106
				array(
107
					'methods'             => \WP_REST_Server::DELETABLE,
108
					'callback'            => array( $this, 'delete_item' ),
109
					'permission_callback' => array( $this, 'delete_item_permissions_check' ),
110
					'args'                => array(
111
						'force' => array(
112
							'default'     => false,
113
							'type'        => 'boolean',
114
							'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce-rest-api' ),
115
						),
116
						'reassign' => array(
117
							'default'     => 0,
118
							'type'        => 'integer',
119
							'description' => __( 'ID to reassign posts to.', 'woocommerce-rest-api' ),
120
						),
121
					),
122
				),
123
				'schema' => array( $this, 'get_public_item_schema' ),
124
			),
125
			true
126
		);
127
128
		$this->register_batch_route();
129
	}
130
131
	/**
132
	 * Get all customers.
133
	 *
134
	 * @param \WP_REST_Request $request Full details about the request.
135
	 * @return \WP_Error|\WP_REST_Response
136
	 */
137
	public function get_items( $request ) {
138
		$prepared_args = array(
139
			'exclude' => $request['exclude'],
140
			'include' => $request['include'],
141
			'order'   => $request['order'],
142
			'number'  => $request['per_page'],
143
		);
144
145
		if ( ! empty( $request['offset'] ) ) {
146
			$prepared_args['offset'] = $request['offset'];
147
		} else {
148
			$prepared_args['offset'] = ( $request['page'] - 1 ) * $prepared_args['number'];
149
		}
150
151
		$orderby_possibles = array(
152
			'id'              => 'ID',
153
			'include'         => 'include',
154
			'name'            => 'display_name',
155
			'registered_date' => 'registered',
156
		);
157
		$prepared_args['orderby'] = $orderby_possibles[ $request['orderby'] ];
158
		$prepared_args['search']  = $request['search'];
159
160
		if ( '' !== $prepared_args['search'] ) {
161
			$prepared_args['search'] = '*' . $prepared_args['search'] . '*';
162
		}
163
164
		// Filter by email.
165
		if ( ! empty( $request['email'] ) ) {
166
			$prepared_args['search']         = $request['email'];
167
			$prepared_args['search_columns'] = array( 'user_email' );
168
		}
169
170
		// Filter by role.
171
		if ( 'all' !== $request['role'] ) {
172
			$prepared_args['role'] = $request['role'];
173
		}
174
175
		/**
176
		 * Filter arguments, before passing to \ WP_User_Query, when querying users via the REST API.
177
		 *
178
		 * @see https://developer.wordpress.org/reference/classes/\ WP_User_Query/
179
		 *
180
		 * @param array           $prepared_args Array of arguments for \ WP_User_Query.
181
		 * @param \WP_REST_Request $request       The current request.
182
		 */
183
		$prepared_args = apply_filters( 'woocommerce_rest_customer_query', $prepared_args, $request );
184
185
		$query = new \WP_User_Query( $prepared_args );
186
187
		$users = array();
188
		foreach ( $query->results as $user ) {
189
			$customer = new \WC_Customer( $user->ID );
190
			$data     = $this->prepare_item_for_response( $customer, $request );
191
			$users[]  = $this->prepare_response_for_collection( $data );
192
		}
193
194
		// Store pagination values for headers then unset for count query.
195
		$per_page = (int) $prepared_args['number'];
196
		$page     = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 );
197
198
		$prepared_args['fields'] = 'ID';
199
200
		$total_users = $query->get_total();
201
202
		if ( $total_users < 1 ) {
203
			// Out-of-bounds, run the query again without LIMIT for total count.
204
			unset( $prepared_args['number'] );
205
			unset( $prepared_args['offset'] );
206
			$count_query = new \ WP_User_Query( $prepared_args );
207
			$total_users = $count_query->get_total();
208
		}
209
210
		$response = rest_ensure_response( $users );
211
		$response = Pagination::add_pagination_headers( $response, $request, $total_users, ceil( $total_users / $per_page ) );
0 ignored issues
show
ceil($total_users / $per_page) of type double is incompatible with the type integer expected by parameter $total_pages of Automattic\WooCommerce\R...dd_pagination_headers(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

211
		$response = Pagination::add_pagination_headers( $response, $request, $total_users, /** @scrutinizer ignore-type */ ceil( $total_users / $per_page ) );
Loading history...
212
213
		return $response;
214
	}
215
216
	/**
217
	 * Create a single customer.
218
	 *
219
	 * @throws \WC_REST_Exception On invalid params.
220
	 * @param \WP_REST_Request $request Full details about the request.
221
	 * @return \WP_Error|\WP_REST_Response
222
	 */
223
	public function create_item( $request ) {
224
		try {
225
			if ( ! empty( $request['id'] ) ) {
226
				throw new \WC_REST_Exception( 'woocommerce_rest_customer_exists', __( 'Cannot create existing resource.', 'woocommerce-rest-api' ), 400 );
227
			}
228
229
			$customer_request = new CustomerRequest( $request );
230
			$customer         = $customer_request->prepare_object();
231
			$customer->save();
232
233
			if ( ! $customer->get_id() ) {
234
				throw new \WC_REST_Exception( 'woocommerce_rest_cannot_create', __( 'This resource cannot be created.', 'woocommerce-rest-api' ), 400 );
235
			}
236
237
			$this->update_additional_fields_for_object( $customer, $request );
0 ignored issues
show
$customer of type WC_Customer is incompatible with the type array expected by parameter $object of WP_REST_Controller::upda...nal_fields_for_object(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

237
			$this->update_additional_fields_for_object( /** @scrutinizer ignore-type */ $customer, $request );
Loading history...
238
239
			/**
240
			 * Fires after a customer is created or updated via the REST API.
241
			 *
242
			 * @param \WC_Customer     $customer Customer object.
243
			 * @param \WP_REST_Request $request   Request object.
244
			 * @param boolean          $creating  True when creating customer, false when updating customer.
245
			 */
246
			do_action( 'woocommerce_rest_insert_customer_object', $customer, $request, true );
247
248
			$request->set_param( 'context', 'edit' );
249
			$response = $this->prepare_item_for_response( $customer, $request );
250
			$response = rest_ensure_response( $response );
251
			$response->set_status( 201 );
252
			$response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $customer->get_id() ) ) );
253
254
			return $response;
255
		} catch ( \Exception $e ) {
256
			return new \WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
0 ignored issues
show
The method getErrorCode() does not exist on Exception. It seems like you code against a sub-type of Exception such as WC_API_Exception or WC_Data_Exception. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

256
			return new \WP_Error( $e->/** @scrutinizer ignore-call */ getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
Loading history...
257
		}
258
	}
259
260
	/**
261
	 * Get a single customer.
262
	 *
263
	 * @param \WP_REST_Request $request Full details about the request.
264
	 * @return \WP_Error|\WP_REST_Response
265
	 */
266
	public function get_item( $request ) {
267
		$id       = (int) $request['id'];
268
		$customer = new \WC_Customer( $id );
269
270
		if ( empty( $id ) || ! $customer->get_id() ) {
271
			return new \WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource ID.', 'woocommerce-rest-api' ), array( 'status' => 404 ) );
272
		}
273
274
		$response = $this->prepare_item_for_response( $customer, $request );
275
		$response = rest_ensure_response( $response );
276
277
		return $response;
278
	}
279
280
	/**
281
	 * Update a single user.
282
	 *
283
	 * @throws \WC_REST_Exception On invalid params.
284
	 *
285
	 * @param \WP_REST_Request $request Full details about the request.
286
	 * @return \WP_Error|\WP_REST_Response
287
	 */
288
	public function update_item( $request ) {
289
		try {
290
			$customer_request = new CustomerRequest( $request );
291
			$customer         = $customer_request->prepare_object();
292
			$customer->save();
293
294
			$this->update_additional_fields_for_object( $customer, $request );
0 ignored issues
show
$customer of type WC_Customer is incompatible with the type array expected by parameter $object of WP_REST_Controller::upda...nal_fields_for_object(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

294
			$this->update_additional_fields_for_object( /** @scrutinizer ignore-type */ $customer, $request );
Loading history...
295
296
			if ( is_multisite() && ! is_user_member_of_blog( $customer->get_id() ) ) {
297
				add_user_to_blog( get_current_blog_id(), $customer->get_id(), 'customer' );
298
			}
299
300
			/**
301
			 * Fires after a customer is created or updated via the REST API.
302
			 *
303
			 * @param \WC_Customer     $customer  Data used to create the customer.
304
			 * @param \WP_REST_Request $request   Request object.
305
			 * @param boolean         $creating  True when creating customer, false when updating customer.
306
			 */
307
			do_action( 'woocommerce_rest_insert_customer_object', $customer, $request, false );
308
309
			$request->set_param( 'context', 'edit' );
310
			$response = $this->prepare_item_for_response( $customer, $request );
311
			$response = rest_ensure_response( $response );
312
			return $response;
313
		} catch ( Exception $e ) {
0 ignored issues
show
The type Automattic\WooCommerce\R...lers\Version4\Exception was not found. Did you mean Exception? If so, make sure to prefix the type with \.
Loading history...
314
			return new \WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
315
		}
316
	}
317
318
	/**
319
	 * Delete a single customer.
320
	 *
321
	 * @param \WP_REST_Request $request Full details about the request.
322
	 * @return \WP_Error|\WP_REST_Response
323
	 */
324
	public function delete_item( $request ) {
325
		$id       = (int) $request['id'];
326
		$reassign = isset( $request['reassign'] ) ? absint( $request['reassign'] ) : null;
327
		$force    = isset( $request['force'] ) ? (bool) $request['force'] : false;
328
329
		// We don't support trashing for this type, error out.
330
		if ( ! $force ) {
331
			return new \WP_Error( 'woocommerce_rest_trash_not_supported', __( 'Customers do not support trashing.', 'woocommerce-rest-api' ), array( 'status' => 501 ) );
332
		}
333
334
		if ( ! get_userdata( $id ) ) {
335
			return new \WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource id.', 'woocommerce-rest-api' ), array( 'status' => 400 ) );
336
		}
337
338
		if ( ! empty( $reassign ) ) {
339
			if ( $reassign === $id || ! get_userdata( $reassign ) ) {
340
				return new \WP_Error( 'woocommerce_rest_customer_invalid_reassign', __( 'Invalid resource id for reassignment.', 'woocommerce-rest-api' ), array( 'status' => 400 ) );
341
			}
342
		}
343
344
		/** Include admin customer functions to get access to wp_delete_user() */
345
		require_once ABSPATH . 'wp-admin/includes/user.php';
346
347
		$customer = new \WC_Customer( $id );
348
349
		$request->set_param( 'context', 'edit' );
350
		$response = $this->prepare_item_for_response( $customer, $request );
351
352
		if ( ! is_null( $reassign ) ) {
353
			$result = $customer->delete_and_reassign( $reassign );
354
		} else {
355
			$result = $customer->delete();
356
		}
357
358
		if ( ! $result ) {
359
			return new \WP_Error( 'woocommerce_rest_cannot_delete', __( 'The resource cannot be deleted.', 'woocommerce-rest-api' ), array( 'status' => 500 ) );
360
		}
361
362
		/**
363
		 * Fires after a customer is deleted via the REST API.
364
		 *
365
		 * @param \WC_Customer      $customer User data.
366
		 * @param \WP_REST_Response $response  The response returned from the API.
367
		 * @param \WP_REST_Request  $request   The request sent to the API.
368
		 */
369
		do_action( 'woocommerce_rest_delete_customer_object', $customer, $response, $request );
370
371
		return $response;
372
	}
373
374
	/**
375
	 * Get data for this object in the format of this endpoint's schema.
376
	 *
377
	 * @param \WC_Customer     $object Object to prepare.
378
	 * @param \WP_REST_Request $request Request object.
379
	 * @return array Array of data in the correct format.
380
	 */
381
	protected function get_data_for_response( $object, $request ) {
382
		$formatter = new CustomerResponse();
383
384
		return $formatter->prepare_response( $object, $this->get_request_context( $request ) );
385
	}
386
387
	/**
388
	 * Prepare links for the request.
389
	 *
390
	 * @param mixed            $item Object to prepare.
391
	 * @param \WP_REST_Request $request Request object.
392
	 * @return array
393
	 */
394
	protected function prepare_links( $item, $request ) {
395
		$links = array(
396
			'self' => array(
397
				'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $item->get_id() ) ),
398
			),
399
			'collection' => array(
400
				'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ),
401
			),
402
		);
403
		return $links;
404
	}
405
406
	/**
407
	 * Get the Customer's schema, conforming to JSON Schema.
408
	 *
409
	 * @return array
410
	 */
411
	public function get_item_schema() {
412
		$schema = array(
413
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
414
			'title'      => 'customer',
415
			'type'       => 'object',
416
			'properties' => array(
417
				'id'                 => array(
418
					'description' => __( 'Unique identifier for the resource.', 'woocommerce-rest-api' ),
419
					'type'        => 'integer',
420
					'context'     => array( 'view', 'edit' ),
421
					'readonly'    => true,
422
				),
423
				'date_created'       => array(
424
					'description' => __( "The date the customer was created, in the site's timezone.", 'woocommerce-rest-api' ),
425
					'type'        => 'date-time',
426
					'context'     => array( 'view', 'edit' ),
427
					'readonly'    => true,
428
				),
429
				'date_created_gmt'   => array(
430
					'description' => __( 'The date the customer was created, as GMT.', 'woocommerce-rest-api' ),
431
					'type'        => 'date-time',
432
					'context'     => array( 'view', 'edit' ),
433
					'readonly'    => true,
434
				),
435
				'date_modified'      => array(
436
					'description' => __( "The date the customer was last modified, in the site's timezone.", 'woocommerce-rest-api' ),
437
					'type'        => 'date-time',
438
					'context'     => array( 'view', 'edit' ),
439
					'readonly'    => true,
440
				),
441
				'date_modified_gmt'  => array(
442
					'description' => __( 'The date the customer was last modified, as GMT.', 'woocommerce-rest-api' ),
443
					'type'        => 'date-time',
444
					'context'     => array( 'view', 'edit' ),
445
					'readonly'    => true,
446
				),
447
				'email'              => array(
448
					'description' => __( 'The email address for the customer.', 'woocommerce-rest-api' ),
449
					'type'        => 'string',
450
					'format'      => 'email',
451
					'context'     => array( 'view', 'edit' ),
452
				),
453
				'first_name'         => array(
454
					'description' => __( 'Customer first name.', 'woocommerce-rest-api' ),
455
					'type'        => 'string',
456
					'context'     => array( 'view', 'edit' ),
457
					'arg_options' => array(
458
						'sanitize_callback' => 'sanitize_text_field',
459
					),
460
				),
461
				'last_name'          => array(
462
					'description' => __( 'Customer last name.', 'woocommerce-rest-api' ),
463
					'type'        => 'string',
464
					'context'     => array( 'view', 'edit' ),
465
					'arg_options' => array(
466
						'sanitize_callback' => 'sanitize_text_field',
467
					),
468
				),
469
				'role'               => array(
470
					'description' => __( 'Customer role.', 'woocommerce-rest-api' ),
471
					'type'        => 'string',
472
					'context'     => array( 'view', 'edit' ),
473
					'readonly'    => true,
474
				),
475
				'username'           => array(
476
					'description' => __( 'Customer login name.', 'woocommerce-rest-api' ),
477
					'type'        => 'string',
478
					'context'     => array( 'view', 'edit' ),
479
					'arg_options' => array(
480
						'sanitize_callback' => 'sanitize_user',
481
					),
482
				),
483
				'password'           => array(
484
					'description' => __( 'Customer password.', 'woocommerce-rest-api' ),
485
					'type'        => 'string',
486
					'context'     => array( 'edit' ),
487
				),
488
				'billing'            => array(
489
					'description' => __( 'List of billing address data.', 'woocommerce-rest-api' ),
490
					'type'        => 'object',
491
					'context'     => array( 'view', 'edit' ),
492
					'properties'  => array(
493
						'first_name' => array(
494
							'description' => __( 'First name.', 'woocommerce-rest-api' ),
495
							'type'        => 'string',
496
							'context'     => array( 'view', 'edit' ),
497
						),
498
						'last_name'  => array(
499
							'description' => __( 'Last name.', 'woocommerce-rest-api' ),
500
							'type'        => 'string',
501
							'context'     => array( 'view', 'edit' ),
502
						),
503
						'company'    => array(
504
							'description' => __( 'Company name.', 'woocommerce-rest-api' ),
505
							'type'        => 'string',
506
							'context'     => array( 'view', 'edit' ),
507
						),
508
						'address_1'  => array(
509
							'description' => __( 'Address line 1', 'woocommerce-rest-api' ),
510
							'type'        => 'string',
511
							'context'     => array( 'view', 'edit' ),
512
						),
513
						'address_2'  => array(
514
							'description' => __( 'Address line 2', 'woocommerce-rest-api' ),
515
							'type'        => 'string',
516
							'context'     => array( 'view', 'edit' ),
517
						),
518
						'city'       => array(
519
							'description' => __( 'City name.', 'woocommerce-rest-api' ),
520
							'type'        => 'string',
521
							'context'     => array( 'view', 'edit' ),
522
						),
523
						'state'      => array(
524
							'description' => __( 'ISO code or name of the state, province or district.', 'woocommerce-rest-api' ),
525
							'type'        => 'string',
526
							'context'     => array( 'view', 'edit' ),
527
						),
528
						'postcode'   => array(
529
							'description' => __( 'Postal code.', 'woocommerce-rest-api' ),
530
							'type'        => 'string',
531
							'context'     => array( 'view', 'edit' ),
532
						),
533
						'country'    => array(
534
							'description' => __( 'ISO code of the country.', 'woocommerce-rest-api' ),
535
							'type'        => 'string',
536
							'context'     => array( 'view', 'edit' ),
537
						),
538
						'email'      => array(
539
							'description' => __( 'Email address.', 'woocommerce-rest-api' ),
540
							'type'        => 'string',
541
							'format'      => 'email',
542
							'context'     => array( 'view', 'edit' ),
543
						),
544
						'phone'      => array(
545
							'description' => __( 'Phone number.', 'woocommerce-rest-api' ),
546
							'type'        => 'string',
547
							'context'     => array( 'view', 'edit' ),
548
						),
549
					),
550
				),
551
				'shipping'           => array(
552
					'description' => __( 'List of shipping address data.', 'woocommerce-rest-api' ),
553
					'type'        => 'object',
554
					'context'     => array( 'view', 'edit' ),
555
					'properties'  => array(
556
						'first_name' => array(
557
							'description' => __( 'First name.', 'woocommerce-rest-api' ),
558
							'type'        => 'string',
559
							'context'     => array( 'view', 'edit' ),
560
						),
561
						'last_name'  => array(
562
							'description' => __( 'Last name.', 'woocommerce-rest-api' ),
563
							'type'        => 'string',
564
							'context'     => array( 'view', 'edit' ),
565
						),
566
						'company'    => array(
567
							'description' => __( 'Company name.', 'woocommerce-rest-api' ),
568
							'type'        => 'string',
569
							'context'     => array( 'view', 'edit' ),
570
						),
571
						'address_1'  => array(
572
							'description' => __( 'Address line 1', 'woocommerce-rest-api' ),
573
							'type'        => 'string',
574
							'context'     => array( 'view', 'edit' ),
575
						),
576
						'address_2'  => array(
577
							'description' => __( 'Address line 2', 'woocommerce-rest-api' ),
578
							'type'        => 'string',
579
							'context'     => array( 'view', 'edit' ),
580
						),
581
						'city'       => array(
582
							'description' => __( 'City name.', 'woocommerce-rest-api' ),
583
							'type'        => 'string',
584
							'context'     => array( 'view', 'edit' ),
585
						),
586
						'state'      => array(
587
							'description' => __( 'ISO code or name of the state, province or district.', 'woocommerce-rest-api' ),
588
							'type'        => 'string',
589
							'context'     => array( 'view', 'edit' ),
590
						),
591
						'postcode'   => array(
592
							'description' => __( 'Postal code.', 'woocommerce-rest-api' ),
593
							'type'        => 'string',
594
							'context'     => array( 'view', 'edit' ),
595
						),
596
						'country'    => array(
597
							'description' => __( 'ISO code of the country.', 'woocommerce-rest-api' ),
598
							'type'        => 'string',
599
							'context'     => array( 'view', 'edit' ),
600
						),
601
					),
602
				),
603
				'is_paying_customer' => array(
604
					'description' => __( 'Is the customer a paying customer?', 'woocommerce-rest-api' ),
605
					'type'        => 'bool',
606
					'context'     => array( 'view', 'edit' ),
607
					'readonly'    => true,
608
				),
609
				'avatar_url'         => array(
610
					'description' => __( 'Avatar URL.', 'woocommerce-rest-api' ),
611
					'type'        => 'string',
612
					'context'     => array( 'view', 'edit' ),
613
					'readonly'    => true,
614
				),
615
				'meta_data'          => array(
616
					'description' => __( 'Meta data.', 'woocommerce-rest-api' ),
617
					'type'        => 'array',
618
					'context'     => array( 'view', 'edit' ),
619
					'items'       => array(
620
						'type'       => 'object',
621
						'properties' => array(
622
							'id'    => array(
623
								'description' => __( 'Meta ID.', 'woocommerce-rest-api' ),
624
								'type'        => 'integer',
625
								'context'     => array( 'view', 'edit' ),
626
								'readonly'    => true,
627
							),
628
							'key'   => array(
629
								'description' => __( 'Meta key.', 'woocommerce-rest-api' ),
630
								'type'        => 'string',
631
								'context'     => array( 'view', 'edit' ),
632
							),
633
							'value' => array(
634
								'description' => __( 'Meta value.', 'woocommerce-rest-api' ),
635
								'type'        => 'mixed',
636
								'context'     => array( 'view', 'edit' ),
637
							),
638
						),
639
					),
640
				),
641
			),
642
		);
643
644
		return $this->add_additional_fields_schema( $schema );
645
	}
646
647
	/**
648
	 * Get role names.
649
	 *
650
	 * @return array
651
	 */
652
	protected function get_role_names() {
653
		global $wp_roles;
654
655
		return array_keys( $wp_roles->role_names );
656
	}
657
658
	/**
659
	 * Get the query params for collections.
660
	 *
661
	 * @return array
662
	 */
663
	public function get_collection_params() {
664
		$params = parent::get_collection_params();
665
666
		$params['context']['default'] = 'view';
667
668
		$params['exclude'] = array(
669
			'description'       => __( 'Ensure result set excludes specific IDs.', 'woocommerce-rest-api' ),
670
			'type'              => 'array',
671
			'items'             => array(
672
				'type'          => 'integer',
673
			),
674
			'default'           => array(),
675
			'sanitize_callback' => 'wp_parse_id_list',
676
		);
677
		$params['include'] = array(
678
			'description'       => __( 'Limit result set to specific IDs.', 'woocommerce-rest-api' ),
679
			'type'              => 'array',
680
			'items'             => array(
681
				'type'          => 'integer',
682
			),
683
			'default'           => array(),
684
			'sanitize_callback' => 'wp_parse_id_list',
685
		);
686
		$params['offset'] = array(
687
			'description'        => __( 'Offset the result set by a specific number of items.', 'woocommerce-rest-api' ),
688
			'type'               => 'integer',
689
			'sanitize_callback'  => 'absint',
690
			'validate_callback'  => 'rest_validate_request_arg',
691
		);
692
		$params['order'] = array(
693
			'default'            => 'asc',
694
			'description'        => __( 'Order sort attribute ascending or descending.', 'woocommerce-rest-api' ),
695
			'enum'               => array( 'asc', 'desc' ),
696
			'sanitize_callback'  => 'sanitize_key',
697
			'type'               => 'string',
698
			'validate_callback'  => 'rest_validate_request_arg',
699
		);
700
		$params['orderby'] = array(
701
			'default'            => 'name',
702
			'description'        => __( 'Sort collection by object attribute.', 'woocommerce-rest-api' ),
703
			'enum'               => array(
704
				'id',
705
				'include',
706
				'name',
707
				'registered_date',
708
			),
709
			'sanitize_callback'  => 'sanitize_key',
710
			'type'               => 'string',
711
			'validate_callback'  => 'rest_validate_request_arg',
712
		);
713
		$params['email'] = array(
714
			'description'        => __( 'Limit result set to resources with a specific email.', 'woocommerce-rest-api' ),
715
			'type'               => 'string',
716
			'format'             => 'email',
717
			'validate_callback'  => 'rest_validate_request_arg',
718
		);
719
		$params['role'] = array(
720
			'description'        => __( 'Limit result set to resources with a specific role.', 'woocommerce-rest-api' ),
721
			'type'               => 'string',
722
			'default'            => 'customer',
723
			'enum'               => array_merge( array( 'all' ), $this->get_role_names() ),
724
			'validate_callback'  => 'rest_validate_request_arg',
725
		);
726
		return $params;
727
	}
728
729
	/**
730
	 * Check if a given ID is valid.
731
	 *
732
	 * @param  \WP_REST_Request $request Full details about the request.
733
	 * @return \WP_Error|boolean
734
	 */
735
	protected function check_valid_customer_id( $request ) {
736
		$id   = $request->get_param( 'id' );
737
		$user = get_userdata( $id );
738
739
		if ( false === $user ) {
740
			return new \WP_Error( 'woocommerce_rest_customer_invalid_id', __( 'Invalid ID.', 'woocommerce-rest-api' ), array( 'status' => 404 ) );
741
		}
742
		return true;
743
	}
744
745
	/**
746
	 * Check if a given request has access to read a webhook.
747
	 *
748
	 * @param  \WP_REST_Request $request Full details about the request.
749
	 * @return \WP_Error|boolean
750
	 */
751
	public function get_item_permissions_check( $request ) {
752
		$check_valid = $this->check_valid_customer_id( $request );
753
754
		if ( is_wp_error( $check_valid ) ) {
755
			return $check_valid;
756
		}
757
758
		return parent::get_item_permissions_check( $request );
759
	}
760
761
	/**
762
	 * Check if a given request has access to delete an item.
763
	 *
764
	 * @param  \WP_REST_Request $request Full details about the request.
765
	 * @return \WP_Error|boolean
766
	 */
767
	public function delete_item_permissions_check( $request ) {
768
		$check_valid = $this->check_valid_customer_id( $request );
769
770
		if ( is_wp_error( $check_valid ) ) {
771
			return $check_valid;
772
		}
773
774
		return parent::delete_item_permissions_check( $request );
775
	}
776
777
	/**
778
	 * Check if a given request has access to update an item.
779
	 *
780
	 * @param  \WP_REST_Request $request Full details about the request.
781
	 * @return \WP_Error|boolean
782
	 */
783
	public function update_item_permissions_check( $request ) {
784
		$check_valid = $this->check_valid_customer_id( $request );
785
786
		if ( is_wp_error( $check_valid ) ) {
787
			return $check_valid;
788
		}
789
790
		return parent::update_item_permissions_check( $request );
791
	}
792
}
793