WC_REST_Taxes_Controller   C
last analyzed

Complexity

Total Complexity 60

Size/Duplication

Total Lines 674
Duplicated Lines 22.26 %

Coupling/Cohesion

Components 1
Dependencies 2

Importance

Changes 0
Metric Value
dl 150
loc 674
rs 5.3407
c 0
b 0
f 0
wmc 60
lcom 1
cbo 2

17 Methods

Rating   Name   Duplication   Size   Complexity  
A get_items_permissions_check() 7 7 2
A create_item_permissions_check() 7 7 2
A get_item_permissions_check() 7 7 2
A update_item_permissions_check() 7 7 2
A delete_item_permissions_check() 7 7 2
C get_items() 22 87 8
C create_or_update_tax() 0 62 16
B create_item() 0 26 2
A get_item() 13 13 3
B update_item() 0 27 3
B delete_item() 0 37 6
B prepare_item_for_response() 5 50 6
A get_collection_params() 0 52 1
A batch_items_permissions_check() 7 7 2
A prepare_links() 12 12 1
B get_item_schema() 0 77 1
A register_routes() 56 56 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complex Class

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like WC_REST_Taxes_Controller 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. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

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 WC_REST_Taxes_Controller, and based on these observations, apply Extract Interface, too.

1
<?php
2
/**
3
 * REST API Taxes controller
4
 *
5
 * Handles requests to the /taxes endpoint.
6
 *
7
 * @author   WooThemes
8
 * @category API
9
 * @package  WooCommerce/API
10
 * @since    2.6.0
11
 */
12
13
if ( ! defined( 'ABSPATH' ) ) {
14
	exit;
15
}
16
17
/**
18
 * REST API Taxes controller class.
19
 *
20
 * @package WooCommerce/API
21
 * @extends WC_REST_Controller
22
 */
23
class WC_REST_Taxes_Controller extends WC_REST_Controller {
24
25
	/**
26
	 * Endpoint namespace.
27
	 *
28
	 * @var string
29
	 */
30
	protected $namespace = 'wc/v1';
31
32
	/**
33
	 * Route base.
34
	 *
35
	 * @var string
36
	 */
37
	protected $rest_base = 'taxes';
38
39
	/**
40
	 * Register the routes for taxes.
41
	 */
42 View Code Duplication
	public function register_routes() {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
43
		register_rest_route( $this->namespace, '/' . $this->rest_base, array(
44
			array(
45
				'methods'             => WP_REST_Server::READABLE,
46
				'callback'            => array( $this, 'get_items' ),
47
				'permission_callback' => array( $this, 'get_items_permissions_check' ),
48
				'args'                => $this->get_collection_params(),
49
			),
50
			array(
51
				'methods'             => WP_REST_Server::CREATABLE,
52
				'callback'            => array( $this, 'create_item' ),
53
				'permission_callback' => array( $this, 'create_item_permissions_check' ),
54
				'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
55
			),
56
			'schema' => array( $this, 'get_public_item_schema' ),
57
		) );
58
59
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
60
			array(
61
				'methods'             => WP_REST_Server::READABLE,
62
				'callback'            => array( $this, 'get_item' ),
63
				'permission_callback' => array( $this, 'get_item_permissions_check' ),
64
				'args'                => array(
65
					'context' => $this->get_context_param( array( 'default' => 'view' ) ),
66
				),
67
			),
68
			array(
69
				'methods'             => WP_REST_Server::EDITABLE,
70
				'callback'            => array( $this, 'update_item' ),
71
				'permission_callback' => array( $this, 'update_item_permissions_check' ),
72
				'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
73
			),
74
			array(
75
				'methods'             => WP_REST_Server::DELETABLE,
76
				'callback'            => array( $this, 'delete_item' ),
77
				'permission_callback' => array( $this, 'delete_item_permissions_check' ),
78
				'args'                => array(
79
					'force' => array(
80
						'default'     => false,
81
						'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce' ),
82
					),
83
				),
84
			),
85
			'schema' => array( $this, 'get_public_item_schema' ),
86
		) );
87
88
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/batch', array(
89
			array(
90
				'methods'             => WP_REST_Server::EDITABLE,
91
				'callback'            => array( $this, 'batch_items' ),
92
				'permission_callback' => array( $this, 'batch_items_permissions_check' ),
93
				'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
94
			),
95
			'schema' => array( $this, 'get_public_batch_schema' ),
96
		) );
97
	}
98
99
	/**
100
	 * Check whether a given request has permission to read taxes.
101
	 *
102
	 * @param  WP_REST_Request $request Full details about the request.
103
	 * @return WP_Error|boolean
104
	 */
105 View Code Duplication
	public function get_items_permissions_check( $request ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
106
		if ( ! wc_rest_check_manager_permissions( 'settings', 'read' ) ) {
107
			return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
108
		}
109
110
		return true;
111
	}
112
113
	/**
114
	 * Check if a given request has access create taxes.
115
	 *
116
	 * @param  WP_REST_Request $request Full details about the request.
117
	 * @return boolean
118
	 */
119 View Code Duplication
	public function create_item_permissions_check( $request ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
120
		if ( ! wc_rest_check_manager_permissions( 'settings', 'create' ) ) {
121
			return new WP_Error( 'woocommerce_rest_cannot_create', __( 'Sorry, you are not allowed to create resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
122
		}
123
124
		return true;
125
	}
126
127
	/**
128
	 * Check if a given request has access to read a tax.
129
	 *
130
	 * @param  WP_REST_Request $request Full details about the request.
131
	 * @return WP_Error|boolean
132
	 */
133 View Code Duplication
	public function get_item_permissions_check( $request ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
134
		if ( ! wc_rest_check_manager_permissions( 'settings', 'read' ) ) {
135
			return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
136
		}
137
138
		return true;
139
	}
140
141
	/**
142
	 * Check if a given request has access update a tax.
143
	 *
144
	 * @param  WP_REST_Request $request Full details about the request.
145
	 * @return boolean
146
	 */
147 View Code Duplication
	public function update_item_permissions_check( $request ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
148
		if ( ! wc_rest_check_manager_permissions( 'settings', 'edit' ) ) {
149
			return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you are not allowed to edit this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
150
		}
151
152
		return true;
153
	}
154
155
	/**
156
	 * Check if a given request has access delete a tax.
157
	 *
158
	 * @param  WP_REST_Request $request Full details about the request.
159
	 * @return boolean
160
	 */
161 View Code Duplication
	public function delete_item_permissions_check( $request ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
162
		if ( ! wc_rest_check_manager_permissions( 'settings', 'delete' ) ) {
163
			return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'Sorry, you are not allowed to delete this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
164
		}
165
166
		return true;
167
	}
168
169
	/**
170
	 * Check if a given request has access batch create, update and delete items.
171
	 *
172
	 * @param  WP_REST_Request $request Full details about the request.
173
	 * @return boolean
174
	 */
175 View Code Duplication
	public function batch_items_permissions_check( $request ) {
0 ignored issues
show
Unused Code introduced by
The parameter $request is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
176
		if ( ! wc_rest_check_manager_permissions( 'settings', 'batch' ) ) {
177
			return new WP_Error( 'woocommerce_rest_cannot_batch', __( 'Sorry, you are not allowed to manipule this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
178
		}
179
180
		return true;
181
	}
182
183
	/**
184
	 * Get all taxes.
185
	 *
186
	 * @param WP_REST_Request $request Full details about the request.
187
	 * @return WP_Error|WP_REST_Response
188
	 */
189
	public function get_items( $request ) {
190
		global $wpdb;
191
192
		$prepared_args = array();
193
		$prepared_args['exclude'] = $request['exclude'];
194
		$prepared_args['include'] = $request['include'];
195
		$prepared_args['order']   = $request['order'];
196
		$prepared_args['number']  = $request['per_page'];
197 View Code Duplication
		if ( ! empty( $request['offset'] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
198
			$prepared_args['offset'] = $request['offset'];
199
		} else {
200
			$prepared_args['offset'] = ( $request['page'] - 1 ) * $prepared_args['number'];
201
		}
202
		$orderby_possibles = array(
203
			'id'    => 'tax_rate_id',
204
			'order' => 'tax_rate_order',
205
		);
206
		$prepared_args['orderby'] = $orderby_possibles[ $request['orderby'] ];
207
		$prepared_args['class']   = $request['class'];
208
209
		/**
210
		 * Filter arguments, before passing to $wpdb->get_results(), when querying taxes via the REST API.
211
		 *
212
		 * @param array           $prepared_args Array of arguments for $wpdb->get_results().
213
		 * @param WP_REST_Request $request       The current request.
214
		 */
215
		$prepared_args = apply_filters( 'woocommerce_rest_tax_query', $prepared_args, $request );
216
217
		$query = "
218
			SELECT *
219
			FROM {$wpdb->prefix}woocommerce_tax_rates
220
			WHERE 1 = 1
221
		";
222
223
		// Filter by tax class.
224 View Code Duplication
		if ( ! empty( $prepared_args['class'] ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
225
			$class = 'standard' !== $prepared_args['class'] ? sanitize_title( $prepared_args['class'] ) : '';
226
			$query .= " AND tax_rate_class = '$class'";
227
		}
228
229
		// Order tax rates.
230
		$order_by = sprintf( ' ORDER BY %s', sanitize_key( $prepared_args['orderby'] ) );
231
232
		// Pagination.
233
		$pagination = sprintf( ' LIMIT %d, %d', $prepared_args['offset'], $prepared_args['number'] );
234
235
		// Query taxes.
236
		$results = $wpdb->get_results( $query . $order_by . $pagination );
237
238
		$taxes = array();
239
		foreach ( $results as $tax ) {
240
			$data = $this->prepare_item_for_response( $tax, $request );
241
			$taxes[] = $this->prepare_response_for_collection( $data );
242
		}
243
244
		$response = rest_ensure_response( $taxes );
245
246
		// Store pagation values for headers then unset for count query.
247
		$per_page = (int) $prepared_args['number'];
248
		$page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 );
249
250
		// Query only for ids.
251
		$wpdb->get_results( str_replace( 'SELECT *', 'SELECT tax_rate_id', $query ) );
252
253
		// Calcule totals.
254
		$total_taxes = (int) $wpdb->num_rows;
255
		$response->header( 'X-WP-Total', (int) $total_taxes );
256
		$max_pages = ceil( $total_taxes / $per_page );
257
		$response->header( 'X-WP-TotalPages', (int) $max_pages );
258
259
		$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
260 View Code Duplication
		if ( $page > 1 ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
261
			$prev_page = $page - 1;
262
			if ( $prev_page > $max_pages ) {
263
				$prev_page = $max_pages;
264
			}
265
			$prev_link = add_query_arg( 'page', $prev_page, $base );
266
			$response->link_header( 'prev', $prev_link );
267
		}
268 View Code Duplication
		if ( $max_pages > $page ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
269
			$next_page = $page + 1;
270
			$next_link = add_query_arg( 'page', $next_page, $base );
271
			$response->link_header( 'next', $next_link );
272
		}
273
274
		return $response;
275
	}
276
277
	/**
278
	 * Take tax data from the request and return the updated or newly created rate.
279
	 *
280
	 * @todo Replace with CRUD in 2.7.0
281
	 * @param WP_REST_Request $request Full details about the request.
282
	 * @param stdClass|null $current Existing tax object.
283
	 * @return stdClass
284
	 */
285
	protected function create_or_update_tax( $request, $current = null ) {
286
		$id          = absint( isset( $request['id'] ) ? $request['id'] : 0 );
287
		$data        = array();
288
		$fields      = array(
289
			'tax_rate_country',
290
			'tax_rate_state',
291
			'tax_rate',
292
			'tax_rate_name',
293
			'tax_rate_priority',
294
			'tax_rate_compound',
295
			'tax_rate_shipping',
296
			'tax_rate_order',
297
			'tax_rate_class',
298
		);
299
300
		foreach ( $fields as $field ) {
301
			// Keys via API differ from the stored names returned by _get_tax_rate.
302
			$key = 'tax_rate' === $field ? 'rate' : str_replace( 'tax_rate_', '', $field );
303
304
			// Remove data that was not posted.
305
			if ( ! isset( $request[ $key ] ) ) {
306
				continue;
307
			}
308
309
			// Test new data against current data.
310
			if ( $current && $current->$field === $request[ $key ] ) {
311
				continue;
312
			}
313
314
			// Add to data array.
315
			switch ( $key ) {
316
				case 'tax_rate_priority' :
317
				case 'tax_rate_compound' :
318
				case 'tax_rate_shipping' :
319
				case 'tax_rate_order' :
320
					$data[ $field ] = absint( $request[ $key ] );
321
					break;
322
				case 'tax_rate_class' :
323
					$data[ $field ] = 'standard' !== $request['tax_rate_class'] ? $request['tax_rate_class'] : '';
324
					break;
325
				default :
326
					$data[ $field ] = wc_clean( $request[ $key ] );
327
					break;
328
			}
329
		}
330
331
		if ( $id ) {
332
			WC_Tax::_update_tax_rate( $id, $data );
333
		} else {
334
			$id = WC_Tax::_insert_tax_rate( $data );
335
		}
336
337
		// Add locales.
338
		if ( ! empty( $request['postcode'] ) ) {
339
			WC_Tax::_update_tax_rate_postcodes( $id, wc_clean( $request['postcode'] ) );
340
		}
341
		if ( ! empty( $request['city'] ) ) {
342
			WC_Tax::_update_tax_rate_cities( $id, wc_clean( $request['city'] ) );
343
		}
344
345
		return WC_Tax::_get_tax_rate( $id, OBJECT );
346
	}
347
348
	/**
349
	 * Create a single tax.
350
	 *
351
	 * @param WP_REST_Request $request Full details about the request.
352
	 * @return WP_Error|WP_REST_Response
353
	 */
354
	public function create_item( $request ) {
355
		if ( ! empty( $request['id'] ) ) {
356
			return new WP_Error( 'woocommerce_rest_tax_exists', __( 'Cannot create existing resource.', 'woocommerce' ), array( 'status' => 400 ) );
357
		}
358
359
		$tax = $this->create_or_update_tax( $request );
360
361
		$this->update_additional_fields_for_object( $tax, $request );
362
363
		/**
364
		 * Fires after a tax is created or updated via the REST API.
365
		 *
366
		 * @param stdClass        $tax       Data used to create the tax.
367
		 * @param WP_REST_Request $request   Request object.
368
		 * @param boolean         $creating  True when creating tax, false when updating tax.
369
		 */
370
		do_action( 'woocommerce_rest_insert_tax', $tax, $request, true );
371
372
		$request->set_param( 'context', 'edit' );
373
		$response = $this->prepare_item_for_response( $tax, $request );
0 ignored issues
show
Documentation introduced by
$tax is of type array, but the function expects a object<stdClass>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
374
		$response = rest_ensure_response( $response );
375
		$response->set_status( 201 );
376
		$response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $tax->tax_rate_id ) ) );
377
378
		return $response;
379
	}
380
381
	/**
382
	 * Get a single tax.
383
	 *
384
	 * @param WP_REST_Request $request Full details about the request.
385
	 * @return WP_Error|WP_REST_Response
386
	 */
387 View Code Duplication
	public function get_item( $request ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
388
		$id      = (int) $request['id'];
389
		$tax_obj = WC_Tax::_get_tax_rate( $id, OBJECT );
390
391
		if ( empty( $id ) || empty( $tax_obj ) ) {
392
			return new WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource id.', 'woocommerce' ), array( 'status' => 404 ) );
393
		}
394
395
		$tax      = $this->prepare_item_for_response( $tax_obj, $request );
0 ignored issues
show
Documentation introduced by
$tax_obj is of type array, but the function expects a object<stdClass>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
396
		$response = rest_ensure_response( $tax );
397
398
		return $response;
399
	}
400
401
	/**
402
	 * Update a single tax.
403
	 *
404
	 * @param WP_REST_Request $request Full details about the request.
405
	 * @return WP_Error|WP_REST_Response
406
	 */
407
	public function update_item( $request ) {
408
		$id      = (int) $request['id'];
409
		$tax_obj = WC_Tax::_get_tax_rate( $id, OBJECT );
410
411
		if ( empty( $id ) || empty( $tax_obj ) ) {
412
			return new WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource id.', 'woocommerce' ), array( 'status' => 404 ) );
413
		}
414
415
		$tax = $this->create_or_update_tax( $request, $tax_obj );
0 ignored issues
show
Documentation introduced by
$tax_obj is of type array, but the function expects a object<stdClass>|null.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
416
417
		$this->update_additional_fields_for_object( $tax, $request );
418
419
		/**
420
		 * Fires after a tax is created or updated via the REST API.
421
		 *
422
		 * @param stdClass        $tax       Data used to create the tax.
423
		 * @param WP_REST_Request $request   Request object.
424
		 * @param boolean         $creating  True when creating tax, false when updating tax.
425
		 */
426
		do_action( 'woocommerce_rest_insert_tax', $tax, $request, false );
427
428
		$request->set_param( 'context', 'edit' );
429
		$response = $this->prepare_item_for_response( $tax, $request );
0 ignored issues
show
Documentation introduced by
$tax is of type array, but the function expects a object<stdClass>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
430
		$response = rest_ensure_response( $response );
431
432
		return $response;
433
	}
434
435
	/**
436
	 * Delete a single tax.
437
	 *
438
	 * @param WP_REST_Request $request Full details about the request.
439
	 * @return WP_Error|WP_REST_Response
440
	 */
441
	public function delete_item( $request ) {
442
		global $wpdb;
443
444
		$id    = (int) $request['id'];
445
		$force = isset( $request['force'] ) ? (bool) $request['force'] : false;
446
447
		// We don't support trashing for this type, error out.
448
		if ( ! $force ) {
449
			return new WP_Error( 'woocommerce_rest_trash_not_supported', __( 'Taxes do not support trashing.', 'woocommerce' ), array( 'status' => 501 ) );
450
		}
451
452
		$tax = WC_Tax::_get_tax_rate( $id, OBJECT );
453
454
		if ( empty( $id ) || empty( $tax ) ) {
455
			return new WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource id.', 'woocommerce' ), array( 'status' => 400 ) );
456
		}
457
458
		$request->set_param( 'context', 'edit' );
459
		$response = $this->prepare_item_for_response( $tax, $request );
0 ignored issues
show
Documentation introduced by
$tax is of type array, but the function expects a object<stdClass>.

It seems like the type of the argument is not accepted by the function/method which you are calling.

In some cases, in particular if PHP’s automatic type-juggling kicks in this might be fine. In other cases, however this might be a bug.

We suggest to add an explicit type cast like in the following example:

function acceptsInteger($int) { }

$x = '123'; // string "123"

// Instead of
acceptsInteger($x);

// we recommend to use
acceptsInteger((integer) $x);
Loading history...
460
461
		WC_Tax::_delete_tax_rate( $id );
462
463
		if ( 0 === $wpdb->rows_affected ) {
464
			return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'The resource cannot be deleted.', 'woocommerce' ), array( 'status' => 500 ) );
465
		}
466
467
		/**
468
		 * Fires after a tax is deleted via the REST API.
469
		 *
470
		 * @param stdClass         $tax      The tax data.
471
		 * @param WP_REST_Response $response The response returned from the API.
472
		 * @param WP_REST_Request  $request  The request sent to the API.
473
		 */
474
		do_action( 'woocommerce_rest_delete_tax', $tax, $response, $request );
475
476
		return $response;
477
	}
478
479
	/**
480
	 * Prepare a single tax output for response.
481
	 *
482
	 * @param stdClass $tax Tax object.
483
	 * @param WP_REST_Request $request Request object.
484
	 * @return WP_REST_Response $response Response data.
485
	 */
486
	public function prepare_item_for_response( $tax, $request ) {
487
		global $wpdb;
488
489
		$id   = (int) $tax->tax_rate_id;
490
		$data = array(
491
			'id'       => $id,
492
			'country'  => $tax->tax_rate_country,
493
			'state'    => $tax->tax_rate_state,
494
			'postcode' => '',
495
			'city'     => '',
496
			'rate'     => $tax->tax_rate,
497
			'name'     => $tax->tax_rate_name,
498
			'priority' => (int) $tax->tax_rate_priority,
499
			'compound' => (bool) $tax->tax_rate_compound,
500
			'shipping' => (bool) $tax->tax_rate_shipping,
501
			'order'    => (int) $tax->tax_rate_order,
502
			'class'    => $tax->tax_rate_class ? $tax->tax_rate_class : 'standard',
503
		);
504
505
		// Get locales from a tax rate.
506
		$locales = $wpdb->get_results( $wpdb->prepare( "
507
			SELECT location_code, location_type
508
			FROM {$wpdb->prefix}woocommerce_tax_rate_locations
509
			WHERE tax_rate_id = %d
510
		", $id ) );
511
512 View Code Duplication
		if ( ! is_wp_error( $tax ) && ! is_null( $tax ) ) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
513
			foreach ( $locales as $locale ) {
514
				$data[ $locale->location_type ] = $locale->location_code;
515
			}
516
		}
517
518
		$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
519
		$data    = $this->add_additional_fields_to_object( $data, $request );
520
		$data    = $this->filter_response_by_context( $data, $context );
521
522
		// Wrap the data in a response object.
523
		$response = rest_ensure_response( $data );
524
525
		$response->add_links( $this->prepare_links( $tax ) );
526
527
		/**
528
		 * Filter tax object returned from the REST API.
529
		 *
530
		 * @param WP_REST_Response $response The response object.
531
		 * @param stdClass         $tax      Tax object used to create response.
532
		 * @param WP_REST_Request  $request  Request object.
533
		 */
534
		return apply_filters( 'woocommerce_rest_prepare_tax', $response, $tax, $request );
535
	}
536
537
	/**
538
	 * Prepare links for the request.
539
	 *
540
	 * @param stdClass $tax Tax object.
541
	 * @return array Links for the given tax.
542
	 */
543 View Code Duplication
	protected function prepare_links( $tax ) {
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
544
		$links = array(
545
			'self' => array(
546
				'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $tax->tax_rate_id ) ),
547
			),
548
			'collection' => array(
549
				'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ),
550
			),
551
		);
552
553
		return $links;
554
	}
555
556
	/**
557
	 * Get the Taxes schema, conforming to JSON Schema.
558
	 *
559
	 * @return array
560
	 */
561
	public function get_item_schema() {
562
		$schema = array(
563
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
564
			'title'      => 'tax',
565
			'type'       => 'object',
566
			'properties' => array(
567
				'id' => array(
568
					'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
569
					'type'        => 'integer',
570
					'context'     => array( 'view', 'edit' ),
571
					'readonly'    => true,
572
				),
573
				'country' => array(
574
					'description' => __( 'Country ISO 3166 code.', 'woocommerce' ),
575
					'type'        => 'string',
576
					'context'     => array( 'view', 'edit' ),
577
				),
578
				'state' => array(
579
					'description' => __( 'State code.', 'woocommerce' ),
580
					'type'        => 'string',
581
					'context'     => array( 'view', 'edit' ),
582
				),
583
				'postcode' => array(
584
					'description' => __( 'Postcode/ZIP.', 'woocommerce' ),
585
					'type'        => 'string',
586
					'context'     => array( 'view', 'edit' ),
587
				),
588
				'city' => array(
589
					'description' => __( 'City name.', 'woocommerce' ),
590
					'type'        => 'string',
591
					'context'     => array( 'view', 'edit' ),
592
				),
593
				'rate' => array(
594
					'description' => __( 'Tax rate.', 'woocommerce' ),
595
					'type'        => 'float',
596
					'context'     => array( 'view', 'edit' ),
597
				),
598
				'name' => array(
599
					'description' => __( 'Tax rate name.', 'woocommerce' ),
600
					'type'        => 'string',
601
					'context'     => array( 'view', 'edit' ),
602
				),
603
				'priority' => array(
604
					'description' => __( 'Tax priority.', 'woocommerce' ),
605
					'type'        => 'integer',
606
					'default'     => 1,
607
					'context'     => array( 'view', 'edit' ),
608
				),
609
				'compound' => array(
610
					'description' => __( 'Whether or not this is a compound rate.', 'woocommerce' ),
611
					'type'        => 'boolean',
612
					'default'     => false,
613
					'context'     => array( 'view', 'edit' ),
614
				),
615
				'shipping' => array(
616
					'description' => __( 'Whether or not this tax rate also gets applied to shipping.', 'woocommerce' ),
617
					'type'        => 'boolean',
618
					'default'     => true,
619
					'context'     => array( 'view', 'edit' ),
620
				),
621
				'order' => array(
622
					'description' => __( 'Indicates the order that will appear in queries.', 'woocommerce' ),
623
					'type'        => 'integer',
624
					'context'     => array( 'view', 'edit' ),
625
				),
626
				'class' => array(
627
					'description' => __( 'Tax class.', 'woocommerce' ),
628
					'type'        => 'string',
629
					'default'     => 'standard',
630
					'enum'        => array_merge( array( 'standard' ), array_map( 'sanitize_title', WC_Tax::get_tax_classes() ) ),
631
					'context'     => array( 'view', 'edit' ),
632
				),
633
			),
634
		);
635
636
		return $this->add_additional_fields_schema( $schema );
637
	}
638
639
	/**
640
	 * Get the query params for collections.
641
	 *
642
	 * @return array
643
	 */
644
	public function get_collection_params() {
645
		$params = parent::get_collection_params();
646
647
		$params['context']['default'] = 'view';
648
649
		$params['exclude'] = array(
650
			'description'        => __( 'Ensure result set excludes specific ids.', 'woocommerce' ),
651
			'type'               => 'array',
652
			'default'            => array(),
653
			'sanitize_callback'  => 'wp_parse_id_list',
654
		);
655
		$params['include'] = array(
656
			'description'        => __( 'Limit result set to specific ids.', 'woocommerce' ),
657
			'type'               => 'array',
658
			'default'            => array(),
659
			'sanitize_callback'  => 'wp_parse_id_list',
660
		);
661
		$params['offset'] = array(
662
			'description'        => __( 'Offset the result set by a specific number of items.', 'woocommerce' ),
663
			'type'               => 'integer',
664
			'sanitize_callback'  => 'absint',
665
			'validate_callback'  => 'rest_validate_request_arg',
666
		);
667
		$params['order'] = array(
668
			'default'            => 'asc',
669
			'description'        => __( 'Order sort attribute ascending or descending.', 'woocommerce' ),
670
			'enum'               => array( 'asc', 'desc' ),
671
			'sanitize_callback'  => 'sanitize_key',
672
			'type'               => 'string',
673
			'validate_callback'  => 'rest_validate_request_arg',
674
		);
675
		$params['orderby'] = array(
676
			'default'            => 'order',
677
			'description'        => __( 'Sort collection by object attribute.', 'woocommerce' ),
678
			'enum'               => array(
679
				'id',
680
				'order',
681
			),
682
			'sanitize_callback'  => 'sanitize_key',
683
			'type'               => 'string',
684
			'validate_callback'  => 'rest_validate_request_arg',
685
		);
686
		$params['class'] = array(
687
			'description'        => __( 'Sort by tax class.', 'woocommerce' ),
688
			'enum'               => array_merge( array( 'standard' ), array_map( 'sanitize_title', WC_Tax::get_tax_classes() ) ),
689
			'sanitize_callback'  => 'sanitize_title',
690
			'type'               => 'string',
691
			'validate_callback'  => 'rest_validate_request_arg',
692
		);
693
694
		return $params;
695
	}
696
}
697