PluginProbe
WooCommerce / 11.1.0
WooCommerce v11.1.0
11.1.0 11.1.0-rc.2 11.1.0-rc.1 11.1.0-beta.2 11.1.0-beta.1 11.0.1 11.0.0 11.0.0-rc.3 11.0.0-rc.2 11.0.0-rc.1 11.0.0-beta.2 11.0.0-beta.1 10.9.4 10.9.3 10.9.2 10.9.1 10.9.0 10.9.0-rc.1 10.9.0-beta.2 10.9.0-beta.1 10.8.1 10.8.0 10.8.0-rc.1 10.8.0-beta.2 10.8.0-beta.1 All 648 releases
woocommerce / includes / rest-api / Controllers / Version3 / class-wc-rest-terms-controller.php
class-wc-rest-terms-controller.php
824 lines 26.0 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Abstract Rest Terms Controller
4 *
5 * @package WooCommerce\RestApi
6 * @version 3.3.0
7 */
8
9 if ( ! defined( 'ABSPATH' ) ) {
10 exit;
11 }
12
13 use Automattic\WooCommerce\Internal\AssignDefaultCategory;
14
15 /**
16 * Terms controller class.
17 */
18 abstract class WC_REST_Terms_Controller extends WC_REST_Controller {
19
20 /**
21 * Route base.
22 *
23 * @var string
24 */
25 protected $rest_base = '';
26
27 /**
28 * Taxonomy.
29 *
30 * @var string
31 */
32 protected $taxonomy = '';
33
34 /**
35 * Cached taxonomies by attribute id.
36 *
37 * @var array
38 */
39 protected $taxonomies_by_id = array();
40
41 /**
42 * Register the routes for terms.
43 */
44 public function register_routes() {
45 register_rest_route(
46 $this->namespace,
47 '/' . $this->rest_base,
48 array(
49 array(
50 'methods' => WP_REST_Server::READABLE,
51 'callback' => array( $this, 'get_items' ),
52 'permission_callback' => array( $this, 'get_items_permissions_check' ),
53 'args' => $this->get_collection_params(),
54 ),
55 array(
56 'methods' => WP_REST_Server::CREATABLE,
57 'callback' => array( $this, 'create_item' ),
58 'permission_callback' => array( $this, 'create_item_permissions_check' ),
59 'args' => array_merge(
60 $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),
61 array(
62 'name' => array(
63 'type' => 'string',
64 'description' => __( 'Name for the resource.', 'woocommerce' ),
65 'required' => true,
66 ),
67 )
68 ),
69 ),
70 'schema' => array( $this, 'get_public_item_schema' ),
71 )
72 );
73
74 register_rest_route(
75 $this->namespace,
76 '/' . $this->rest_base . '/(?P<id>[\d]+)',
77 array(
78 'args' => array(
79 'id' => array(
80 'description' => __( 'Unique identifier for the resource.', 'woocommerce' ),
81 'type' => 'integer',
82 ),
83 ),
84 array(
85 'methods' => WP_REST_Server::READABLE,
86 'callback' => array( $this, 'get_item' ),
87 'permission_callback' => array( $this, 'get_item_permissions_check' ),
88 'args' => array(
89 'context' => $this->get_context_param( array( 'default' => 'view' ) ),
90 ),
91 ),
92 array(
93 'methods' => WP_REST_Server::EDITABLE,
94 'callback' => array( $this, 'update_item' ),
95 'permission_callback' => array( $this, 'update_item_permissions_check' ),
96 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
97 ),
98 array(
99 'methods' => WP_REST_Server::DELETABLE,
100 'callback' => array( $this, 'delete_item' ),
101 'permission_callback' => array( $this, 'delete_item_permissions_check' ),
102 'args' => array(
103 'force' => array(
104 'default' => false,
105 'type' => 'boolean',
106 'description' => __( 'Required to be true, as resource does not support trashing.', 'woocommerce' ),
107 ),
108 ),
109 ),
110 'schema' => array( $this, 'get_public_item_schema' ),
111 )
112 );
113
114 register_rest_route(
115 $this->namespace,
116 '/' . $this->rest_base . '/batch',
117 array(
118 array(
119 'methods' => WP_REST_Server::EDITABLE,
120 'callback' => array( $this, 'batch_items' ),
121 'permission_callback' => array( $this, 'batch_items_permissions_check' ),
122 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),
123 ),
124 'schema' => array( $this, 'get_public_batch_schema' ),
125 )
126 );
127 }
128
129 /**
130 * Check if a given request has access to read the terms.
131 *
132 * @param WP_REST_Request $request Full details about the request.
133 * @return WP_Error|boolean
134 */
135 public function get_items_permissions_check( $request ) {
136 $permissions = $this->check_permissions( $request, 'read' );
137 if ( is_wp_error( $permissions ) ) {
138 return $permissions;
139 }
140
141 if ( ! $permissions ) {
142 return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot list resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
143 }
144
145 return true;
146 }
147
148 /**
149 * Check if a given request has access to create a term.
150 *
151 * @param WP_REST_Request $request Full details about the request.
152 * @return WP_Error|boolean
153 */
154 public function create_item_permissions_check( $request ) {
155 $permissions = $this->check_permissions( $request, 'create' );
156 if ( is_wp_error( $permissions ) ) {
157 return $permissions;
158 }
159
160 if ( ! $permissions ) {
161 return new WP_Error( 'woocommerce_rest_cannot_create', __( 'Sorry, you are not allowed to create resources.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
162 }
163
164 return true;
165 }
166
167 /**
168 * Check if a given request has access to read a term.
169 *
170 * @param WP_REST_Request $request Full details about the request.
171 * @return WP_Error|boolean
172 */
173 public function get_item_permissions_check( $request ) {
174 $permissions = $this->check_permissions( $request, 'read' );
175 if ( is_wp_error( $permissions ) ) {
176 return $permissions;
177 }
178
179 if ( ! $permissions ) {
180 return new WP_Error( 'woocommerce_rest_cannot_view', __( 'Sorry, you cannot view this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
181 }
182
183 return true;
184 }
185
186 /**
187 * Check if a given request has access to update a term.
188 *
189 * @param WP_REST_Request $request Full details about the request.
190 * @return WP_Error|boolean
191 */
192 public function update_item_permissions_check( $request ) {
193 $permissions = $this->check_permissions( $request, 'edit' );
194 if ( is_wp_error( $permissions ) ) {
195 return $permissions;
196 }
197
198 if ( ! $permissions ) {
199 return new WP_Error( 'woocommerce_rest_cannot_edit', __( 'Sorry, you are not allowed to edit this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
200 }
201
202 return true;
203 }
204
205 /**
206 * Check if a given request has access to delete a term.
207 *
208 * @param WP_REST_Request $request Full details about the request.
209 * @return WP_Error|boolean
210 */
211 public function delete_item_permissions_check( $request ) {
212 $permissions = $this->check_permissions( $request, 'delete' );
213 if ( is_wp_error( $permissions ) ) {
214 return $permissions;
215 }
216
217 if ( ! $permissions ) {
218 return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'Sorry, you are not allowed to delete this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
219 }
220
221 return true;
222 }
223
224 /**
225 * Check if a given request has access batch create, update and delete items.
226 *
227 * @param WP_REST_Request $request Full details about the request.
228 * @return boolean|WP_Error
229 */
230 public function batch_items_permissions_check( $request ) {
231 $permissions = $this->check_permissions( $request, 'batch' );
232 if ( is_wp_error( $permissions ) ) {
233 return $permissions;
234 }
235
236 if ( ! $permissions ) {
237 return new WP_Error( 'woocommerce_rest_cannot_batch', __( 'Sorry, you are not allowed to batch manipulate this resource.', 'woocommerce' ), array( 'status' => rest_authorization_required_code() ) );
238 }
239
240 return true;
241 }
242
243 /**
244 * Check permissions.
245 *
246 * @param WP_REST_Request $request Full details about the request.
247 * @param string $context Request context.
248 * @return bool|WP_Error
249 */
250 protected function check_permissions( $request, $context = 'read' ) {
251 // Get taxonomy.
252 $taxonomy = $this->get_taxonomy( $request );
253 if ( ! $taxonomy || ! taxonomy_exists( $taxonomy ) ) {
254 return new WP_Error( 'woocommerce_rest_taxonomy_invalid', __( 'Taxonomy does not exist.', 'woocommerce' ), array( 'status' => 404 ) );
255 }
256
257 // Check permissions for a single term.
258 $id = intval( $request['id'] );
259 if ( $id ) {
260 $term = get_term( $id, $taxonomy );
261
262 if ( is_wp_error( $term ) || ! $term || $term->taxonomy !== $taxonomy ) {
263 return new WP_Error( 'woocommerce_rest_term_invalid', __( 'Resource does not exist.', 'woocommerce' ), array( 'status' => 404 ) );
264 }
265
266 return wc_rest_check_product_term_permissions( $taxonomy, $context, $term->term_id );
267 }
268
269 return wc_rest_check_product_term_permissions( $taxonomy, $context );
270 }
271
272 /**
273 * Get terms associated with a taxonomy.
274 *
275 * @param WP_REST_Request $request Full details about the request.
276 * @return WP_REST_Response|WP_Error
277 */
278 public function get_items( $request ) {
279 $taxonomy = $this->get_taxonomy( $request );
280 $prepared_args = array(
281 'exclude' => $request['exclude'],
282 'include' => $request['include'],
283 'order' => $request['order'],
284 'orderby' => $request['orderby'],
285 'product' => $request['product'],
286 'hide_empty' => $request['hide_empty'],
287 'number' => $request['per_page'],
288 'search' => $request['search'],
289 'slug' => $request['slug'],
290 );
291
292 if ( ! empty( $request['offset'] ) ) {
293 $prepared_args['offset'] = $request['offset'];
294 } else {
295 $prepared_args['offset'] = ( $request['page'] - 1 ) * $prepared_args['number'];
296 }
297
298 $taxonomy_obj = get_taxonomy( $taxonomy );
299
300 if ( $taxonomy_obj->hierarchical && isset( $request['parent'] ) ) {
301 if ( 0 === $request['parent'] ) {
302 // Only query top-level terms.
303 $prepared_args['parent'] = 0;
304 } else {
305 if ( $request['parent'] ) {
306 $prepared_args['parent'] = $request['parent'];
307 }
308 }
309 }
310
311 /**
312 * Filter the query arguments, before passing them to `get_terms()`.
313 *
314 * Enables adding extra arguments or setting defaults for a terms
315 * collection request.
316 *
317 * @see https://developer.wordpress.org/reference/functions/get_terms/
318 *
319 * @param array $prepared_args Array of arguments to be
320 * passed to get_terms.
321 * @param WP_REST_Request $request The current request.
322 */
323 $prepared_args = apply_filters( "woocommerce_rest_{$taxonomy}_query", $prepared_args, $request );
324
325 if ( ! empty( $prepared_args['product'] ) ) {
326 $query_result = $this->get_terms_for_product( $prepared_args, $request );
327 $total_terms = $this->total_terms;
328 } else {
329 $query_result = get_terms( $taxonomy, $prepared_args );
330
331 $count_args = $prepared_args;
332 unset( $count_args['number'] );
333 unset( $count_args['offset'] );
334 $total_terms = wp_count_terms( $taxonomy, $count_args );
335
336 // Ensure we don't return results when offset is out of bounds.
337 // See https://core.trac.wordpress.org/ticket/35935.
338 if ( $prepared_args['offset'] && $prepared_args['offset'] >= $total_terms ) {
339 $query_result = array();
340 }
341
342 // wp_count_terms can return a falsy value when the term has no children.
343 if ( ! $total_terms ) {
344 $total_terms = 0;
345 }
346 }
347 $response = array();
348 if ( is_array( $query_result ) ) {
349 $terms = array_filter( $query_result, static fn( $term ) => $term instanceof \WP_Term );
350 $attachment_ids = array_filter( array_map( static fn( $term ) => (int) get_term_meta( $term->term_id, 'thumbnail_id', true ), $terms ) );
351 if ( ! empty( $attachment_ids ) ) {
352 // Prime caches to reduce future queries.
353 _prime_post_caches( $attachment_ids );
354 }
355
356 foreach ( $query_result as $term ) {
357 $data = $this->prepare_item_for_response( $term, $request );
358 $response[] = $this->prepare_response_for_collection( $data );
359 }
360 }
361
362 $response = rest_ensure_response( $response );
363
364 // Store pagination values for headers then unset for count query.
365 $per_page = (int) $prepared_args['number'];
366 $page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 );
367
368 $response->header( 'X-WP-Total', (int) $total_terms );
369 $max_pages = ceil( $total_terms / $per_page );
370 $response->header( 'X-WP-TotalPages', (int) $max_pages );
371
372 $base = str_replace( '(?P<attribute_id>[\d]+)', $request['attribute_id'] ?? '', $this->rest_base );
373 $base = add_query_arg( $request->get_query_params(), rest_url( '/' . $this->namespace . '/' . $base ) );
374 if ( $page > 1 ) {
375 $prev_page = $page - 1;
376 if ( $prev_page > $max_pages ) {
377 $prev_page = $max_pages;
378 }
379 $prev_link = add_query_arg( 'page', $prev_page, $base );
380 $response->link_header( 'prev', $prev_link );
381 }
382 if ( $max_pages > $page ) {
383 $next_page = $page + 1;
384 $next_link = add_query_arg( 'page', $next_page, $base );
385 $response->link_header( 'next', $next_link );
386 }
387
388 return $response;
389 }
390
391 /**
392 * Create a single term for a taxonomy.
393 *
394 * @param WP_REST_Request $request Full details about the request.
395 * @return WP_REST_Request|WP_Error
396 */
397 public function create_item( $request ) {
398 $taxonomy = $this->get_taxonomy( $request );
399 $name = $request['name'];
400 $args = array();
401 $schema = $this->get_item_schema();
402
403 if ( ! empty( $schema['properties']['description'] ) && isset( $request['description'] ) ) {
404 $args['description'] = $request['description'];
405 }
406 if ( isset( $request['slug'] ) ) {
407 $args['slug'] = $request['slug'];
408 }
409 if ( isset( $request['parent'] ) ) {
410 if ( ! is_taxonomy_hierarchical( $taxonomy ) ) {
411 return new WP_Error( 'woocommerce_rest_taxonomy_not_hierarchical', __( 'Can not set resource parent, taxonomy is not hierarchical.', 'woocommerce' ), array( 'status' => 400 ) );
412 }
413 $args['parent'] = $request['parent'];
414 }
415
416 $term = wp_insert_term( $name, $taxonomy, $args );
417 if ( is_wp_error( $term ) ) {
418 $error_data = array( 'status' => 400 );
419
420 // If we're going to inform the client that the term exists,
421 // give them the identifier they can actually use.
422 $term_id = $term->get_error_data( 'term_exists' );
423 if ( $term_id ) {
424 $error_data['resource_id'] = $term_id;
425 }
426
427 return new WP_Error( $term->get_error_code(), $term->get_error_message(), $error_data );
428 }
429
430 $term = get_term( $term['term_id'], $taxonomy );
431
432 $this->update_additional_fields_for_object( $term, $request );
433
434 // Add term data.
435 $meta_fields = $this->update_term_meta_fields( $term, $request );
436 if ( is_wp_error( $meta_fields ) ) {
437 wp_delete_term( $term->term_id, $taxonomy );
438
439 return $meta_fields;
440 }
441
442 /**
443 * Fires after a single term is created or updated via the REST API.
444 *
445 * @param WP_Term $term Inserted Term object.
446 * @param WP_REST_Request $request Request object.
447 * @param boolean $creating True when creating term, false when updating.
448 */
449 do_action( "woocommerce_rest_insert_{$taxonomy}", $term, $request, true );
450
451 $request->set_param( 'context', 'edit' );
452 $response = $this->prepare_item_for_response( $term, $request );
453 $response = rest_ensure_response( $response );
454 $response->set_status( 201 );
455
456 $base = '/' . $this->namespace . '/' . $this->rest_base;
457 if ( ! empty( $request['attribute_id'] ) ) {
458 $base = str_replace( '(?P<attribute_id>[\d]+)', (int) $request['attribute_id'], $base );
459 }
460
461 $response->header( 'Location', rest_url( $base . '/' . $term->term_id ) );
462
463 return $response;
464 }
465
466 /**
467 * Get a single term from a taxonomy.
468 *
469 * @param WP_REST_Request $request Full details about the request.
470 * @return WP_REST_Request|WP_Error
471 */
472 public function get_item( $request ) {
473 $taxonomy = $this->get_taxonomy( $request );
474 $term = get_term( (int) $request['id'], $taxonomy );
475
476 if ( is_wp_error( $term ) ) {
477 return $term;
478 }
479
480 $response = $this->prepare_item_for_response( $term, $request );
481
482 return rest_ensure_response( $response );
483 }
484
485 /**
486 * Update a single term from a taxonomy.
487 *
488 * @param WP_REST_Request $request Full details about the request.
489 * @return WP_REST_Request|WP_Error
490 */
491 public function update_item( $request ) {
492 $taxonomy = $this->get_taxonomy( $request );
493 $term = get_term( (int) $request['id'], $taxonomy );
494 $schema = $this->get_item_schema();
495 $prepared_args = array();
496
497 if ( isset( $request['name'] ) ) {
498 $prepared_args['name'] = $request['name'];
499 }
500 if ( ! empty( $schema['properties']['description'] ) && isset( $request['description'] ) ) {
501 $prepared_args['description'] = $request['description'];
502 }
503 if ( isset( $request['slug'] ) ) {
504 $prepared_args['slug'] = $request['slug'];
505 }
506 if ( isset( $request['parent'] ) ) {
507 if ( ! is_taxonomy_hierarchical( $taxonomy ) ) {
508 return new WP_Error( 'woocommerce_rest_taxonomy_not_hierarchical', __( 'Can not set resource parent, taxonomy is not hierarchical.', 'woocommerce' ), array( 'status' => 400 ) );
509 }
510 $prepared_args['parent'] = $request['parent'];
511 }
512
513 // Only update the term if we haz something to update.
514 if ( ! empty( $prepared_args ) ) {
515 $update = wp_update_term( $term->term_id, $term->taxonomy, $prepared_args );
516 if ( is_wp_error( $update ) ) {
517 return $update;
518 }
519 }
520
521 $term = get_term( (int) $request['id'], $taxonomy );
522
523 $this->update_additional_fields_for_object( $term, $request );
524
525 // Update term data.
526 $meta_fields = $this->update_term_meta_fields( $term, $request );
527 if ( is_wp_error( $meta_fields ) ) {
528 return $meta_fields;
529 }
530
531 /**
532 * Fires after a single term is created or updated via the REST API.
533 *
534 * @param WP_Term $term Inserted Term object.
535 * @param WP_REST_Request $request Request object.
536 * @param boolean $creating True when creating term, false when updating.
537 */
538 do_action( "woocommerce_rest_insert_{$taxonomy}", $term, $request, false );
539
540 $request->set_param( 'context', 'edit' );
541 $response = $this->prepare_item_for_response( $term, $request );
542 return rest_ensure_response( $response );
543 }
544
545 /**
546 * Delete a single term from a taxonomy.
547 *
548 * @param WP_REST_Request $request Full details about the request.
549 * @return WP_REST_Response|WP_Error
550 */
551 public function delete_item( $request ) {
552 $taxonomy = $this->get_taxonomy( $request );
553 $force = isset( $request['force'] ) ? (bool) $request['force'] : false;
554
555 // We don't support trashing for this type, error out.
556 if ( ! $force ) {
557 return new WP_Error( 'woocommerce_rest_trash_not_supported', __( 'Resource does not support trashing.', 'woocommerce' ), array( 'status' => 501 ) );
558 }
559
560 $term = get_term( (int) $request['id'], $taxonomy );
561 // Get default category id.
562 $default_category_id = absint( get_option( 'default_product_cat', 0 ) );
563
564 // Prevent deleting the default product category.
565 if ( $default_category_id === (int) $request['id'] ) {
566 return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'Default product category cannot be deleted.', 'woocommerce' ), array( 'status' => 500 ) );
567 }
568
569 $request->set_param( 'context', 'edit' );
570 $response = $this->prepare_item_for_response( $term, $request );
571
572 $retval = wp_delete_term( $term->term_id, $term->taxonomy );
573 if ( ! $retval ) {
574 return new WP_Error( 'woocommerce_rest_cannot_delete', __( 'The resource cannot be deleted.', 'woocommerce' ), array( 'status' => 500 ) );
575 }
576
577 // Schedule action to assign default category.
578 wc_get_container()->get( AssignDefaultCategory::class )->schedule_action();
579
580 /**
581 * Fires after a single term is deleted via the REST API.
582 *
583 * @param WP_Term $term The deleted term.
584 * @param WP_REST_Response $response The response data.
585 * @param WP_REST_Request $request The request sent to the API.
586 */
587 do_action( "woocommerce_rest_delete_{$taxonomy}", $term, $response, $request );
588
589 return $response;
590 }
591
592 /**
593 * Prepare links for the request.
594 *
595 * @param object $term Term object.
596 * @param WP_REST_Request $request Full details about the request.
597 * @return array Links for the given term.
598 */
599 protected function prepare_links( $term, $request ) {
600 $base = '/' . $this->namespace . '/' . $this->rest_base;
601
602 if ( ! empty( $request['attribute_id'] ) ) {
603 $base = str_replace( '(?P<attribute_id>[\d]+)', (int) $request['attribute_id'], $base );
604 }
605
606 $links = array(
607 'self' => array(
608 'href' => rest_url( trailingslashit( $base ) . $term->term_id ),
609 ),
610 'collection' => array(
611 'href' => rest_url( $base ),
612 ),
613 );
614
615 if ( $term->parent ) {
616 $parent_term = get_term( (int) $term->parent, $term->taxonomy );
617 if ( $parent_term ) {
618 $links['up'] = array(
619 'href' => rest_url( trailingslashit( $base ) . $parent_term->term_id ),
620 );
621 }
622 }
623
624 return $links;
625 }
626
627 /**
628 * Update term meta fields.
629 *
630 * @param WP_Term $term Term object.
631 * @param WP_REST_Request $request Full details about the request.
632 * @return bool|WP_Error
633 */
634 protected function update_term_meta_fields( $term, $request ) {
635 return true;
636 }
637
638 /**
639 * Get the terms attached to a product.
640 *
641 * This is an alternative to `get_terms()` that uses `get_the_terms()`
642 * instead, which hits the object cache. There are a few things not
643 * supported, notably `include`, `exclude`. In `self::get_items()` these
644 * are instead treated as a full query.
645 *
646 * @param array $prepared_args Arguments for `get_terms()`.
647 * @param WP_REST_Request $request Full details about the request.
648 * @return array List of term objects. (Total count in `$this->total_terms`).
649 */
650 protected function get_terms_for_product( $prepared_args, $request ) {
651 $taxonomy = $this->get_taxonomy( $request );
652
653 $query_result = get_the_terms( $prepared_args['product'], $taxonomy );
654 if ( empty( $query_result ) ) {
655 $this->total_terms = 0;
656 return array();
657 }
658
659 // get_items() verifies that we don't have `include` set, and default.
660 // ordering is by `name`.
661 if ( ! in_array( $prepared_args['orderby'], array( 'name', 'none', 'include' ), true ) ) {
662 switch ( $prepared_args['orderby'] ) {
663 case 'id':
664 $this->sort_column = 'term_id';
665 break;
666 case 'slug':
667 case 'term_group':
668 case 'description':
669 case 'count':
670 $this->sort_column = $prepared_args['orderby'];
671 break;
672 }
673 usort( $query_result, array( $this, 'compare_terms' ) );
674 }
675 if ( strtolower( $prepared_args['order'] ) !== 'asc' ) {
676 $query_result = array_reverse( $query_result );
677 }
678
679 // Pagination.
680 $this->total_terms = count( $query_result );
681 $query_result = array_slice( $query_result, $prepared_args['offset'], $prepared_args['number'] );
682
683 return $query_result;
684 }
685
686 /**
687 * Comparison function for sorting terms by a column.
688 *
689 * Uses `$this->sort_column` to determine field to sort by.
690 *
691 * @param stdClass $left Term object.
692 * @param stdClass $right Term object.
693 * @return int <0 if left is higher "priority" than right, 0 if equal, >0 if right is higher "priority" than left.
694 */
695 protected function compare_terms( $left, $right ) {
696 $col = $this->sort_column;
697 $left_val = $left->$col;
698 $right_val = $right->$col;
699
700 if ( is_int( $left_val ) && is_int( $right_val ) ) {
701 return $left_val - $right_val;
702 }
703
704 return strcmp( $left_val, $right_val );
705 }
706
707 /**
708 * Get the query params for collections
709 *
710 * @return array
711 */
712 public function get_collection_params() {
713 $params = parent::get_collection_params();
714
715 $params['context']['default'] = 'view';
716
717 $params['exclude'] = array(
718 'description' => __( 'Ensure result set excludes specific IDs.', 'woocommerce' ),
719 'type' => 'array',
720 'items' => array(
721 'type' => 'integer',
722 ),
723 'default' => array(),
724 'sanitize_callback' => 'wp_parse_id_list',
725 );
726 $params['include'] = array(
727 'description' => __( 'Limit result set to specific ids.', 'woocommerce' ),
728 'type' => 'array',
729 'items' => array(
730 'type' => 'integer',
731 ),
732 'default' => array(),
733 'sanitize_callback' => 'wp_parse_id_list',
734 );
735 $params['offset'] = array(
736 'description' => __( 'Offset the result set by a specific number of items. Applies to hierarchical taxonomies only.', 'woocommerce' ),
737 'type' => 'integer',
738 'sanitize_callback' => 'absint',
739 'validate_callback' => 'rest_validate_request_arg',
740 );
741 $params['order'] = array(
742 'description' => __( 'Order sort attribute ascending or descending.', 'woocommerce' ),
743 'type' => 'string',
744 'sanitize_callback' => 'sanitize_key',
745 'default' => 'asc',
746 'enum' => array(
747 'asc',
748 'desc',
749 ),
750 'validate_callback' => 'rest_validate_request_arg',
751 );
752 $params['orderby'] = array(
753 'description' => __( 'Sort collection by resource attribute.', 'woocommerce' ),
754 'type' => 'string',
755 'sanitize_callback' => 'sanitize_key',
756 'default' => 'name',
757 'enum' => array(
758 'id',
759 'include',
760 'name',
761 'slug',
762 'term_group',
763 'description',
764 'count',
765 ),
766 'validate_callback' => 'rest_validate_request_arg',
767 );
768 $params['hide_empty'] = array(
769 'description' => __( 'Whether to hide resources not assigned to any products.', 'woocommerce' ),
770 'type' => 'boolean',
771 'default' => false,
772 'validate_callback' => 'rest_validate_request_arg',
773 );
774 $params['parent'] = array(
775 'description' => __( 'Limit result set to resources assigned to a specific parent. Applies to hierarchical taxonomies only.', 'woocommerce' ),
776 'type' => 'integer',
777 'sanitize_callback' => 'absint',
778 'validate_callback' => 'rest_validate_request_arg',
779 );
780 $params['product'] = array(
781 'description' => __( 'Limit result set to resources assigned to a specific product.', 'woocommerce' ),
782 'type' => 'integer',
783 'default' => null,
784 'validate_callback' => 'rest_validate_request_arg',
785 );
786 $params['slug'] = array(
787 'description' => __( 'Limit result set to resources with a specific slug.', 'woocommerce' ),
788 'type' => array( 'string', 'array' ),
789 'items' => array(
790 'type' => 'string',
791 ),
792 'validate_callback' => 'rest_validate_request_arg',
793 );
794
795 return $params;
796 }
797
798 /**
799 * Get taxonomy.
800 *
801 * @param WP_REST_Request $request Full details about the request.
802 * @return int|WP_Error
803 */
804 protected function get_taxonomy( $request ) {
805 $attribute_id = $request['attribute_id'];
806
807 if ( empty( $attribute_id ) ) {
808 return $this->taxonomy;
809 }
810
811 if ( isset( $this->taxonomies_by_id[ $attribute_id ] ) ) {
812 return $this->taxonomies_by_id[ $attribute_id ];
813 }
814
815 $taxonomy = WC()->call_function( 'wc_attribute_taxonomy_name_by_id', (int) $request['attribute_id'] );
816 if ( ! empty( $taxonomy ) ) {
817 $this->taxonomy = $taxonomy;
818 $this->taxonomies_by_id[ $attribute_id ] = $taxonomy;
819 }
820
821 return $taxonomy;
822 }
823 }
824