Passed
Push — master ( bd3828...767719 )
by Mike
06:57 queued 18s
created

Customers   A

Complexity

Total Complexity 42

Size/Duplication

Total Lines 769
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 433
dl 0
loc 769
rs 9.0399
c 0
b 0
f 0
wmc 42

15 Methods

Rating   Name   Duplication   Size   Complexity  
A update_item_permissions_check() 0 8 2
A update_item() 0 27 4
A get_data_for_response() 0 4 1
B get_item_schema() 0 234 1
B register_routes() 0 88 1
A get_item() 0 12 3
A delete_item_permissions_check() 0 8 2
B get_items() 0 77 7
A get_collection_params() 0 64 1
A create_item() 0 34 4
A check_valid_customer_id() 0 8 2
B delete_item() 0 48 10
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
use 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' ),
63
							),
64
							'username' => array(
65
								'required'    => 'no' === get_option( 'woocommerce_registration_generate_username', 'yes' ),
66
								'description' => __( 'New user username.', 'woocommerce' ),
67
								'type'        => 'string',
68
							),
69
							'password' => array(
70
								'required'    => 'no' === get_option( 'woocommerce_registration_generate_password', 'no' ),
71
								'description' => __( 'New user password.', 'woocommerce' ),
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' ),
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' ),
115
						),
116
						'reassign' => array(
117
							'default'     => 0,
118
							'type'        => 'integer',
119
							'description' => __( 'ID to reassign posts to.', 'woocommerce' ),
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
Bug introduced by
ceil($total_users / $per_page) of type double is incompatible with the type integer expected by parameter $total_pages of WooCommerce\RestApi\Cont...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' ), 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' ), 400 );
235
			}
236
237
			$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

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
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

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' ), 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
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

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
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...
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' ), array( 'status' => 501 ) );
332
		}
333
334
		if ( ! get_userdata( $id ) ) {
335
			return new \WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource id.', 'woocommerce' ), 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' ), 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' ), 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' ),
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' ),
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' ),
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' ),
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' ),
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' ),
449
					'type'        => 'string',
450
					'format'      => 'email',
451
					'context'     => array( 'view', 'edit' ),
452
				),
453
				'first_name'         => array(
454
					'description' => __( 'Customer first name.', 'woocommerce' ),
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' ),
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' ),
471
					'type'        => 'string',
472
					'context'     => array( 'view', 'edit' ),
473
					'readonly'    => true,
474
				),
475
				'username'           => array(
476
					'description' => __( 'Customer login name.', 'woocommerce' ),
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' ),
485
					'type'        => 'string',
486
					'context'     => array( 'edit' ),
487
				),
488
				'billing'            => array(
489
					'description' => __( 'List of billing address data.', 'woocommerce' ),
490
					'type'        => 'object',
491
					'context'     => array( 'view', 'edit' ),
492
					'properties'  => array(
493
						'first_name' => array(
494
							'description' => __( 'First name.', 'woocommerce' ),
495
							'type'        => 'string',
496
							'context'     => array( 'view', 'edit' ),
497
						),
498
						'last_name'  => array(
499
							'description' => __( 'Last name.', 'woocommerce' ),
500
							'type'        => 'string',
501
							'context'     => array( 'view', 'edit' ),
502
						),
503
						'company'    => array(
504
							'description' => __( 'Company name.', 'woocommerce' ),
505
							'type'        => 'string',
506
							'context'     => array( 'view', 'edit' ),
507
						),
508
						'address_1'  => array(
509
							'description' => __( 'Address line 1', 'woocommerce' ),
510
							'type'        => 'string',
511
							'context'     => array( 'view', 'edit' ),
512
						),
513
						'address_2'  => array(
514
							'description' => __( 'Address line 2', 'woocommerce' ),
515
							'type'        => 'string',
516
							'context'     => array( 'view', 'edit' ),
517
						),
518
						'city'       => array(
519
							'description' => __( 'City name.', 'woocommerce' ),
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' ),
525
							'type'        => 'string',
526
							'context'     => array( 'view', 'edit' ),
527
						),
528
						'postcode'   => array(
529
							'description' => __( 'Postal code.', 'woocommerce' ),
530
							'type'        => 'string',
531
							'context'     => array( 'view', 'edit' ),
532
						),
533
						'country'    => array(
534
							'description' => __( 'ISO code of the country.', 'woocommerce' ),
535
							'type'        => 'string',
536
							'context'     => array( 'view', 'edit' ),
537
						),
538
						'email'      => array(
539
							'description' => __( 'Email address.', 'woocommerce' ),
540
							'type'        => 'string',
541
							'format'      => 'email',
542
							'context'     => array( 'view', 'edit' ),
543
						),
544
						'phone'      => array(
545
							'description' => __( 'Phone number.', 'woocommerce' ),
546
							'type'        => 'string',
547
							'context'     => array( 'view', 'edit' ),
548
						),
549
					),
550
				),
551
				'shipping'           => array(
552
					'description' => __( 'List of shipping address data.', 'woocommerce' ),
553
					'type'        => 'object',
554
					'context'     => array( 'view', 'edit' ),
555
					'properties'  => array(
556
						'first_name' => array(
557
							'description' => __( 'First name.', 'woocommerce' ),
558
							'type'        => 'string',
559
							'context'     => array( 'view', 'edit' ),
560
						),
561
						'last_name'  => array(
562
							'description' => __( 'Last name.', 'woocommerce' ),
563
							'type'        => 'string',
564
							'context'     => array( 'view', 'edit' ),
565
						),
566
						'company'    => array(
567
							'description' => __( 'Company name.', 'woocommerce' ),
568
							'type'        => 'string',
569
							'context'     => array( 'view', 'edit' ),
570
						),
571
						'address_1'  => array(
572
							'description' => __( 'Address line 1', 'woocommerce' ),
573
							'type'        => 'string',
574
							'context'     => array( 'view', 'edit' ),
575
						),
576
						'address_2'  => array(
577
							'description' => __( 'Address line 2', 'woocommerce' ),
578
							'type'        => 'string',
579
							'context'     => array( 'view', 'edit' ),
580
						),
581
						'city'       => array(
582
							'description' => __( 'City name.', 'woocommerce' ),
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' ),
588
							'type'        => 'string',
589
							'context'     => array( 'view', 'edit' ),
590
						),
591
						'postcode'   => array(
592
							'description' => __( 'Postal code.', 'woocommerce' ),
593
							'type'        => 'string',
594
							'context'     => array( 'view', 'edit' ),
595
						),
596
						'country'    => array(
597
							'description' => __( 'ISO code of the country.', 'woocommerce' ),
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' ),
605
					'type'        => 'bool',
606
					'context'     => array( 'view', 'edit' ),
607
					'readonly'    => true,
608
				),
609
				'avatar_url'         => array(
610
					'description' => __( 'Avatar URL.', 'woocommerce' ),
611
					'type'        => 'string',
612
					'context'     => array( 'view', 'edit' ),
613
					'readonly'    => true,
614
				),
615
				'meta_data'          => array(
616
					'description' => __( 'Meta data.', 'woocommerce' ),
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' ),
624
								'type'        => 'integer',
625
								'context'     => array( 'view', 'edit' ),
626
								'readonly'    => true,
627
							),
628
							'key'   => array(
629
								'description' => __( 'Meta key.', 'woocommerce' ),
630
								'type'        => 'string',
631
								'context'     => array( 'view', 'edit' ),
632
							),
633
							'value' => array(
634
								'description' => __( 'Meta value.', 'woocommerce' ),
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' ),
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' ),
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' ),
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' ),
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' ),
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' ),
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' ),
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' ), 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