Passed
Push — master ( 5bd17a...71a32c )
by Mike
04:53
created

Customers   B

Complexity

Total Complexity 45

Size/Duplication

Total Lines 785
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 446
dl 0
loc 785
rs 8.8
c 0
b 0
f 0
wmc 45

15 Methods

Rating   Name   Duplication   Size   Complexity  
A update_item() 0 27 4
B register_routes() 0 88 1
A get_item() 0 12 3
D get_items() 0 93 10
B delete_item() 0 48 10
A create_item() 0 34 4
A update_item_permissions_check() 0 8 2
A get_data_for_response() 0 4 1
B get_item_schema() 0 234 1
A delete_item_permissions_check() 0 8 2
A get_collection_params() 0 64 1
A check_valid_customer_id() 0 8 2
A get_role_names() 0 4 1
A prepare_links() 0 10 1
A get_item_permissions_check() 0 8 2

How to fix   Complexity   

Complex Class

Complex classes like Customers often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Customers, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * REST API Customers controller
4
 *
5
 * Handles requests to the /customers endpoint.
6
 *
7
 * @package WooCommerce/RestApi
8
 */
9
10
namespace WooCommerce\RestApi\Controllers\Version4;
11
12
defined( 'ABSPATH' ) || exit;
13
14
use \WP_REST_Server;
15
use WooCommerce\RestApi\Controllers\Version4\Requests\CustomerRequest;
16
use WooCommerce\RestApi\Controllers\Version4\Responses\CustomerResponse;
17
18
/**
19
 * REST API Customers controller class.
20
 */
21
class Customers extends AbstractController {
22
23
	/**
24
	 * Route base.
25
	 *
26
	 * @var string
27
	 */
28
	protected $rest_base = 'customers';
29
30
	/**
31
	 * Permission to check.
32
	 *
33
	 * @var string
34
	 */
35
	protected $resource_type = 'customers';
36
37
	/**
38
	 * Register the routes for customers.
39
	 */
40
	public function register_routes() {
41
		register_rest_route(
42
			$this->namespace,
43
			'/' . $this->rest_base,
44
			array(
45
				array(
46
					'methods'             => \WP_REST_Server::READABLE,
47
					'callback'            => array( $this, 'get_items' ),
48
					'permission_callback' => array( $this, 'get_items_permissions_check' ),
49
					'args'                => $this->get_collection_params(),
50
				),
51
				array(
52
					'methods'             => \WP_REST_Server::CREATABLE,
53
					'callback'            => array( $this, 'create_item' ),
54
					'permission_callback' => array( $this, 'create_item_permissions_check' ),
55
					'args'                => array_merge(
56
						$this->get_endpoint_args_for_item_schema( \WP_REST_Server::CREATABLE ),
57
						array(
58
							'email' => array(
59
								'required'    => true,
60
								'type'        => 'string',
61
								'description' => __( 'New user email address.', 'woocommerce' ),
62
							),
63
							'username' => array(
64
								'required'    => 'no' === get_option( 'woocommerce_registration_generate_username', 'yes' ),
65
								'description' => __( 'New user username.', 'woocommerce' ),
66
								'type'        => 'string',
67
							),
68
							'password' => array(
69
								'required'    => 'no' === get_option( 'woocommerce_registration_generate_password', 'no' ),
70
								'description' => __( 'New user password.', 'woocommerce' ),
71
								'type'        => 'string',
72
							),
73
						)
74
					),
75
				),
76
				'schema' => array( $this, 'get_public_item_schema' ),
77
			),
78
			true
79
		);
80
81
		register_rest_route(
82
			$this->namespace,
83
			'/' . $this->rest_base . '/(?P<id>[\d]+)',
84
			array(
85
				'args' => array(
86
					'id' => array(
87
						'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
88
						'type'        => 'integer',
89
					),
90
				),
91
				array(
92
					'methods'             => \WP_REST_Server::READABLE,
93
					'callback'            => array( $this, 'get_item' ),
94
					'permission_callback' => array( $this, 'get_item_permissions_check' ),
95
					'args'                => array(
96
						'context' => $this->get_context_param( array( 'default' => 'view' ) ),
97
					),
98
				),
99
				array(
100
					'methods'             => \WP_REST_Server::EDITABLE,
101
					'callback'            => array( $this, 'update_item' ),
102
					'permission_callback' => array( $this, 'update_item_permissions_check' ),
103
					'args'                => $this->get_endpoint_args_for_item_schema( \WP_REST_Server::EDITABLE ),
104
				),
105
				array(
106
					'methods'             => \WP_REST_Server::DELETABLE,
107
					'callback'            => array( $this, 'delete_item' ),
108
					'permission_callback' => array( $this, 'delete_item_permissions_check' ),
109
					'args'                => array(
110
						'force' => array(
111
							'default'     => false,
112
							'type'        => 'boolean',
113
							'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce' ),
114
						),
115
						'reassign' => array(
116
							'default'     => 0,
117
							'type'        => 'integer',
118
							'description' => __( 'ID to reassign posts to.', 'woocommerce' ),
119
						),
120
					),
121
				),
122
				'schema' => array( $this, 'get_public_item_schema' ),
123
			),
124
			true
125
		);
126
127
		$this->register_batch_route();
128
	}
129
130
	/**
131
	 * Get all customers.
132
	 *
133
	 * @param \WP_REST_Request $request Full details about the request.
134
	 * @return \WP_Error|\WP_REST_Response
135
	 */
136
	public function get_items( $request ) {
137
		$prepared_args = array(
138
			'exclude' => $request['exclude'],
139
			'include' => $request['include'],
140
			'order'   => $request['order'],
141
			'number'  => $request['per_page'],
142
		);
143
144
		if ( ! empty( $request['offset'] ) ) {
145
			$prepared_args['offset'] = $request['offset'];
146
		} else {
147
			$prepared_args['offset'] = ( $request['page'] - 1 ) * $prepared_args['number'];
148
		}
149
150
		$orderby_possibles = array(
151
			'id'              => 'ID',
152
			'include'         => 'include',
153
			'name'            => 'display_name',
154
			'registered_date' => 'registered',
155
		);
156
		$prepared_args['orderby'] = $orderby_possibles[ $request['orderby'] ];
157
		$prepared_args['search']  = $request['search'];
158
159
		if ( '' !== $prepared_args['search'] ) {
160
			$prepared_args['search'] = '*' . $prepared_args['search'] . '*';
161
		}
162
163
		// Filter by email.
164
		if ( ! empty( $request['email'] ) ) {
165
			$prepared_args['search']         = $request['email'];
166
			$prepared_args['search_columns'] = array( 'user_email' );
167
		}
168
169
		// Filter by role.
170
		if ( 'all' !== $request['role'] ) {
171
			$prepared_args['role'] = $request['role'];
172
		}
173
174
		/**
175
		 * Filter arguments, before passing to \ WP_User_Query, when querying users via the REST API.
176
		 *
177
		 * @see https://developer.wordpress.org/reference/classes/\ WP_User_Query/
178
		 *
179
		 * @param array           $prepared_args Array of arguments for \ WP_User_Query.
180
		 * @param \WP_REST_Request $request       The current request.
181
		 */
182
		$prepared_args = apply_filters( 'woocommerce_rest_customer_query', $prepared_args, $request );
183
184
		$query = new \WP_User_Query( $prepared_args );
185
186
		$users = array();
187
		foreach ( $query->results as $user ) {
188
			$customer = new \WC_Customer( $user->ID );
189
			$data     = $this->prepare_item_for_response( $customer, $request );
190
			$users[]  = $this->prepare_response_for_collection( $data );
191
		}
192
193
		$response = rest_ensure_response( $users );
194
195
		// Store pagination values for headers then unset for count query.
196
		$per_page = (int) $prepared_args['number'];
197
		$page     = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 );
198
199
		$prepared_args['fields'] = 'ID';
200
201
		$total_users = $query->get_total();
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
		$response->header( 'X-WP-Total', (int) $total_users );
210
		$max_pages = ceil( $total_users / $per_page );
211
		$response->header( 'X-WP-TotalPages', (int) $max_pages );
212
213
		$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
214
		if ( $page > 1 ) {
215
			$prev_page = $page - 1;
216
			if ( $prev_page > $max_pages ) {
217
				$prev_page = $max_pages;
218
			}
219
			$prev_link = add_query_arg( 'page', $prev_page, $base );
220
			$response->link_header( 'prev', $prev_link );
221
		}
222
		if ( $max_pages > $page ) {
223
			$next_page = $page + 1;
224
			$next_link = add_query_arg( 'page', $next_page, $base );
225
			$response->link_header( 'next', $next_link );
226
		}
227
228
		return $response;
229
	}
230
231
	/**
232
	 * Create a single customer.
233
	 *
234
	 * @throws \WC_REST_Exception On invalid params.
235
	 * @param \WP_REST_Request $request Full details about the request.
236
	 * @return \WP_Error|\WP_REST_Response
237
	 */
238
	public function create_item( $request ) {
239
		try {
240
			if ( ! empty( $request['id'] ) ) {
241
				throw new \WC_REST_Exception( 'woocommerce_rest_customer_exists', __( 'Cannot create existing resource.', 'woocommerce' ), 400 );
242
			}
243
244
			$customer_request = new CustomerRequest( $request );
245
			$customer         = $customer_request->prepare_object();
246
			$customer->save();
247
248
			if ( ! $customer->get_id() ) {
249
				throw new \WC_REST_Exception( 'woocommerce_rest_cannot_create', __( 'This resource cannot be created.', 'woocommerce' ), 400 );
250
			}
251
252
			$this->update_additional_fields_for_object( $customer, $request );
0 ignored issues
show
Bug introduced by
$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

252
			$this->update_additional_fields_for_object( /** @scrutinizer ignore-type */ $customer, $request );
Loading history...
253
254
			/**
255
			 * Fires after a customer is created or updated via the REST API.
256
			 *
257
			 * @param \WC_Customer     $customer Customer object.
258
			 * @param \WP_REST_Request $request   Request object.
259
			 * @param boolean          $creating  True when creating customer, false when updating customer.
260
			 */
261
			do_action( 'woocommerce_rest_insert_customer_object', $customer, $request, true );
262
263
			$request->set_param( 'context', 'edit' );
264
			$response = $this->prepare_item_for_response( $customer, $request );
265
			$response = rest_ensure_response( $response );
266
			$response->set_status( 201 );
267
			$response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $customer->get_id() ) ) );
268
269
			return $response;
270
		} catch ( \Exception $e ) {
271
			return new \WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
0 ignored issues
show
Bug introduced by
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

271
			return new \WP_Error( $e->/** @scrutinizer ignore-call */ getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
Loading history...
272
		}
273
	}
274
275
	/**
276
	 * Get a single customer.
277
	 *
278
	 * @param \WP_REST_Request $request Full details about the request.
279
	 * @return \WP_Error|\WP_REST_Response
280
	 */
281
	public function get_item( $request ) {
282
		$id       = (int) $request['id'];
283
		$customer = new \WC_Customer( $id );
284
285
		if ( empty( $id ) || ! $customer->get_id() ) {
286
			return new \WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource ID.', 'woocommerce' ), array( 'status' => 404 ) );
287
		}
288
289
		$response = $this->prepare_item_for_response( $customer, $request );
290
		$response = rest_ensure_response( $response );
291
292
		return $response;
293
	}
294
295
	/**
296
	 * Update a single user.
297
	 *
298
	 * @throws \WC_REST_Exception On invalid params.
299
	 *
300
	 * @param \WP_REST_Request $request Full details about the request.
301
	 * @return \WP_Error|\WP_REST_Response
302
	 */
303
	public function update_item( $request ) {
304
		try {
305
			$customer_request = new CustomerRequest( $request );
306
			$customer         = $customer_request->prepare_object();
307
			$customer->save();
308
309
			$this->update_additional_fields_for_object( $customer, $request );
0 ignored issues
show
Bug introduced by
$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

309
			$this->update_additional_fields_for_object( /** @scrutinizer ignore-type */ $customer, $request );
Loading history...
310
311
			if ( is_multisite() && ! is_user_member_of_blog( $customer->get_id() ) ) {
312
				add_user_to_blog( get_current_blog_id(), $customer->get_id(), 'customer' );
313
			}
314
315
			/**
316
			 * Fires after a customer is created or updated via the REST API.
317
			 *
318
			 * @param \WC_Customer     $customer  Data used to create the customer.
319
			 * @param \WP_REST_Request $request   Request object.
320
			 * @param boolean         $creating  True when creating customer, false when updating customer.
321
			 */
322
			do_action( 'woocommerce_rest_insert_customer_object', $customer, $request, false );
323
324
			$request->set_param( 'context', 'edit' );
325
			$response = $this->prepare_item_for_response( $customer, $request );
326
			$response = rest_ensure_response( $response );
327
			return $response;
328
		} catch ( Exception $e ) {
0 ignored issues
show
Bug introduced by
The type WooCommerce\RestApi\Controllers\Version4\Exception was not found. Did you mean Exception? If so, make sure to prefix the type with \.
Loading history...
329
			return new \WP_Error( $e->getErrorCode(), $e->getMessage(), array( 'status' => $e->getCode() ) );
330
		}
331
	}
332
333
	/**
334
	 * Delete a single customer.
335
	 *
336
	 * @param \WP_REST_Request $request Full details about the request.
337
	 * @return \WP_Error|\WP_REST_Response
338
	 */
339
	public function delete_item( $request ) {
340
		$id       = (int) $request['id'];
341
		$reassign = isset( $request['reassign'] ) ? absint( $request['reassign'] ) : null;
342
		$force    = isset( $request['force'] ) ? (bool) $request['force'] : false;
343
344
		// We don't support trashing for this type, error out.
345
		if ( ! $force ) {
346
			return new \WP_Error( 'woocommerce_rest_trash_not_supported', __( 'Customers do not support trashing.', 'woocommerce' ), array( 'status' => 501 ) );
347
		}
348
349
		if ( ! get_userdata( $id ) ) {
350
			return new \WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource id.', 'woocommerce' ), array( 'status' => 400 ) );
351
		}
352
353
		if ( ! empty( $reassign ) ) {
354
			if ( $reassign === $id || ! get_userdata( $reassign ) ) {
355
				return new \WP_Error( 'woocommerce_rest_customer_invalid_reassign', __( 'Invalid resource id for reassignment.', 'woocommerce' ), array( 'status' => 400 ) );
356
			}
357
		}
358
359
		/** Include admin customer functions to get access to wp_delete_user() */
360
		require_once ABSPATH . 'wp-admin/includes/user.php';
361
362
		$customer = new \WC_Customer( $id );
363
364
		$request->set_param( 'context', 'edit' );
365
		$response = $this->prepare_item_for_response( $customer, $request );
366
367
		if ( ! is_null( $reassign ) ) {
368
			$result = $customer->delete_and_reassign( $reassign );
369
		} else {
370
			$result = $customer->delete();
371
		}
372
373
		if ( ! $result ) {
374
			return new \WP_Error( 'woocommerce_rest_cannot_delete', __( 'The resource cannot be deleted.', 'woocommerce' ), array( 'status' => 500 ) );
375
		}
376
377
		/**
378
		 * Fires after a customer is deleted via the REST API.
379
		 *
380
		 * @param \WC_Customer      $customer User data.
381
		 * @param \WP_REST_Response $response  The response returned from the API.
382
		 * @param \WP_REST_Request  $request   The request sent to the API.
383
		 */
384
		do_action( 'woocommerce_rest_delete_customer_object', $customer, $response, $request );
385
386
		return $response;
387
	}
388
389
	/**
390
	 * Get data for this object in the format of this endpoint's schema.
391
	 *
392
	 * @param \WP_Comment      $object Object to prepare.
393
	 * @param \WP_REST_Request $request Request object.
394
	 * @return array Array of data in the correct format.
395
	 */
396
	protected function get_data_for_response( $object, $request ) {
397
		$formatter = new CustomerResponse();
398
399
		return $formatter->prepare_response( $object, $this->get_request_context( $request ) );
0 ignored issues
show
Bug introduced by
$object of type WP_Comment is incompatible with the type WC_Customer expected by parameter $object of WooCommerce\RestApi\Cont...nse::prepare_response(). ( Ignorable by Annotation )

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

399
		return $formatter->prepare_response( /** @scrutinizer ignore-type */ $object, $this->get_request_context( $request ) );
Loading history...
400
	}
401
402
	/**
403
	 * Prepare links for the request.
404
	 *
405
	 * @param mixed            $item Object to prepare.
406
	 * @param \WP_REST_Request $request Request object.
407
	 * @return array
408
	 */
409
	protected function prepare_links( $item, $request ) {
410
		$links = array(
411
			'self' => array(
412
				'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $item->get_id() ) ),
413
			),
414
			'collection' => array(
415
				'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ),
416
			),
417
		);
418
		return $links;
419
	}
420
421
	/**
422
	 * Get the Customer's schema, conforming to JSON Schema.
423
	 *
424
	 * @return array
425
	 */
426
	public function get_item_schema() {
427
		$schema = array(
428
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
429
			'title'      => 'customer',
430
			'type'       => 'object',
431
			'properties' => array(
432
				'id'                 => array(
433
					'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
434
					'type'        => 'integer',
435
					'context'     => array( 'view', 'edit' ),
436
					'readonly'    => true,
437
				),
438
				'date_created'       => array(
439
					'description' => __( "The date the customer was created, in the site's timezone.", 'woocommerce' ),
440
					'type'        => 'date-time',
441
					'context'     => array( 'view', 'edit' ),
442
					'readonly'    => true,
443
				),
444
				'date_created_gmt'   => array(
445
					'description' => __( 'The date the customer was created, as GMT.', 'woocommerce' ),
446
					'type'        => 'date-time',
447
					'context'     => array( 'view', 'edit' ),
448
					'readonly'    => true,
449
				),
450
				'date_modified'      => array(
451
					'description' => __( "The date the customer was last modified, in the site's timezone.", 'woocommerce' ),
452
					'type'        => 'date-time',
453
					'context'     => array( 'view', 'edit' ),
454
					'readonly'    => true,
455
				),
456
				'date_modified_gmt'  => array(
457
					'description' => __( 'The date the customer was last modified, as GMT.', 'woocommerce' ),
458
					'type'        => 'date-time',
459
					'context'     => array( 'view', 'edit' ),
460
					'readonly'    => true,
461
				),
462
				'email'              => array(
463
					'description' => __( 'The email address for the customer.', 'woocommerce' ),
464
					'type'        => 'string',
465
					'format'      => 'email',
466
					'context'     => array( 'view', 'edit' ),
467
				),
468
				'first_name'         => array(
469
					'description' => __( 'Customer first name.', 'woocommerce' ),
470
					'type'        => 'string',
471
					'context'     => array( 'view', 'edit' ),
472
					'arg_options' => array(
473
						'sanitize_callback' => 'sanitize_text_field',
474
					),
475
				),
476
				'last_name'          => array(
477
					'description' => __( 'Customer last name.', 'woocommerce' ),
478
					'type'        => 'string',
479
					'context'     => array( 'view', 'edit' ),
480
					'arg_options' => array(
481
						'sanitize_callback' => 'sanitize_text_field',
482
					),
483
				),
484
				'role'               => array(
485
					'description' => __( 'Customer role.', 'woocommerce' ),
486
					'type'        => 'string',
487
					'context'     => array( 'view', 'edit' ),
488
					'readonly'    => true,
489
				),
490
				'username'           => array(
491
					'description' => __( 'Customer login name.', 'woocommerce' ),
492
					'type'        => 'string',
493
					'context'     => array( 'view', 'edit' ),
494
					'arg_options' => array(
495
						'sanitize_callback' => 'sanitize_user',
496
					),
497
				),
498
				'password'           => array(
499
					'description' => __( 'Customer password.', 'woocommerce' ),
500
					'type'        => 'string',
501
					'context'     => array( 'edit' ),
502
				),
503
				'billing'            => array(
504
					'description' => __( 'List of billing address data.', 'woocommerce' ),
505
					'type'        => 'object',
506
					'context'     => array( 'view', 'edit' ),
507
					'properties'  => array(
508
						'first_name' => array(
509
							'description' => __( 'First name.', 'woocommerce' ),
510
							'type'        => 'string',
511
							'context'     => array( 'view', 'edit' ),
512
						),
513
						'last_name'  => array(
514
							'description' => __( 'Last name.', 'woocommerce' ),
515
							'type'        => 'string',
516
							'context'     => array( 'view', 'edit' ),
517
						),
518
						'company'    => array(
519
							'description' => __( 'Company name.', 'woocommerce' ),
520
							'type'        => 'string',
521
							'context'     => array( 'view', 'edit' ),
522
						),
523
						'address_1'  => array(
524
							'description' => __( 'Address line 1', 'woocommerce' ),
525
							'type'        => 'string',
526
							'context'     => array( 'view', 'edit' ),
527
						),
528
						'address_2'  => array(
529
							'description' => __( 'Address line 2', 'woocommerce' ),
530
							'type'        => 'string',
531
							'context'     => array( 'view', 'edit' ),
532
						),
533
						'city'       => array(
534
							'description' => __( 'City name.', 'woocommerce' ),
535
							'type'        => 'string',
536
							'context'     => array( 'view', 'edit' ),
537
						),
538
						'state'      => array(
539
							'description' => __( 'ISO code or name of the state, province or district.', 'woocommerce' ),
540
							'type'        => 'string',
541
							'context'     => array( 'view', 'edit' ),
542
						),
543
						'postcode'   => array(
544
							'description' => __( 'Postal code.', 'woocommerce' ),
545
							'type'        => 'string',
546
							'context'     => array( 'view', 'edit' ),
547
						),
548
						'country'    => array(
549
							'description' => __( 'ISO code of the country.', 'woocommerce' ),
550
							'type'        => 'string',
551
							'context'     => array( 'view', 'edit' ),
552
						),
553
						'email'      => array(
554
							'description' => __( 'Email address.', 'woocommerce' ),
555
							'type'        => 'string',
556
							'format'      => 'email',
557
							'context'     => array( 'view', 'edit' ),
558
						),
559
						'phone'      => array(
560
							'description' => __( 'Phone number.', 'woocommerce' ),
561
							'type'        => 'string',
562
							'context'     => array( 'view', 'edit' ),
563
						),
564
					),
565
				),
566
				'shipping'           => array(
567
					'description' => __( 'List of shipping address data.', 'woocommerce' ),
568
					'type'        => 'object',
569
					'context'     => array( 'view', 'edit' ),
570
					'properties'  => array(
571
						'first_name' => array(
572
							'description' => __( 'First name.', 'woocommerce' ),
573
							'type'        => 'string',
574
							'context'     => array( 'view', 'edit' ),
575
						),
576
						'last_name'  => array(
577
							'description' => __( 'Last name.', 'woocommerce' ),
578
							'type'        => 'string',
579
							'context'     => array( 'view', 'edit' ),
580
						),
581
						'company'    => array(
582
							'description' => __( 'Company name.', 'woocommerce' ),
583
							'type'        => 'string',
584
							'context'     => array( 'view', 'edit' ),
585
						),
586
						'address_1'  => array(
587
							'description' => __( 'Address line 1', 'woocommerce' ),
588
							'type'        => 'string',
589
							'context'     => array( 'view', 'edit' ),
590
						),
591
						'address_2'  => array(
592
							'description' => __( 'Address line 2', 'woocommerce' ),
593
							'type'        => 'string',
594
							'context'     => array( 'view', 'edit' ),
595
						),
596
						'city'       => array(
597
							'description' => __( 'City name.', 'woocommerce' ),
598
							'type'        => 'string',
599
							'context'     => array( 'view', 'edit' ),
600
						),
601
						'state'      => array(
602
							'description' => __( 'ISO code or name of the state, province or district.', 'woocommerce' ),
603
							'type'        => 'string',
604
							'context'     => array( 'view', 'edit' ),
605
						),
606
						'postcode'   => array(
607
							'description' => __( 'Postal code.', 'woocommerce' ),
608
							'type'        => 'string',
609
							'context'     => array( 'view', 'edit' ),
610
						),
611
						'country'    => array(
612
							'description' => __( 'ISO code of the country.', 'woocommerce' ),
613
							'type'        => 'string',
614
							'context'     => array( 'view', 'edit' ),
615
						),
616
					),
617
				),
618
				'is_paying_customer' => array(
619
					'description' => __( 'Is the customer a paying customer?', 'woocommerce' ),
620
					'type'        => 'bool',
621
					'context'     => array( 'view', 'edit' ),
622
					'readonly'    => true,
623
				),
624
				'avatar_url'         => array(
625
					'description' => __( 'Avatar URL.', 'woocommerce' ),
626
					'type'        => 'string',
627
					'context'     => array( 'view', 'edit' ),
628
					'readonly'    => true,
629
				),
630
				'meta_data'          => array(
631
					'description' => __( 'Meta data.', 'woocommerce' ),
632
					'type'        => 'array',
633
					'context'     => array( 'view', 'edit' ),
634
					'items'       => array(
635
						'type'       => 'object',
636
						'properties' => array(
637
							'id'    => array(
638
								'description' => __( 'Meta ID.', 'woocommerce' ),
639
								'type'        => 'integer',
640
								'context'     => array( 'view', 'edit' ),
641
								'readonly'    => true,
642
							),
643
							'key'   => array(
644
								'description' => __( 'Meta key.', 'woocommerce' ),
645
								'type'        => 'string',
646
								'context'     => array( 'view', 'edit' ),
647
							),
648
							'value' => array(
649
								'description' => __( 'Meta value.', 'woocommerce' ),
650
								'type'        => 'mixed',
651
								'context'     => array( 'view', 'edit' ),
652
							),
653
						),
654
					),
655
				),
656
			),
657
		);
658
659
		return $this->add_additional_fields_schema( $schema );
660
	}
661
662
	/**
663
	 * Get role names.
664
	 *
665
	 * @return array
666
	 */
667
	protected function get_role_names() {
668
		global $wp_roles;
669
670
		return array_keys( $wp_roles->role_names );
671
	}
672
673
	/**
674
	 * Get the query params for collections.
675
	 *
676
	 * @return array
677
	 */
678
	public function get_collection_params() {
679
		$params = parent::get_collection_params();
680
681
		$params['context']['default'] = 'view';
682
683
		$params['exclude'] = array(
684
			'description'       => __( 'Ensure result set excludes specific IDs.', 'woocommerce' ),
685
			'type'              => 'array',
686
			'items'             => array(
687
				'type'          => 'integer',
688
			),
689
			'default'           => array(),
690
			'sanitize_callback' => 'wp_parse_id_list',
691
		);
692
		$params['include'] = array(
693
			'description'       => __( 'Limit result set to specific IDs.', 'woocommerce' ),
694
			'type'              => 'array',
695
			'items'             => array(
696
				'type'          => 'integer',
697
			),
698
			'default'           => array(),
699
			'sanitize_callback' => 'wp_parse_id_list',
700
		);
701
		$params['offset'] = array(
702
			'description'        => __( 'Offset the result set by a specific number of items.', 'woocommerce' ),
703
			'type'               => 'integer',
704
			'sanitize_callback'  => 'absint',
705
			'validate_callback'  => 'rest_validate_request_arg',
706
		);
707
		$params['order'] = array(
708
			'default'            => 'asc',
709
			'description'        => __( 'Order sort attribute ascending or descending.', 'woocommerce' ),
710
			'enum'               => array( 'asc', 'desc' ),
711
			'sanitize_callback'  => 'sanitize_key',
712
			'type'               => 'string',
713
			'validate_callback'  => 'rest_validate_request_arg',
714
		);
715
		$params['orderby'] = array(
716
			'default'            => 'name',
717
			'description'        => __( 'Sort collection by object attribute.', 'woocommerce' ),
718
			'enum'               => array(
719
				'id',
720
				'include',
721
				'name',
722
				'registered_date',
723
			),
724
			'sanitize_callback'  => 'sanitize_key',
725
			'type'               => 'string',
726
			'validate_callback'  => 'rest_validate_request_arg',
727
		);
728
		$params['email'] = array(
729
			'description'        => __( 'Limit result set to resources with a specific email.', 'woocommerce' ),
730
			'type'               => 'string',
731
			'format'             => 'email',
732
			'validate_callback'  => 'rest_validate_request_arg',
733
		);
734
		$params['role'] = array(
735
			'description'        => __( 'Limit result set to resources with a specific role.', 'woocommerce' ),
736
			'type'               => 'string',
737
			'default'            => 'customer',
738
			'enum'               => array_merge( array( 'all' ), $this->get_role_names() ),
739
			'validate_callback'  => 'rest_validate_request_arg',
740
		);
741
		return $params;
742
	}
743
744
	/**
745
	 * Check if a given ID is valid.
746
	 *
747
	 * @param  \WP_REST_Request $request Full details about the request.
748
	 * @return \WP_Error|boolean
749
	 */
750
	protected function check_valid_customer_id( $request ) {
751
		$id   = $request->get_param( 'id' );
752
		$user = get_userdata( $id );
753
754
		if ( false === $user ) {
755
			return new \WP_Error( 'woocommerce_rest_customer_invalid_id', __( 'Invalid ID.', 'woocommerce' ), array( 'status' => 404 ) );
756
		}
757
		return true;
758
	}
759
760
	/**
761
	 * Check if a given request has access to read a webhook.
762
	 *
763
	 * @param  \WP_REST_Request $request Full details about the request.
764
	 * @return \WP_Error|boolean
765
	 */
766
	public function get_item_permissions_check( $request ) {
767
		$check_valid = $this->check_valid_customer_id( $request );
768
769
		if ( is_wp_error( $check_valid ) ) {
770
			return $check_valid;
771
		}
772
773
		return parent::get_item_permissions_check( $request );
774
	}
775
776
	/**
777
	 * Check if a given request has access to delete an item.
778
	 *
779
	 * @param  \WP_REST_Request $request Full details about the request.
780
	 * @return \WP_Error|boolean
781
	 */
782
	public function delete_item_permissions_check( $request ) {
783
		$check_valid = $this->check_valid_customer_id( $request );
784
785
		if ( is_wp_error( $check_valid ) ) {
786
			return $check_valid;
787
		}
788
789
		return parent::delete_item_permissions_check( $request );
790
	}
791
792
	/**
793
	 * Check if a given request has access to update an item.
794
	 *
795
	 * @param  \WP_REST_Request $request Full details about the request.
796
	 * @return \WP_Error|boolean
797
	 */
798
	public function update_item_permissions_check( $request ) {
799
		$check_valid = $this->check_valid_customer_id( $request );
800
801
		if ( is_wp_error( $check_valid ) ) {
802
			return $check_valid;
803
		}
804
805
		return parent::update_item_permissions_check( $request );
806
	}
807
}
808