Completed
Push — master ( dac471...2b1d4e )
by Claudio
07:25
created

batch_items_permissions_check()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 3
eloc 7
c 1
b 0
f 0
nc 3
nop 1
dl 0
loc 12
rs 9.4285
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 15 and the first side effect is on line 4.

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
if ( ! defined( 'ABSPATH' ) ) {
4
	exit;
5
}
6
7
/**
8
 * Abstract Rest Terms Controler Class
9
 *
10
 * @author   WooThemes
11
 * @category API
12
 * @package  WooCommerce/Abstracts
13
 * @version  2.6.0
14
 */
15
abstract class WC_REST_Terms_Controller extends WC_REST_Controller {
16
17
	/**
18
	 * Route base.
19
	 *
20
	 * @var string
21
	 */
22
	protected $rest_base = '';
23
24
	/**
25
	 * Taxonomy.
26
	 *
27
	 * @var string
28
	 */
29
	protected $taxonomy = '';
30
31
	/**
32
	 * Register the routes for terms.
33
	 */
34 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...
35
		register_rest_route( $this->namespace, '/' . $this->rest_base, array(
36
			array(
37
				'methods'             => WP_REST_Server::READABLE,
38
				'callback'            => array( $this, 'get_items' ),
39
				'permission_callback' => array( $this, 'get_items_permissions_check' ),
40
				'args'                => $this->get_collection_params(),
41
			),
42
			array(
43
				'methods'             => WP_REST_Server::CREATABLE,
44
				'callback'            => array( $this, 'create_item' ),
45
				'permission_callback' => array( $this, 'create_item_permissions_check' ),
46
				'args'                => array_merge( $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ), array(
47
					'name' => array(
48
						'required' => true,
49
					),
50
				) ),
51
			),
52
			'schema' => array( $this, 'get_public_item_schema' ),
53
		));
54
55
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array(
56
			array(
57
				'methods'             => WP_REST_Server::READABLE,
58
				'callback'            => array( $this, 'get_item' ),
59
				'permission_callback' => array( $this, 'get_item_permissions_check' ),
60
				'args'                => array(
61
					'context'         => $this->get_context_param( array( 'default' => 'view' ) ),
62
				),
63
			),
64
			array(
65
				'methods'             => WP_REST_Server::EDITABLE,
66
				'callback'            => array( $this, 'update_item' ),
67
				'permission_callback' => array( $this, 'update_item_permissions_check' ),
68
				'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
69
			),
70
			array(
71
				'methods'             => WP_REST_Server::DELETABLE,
72
				'callback'            => array( $this, 'delete_item' ),
73
				'permission_callback' => array( $this, 'delete_item_permissions_check' ),
74
				'args'                => array(
75
					'force' => array(
76
						'default'     => false,
77
						'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce' ),
78
					),
79
				),
80
			),
81
			'schema' => array( $this, 'get_public_item_schema' ),
82
		) );
83
84
		register_rest_route( $this->namespace, '/' . $this->rest_base . '/batch', array(
85
			array(
86
				'methods'             => WP_REST_Server::EDITABLE,
87
				'callback'            => array( $this, 'batch_items' ),
88
				'permission_callback' => array( $this, 'batch_items_permissions_check' ),
89
				'args'                => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
90
			),
91
			'schema' => array( $this, 'get_public_batch_schema' ),
92
		) );
93
	}
94
95
	/**
96
	 * Check if a given request has access to read the terms.
97
	 *
98
	 * @param  WP_REST_Request $request Full details about the request.
99
	 * @return WP_Error|boolean
100
	 */
101 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...
102
		$permissions = $this->check_permissions( $request, 'read' );
103
		if ( is_wp_error( $permissions ) ) {
104
			return $permissions;
105
		}
106
107
		if ( ! $permissions ) {
108
			return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
109
		}
110
111
		return true;
112
	}
113
114
	/**
115
	 * Check if a given request has access to create a term.
116
	 *
117
	 * @param  WP_REST_Request $request Full details about the request.
118
	 * @return WP_Error|boolean
119
	 */
120 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...
121
		$permissions = $this->check_permissions( $request, 'create' );
122
		if ( is_wp_error( $permissions ) ) {
123
			return $permissions;
124
		}
125
126
		if ( ! $permissions ) {
127
			return new WP_Error( 'woocommerce_rest_cannot_create', __( 'Sorry, you cannot create new resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
128
		}
129
130
		return true;
131
	}
132
133
	/**
134
	 * Check if a given request has access to read a term.
135
	 *
136
	 * @param  WP_REST_Request $request Full details about the request.
137
	 * @return WP_Error|boolean
138
	 */
139 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...
140
		$permissions = $this->check_permissions( $request, 'read' );
141
		if ( is_wp_error( $permissions ) ) {
142
			return $permissions;
143
		}
144
145
		if ( ! $permissions ) {
146
			return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
147
		}
148
149
		return true;
150
	}
151
152
	/**
153
	 * Check if a given request has access to update a term.
154
	 *
155
	 * @param  WP_REST_Request $request Full details about the request.
156
	 * @return WP_Error|boolean
157
	 */
158 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...
159
		$permissions = $this->check_permissions( $request, 'edit' );
160
		if ( is_wp_error( $permissions ) ) {
161
			return $permissions;
162
		}
163
164
		if ( ! $permissions ) {
165
			return new WP_Error( 'woocommerce_rest_cannot_update', __( 'Sorry, you cannot update resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
166
		}
167
168
		return true;
169
	}
170
171
	/**
172
	 * Check if a given request has access to delete a term.
173
	 *
174
	 * @param  WP_REST_Request $request Full details about the request.
175
	 * @return WP_Error|boolean
176
	 */
177 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...
178
		$permissions = $this->check_permissions( $request, 'delete' );
179
		if ( is_wp_error( $permissions ) ) {
180
			return $permissions;
181
		}
182
183
		if ( ! $permissions ) {
184
			return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'Sorry, you cannot delete resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
185
		}
186
187
		return true;
188
	}
189
190
	/**
191
	 * Check if a given request has access batch create, update and delete items.
192
	 *
193
	 * @param  WP_REST_Request $request Full details about the request.
194
	 * @return boolean
195
	 */
196
	public function batch_items_permissions_check( $request ) {
197
		$permissions = $this->check_permissions( $request, 'batch' );
198
		if ( is_wp_error( $permissions ) ) {
199
			return $permissions;
200
		}
201
202
		if ( ! $permissions ) {
203
			return new WP_Error( 'woocommerce_rest_cannot_batch', __( 'Sorry, you are not allowed to manipule this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
204
		}
205
206
		return true;
207
	}
208
209
	/**
210
	 * Check permissions.
211
	 *
212
	 * @param WP_REST_Request $request Full details about the request.
213
	 * @param string $context Request context.
214
	 * @return bool|WP_Error
215
	 */
216
	protected function check_permissions( $request, $context = 'read' ) {
217
		// Get taxonomy.
218
		$taxonomy = $this->get_taxonomy( $request );
219
		if ( ! $taxonomy ) {
220
			return new WP_Error( 'woocommerce_rest_taxonomy_invalid', __( "Taxonomy doesn't exist.", 'woocommerce' ), array( 'status' => 404 ) );
221
		}
222
223
		// Check permissions for a single term.
224
		if ( $id = intval( $request['id'] ) ) {
225
			$term = get_term( $id, $taxonomy );
226
227
			if ( ! $term || $term->taxonomy !== $taxonomy ) {
228
				return new WP_Error( 'woocommerce_rest_term_invalid', __( "Resource doesn't exist.", 'woocommerce' ), array( 'status' => 404 ) );
229
			}
230
231
			return wc_rest_check_product_term_permissions( $taxonomy, $context, $term->term_id );
232
		}
233
234
		return wc_rest_check_product_term_permissions( $taxonomy, $context );
235
	}
236
237
	/**
238
	 * Get terms associated with a taxonomy.
239
	 *
240
	 * @param WP_REST_Request $request Full details about the request.
241
	 * @return WP_REST_Response|WP_Error
242
	 */
243
	public function get_items( $request ) {
244
		$taxonomy      = $this->get_taxonomy( $request );
245
		$prepared_args = array(
246
			'exclude'    => $request['exclude'],
247
			'include'    => $request['include'],
248
			'order'      => $request['order'],
249
			'orderby'    => $request['orderby'],
250
			'product'    => $request['product'],
251
			'hide_empty' => $request['hide_empty'],
252
			'number'     => $request['per_page'],
253
			'search'     => $request['search'],
254
			'slug'       => $request['slug'],
255
		);
256
257 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...
258
			$prepared_args['offset'] = $request['offset'];
259
		} else {
260
			$prepared_args['offset']  = ( $request['page'] - 1 ) * $prepared_args['number'];
261
		}
262
263
		$taxonomy_obj = get_taxonomy( $taxonomy );
264
265
		if ( $taxonomy_obj->hierarchical && isset( $request['parent'] ) ) {
266
			if ( 0 === $request['parent'] ) {
267
				// Only query top-level terms.
268
				$prepared_args['parent'] = 0;
269
			} else {
270
				if ( $request['parent'] ) {
271
					$prepared_args['parent'] = $request['parent'];
272
				}
273
			}
274
		}
275
276
		/**
277
		 * Filter the query arguments, before passing them to `get_terms()`.
278
		 *
279
		 * Enables adding extra arguments or setting defaults for a terms
280
		 * collection request.
281
		 *
282
		 * @see https://developer.wordpress.org/reference/functions/get_terms/
283
		 *
284
		 * @param array           $prepared_args Array of arguments to be
285
		 *                                       passed to get_terms.
286
		 * @param WP_REST_Request $request       The current request.
287
		 */
288
		$prepared_args = apply_filters( "woocommerce_rest_{$taxonomy}_query", $prepared_args, $request );
289
290
		if ( ! empty( $prepared_args['product'] )  ) {
291
			$query_result = $this->get_terms_for_product( $prepared_args );
292
			$total_terms = $this->total_terms;
293
		} else {
294
			$query_result = get_terms( $taxonomy, $prepared_args );
295
296
			$count_args = $prepared_args;
297
			unset( $count_args['number'] );
298
			unset( $count_args['offset'] );
299
			$total_terms = wp_count_terms( $taxonomy, $count_args );
300
301
			// Ensure we don't return results when offset is out of bounds.
302
			// See https://core.trac.wordpress.org/ticket/35935
303
			if ( $prepared_args['offset'] >= $total_terms ) {
304
				$query_result = array();
305
			}
306
307
			// wp_count_terms can return a falsy value when the term has no children.
308
			if ( ! $total_terms ) {
309
				$total_terms = 0;
310
			}
311
		}
312
		$response = array();
313
		foreach ( $query_result as $term ) {
314
			$data = $this->prepare_item_for_response( $term, $request );
315
			$response[] = $this->prepare_response_for_collection( $data );
316
		}
317
318
		$response = rest_ensure_response( $response );
319
320
		// Store pagation values for headers then unset for count query.
321
		$per_page = (int) $prepared_args['number'];
322
		$page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 );
323
324
		$response->header( 'X-WP-Total', (int) $total_terms );
325
		$max_pages = ceil( $total_terms / $per_page );
326
		$response->header( 'X-WP-TotalPages', (int) $max_pages );
327
328
		$base = add_query_arg( $request->get_query_params(), rest_url( '/' . $this->namespace . '/' . $this->rest_base ) );
329 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...
330
			$prev_page = $page - 1;
331
			if ( $prev_page > $max_pages ) {
332
				$prev_page = $max_pages;
333
			}
334
			$prev_link = add_query_arg( 'page', $prev_page, $base );
335
			$response->link_header( 'prev', $prev_link );
336
		}
337 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...
338
			$next_page = $page + 1;
339
			$next_link = add_query_arg( 'page', $next_page, $base );
340
			$response->link_header( 'next', $next_link );
341
		}
342
343
		return $response;
344
	}
345
346
	/**
347
	 * Create a single term for a taxonomy.
348
	 *
349
	 * @param WP_REST_Request $request Full details about the request.
350
	 * @return WP_REST_Request|WP_Error
351
	 */
352
	public function create_item( $request ) {
353
		$taxonomy = $this->get_taxonomy( $request );
354
		$name     = $request['name'];
355
		$args     = array();
356
		$schema   = $this->get_item_schema();
357
358 View Code Duplication
		if ( ! empty( $schema['properties']['description'] ) && isset( $request['description'] ) ) {
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...
359
			$args['description'] = $request['description'];
360
		}
361
		if ( isset( $request['slug'] ) ) {
362
			$args['slug'] = $request['slug'];
363
		}
364
365 View Code Duplication
		if ( isset( $request['parent'] ) ) {
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...
366
			if ( ! is_taxonomy_hierarchical( $taxonomy ) ) {
367
				return new WP_Error( 'woocommerce_rest_taxonomy_not_hierarchical', __( 'Can not set resource parent, taxonomy is not hierarchical.', 'woocommerce' ), array( 'status' => 400 ) );
368
			}
369
370
			$parent = get_term( (int) $request['parent'], $taxonomy );
371
372
			if ( ! $parent ) {
373
				return new WP_Error( 'woocommerce_rest_term_invalid', __( "Parent resource doesn't exist.", 'woocommerce' ), array( 'status' => 404 ) );
374
			}
375
376
			$args['parent'] = $parent->term_id;
377
		}
378
379
		$term = wp_insert_term( $name, $taxonomy, $args );
380
		if ( is_wp_error( $term ) ) {
381
382
			// If we're going to inform the client that the term exists, give them the identifier
383
			// they can actually use.
384
			if ( ( $term_id = $term->get_error_data( 'term_exists' ) ) ) {
385
				$existing_term = get_term( $term_id, $taxonomy );
386
				$term->add_data( $existing_term->term_id, 'term_exists' );
387
			}
388
389
			return $term;
390
		}
391
392
		$term = get_term( $term['term_id'], $taxonomy );
393
394
		$this->update_additional_fields_for_object( $term, $request );
395
396
		// Add term data.
397
		$meta_fields = $this->update_term_meta_fields( $term, $request );
398
		if ( is_wp_error( $meta_fields ) ) {
399
			return $meta_fields;
400
		}
401
402
		/**
403
		 * Fires after a single term is created or updated via the REST API.
404
		 *
405
		 * @param WP_Term         $term      Inserted Term object.
406
		 * @param WP_REST_Request $request   Request object.
407
		 * @param boolean         $creating  True when creating term, false when updating.
408
		 */
409
		do_action( "woocommerce_rest_insert_{$taxonomy}", $term, $request, true );
410
411
		$request->set_param( 'context', 'edit' );
412
		$response = $this->prepare_item_for_response( $term, $request );
413
		$response = rest_ensure_response( $response );
414
		$response->set_status( 201 );
415
416
		$base = '/' . $this->namespace . '/' . $this->rest_base;
417 View Code Duplication
		if ( ! empty( $request['attribute_id'] ) ) {
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...
418
			$base = str_replace( '(?P<attribute_id>[\d]+)', (int) $request['attribute_id'], $base );
419
		}
420
421
		$response->header( 'Location', rest_url( $base . '/' . $term->term_id ) );
422
423
		return $response;
424
	}
425
426
	/**
427
	 * Get a single term from a taxonomy.
428
	 *
429
	 * @param WP_REST_Request $request Full details about the request.
430
	 * @return WP_REST_Request|WP_Error
431
	 */
432
	public function get_item( $request ) {
433
		$taxonomy = $this->get_taxonomy( $request );
434
		$term     = get_term( (int) $request['id'], $taxonomy );
435
436
		if ( is_wp_error( $term ) ) {
437
			return $term;
438
		}
439
440
		$response = $this->prepare_item_for_response( $term, $request );
441
442
		return rest_ensure_response( $response );
443
	}
444
445
	/**
446
	 * Update a single term from a taxonomy.
447
	 *
448
	 * @param WP_REST_Request $request Full details about the request.
449
	 * @return WP_REST_Request|WP_Error
450
	 */
451
	public function update_item( $request ) {
452
		$taxonomy      = $this->get_taxonomy( $request );
453
		$term          = get_term( (int) $request['id'], $taxonomy );
454
		$schema        = $this->get_item_schema();
455
		$prepared_args = array();
456
457
		if ( isset( $request['name'] ) ) {
458
			$prepared_args['name'] = $request['name'];
459
		}
460 View Code Duplication
		if ( ! empty( $schema['properties']['description'] ) && isset( $request['description'] ) ) {
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...
461
			$prepared_args['description'] = $request['description'];
462
		}
463
		if ( isset( $request['slug'] ) ) {
464
			$prepared_args['slug'] = $request['slug'];
465
		}
466
467 View Code Duplication
		if ( isset( $request['parent'] ) ) {
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...
468
			if ( ! is_taxonomy_hierarchical( $taxonomy ) ) {
469
				return new WP_Error( 'woocommerce_rest_taxonomy_not_hierarchical', __( 'Can not set resource parent, taxonomy is not hierarchical.', 'woocommerce' ), array( 'status' => 400 ) );
470
			}
471
472
			$parent = get_term( (int) $request['parent'], $taxonomy );
473
474
			if ( ! $parent ) {
475
				return new WP_Error( 'woocommerce_rest_term_invalid', __( "Parent resource doesn't exist.", 'woocommerce' ), array( 'status' => 400 ) );
476
			}
477
478
			$prepared_args['parent'] = $parent->term_id;
479
		}
480
481
		// Only update the term if we haz something to update.
482
		if ( ! empty( $prepared_args ) ) {
483
			$update = wp_update_term( $term->term_id, $term->taxonomy, $prepared_args );
484
			if ( is_wp_error( $update ) ) {
485
				return $update;
486
			}
487
		}
488
489
		$term = get_term( (int) $request['id'], $taxonomy );
490
491
		$this->update_additional_fields_for_object( $term, $request );
492
493
		// Update term data.
494
		$meta_fields = $this->update_term_meta_fields( $term, $request );
495
		if ( is_wp_error( $meta_fields ) ) {
496
			return $meta_fields;
497
		}
498
499
		/**
500
		 * Fires after a single term is created or updated via the REST API.
501
		 *
502
		 * @param WP_Term         $term      Inserted Term object.
503
		 * @param WP_REST_Request $request   Request object.
504
		 * @param boolean         $creating  True when creating term, false when updating.
505
		 */
506
		do_action( "woocommerce_rest_insert_{$taxonomy}", $term, $request, false );
507
508
		$request->set_param( 'context', 'edit' );
509
		$response = $this->prepare_item_for_response( $term, $request );
510
		return rest_ensure_response( $response );
511
	}
512
513
	/**
514
	 * Delete a single term from a taxonomy.
515
	 *
516
	 * @param WP_REST_Request $request Full details about the request.
517
	 * @return WP_REST_Response|WP_Error
518
	 */
519
	public function delete_item( $request ) {
520
		$taxonomy = $this->get_taxonomy( $request );
521
		$term     = get_term( (int) $request['id'], $taxonomy );
0 ignored issues
show
Unused Code introduced by
$term is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
522
		$force    = isset( $request['force'] ) ? (bool) $request['force'] : false;
523
524
		// We don't support trashing for this type, error out.
525
		if ( ! $force ) {
526
			return new WP_Error( 'woocommerce_rest_trash_not_supported', __( 'Resource does not support trashing.', 'woocommerce' ), array( 'status' => 501 ) );
527
		}
528
529
		$term = get_term( (int) $request['id'], $taxonomy );
530
		$request->set_param( 'context', 'edit' );
531
		$response = $this->prepare_item_for_response( $term, $request );
532
533
		$retval = wp_delete_term( $term->term_id, $term->taxonomy );
534
		if ( ! $retval ) {
535
			return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'The resource cannot be deleted.', 'woocommerce' ), array( 'status' => 500 ) );
536
		}
537
538
		/**
539
		 * Fires after a single term is deleted via the REST API.
540
		 *
541
		 * @param WP_Term          $term     The deleted term.
542
		 * @param WP_REST_Response $response The response data.
543
		 * @param WP_REST_Request  $request  The request sent to the API.
544
		 */
545
		do_action( "woocommerce_rest_delete_{$taxonomy}", $term, $response, $request );
546
547
		return $response;
548
	}
549
550
	/**
551
	 * Prepare links for the request.
552
	 *
553
	 * @param object $term Term object.
554
	 * @param WP_REST_Request $request Full details about the request.
555
	 * @return array Links for the given term.
556
	 */
557
	protected function prepare_links( $term, $request ) {
558
		$base = '/' . $this->namespace . '/' . $this->rest_base;
559
560 View Code Duplication
		if ( ! empty( $request['attribute_id'] ) ) {
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...
561
			$base = str_replace( '(?P<attribute_id>[\d]+)', (int) $request['attribute_id'], $base );
562
		}
563
564
		$links = array(
565
			'self' => array(
566
				'href' => rest_url( trailingslashit( $base ) . $term->term_id ),
567
			),
568
			'collection' => array(
569
				'href' => rest_url( $base ),
570
			),
571
		);
572
573
		if ( $term->parent ) {
574
			$parent_term = get_term( (int) $term->parent, $term->taxonomy );
575
			if ( $parent_term ) {
576
				$links['up'] = array(
577
					'href' => rest_url( trailingslashit( $base ) . $parent_term->term_id ),
578
				);
579
			}
580
		}
581
582
		return $links;
583
	}
584
585
	/**
586
	 * Update term meta fields.
587
	 *
588
	 * @param WP_Term $term
589
	 * @param WP_REST_Request $request
590
	 * @return bool|WP_Error
591
	 */
592
	protected function update_term_meta_fields( $term, $request ) {
593
		return true;
594
	}
595
596
	/**
597
	 * Get the terms attached to a product.
598
	 *
599
	 * This is an alternative to `get_terms()` that uses `get_the_terms()`
600
	 * instead, which hits the object cache. There are a few things not
601
	 * supported, notably `include`, `exclude`. In `self::get_items()` these
602
	 * are instead treated as a full query.
603
	 *
604
	 * @param array $prepared_args Arguments for `get_terms()`.
605
	 * @return array List of term objects. (Total count in `$this->total_terms`).
606
	 */
607
	protected function get_terms_for_product( $prepared_args ) {
608
		$taxonomy = $this->get_taxonomy( $request );
609
610
		$query_result = get_the_terms( $prepared_args['product'], $taxonomy );
611
		if ( empty( $query_result ) ) {
612
			$this->total_terms = 0;
613
			return array();
614
		}
615
616
		// get_items() verifies that we don't have `include` set, and default.
617
		// ordering is by `name`.
618
		if ( ! in_array( $prepared_args['orderby'], array( 'name', 'none', 'include' ) ) ) {
619
			switch ( $prepared_args['orderby'] ) {
620
				case 'id' :
621
					$this->sort_column = 'term_id';
622
					break;
623
624
				case 'slug' :
625
				case 'term_group' :
626
				case 'description' :
627
				case 'count' :
628
					$this->sort_column = $prepared_args['orderby'];
629
					break;
630
			}
631
			usort( $query_result, array( $this, 'compare_terms' ) );
632
		}
633
		if ( strtolower( $prepared_args['order'] ) !== 'asc' ) {
634
			$query_result = array_reverse( $query_result );
635
		}
636
637
		// Pagination.
638
		$this->total_terms = count( $query_result );
639
		$query_result = array_slice( $query_result, $prepared_args['offset'], $prepared_args['number'] );
640
641
		return $query_result;
642
	}
643
644
	/**
645
	 * Comparison function for sorting terms by a column.
646
	 *
647
	 * Uses `$this->sort_column` to determine field to sort by.
648
	 *
649
	 * @param stdClass $left Term object.
650
	 * @param stdClass $right Term object.
651
	 * @return int <0 if left is higher "priority" than right, 0 if equal, >0 if right is higher "priority" than left.
652
	 */
653
	protected function compare_terms( $left, $right ) {
654
		$col       = $this->sort_column;
655
		$left_val  = $left->$col;
656
		$right_val = $right->$col;
657
658
		if ( is_int( $left_val ) && is_int( $right_val ) ) {
659
			return $left_val - $right_val;
660
		}
661
662
		return strcmp( $left_val, $right_val );
663
	}
664
665
	/**
666
	 * Get the query params for collections
667
	 *
668
	 * @return array
669
	 */
670
	public function get_collection_params() {
671
		$params = parent::get_collection_params();
672
673
		if ( '' !== $this->taxonomy ) {
674
			$taxonomy = get_taxonomy( $this->taxonomy );
675
		} else {
676
			$taxonomy = new stdClass();
677
			$taxonomy->hierarchical = true;
678
		}
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 View Code Duplication
		if ( ! $taxonomy->hierarchical ) {
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...
695
			$params['offset'] = array(
696
				'description'        => __( 'Offset the result set by a specific number of items.', 'woocommerce' ),
697
				'type'               => 'integer',
698
				'sanitize_callback'  => 'absint',
699
				'validate_callback'  => 'rest_validate_request_arg',
700
			);
701
		}
702
		$params['order']      = array(
703
			'description'           => __( 'Order sort attribute ascending or descending.', 'woocommerce' ),
704
			'type'                  => 'string',
705
			'sanitize_callback'     => 'sanitize_key',
706
			'default'               => 'asc',
707
			'enum'                  => array(
708
				'asc',
709
				'desc',
710
			),
711
			'validate_callback'     => 'rest_validate_request_arg',
712
		);
713
		$params['orderby']    = array(
714
			'description'           => __( 'Sort collection by resource attribute.', 'woocommerce' ),
715
			'type'                  => 'string',
716
			'sanitize_callback'     => 'sanitize_key',
717
			'default'               => 'name',
718
			'enum'                  => array(
719
				'id',
720
				'include',
721
				'name',
722
				'slug',
723
				'term_group',
724
				'description',
725
				'count',
726
			),
727
			'validate_callback'     => 'rest_validate_request_arg',
728
		);
729
		$params['hide_empty'] = array(
730
			'description'           => __( 'Whether to hide resources not assigned to any products.', 'woocommerce' ),
731
			'type'                  => 'boolean',
732
			'default'               => false,
733
			'validate_callback'     => 'rest_validate_request_arg',
734
		);
735 View Code Duplication
		if ( $taxonomy->hierarchical ) {
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...
736
			$params['parent'] = array(
737
				'description'        => __( 'Limit result set to resources assigned to a specific parent.', 'woocommerce' ),
738
				'type'               => 'integer',
739
				'sanitize_callback'  => 'absint',
740
				'validate_callback'  => 'rest_validate_request_arg',
741
			);
742
		}
743
		$params['product'] = array(
744
			'description'           => __( 'Limit result set to resources assigned to a specific product.', 'woocommerce' ),
745
			'type'                  => 'integer',
746
			'default'               => null,
747
			'validate_callback'     => 'rest_validate_request_arg',
748
		);
749
		$params['slug']    = array(
750
			'description'        => __( 'Limit result set to resources with a specific slug.', 'woocommerce' ),
751
			'type'               => 'string',
752
			'validate_callback'  => 'rest_validate_request_arg',
753
		);
754
755
		return $params;
756
	}
757
758
	/**
759
	 * Get taxonomy.
760
	 *
761
	 * @param WP_REST_Request $request Full details about the request.
762
	 * @return int|WP_Error
763
	 */
764
	protected function get_taxonomy( $request ) {
765
		// Check if taxonomy is defined.
766
		// Prevents check for attribute taxonomy more than one time for each query.
767
		if ( '' !== $this->taxonomy ) {
768
			return $this->taxonomy;
769
		}
770
771
		if ( ! empty( $request['attribute_id'] ) ) {
772
			$taxonomy = wc_attribute_taxonomy_name_by_id( (int) $request['attribute_id'] );
773
774
			$this->taxonomy = $taxonomy;
775
		}
776
777
		return $this->taxonomy;
778
	}
779
}
780