Completed
Push — master ( ad0743...ebd461 )
by Claudio
12:10
created

WC_REST_Taxes_Controller::prepare_links()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 12
Code Lines 7

Duplication

Lines 12
Ratio 100 %
Metric Value
dl 12
loc 12
rs 9.4285
cc 1
eloc 7
nc 1
nop 1
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 . '/bulk', array(
89
			array(
90
				'methods'             => WP_REST_Server::EDITABLE,
91
				'callback'            => array( $this, 'bulk_items' ),
92
				'permission_callback' => array( $this, 'create_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
	 * Create a single tax.
265
	 *
266
	 * @param WP_REST_Request $request Full details about the request.
267
	 * @return WP_Error|WP_REST_Response
268
	 */
269
	public function create_item( $request ) {
270
		if ( ! empty( $request['id'] ) ) {
271
			return new WP_Error( 'woocommerce_rest_tax_exists', __( 'Cannot create existing resource.', 'woocommerce' ), array( 'status' => 400 ) );
272
		}
273
274
		$data = array(
275
			'tax_rate_country'  => $request['country'],
276
			'tax_rate_state'    => $request['state'],
277
			'tax_rate'          => $request['rate'],
278
			'tax_rate_name'     => $request['name'],
279
			'tax_rate_priority' => (int) $request['priority'],
280
			'tax_rate_compound' => (int) $request['compound'],
281
			'tax_rate_shipping' => (int) $request['shipping'],
282
			'tax_rate_order'    => (int) $request['order'],
283
			'tax_rate_class'    => 'standard' !== $request['class'] ? $request['class'] : '',
284
		);
285
286
		// Create tax rate.
287
		$id = WC_Tax::_insert_tax_rate( $data );
288
289
		// Add locales.
290
		if ( ! empty( $request['postcode'] ) ) {
291
			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...
292
		}
293
		if ( ! empty( $request['city'] ) ) {
294
			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...
295
		}
296
297
		$tax = WC_Tax::_get_tax_rate( $id, OBJECT );
298
299
		$this->update_additional_fields_for_object( $tax, $request );
300
301
		/**
302
		 * Fires after a tax is created or updated via the REST API.
303
		 *
304
		 * @param stdClass        $tax       Data used to create the tax.
305
		 * @param WP_REST_Request $request   Request object.
306
		 * @param boolean         $creating  True when creating tax, false when updating tax.
307
		 */
308
		do_action( 'woocommerce_rest_insert_tax', $tax, $request, true );
309
310
		$request->set_param( 'context', 'edit' );
311
		$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...
312
		$response = rest_ensure_response( $response );
313
		$response->set_status( 201 );
314
		$response->header( 'Location', rest_url( sprintf( '/%s/%s/%d', $this->namespace, $this->rest_base, $id ) ) );
315
316
		return $response;
317
	}
318
319
	/**
320
	 * Get a single tax.
321
	 *
322
	 * @param WP_REST_Request $request Full details about the request.
323
	 * @return WP_Error|WP_REST_Response
324
	 */
325 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...
326
		$id       = (int) $request['id'];
327
		$tax_obj = WC_Tax::_get_tax_rate( $id, OBJECT );
328
329
		if ( empty( $id ) || empty( $tax_obj ) ) {
330
			return new WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource id.', 'woocommerce' ), array( 'status' => 404 ) );
331
		}
332
333
		$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...
334
		$response = rest_ensure_response( $tax );
335
336
		return $response;
337
	}
338
339
	/**
340
	 * Update a single tax.
341
	 *
342
	 * @param WP_REST_Request $request Full details about the request.
343
	 * @return WP_Error|WP_REST_Response
344
	 */
345
	public function update_item( $request ) {
346
		$id          = (int) $request['id'];
347
		$current_tax = WC_Tax::_get_tax_rate( $id, OBJECT );
348
349
		if ( empty( $id ) || empty( $current_tax ) ) {
350
			return new WP_Error( 'woocommerce_rest_invalid_id', __( 'Invalid resource id.', 'woocommerce' ), array( 'status' => 404 ) );
351
		}
352
353
		$data   = array();
354
		$fields = array(
355
			'tax_rate_country',
356
			'tax_rate_state',
357
			'tax_rate',
358
			'tax_rate_name',
359
			'tax_rate_priority',
360
			'tax_rate_compound',
361
			'tax_rate_shipping',
362
			'tax_rate_order',
363
			'tax_rate_class'
364
		);
365
366
		foreach ( $fields as $field ) {
367
			$key = 'tax_rate' === $field ? 'rate' : str_replace( 'tax_rate_', '', $field );
368
369
			if ( ! isset( $request[ $key ] ) ) {
370
				continue;
371
			}
372
373
			$value = $request[ $key ];
374
375
			// Fix compund and shipping values.
376
			if ( in_array( $key, array( 'compound', 'shipping' ) ) ) {
377
				$value = (int) $request[ $key ];
378
			}
379
380
			// Test new data against current data.
381
			if ( $current_tax->$field === $value ) {
382
				continue;
383
			}
384
385
			$data[ $field ] = $request[ $key ];
386
		}
387
388
		// Update tax rate.
389
		WC_Tax::_update_tax_rate( $id, $data );
390
391
		// Update locales.
392
		if ( ! isset( $request['postcode'] ) ) {
393
			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...
394
		}
395
396
		if ( ! isset( $request['city'] ) ) {
397
			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...
398
		}
399
400
		$tax = WC_Tax::_get_tax_rate( $id, OBJECT );
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 bulk_items( $request ) {
548
		/** @var WP_REST_Server $wp_rest_server */
549
		global $wp_rest_server;
550
551
		// Get the request params.
552
		$items = $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 don't exists.
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