Completed
Push — master ( 8fa673...1a1c33 )
by Claudio
11:12
created

WC_REST_Taxes_Controller::update_items()   B

Complexity

Conditions 5
Paths 6

Size

Total Lines 41
Code Lines 23

Duplication

Lines 3
Ratio 7.32 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 5
eloc 23
c 1
b 0
f 0
nc 6
nop 1
dl 3
loc 41
rs 8.439
1
<?php
1 ignored issue
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 23 and the first side effect is on line 14.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
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 WP_REST_Controller
22
 */
23
class WC_REST_Taxes_Controller extends WP_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
	public function register_routes() {
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 . '/update_items', array(
89
			array(
90
				'methods'             => WP_REST_Server::EDITABLE,
91
				'callback'            => array( $this, 'update_items' ),
92
				'permission_callback' => array( $this, 'update_item_permissions_check' ),
93
				'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
94
			),
95
			'schema' => array( $this, 'get_public_item_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 taxes.', '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
	 * Get all taxes.
171
	 *
172
	 * @param WP_REST_Request $request Full details about the request.
173
	 * @return WP_Error|WP_REST_Response
174
	 */
175
	public function get_items( $request ) {
176
		global $wpdb;
177
178
		$prepared_args = array();
179
		$prepared_args['exclude'] = $request['exclude'];
180
		$prepared_args['include'] = $request['include'];
181
		$prepared_args['order']   = $request['order'];
182
		$prepared_args['number']  = $request['per_page'];
183 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...
184
			$prepared_args['offset'] = $request['offset'];
185
		} else {
186
			$prepared_args['offset'] = ( $request['page'] - 1 ) * $prepared_args['number'];
187
		}
188
		$orderby_possibles = array(
189
			'id'    => 'tax_rate_id',
190
			'order' => 'tax_rate_order',
191
		);
192
		$prepared_args['orderby'] = $orderby_possibles[ $request['orderby'] ];
193
		$prepared_args['class']   = $request['class'];
194
195
		/**
196
		 * Filter arguments, before passing to $wpdb->get_results(), when querying taxes via the REST API.
197
		 *
198
		 * @param array           $prepared_args Array of arguments for $wpdb->get_results().
199
		 * @param WP_REST_Request $request       The current request.
200
		 */
201
		$prepared_args = apply_filters( 'woocommerce_rest_tax_query', $prepared_args, $request );
202
203
		$query = "
204
			SELECT *
205
			FROM {$wpdb->prefix}woocommerce_tax_rates
206
			WHERE 1 = 1
207
		";
208
209
		// Filter by tax class.
210 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...
211
			$class = 'standard' !== $prepared_args['class'] ? sanitize_title( $prepared_args['class'] ) : '';
212
			$query .= " AND tax_rate_class = '$class'";
213
		}
214
215
		// Order tax rates.
216
		$order_by = sprintf( ' ORDER BY %s', sanitize_key( $prepared_args['orderby'] ) );
217
218
		// Pagination.
219
		$pagination = sprintf( ' LIMIT %d, %d', $prepared_args['offset'], $prepared_args['number'] );
220
221
		// Query taxes.
222
		$results = $wpdb->get_results( $query . $order_by . $pagination );
223
224
		$taxes = array();
225
		foreach ( $results as $tax ) {
226
			$data = $this->prepare_item_for_response( $tax, $request );
227
			$taxes[] = $this->prepare_response_for_collection( $data );
228
		}
229
230
		$response = rest_ensure_response( $taxes );
231
232
		// Store pagation values for headers then unset for count query.
233
		$per_page = (int) $prepared_args['number'];
234
		$page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 );
235
236
		// Query only for ids.
237
		$wpdb->get_results( str_replace( 'SELECT *', 'SELECT tax_rate_id', $query ) );
238
239
		// Calcule totals.
240
		$total_taxes = (int) $wpdb->num_rows;
241
		$response->header( 'X-WP-Total', (int) $total_taxes );
242
		$max_pages = ceil( $total_taxes / $per_page );
243
		$response->header( 'X-WP-TotalPages', (int) $max_pages );
244
245
		$base = add_query_arg( $request->get_query_params(), rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ) );
246 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...
247
			$prev_page = $page - 1;
248
			if ( $prev_page > $max_pages ) {
249
				$prev_page = $max_pages;
250
			}
251
			$prev_link = add_query_arg( 'page', $prev_page, $base );
252
			$response->link_header( 'prev', $prev_link );
253
		}
254 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...
255
			$next_page = $page + 1;
256
			$next_link = add_query_arg( 'page', $next_page, $base );
257
			$response->link_header( 'next', $next_link );
258
		}
259
260
		return $response;
261
	}
262
263
	/**
264
	 * Take tax data from the request and return the updated or newly created rate.
265
	 *
266
	 * @todo Replace with CRUD in 2.7.0
267
	 * @param WP_REST_Request $request Full details about the request.
268
	 * @param stdClass|null $current Existing tax object.
269
	 * @return stdClass
270
	 */
271
	protected function create_or_update_tax( $request, $current = null ) {
272
		$id          = absint( isset( $request['id'] ) ? $request['id'] : 0 );
273
		$data        = array();
274
		$fields      = array(
275
			'tax_rate_country',
276
			'tax_rate_state',
277
			'tax_rate',
278
			'tax_rate_name',
279
			'tax_rate_priority',
280
			'tax_rate_compound',
281
			'tax_rate_shipping',
282
			'tax_rate_order',
283
			'tax_rate_class',
284
		);
285
286
		foreach ( $fields as $field ) {
287
			// Keys via API differ from the stored names returned by _get_tax_rate.
288
			$key = 'tax_rate' === $field ? 'rate' : str_replace( 'tax_rate_', '', $field );
289
290
			// Remove data that was not posted.
291
			if ( ! isset( $request[ $key ] ) ) {
292
				continue;
293
			}
294
295
			// Test new data against current data.
296
			if ( $current && $current->$field === $request[ $key ] ) {
297
				continue;
298
			}
299
300
			// Add to data array.
301
			switch ( $key ) {
302
				case 'tax_rate_priority' :
303
				case 'tax_rate_compound' :
304
				case 'tax_rate_shipping' :
305
				case 'tax_rate_order' :
306
					$data[ $field ] = absint( $request[ $key ] );
307
					break;
308
				case 'tax_rate_class' :
309
					$data[ $field ] = 'standard' !== $request['tax_rate_class'] ? $request['tax_rate_class'] : '';
310
					break;
311
				default :
312
					$data[ $field ] = wc_clean( $request[ $key ] );
313
					break;
314
			}
315
		}
316
317
		if ( $id ) {
318
			WC_Tax::_update_tax_rate( $id, $data );
319
		} else {
320
			$id = WC_Tax::_insert_tax_rate( $data );
321
		}
322
323
		// Add locales.
324
		if ( ! empty( $request['postcode'] ) ) {
325
			WC_Tax::_update_tax_rate_postcodes( $id, wc_clean( $request['postcode'] ) );
1 ignored issue
show
Bug introduced by
It seems like wc_clean($request['postcode']) targeting wc_clean() can also be of type array; however, WC_Tax::_update_tax_rate_postcodes() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
326
		}
327
		if ( ! empty( $request['city'] ) ) {
328
			WC_Tax::_update_tax_rate_cities( $id, wc_clean( $request['city'] ) );
1 ignored issue
show
Bug introduced by
It seems like wc_clean($request['city']) targeting wc_clean() can also be of type array; however, WC_Tax::_update_tax_rate_cities() does only seem to accept string, maybe add an additional type check?

This check looks at variables that are passed out again to other methods.

If the outgoing method call has stricter type requirements than the method itself, an issue is raised.

An additional type check may prevent trouble.

Loading history...
329
		}
330
331
		return WC_Tax::_get_tax_rate( $id, OBJECT );
332
	}
333
334
	/**
335
	 * Create a single tax.
336
	 *
337
	 * @param WP_REST_Request $request Full details about the request.
338
	 * @return WP_Error|WP_REST_Response
339
	 */
340
	public function create_item( $request ) {
341
		if ( ! empty( $request['id'] ) ) {
342
			return new WP_Error( 'woocommerce_rest_tax_exists', __( 'Cannot create existing resource.', 'woocommerce' ), array( 'status' => 400 ) );
343
		}
344
345
		$tax = $this->create_or_update_tax( $request );
346
347
		$this->update_additional_fields_for_object( $tax, $request );
348
349
		/**
350
		 * Fires after a tax is created or updated via the REST API.
351
		 *
352
		 * @param stdClass        $tax       Data used to create the tax.
353
		 * @param WP_REST_Request $request   Request object.
354
		 * @param boolean         $creating  True when creating tax, false when updating tax.
355
		 */
356
		do_action( 'woocommerce_rest_insert_tax', $tax, $request, true );
357
358
		$request->set_param( 'context', 'edit' );
359
		$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...
360
		$response = rest_ensure_response( $response );
361
		$response->set_status( 201 );
362
		$response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $tax->tax_rate_id ) ) );
363
364
		return $response;
365
	}
366
367
	/**
368
	 * Get a single tax.
369
	 *
370
	 * @param WP_REST_Request $request Full details about the request.
371
	 * @return WP_Error|WP_REST_Response
372
	 */
373 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...
374
		$id      = (int) $request['id'];
375
		$tax_obj = WC_Tax::_get_tax_rate( $id, OBJECT );
376
377
		if ( empty( $id ) || empty( $tax_obj ) ) {
378
			return new WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource id.', 'woocommerce' ), array( 'status' => 404 ) );
379
		}
380
381
		$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...
382
		$response = rest_ensure_response( $tax );
383
384
		return $response;
385
	}
386
387
	/**
388
	 * Update a single tax.
389
	 *
390
	 * @param WP_REST_Request $request Full details about the request.
391
	 * @return WP_Error|WP_REST_Response
392
	 */
393
	public function update_item( $request ) {
394
		$id      = (int) $request['id'];
395
		$current = WC_Tax::_get_tax_rate( $id, OBJECT );
396
		if ( empty( $id ) || empty( $current ) ) {
397
			return new WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource id.', 'woocommerce' ), array( 'status' => 404 ) );
398
		}
399
400
		$tax = $this->create_or_update_tax( $request, $current );
0 ignored issues
show
Documentation introduced by
$current 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...
401
402
		$this->update_additional_fields_for_object( $tax, $request );
403
404
		/**
405
		 * Fires after a tax is created or updated via the REST API.
406
		 *
407
		 * @param stdClass        $tax       Data used to create the tax.
408
		 * @param WP_REST_Request $request   Request object.
409
		 * @param boolean         $creating  True when creating tax, false when updating tax.
410
		 */
411
		do_action( 'woocommerce_rest_insert_tax', $tax, $request, false );
412
413
		$request->set_param( 'context', 'edit' );
414
		$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...
415
		$response = rest_ensure_response( $response );
416
417
		return $response;
418
	}
419
420
	/**
421
	 * Delete a single tax.
422
	 *
423
	 * @param WP_REST_Request $request Full details about the request.
424
	 * @return WP_Error|WP_REST_Response
425
	 */
426
	public function delete_item( $request ) {
427
		global $wpdb;
428
429
		$id    = (int) $request['id'];
430
		$force = isset( $request['force'] ) ? (bool) $request['force'] : false;
431
432
		// We don't support trashing for this type, error out.
433
		if ( ! $force ) {
434
			return new WP_Error( 'woocommerce_rest_trash_not_supported', __( 'Taxes do not support trashing.', 'woocommerce' ), array( 'status' => 501 ) );
435
		}
436
437
		$tax = WC_Tax::_get_tax_rate( $id, OBJECT );
438
439
		if ( empty( $id ) || empty( $tax ) ) {
440
			return new WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource id.', 'woocommerce' ), array( 'status' => 400 ) );
441
		}
442
443
		$request->set_param( 'context', 'edit' );
444
		$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...
445
446
		WC_Tax::_delete_tax_rate( $id );
447
448
		if ( 0 === $wpdb->rows_affected ) {
449
			return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'The resource cannot be deleted.', 'woocommerce' ), array( 'status' => 500 ) );
450
		}
451
452
		/**
453
		 * Fires after a tax is deleted via the REST API.
454
		 *
455
		 * @param stdClass         $tax      The tax data.
456
		 * @param WP_REST_Response $response The response returned from the API.
457
		 * @param WP_REST_Request  $request  The request sent to the API.
458
		 */
459
		do_action( 'woocommerce_rest_delete_tax', $tax, $response, $request );
460
461
		return $response;
462
	}
463
464
	/**
465
	 * Prepare a single tax output for response.
466
	 *
467
	 * @param stdClass $tax Tax object.
468
	 * @param WP_REST_Request $request Request object.
469
	 * @return WP_REST_Response $response Response data.
470
	 */
471
	public function prepare_item_for_response( $tax, $request ) {
472
		global $wpdb;
473
474
		$id   = (int) $tax->tax_rate_id;
475
		$data = array(
476
			'id'       => $id,
477
			'country'  => $tax->tax_rate_country,
478
			'state'    => $tax->tax_rate_state,
479
			'postcode' => '',
480
			'city'     => '',
481
			'rate'     => $tax->tax_rate,
482
			'name'     => $tax->tax_rate_name,
483
			'priority' => (int) $tax->tax_rate_priority,
484
			'compound' => (bool) $tax->tax_rate_compound,
485
			'shipping' => (bool) $tax->tax_rate_shipping,
486
			'order'    => (int) $tax->tax_rate_order,
487
			'class'    => $tax->tax_rate_class ? $tax->tax_rate_class : 'standard',
488
		);
489
490
		// Get locales from a tax rate.
491
		$locales = $wpdb->get_results( $wpdb->prepare( "
492
			SELECT location_code, location_type
493
			FROM {$wpdb->prefix}woocommerce_tax_rate_locations
494
			WHERE tax_rate_id = %d
495
		", $id ) );
496
497 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...
498
			foreach ( $locales as $locale ) {
499
				$data[ $locale->location_type ] = $locale->location_code;
500
			}
501
		}
502
503
		$context = ! empty( $request['context'] ) ? $request['context'] : 'view';
504
		$data    = $this->add_additional_fields_to_object( $data, $request );
505
		$data    = $this->filter_response_by_context( $data, $context );
506
507
		// Wrap the data in a response object.
508
		$response = rest_ensure_response( $data );
509
510
		$response->add_links( $this->prepare_links( $tax ) );
511
512
		/**
513
		 * Filter tax object returned from the REST API.
514
		 *
515
		 * @param WP_REST_Response $response The response object.
516
		 * @param stdClass         $tax      Tax object used to create response.
517
		 * @param WP_REST_Request  $request  Request object.
518
		 */
519
		return apply_filters( 'woocommerce_rest_prepare_tax', $response, $tax, $request );
520
	}
521
522
	/**
523
	 * Prepare links for the request.
524
	 *
525
	 * @param stdClass $tax Tax object.
526
	 * @return array Links for the given tax.
527
	 */
528 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...
529
		$links = array(
530
			'self' => array(
531
				'href' => rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $tax->tax_rate_id ) ),
532
			),
533
			'collection' => array(
534
				'href' => rest_url( sprintf( '/%s/%s', $this->namespace, $this->rest_base ) ),
535
			),
536
		);
537
538
		return $links;
539
	}
540
541
	/**
542
	 * Bulk update or create items.
543
	 *
544
	 * @param WP_REST_Request $request Full details about the request.
545
	 * @return array Of WP_Error or WP_REST_Response.
546
	 */
547
	public function update_items( $request ) {
548
		/** @var WP_REST_Server $wp_rest_server */
549
		global $wp_rest_server;
550
551
		// Get the request params.
552
		$items = array_filter( $request->get_params() );
553
554
		// Limit bulk operation.
555
		$limit = apply_filters( 'woocommerce_rest_bulk_items_limit', 100, 'taxes' );
556 View Code Duplication
		if ( count( $items ) > $limit ) {
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...
557
			return new WP_Error( 'woocommerce_rest_request_entity_too_large', sprintf( __( 'Unable to accept more than %s items for this request.', 'woocommerce' ), $limit ), array( 'status' => 413 ) );
558
		}
559
560
		$response = array();
561
562
		foreach ( $items as $item ) {
563
			// Item exists.
564
			if ( ! empty( $item['id'] ) ) {
565
				$_item = new WP_REST_Request( 'PUT' );
566
				$_item->set_body_params( $item );
567
				$_response = $this->update_item( $_item );
568
569
			// Item does not exist.
570
			} else {
571
				$_item  = new WP_REST_Request( 'POST' );
572
				$_item->set_body_params( $item );
573
				$_response = $this->create_item( $_item );
574
			}
575
576
			if ( is_wp_error( $_response ) ) {
577
				$response[] = array(
578
					'id'    => $item['id'],
579
					'error' => array( 'code' => $_response->get_error_code(), 'message' => $_response->get_error_message(), 'data' => $_response->get_error_data() ),
580
				);
581
			} else {
582
				$response[] = $wp_rest_server->response_to_data( $_response, '' );
583
			}
584
		}
585
586
		return $response;
587
	}
588
589
	/**
590
	 * Get the Taxes schema, conforming to JSON Schema.
591
	 *
592
	 * @return array
593
	 */
594
	public function get_item_schema() {
595
		$schema = array(
596
			'$schema'    => 'http://json-schema.org/draft-04/schema#',
597
			'title'      => 'tax',
598
			'type'       => 'object',
599
			'properties' => array(
600
				'id' => array(
601
					'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
602
					'type'        => 'integer',
603
					'context'     => array( 'view', 'edit' ),
604
					'readonly'    => true,
605
				),
606
				'country' => array(
607
					'description' => __( 'Country ISO 3166 code.', 'woocommerce' ),
608
					'type'        => 'string',
609
					'context'     => array( 'view', 'edit' ),
610
				),
611
				'state' => array(
612
					'description' => __( 'State code.', 'woocommerce' ),
613
					'type'        => 'string',
614
					'context'     => array( 'view', 'edit' ),
615
				),
616
				'postcode' => array(
617
					'description' => __( 'Postcode/ZIP.', 'woocommerce' ),
618
					'type'        => 'string',
619
					'context'     => array( 'view', 'edit' ),
620
				),
621
				'city' => array(
622
					'description' => __( 'City name.', 'woocommerce' ),
623
					'type'        => 'string',
624
					'context'     => array( 'view', 'edit' ),
625
				),
626
				'rate' => array(
627
					'description' => __( 'Tax rate.', 'woocommerce' ),
628
					'type'        => 'float',
629
					'context'     => array( 'view', 'edit' ),
630
				),
631
				'name' => array(
632
					'description' => __( 'Tax rate name.', 'woocommerce' ),
633
					'type'        => 'string',
634
					'context'     => array( 'view', 'edit' ),
635
				),
636
				'priority' => array(
637
					'description' => __( 'Tax priority.', 'woocommerce' ),
638
					'type'        => 'integer',
639
					'default'     => 1,
640
					'context'     => array( 'view', 'edit' ),
641
				),
642
				'compound' => array(
643
					'description' => __( 'Whether or not this is a compound rate.', 'woocommerce' ),
644
					'type'        => 'boolean',
645
					'default'     => false,
646
					'context'     => array( 'view', 'edit' ),
647
				),
648
				'shipping' => array(
649
					'description' => __( 'Whether or not this tax rate also gets applied to shipping.', 'woocommerce' ),
650
					'type'        => 'boolean',
651
					'default'     => true,
652
					'context'     => array( 'view', 'edit' ),
653
				),
654
				'order' => array(
655
					'description' => __( 'Indicates the order that will appear in queries.', 'woocommerce' ),
656
					'type'        => 'integer',
657
					'context'     => array( 'view', 'edit' ),
658
				),
659
				'class' => array(
660
					'description' => __( 'Tax class.', 'woocommerce' ),
661
					'type'        => 'string',
662
					'default'     => 'standard',
663
					'enum'        => array_merge( array( 'standard' ), array_map( 'sanitize_title', WC_Tax::get_tax_classes() ) ),
664
					'context'     => array( 'view', 'edit' ),
665
				),
666
			),
667
		);
668
669
		return $this->add_additional_fields_schema( $schema );
670
	}
671
672
	/**
673
	 * Get the query params for collections.
674
	 *
675
	 * @return array
676
	 */
677
	public function get_collection_params() {
678
		$params = parent::get_collection_params();
679
680
		$params['context']['default'] = 'view';
681
682
		$params['exclude'] = array(
683
			'description'        => __( 'Ensure result set excludes specific ids.', 'woocommerce' ),
684
			'type'               => 'array',
685
			'default'            => array(),
686
			'sanitize_callback'  => 'wp_parse_id_list',
687
		);
688
		$params['include'] = array(
689
			'description'        => __( 'Limit result set to specific ids.', 'woocommerce' ),
690
			'type'               => 'array',
691
			'default'            => array(),
692
			'sanitize_callback'  => 'wp_parse_id_list',
693
		);
694
		$params['offset'] = array(
695
			'description'        => __( 'Offset the result set by a specific number of items.', 'woocommerce' ),
696
			'type'               => 'integer',
697
			'sanitize_callback'  => 'absint',
698
			'validate_callback'  => 'rest_validate_request_arg',
699
		);
700
		$params['order'] = array(
701
			'default'            => 'asc',
702
			'description'        => __( 'Order sort attribute ascending or descending.', 'woocommerce' ),
703
			'enum'               => array( 'asc', 'desc' ),
704
			'sanitize_callback'  => 'sanitize_key',
705
			'type'               => 'string',
706
			'validate_callback'  => 'rest_validate_request_arg',
707
		);
708
		$params['orderby'] = array(
709
			'default'            => 'order',
710
			'description'        => __( 'Sort collection by object attribute.', 'woocommerce' ),
711
			'enum'               => array(
712
				'id',
713
				'order',
714
			),
715
			'sanitize_callback'  => 'sanitize_key',
716
			'type'               => 'string',
717
			'validate_callback'  => 'rest_validate_request_arg',
718
		);
719
		$params['class'] = array(
720
			'description'        => __( 'Sort by tax class.', 'woocommerce' ),
721
			'enum'               => array_merge( array( 'standard' ), array_map( 'sanitize_title', WC_Tax::get_tax_classes() ) ),
722
			'sanitize_callback'  => 'sanitize_title',
723
			'type'               => 'string',
724
			'validate_callback'  => 'rest_validate_request_arg',
725
		);
726
727
		return $params;
728
	}
729
}
730