WP_REST_Terms_Controller

Core class used to managed terms associated with a taxonomy via the REST API.

Defined (1)

The class is defined in the following location(s).

/wp-includes/rest-api/endpoints/class-wp-rest-terms-controller.php  
  1. class WP_REST_Terms_Controller extends WP_REST_Controller { 
  2.  
  3. /** 
  4. * Taxonomy key. 
  5. * @since 4.7.0 
  6. * @access protected 
  7. * @var string 
  8. */ 
  9. protected $taxonomy; 
  10.  
  11. /** 
  12. * Instance of a term meta fields object. 
  13. * @since 4.7.0 
  14. * @access protected 
  15. * @var WP_REST_Term_Meta_Fields 
  16. */ 
  17. protected $meta; 
  18.  
  19. /** 
  20. * Column to have the terms be sorted by. 
  21. * @since 4.7.0 
  22. * @access protected 
  23. * @var string 
  24. */ 
  25. protected $sort_column; 
  26.  
  27. /** 
  28. * Number of terms that were found. 
  29. * @since 4.7.0 
  30. * @access protected 
  31. * @var int 
  32. */ 
  33. protected $total_terms; 
  34.  
  35. /** 
  36. * Constructor. 
  37. * @since 4.7.0 
  38. * @access public 
  39. * @param string $taxonomy Taxonomy key. 
  40. */ 
  41. public function __construct( $taxonomy ) { 
  42. $this->taxonomy = $taxonomy; 
  43. $this->namespace = 'wp/v2'; 
  44. $tax_obj = get_taxonomy( $taxonomy ); 
  45. $this->rest_base = ! empty( $tax_obj->rest_base ) ? $tax_obj->rest_base : $tax_obj->name; 
  46.  
  47. $this->meta = new WP_REST_Term_Meta_Fields( $taxonomy ); 
  48.  
  49. /** 
  50. * Registers the routes for the objects of the controller. 
  51. * @since 4.7.0 
  52. * @access public 
  53. * @see register_rest_route() 
  54. */ 
  55. public function register_routes() { 
  56.  
  57. register_rest_route( $this->namespace, '/' . $this->rest_base, array( 
  58. array( 
  59. 'methods' => WP_REST_Server::READABLE,  
  60. 'callback' => array( $this, 'get_items' ),  
  61. 'permission_callback' => array( $this, 'get_items_permissions_check' ),  
  62. 'args' => $this->get_collection_params(),  
  63. ),  
  64. array( 
  65. 'methods' => WP_REST_Server::CREATABLE,  
  66. 'callback' => array( $this, 'create_item' ),  
  67. 'permission_callback' => array( $this, 'create_item_permissions_check' ),  
  68. 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::CREATABLE ),  
  69. ),  
  70. 'schema' => array( $this, 'get_public_item_schema' ),  
  71. ) ); 
  72.  
  73. register_rest_route( $this->namespace, '/' . $this->rest_base . '/(?P<id>[\d]+)', array( 
  74. 'args' => array( 
  75. 'id' => array( 
  76. 'description' => __( 'Unique identifier for the term.' ),  
  77. 'type' => 'integer',  
  78. ),  
  79. ),  
  80. array( 
  81. 'methods' => WP_REST_Server::READABLE,  
  82. 'callback' => array( $this, 'get_item' ),  
  83. 'permission_callback' => array( $this, 'get_item_permissions_check' ),  
  84. 'args' => array( 
  85. 'context' => $this->get_context_param( array( 'default' => 'view' ) ),  
  86. ),  
  87. ),  
  88. array( 
  89. 'methods' => WP_REST_Server::EDITABLE,  
  90. 'callback' => array( $this, 'update_item' ),  
  91. 'permission_callback' => array( $this, 'update_item_permissions_check' ),  
  92. 'args' => $this->get_endpoint_args_for_item_schema( WP_REST_Server::EDITABLE ),  
  93. ),  
  94. array( 
  95. 'methods' => WP_REST_Server::DELETABLE,  
  96. 'callback' => array( $this, 'delete_item' ),  
  97. 'permission_callback' => array( $this, 'delete_item_permissions_check' ),  
  98. 'args' => array( 
  99. 'force' => array( 
  100. 'type' => 'boolean',  
  101. 'default' => false,  
  102. 'description' => __( 'Required to be true, as terms do not support trashing.' ),  
  103. ),  
  104. ),  
  105. ),  
  106. 'schema' => array( $this, 'get_public_item_schema' ),  
  107. ) ); 
  108.  
  109. /** 
  110. * Checks if a request has access to read terms in the specified taxonomy. 
  111. * @since 4.7.0 
  112. * @access public 
  113. * @param WP_REST_Request $request Full details about the request. 
  114. * @return bool|WP_Error True if the request has read access, otherwise false or WP_Error object. 
  115. */ 
  116. public function get_items_permissions_check( $request ) { 
  117. $tax_obj = get_taxonomy( $this->taxonomy ); 
  118. if ( ! $tax_obj || ! $this->check_is_taxonomy_allowed( $this->taxonomy ) ) { 
  119. return false; 
  120. if ( 'edit' === $request['context'] && ! current_user_can( $tax_obj->cap->edit_terms ) ) { 
  121. return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit terms in this taxonomy.' ), array( 'status' => rest_authorization_required_code() ) ); 
  122. return true; 
  123.  
  124. /** 
  125. * Retrieves terms associated with a taxonomy. 
  126. * @since 4.7.0 
  127. * @access public 
  128. * @param WP_REST_Request $request Full details about the request. 
  129. * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. 
  130. */ 
  131. public function get_items( $request ) { 
  132.  
  133. // Retrieve the list of registered collection query parameters. 
  134. $registered = $this->get_collection_params(); 
  135.  
  136. /** 
  137. * This array defines mappings between public API query parameters whose 
  138. * values are accepted as-passed, and their internal WP_Query parameter 
  139. * name equivalents (some are the same). Only values which are also 
  140. * present in $registered will be set. 
  141. */ 
  142. $parameter_mappings = array( 
  143. 'exclude' => 'exclude',  
  144. 'include' => 'include',  
  145. 'order' => 'order',  
  146. 'orderby' => 'orderby',  
  147. 'post' => 'post',  
  148. 'hide_empty' => 'hide_empty',  
  149. 'per_page' => 'number',  
  150. 'search' => 'search',  
  151. 'slug' => 'slug',  
  152. ); 
  153.  
  154. $prepared_args = array(); 
  155.  
  156. /** 
  157. * For each known parameter which is both registered and present in the request,  
  158. * set the parameter's value on the query $prepared_args. 
  159. */ 
  160. foreach ( $parameter_mappings as $api_param => $wp_param ) { 
  161. if ( isset( $registered[ $api_param ], $request[ $api_param ] ) ) { 
  162. $prepared_args[ $wp_param ] = $request[ $api_param ]; 
  163.  
  164. if ( isset( $registered['offset'] ) && ! empty( $request['offset'] ) ) { 
  165. $prepared_args['offset'] = $request['offset']; 
  166. } else { 
  167. $prepared_args['offset'] = ( $request['page'] - 1 ) * $prepared_args['number']; 
  168.  
  169. $taxonomy_obj = get_taxonomy( $this->taxonomy ); 
  170.  
  171. if ( $taxonomy_obj->hierarchical && isset( $registered['parent'], $request['parent'] ) ) { 
  172. if ( 0 === $request['parent'] ) { 
  173. // Only query top-level terms. 
  174. $prepared_args['parent'] = 0; 
  175. } else { 
  176. if ( $request['parent'] ) { 
  177. $prepared_args['parent'] = $request['parent']; 
  178.  
  179. /** 
  180. * Filters the query arguments before passing them to get_terms(). 
  181. * The dynamic portion of the hook name, `$this->taxonomy`, refers to the taxonomy slug. 
  182. * Enables adding extra arguments or setting defaults for a terms 
  183. * collection request. 
  184. * @since 4.7.0 
  185. * @link https://developer.wordpress.org/reference/functions/get_terms/ 
  186. * @param array $prepared_args Array of arguments to be 
  187. * passed to get_terms(). 
  188. * @param WP_REST_Request $request The current request. 
  189. */ 
  190. $prepared_args = apply_filters( "rest_{$this->taxonomy}_query", $prepared_args, $request ); 
  191.  
  192. if ( ! empty( $prepared_args['post'] ) ) { 
  193. $query_result = wp_get_object_terms( $prepared_args['post'], $this->taxonomy, $prepared_args ); 
  194.  
  195. // Used when calling wp_count_terms() below. 
  196. $prepared_args['object_ids'] = $prepared_args['post']; 
  197. } else { 
  198. $query_result = get_terms( $this->taxonomy, $prepared_args ); 
  199.  
  200. $count_args = $prepared_args; 
  201.  
  202. unset( $count_args['number'], $count_args['offset'] ); 
  203.  
  204. $total_terms = wp_count_terms( $this->taxonomy, $count_args ); 
  205.  
  206. // wp_count_terms can return a falsy value when the term has no children. 
  207. if ( ! $total_terms ) { 
  208. $total_terms = 0; 
  209.  
  210. $response = array(); 
  211.  
  212. foreach ( $query_result as $term ) { 
  213. $data = $this->prepare_item_for_response( $term, $request ); 
  214. $response[] = $this->prepare_response_for_collection( $data ); 
  215.  
  216. $response = rest_ensure_response( $response ); 
  217.  
  218. // Store pagination values for headers. 
  219. $per_page = (int) $prepared_args['number']; 
  220. $page = ceil( ( ( (int) $prepared_args['offset'] ) / $per_page ) + 1 ); 
  221.  
  222. $response->header( 'X-WP-Total', (int) $total_terms ); 
  223.  
  224. $max_pages = ceil( $total_terms / $per_page ); 
  225.  
  226. $response->header( 'X-WP-TotalPages', (int) $max_pages ); 
  227.  
  228. $base = add_query_arg( $request->get_query_params(), rest_url( $this->namespace . '/' . $this->rest_base ) ); 
  229. if ( $page > 1 ) { 
  230. $prev_page = $page - 1; 
  231.  
  232. if ( $prev_page > $max_pages ) { 
  233. $prev_page = $max_pages; 
  234.  
  235. $prev_link = add_query_arg( 'page', $prev_page, $base ); 
  236. $response->link_header( 'prev', $prev_link ); 
  237. if ( $max_pages > $page ) { 
  238. $next_page = $page + 1; 
  239. $next_link = add_query_arg( 'page', $next_page, $base ); 
  240.  
  241. $response->link_header( 'next', $next_link ); 
  242.  
  243. return $response; 
  244.  
  245. /** 
  246. * Get the term, if the ID is valid. 
  247. * @since 4.7.2 
  248. * @param int $id Supplied ID. 
  249. * @return WP_Term|WP_Error Term object if ID is valid, WP_Error otherwise. 
  250. */ 
  251. protected function get_term( $id ) { 
  252. $error = new WP_Error( 'rest_term_invalid', __( 'Term does not exist.' ), array( 'status' => 404 ) ); 
  253.  
  254. if ( ! $this->check_is_taxonomy_allowed( $this->taxonomy ) ) { 
  255. return $error; 
  256.  
  257. if ( (int) $id <= 0 ) { 
  258. return $error; 
  259.  
  260. $term = get_term( (int) $id, $this->taxonomy ); 
  261. if ( empty( $term ) || $term->taxonomy !== $this->taxonomy ) { 
  262. return $error; 
  263.  
  264. return $term; 
  265.  
  266. /** 
  267. * Checks if a request has access to read or edit the specified term. 
  268. * @since 4.7.0 
  269. * @access public 
  270. * @param WP_REST_Request $request Full details about the request. 
  271. * @return bool|WP_Error True if the request has read access for the item, otherwise false or WP_Error object. 
  272. */ 
  273. public function get_item_permissions_check( $request ) { 
  274. $term = $this->get_term( $request['id'] ); 
  275. if ( is_wp_error( $term ) ) { 
  276. return $term; 
  277.  
  278. if ( 'edit' === $request['context'] && ! current_user_can( 'edit_term', $term->term_id ) ) { 
  279. return new WP_Error( 'rest_forbidden_context', __( 'Sorry, you are not allowed to edit this term.' ), array( 'status' => rest_authorization_required_code() ) ); 
  280. return true; 
  281.  
  282. /** 
  283. * Gets a single term from a taxonomy. 
  284. * @since 4.7.0 
  285. * @access public 
  286. * @param WP_REST_Request $request Full details about the request. 
  287. * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. 
  288. */ 
  289. public function get_item( $request ) { 
  290. $term = $this->get_term( $request['id'] ); 
  291.  
  292. if ( is_wp_error( $term ) ) { 
  293. return $term; 
  294.  
  295. $response = $this->prepare_item_for_response( $term, $request ); 
  296.  
  297. return rest_ensure_response( $response ); 
  298.  
  299. /** 
  300. * Checks if a request has access to create a term. 
  301. * @since 4.7.0 
  302. * @access public 
  303. * @param WP_REST_Request $request Full details about the request. 
  304. * @return bool|WP_Error True if the request has access to create items, false or WP_Error object otherwise. 
  305. */ 
  306. public function create_item_permissions_check( $request ) { 
  307.  
  308. if ( ! $this->check_is_taxonomy_allowed( $this->taxonomy ) ) { 
  309. return false; 
  310.  
  311. $taxonomy_obj = get_taxonomy( $this->taxonomy ); 
  312. if ( ! current_user_can( $taxonomy_obj->cap->edit_terms ) ) { 
  313. return new WP_Error( 'rest_cannot_create', __( 'Sorry, you are not allowed to create new terms.' ), array( 'status' => rest_authorization_required_code() ) ); 
  314.  
  315. return true; 
  316.  
  317. /** 
  318. * Creates a single term in a taxonomy. 
  319. * @since 4.7.0 
  320. * @access public 
  321. * @param WP_REST_Request $request Full details about the request. 
  322. * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. 
  323. */ 
  324. public function create_item( $request ) { 
  325. if ( isset( $request['parent'] ) ) { 
  326. if ( ! is_taxonomy_hierarchical( $this->taxonomy ) ) { 
  327. return new WP_Error( 'rest_taxonomy_not_hierarchical', __( 'Can not set parent term, taxonomy is not hierarchical.' ), array( 'status' => 400 ) ); 
  328.  
  329. $parent = get_term( (int) $request['parent'], $this->taxonomy ); 
  330.  
  331. if ( ! $parent ) { 
  332. return new WP_Error( 'rest_term_invalid', __( "Parent term doesn't exist." ), array( 'status' => 400 ) ); 
  333.  
  334. $prepared_term = $this->prepare_item_for_database( $request ); 
  335.  
  336. $term = wp_insert_term( wp_slash( $prepared_term->name ), $this->taxonomy, wp_slash( (array) $prepared_term ) ); 
  337. if ( is_wp_error( $term ) ) { 
  338. /** 
  339. * If we're going to inform the client that the term already exists,  
  340. * give them the identifier for future use. 
  341. */ 
  342. if ( $term_id = $term->get_error_data( 'term_exists' ) ) { 
  343. $existing_term = get_term( $term_id, $this->taxonomy ); 
  344. $term->add_data( $existing_term->term_id, 'term_exists' ); 
  345.  
  346. return $term; 
  347.  
  348. $term = get_term( $term['term_id'], $this->taxonomy ); 
  349.  
  350. /** 
  351. * Fires after a single term is created or updated via the REST API. 
  352. * The dynamic portion of the hook name, `$this->taxonomy`, refers to the taxonomy slug. 
  353. * @since 4.7.0 
  354. * @param WP_Term $term Inserted or updated term object. 
  355. * @param WP_REST_Request $request Request object. 
  356. * @param bool $creating True when creating a term, false when updating. 
  357. */ 
  358. do_action( "rest_insert_{$this->taxonomy}", $term, $request, true ); 
  359.  
  360. $schema = $this->get_item_schema(); 
  361. if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) { 
  362. $meta_update = $this->meta->update_value( $request['meta'], (int) $request['id'] ); 
  363.  
  364. if ( is_wp_error( $meta_update ) ) { 
  365. return $meta_update; 
  366.  
  367. $fields_update = $this->update_additional_fields_for_object( $term, $request ); 
  368.  
  369. if ( is_wp_error( $fields_update ) ) { 
  370. return $fields_update; 
  371.  
  372. $request->set_param( 'context', 'view' ); 
  373.  
  374. $response = $this->prepare_item_for_response( $term, $request ); 
  375. $response = rest_ensure_response( $response ); 
  376.  
  377. $response->set_status( 201 ); 
  378. $response->header( 'Location', rest_url( $this->namespace . '/' . $this->rest_base . '/' . $term->term_id ) ); 
  379.  
  380. return $response; 
  381.  
  382. /** 
  383. * Checks if a request has access to update the specified term. 
  384. * @since 4.7.0 
  385. * @access public 
  386. * @param WP_REST_Request $request Full details about the request. 
  387. * @return bool|WP_Error True if the request has access to update the item, false or WP_Error object otherwise. 
  388. */ 
  389. public function update_item_permissions_check( $request ) { 
  390. $term = $this->get_term( $request['id'] ); 
  391. if ( is_wp_error( $term ) ) { 
  392. return $term; 
  393.  
  394. if ( ! current_user_can( 'edit_term', $term->term_id ) ) { 
  395. return new WP_Error( 'rest_cannot_update', __( 'Sorry, you are not allowed to edit this term.' ), array( 'status' => rest_authorization_required_code() ) ); 
  396.  
  397. return true; 
  398.  
  399. /** 
  400. * Updates a single term from a taxonomy. 
  401. * @since 4.7.0 
  402. * @access public 
  403. * @param WP_REST_Request $request Full details about the request. 
  404. * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. 
  405. */ 
  406. public function update_item( $request ) { 
  407. $term = $this->get_term( $request['id'] ); 
  408. if ( is_wp_error( $term ) ) { 
  409. return $term; 
  410.  
  411. if ( isset( $request['parent'] ) ) { 
  412. if ( ! is_taxonomy_hierarchical( $this->taxonomy ) ) { 
  413. return new WP_Error( 'rest_taxonomy_not_hierarchical', __( 'Can not set parent term, taxonomy is not hierarchical.' ), array( 'status' => 400 ) ); 
  414.  
  415. $parent = get_term( (int) $request['parent'], $this->taxonomy ); 
  416.  
  417. if ( ! $parent ) { 
  418. return new WP_Error( 'rest_term_invalid', __( "Parent term doesn't exist." ), array( 'status' => 400 ) ); 
  419.  
  420. $prepared_term = $this->prepare_item_for_database( $request ); 
  421.  
  422. // Only update the term if we haz something to update. 
  423. if ( ! empty( $prepared_term ) ) { 
  424. $update = wp_update_term( $term->term_id, $term->taxonomy, wp_slash( (array) $prepared_term ) ); 
  425.  
  426. if ( is_wp_error( $update ) ) { 
  427. return $update; 
  428.  
  429. $term = get_term( $term->term_id, $this->taxonomy ); 
  430.  
  431. /** This action is documented in lib/endpoints/class-wp-rest-terms-controller.php */ 
  432. do_action( "rest_insert_{$this->taxonomy}", $term, $request, false ); 
  433.  
  434. $schema = $this->get_item_schema(); 
  435. if ( ! empty( $schema['properties']['meta'] ) && isset( $request['meta'] ) ) { 
  436. $meta_update = $this->meta->update_value( $request['meta'], $term->term_id ); 
  437.  
  438. if ( is_wp_error( $meta_update ) ) { 
  439. return $meta_update; 
  440.  
  441. $fields_update = $this->update_additional_fields_for_object( $term, $request ); 
  442.  
  443. if ( is_wp_error( $fields_update ) ) { 
  444. return $fields_update; 
  445.  
  446. $request->set_param( 'context', 'view' ); 
  447.  
  448. $response = $this->prepare_item_for_response( $term, $request ); 
  449.  
  450. return rest_ensure_response( $response ); 
  451.  
  452. /** 
  453. * Checks if a request has access to delete the specified term. 
  454. * @since 4.7.0 
  455. * @access public 
  456. * @param WP_REST_Request $request Full details about the request. 
  457. * @return bool|WP_Error True if the request has access to delete the item, otherwise false or WP_Error object. 
  458. */ 
  459. public function delete_item_permissions_check( $request ) { 
  460. $term = $this->get_term( $request['id'] ); 
  461. if ( is_wp_error( $term ) ) { 
  462. return $term; 
  463.  
  464. if ( ! current_user_can( 'delete_term', $term->term_id ) ) { 
  465. return new WP_Error( 'rest_cannot_delete', __( 'Sorry, you are not allowed to delete this term.' ), array( 'status' => rest_authorization_required_code() ) ); 
  466.  
  467. return true; 
  468.  
  469. /** 
  470. * Deletes a single term from a taxonomy. 
  471. * @since 4.7.0 
  472. * @access public 
  473. * @param WP_REST_Request $request Full details about the request. 
  474. * @return WP_REST_Response|WP_Error Response object on success, or WP_Error object on failure. 
  475. */ 
  476. public function delete_item( $request ) { 
  477. $term = $this->get_term( $request['id'] ); 
  478. if ( is_wp_error( $term ) ) { 
  479. return $term; 
  480.  
  481. $force = isset( $request['force'] ) ? (bool) $request['force'] : false; 
  482.  
  483. // We don't support trashing for terms. 
  484. if ( ! $force ) { 
  485. return new WP_Error( 'rest_trash_not_supported', __( 'Terms do not support trashing. Set force=true to delete.' ), array( 'status' => 501 ) ); 
  486.  
  487. $request->set_param( 'context', 'view' ); 
  488.  
  489. $previous = $this->prepare_item_for_response( $term, $request ); 
  490.  
  491. $retval = wp_delete_term( $term->term_id, $term->taxonomy ); 
  492.  
  493. if ( ! $retval ) { 
  494. return new WP_Error( 'rest_cannot_delete', __( 'The term cannot be deleted.' ), array( 'status' => 500 ) ); 
  495.  
  496. $response = new WP_REST_Response(); 
  497. $response->set_data( array( 'deleted' => true, 'previous' => $previous->get_data() ) ); 
  498.  
  499. /** 
  500. * Fires after a single term is deleted via the REST API. 
  501. * The dynamic portion of the hook name, `$this->taxonomy`, refers to the taxonomy slug. 
  502. * @since 4.7.0 
  503. * @param WP_Term $term The deleted term. 
  504. * @param WP_REST_Response $response The response data. 
  505. * @param WP_REST_Request $request The request sent to the API. 
  506. */ 
  507. do_action( "rest_delete_{$this->taxonomy}", $term, $response, $request ); 
  508.  
  509. return $response; 
  510.  
  511. /** 
  512. * Prepares a single term for create or update. 
  513. * @since 4.7.0 
  514. * @access public 
  515. * @param WP_REST_Request $request Request object. 
  516. * @return object $prepared_term Term object. 
  517. */ 
  518. public function prepare_item_for_database( $request ) { 
  519. $prepared_term = new stdClass; 
  520.  
  521. $schema = $this->get_item_schema(); 
  522. if ( isset( $request['name'] ) && ! empty( $schema['properties']['name'] ) ) { 
  523. $prepared_term->name = $request['name']; 
  524.  
  525. if ( isset( $request['slug'] ) && ! empty( $schema['properties']['slug'] ) ) { 
  526. $prepared_term->slug = $request['slug']; 
  527.  
  528. if ( isset( $request['taxonomy'] ) && ! empty( $schema['properties']['taxonomy'] ) ) { 
  529. $prepared_term->taxonomy = $request['taxonomy']; 
  530.  
  531. if ( isset( $request['description'] ) && ! empty( $schema['properties']['description'] ) ) { 
  532. $prepared_term->description = $request['description']; 
  533.  
  534. if ( isset( $request['parent'] ) && ! empty( $schema['properties']['parent'] ) ) { 
  535. $parent_term_id = 0; 
  536. $parent_term = get_term( (int) $request['parent'], $this->taxonomy ); 
  537.  
  538. if ( $parent_term ) { 
  539. $parent_term_id = $parent_term->term_id; 
  540.  
  541. $prepared_term->parent = $parent_term_id; 
  542.  
  543. /** 
  544. * Filters term data before inserting term via the REST API. 
  545. * The dynamic portion of the hook name, `$this->taxonomy`, refers to the taxonomy slug. 
  546. * @since 4.7.0 
  547. * @param object $prepared_term Term object. 
  548. * @param WP_REST_Request $request Request object. 
  549. */ 
  550. return apply_filters( "rest_pre_insert_{$this->taxonomy}", $prepared_term, $request ); 
  551.  
  552. /** 
  553. * Prepares a single term output for response. 
  554. * @since 4.7.0 
  555. * @access public 
  556. * @param obj $item Term object. 
  557. * @param WP_REST_Request $request Request object. 
  558. * @return WP_REST_Response $response Response object. 
  559. */ 
  560. public function prepare_item_for_response( $item, $request ) { 
  561.  
  562. $schema = $this->get_item_schema(); 
  563. $data = array(); 
  564.  
  565. if ( ! empty( $schema['properties']['id'] ) ) { 
  566. $data['id'] = (int) $item->term_id; 
  567.  
  568. if ( ! empty( $schema['properties']['count'] ) ) { 
  569. $data['count'] = (int) $item->count; 
  570.  
  571. if ( ! empty( $schema['properties']['description'] ) ) { 
  572. $data['description'] = $item->description; 
  573.  
  574. if ( ! empty( $schema['properties']['link'] ) ) { 
  575. $data['link'] = get_term_link( $item ); 
  576.  
  577. if ( ! empty( $schema['properties']['name'] ) ) { 
  578. $data['name'] = $item->name; 
  579.  
  580. if ( ! empty( $schema['properties']['slug'] ) ) { 
  581. $data['slug'] = $item->slug; 
  582.  
  583. if ( ! empty( $schema['properties']['taxonomy'] ) ) { 
  584. $data['taxonomy'] = $item->taxonomy; 
  585.  
  586. if ( ! empty( $schema['properties']['parent'] ) ) { 
  587. $data['parent'] = (int) $item->parent; 
  588.  
  589. if ( ! empty( $schema['properties']['meta'] ) ) { 
  590. $data['meta'] = $this->meta->get_value( $item->term_id, $request ); 
  591.  
  592. $context = ! empty( $request['context'] ) ? $request['context'] : 'view'; 
  593. $data = $this->add_additional_fields_to_object( $data, $request ); 
  594. $data = $this->filter_response_by_context( $data, $context ); 
  595.  
  596. $response = rest_ensure_response( $data ); 
  597.  
  598. $response->add_links( $this->prepare_links( $item ) ); 
  599.  
  600. /** 
  601. * Filters a term item returned from the API. 
  602. * The dynamic portion of the hook name, `$this->taxonomy`, refers to the taxonomy slug. 
  603. * Allows modification of the term data right before it is returned. 
  604. * @since 4.7.0 
  605. * @param WP_REST_Response $response The response object. 
  606. * @param object $item The original term object. 
  607. * @param WP_REST_Request $request Request used to generate the response. 
  608. */ 
  609. return apply_filters( "rest_prepare_{$this->taxonomy}", $response, $item, $request ); 
  610.  
  611. /** 
  612. * Prepares links for the request. 
  613. * @since 4.7.0 
  614. * @access protected 
  615. * @param object $term Term object. 
  616. * @return array Links for the given term. 
  617. */ 
  618. protected function prepare_links( $term ) { 
  619. $base = $this->namespace . '/' . $this->rest_base; 
  620. $links = array( 
  621. 'self' => array( 
  622. 'href' => rest_url( trailingslashit( $base ) . $term->term_id ),  
  623. ),  
  624. 'collection' => array( 
  625. 'href' => rest_url( $base ),  
  626. ),  
  627. 'about' => array( 
  628. 'href' => rest_url( sprintf( 'wp/v2/taxonomies/%s', $this->taxonomy ) ),  
  629. ),  
  630. ); 
  631.  
  632. if ( $term->parent ) { 
  633. $parent_term = get_term( (int) $term->parent, $term->taxonomy ); 
  634.  
  635. if ( $parent_term ) { 
  636. $links['up'] = array( 
  637. 'href' => rest_url( trailingslashit( $base ) . $parent_term->term_id ),  
  638. 'embeddable' => true,  
  639. ); 
  640.  
  641. $taxonomy_obj = get_taxonomy( $term->taxonomy ); 
  642.  
  643. if ( empty( $taxonomy_obj->object_type ) ) { 
  644. return $links; 
  645.  
  646. $post_type_links = array(); 
  647.  
  648. foreach ( $taxonomy_obj->object_type as $type ) { 
  649. $post_type_object = get_post_type_object( $type ); 
  650.  
  651. if ( empty( $post_type_object->show_in_rest ) ) { 
  652. continue; 
  653.  
  654. $rest_base = ! empty( $post_type_object->rest_base ) ? $post_type_object->rest_base : $post_type_object->name; 
  655. $post_type_links[] = array( 
  656. 'href' => add_query_arg( $this->rest_base, $term->term_id, rest_url( sprintf( 'wp/v2/%s', $rest_base ) ) ),  
  657. ); 
  658.  
  659. if ( ! empty( $post_type_links ) ) { 
  660. $links['https://api.w.org/post_type'] = $post_type_links; 
  661.  
  662. return $links; 
  663.  
  664. /** 
  665. * Retrieves the term's schema, conforming to JSON Schema. 
  666. * @since 4.7.0 
  667. * @access public 
  668. * @return array Item schema data. 
  669. */ 
  670. public function get_item_schema() { 
  671. $schema = array( 
  672. '$schema' => 'http://json-schema.org/schema#',  
  673. 'title' => 'post_tag' === $this->taxonomy ? 'tag' : $this->taxonomy,  
  674. 'type' => 'object',  
  675. 'properties' => array( 
  676. 'id' => array( 
  677. 'description' => __( 'Unique identifier for the term.' ),  
  678. 'type' => 'integer',  
  679. 'context' => array( 'view', 'embed', 'edit' ),  
  680. 'readonly' => true,  
  681. ),  
  682. 'count' => array( 
  683. 'description' => __( 'Number of published posts for the term.' ),  
  684. 'type' => 'integer',  
  685. 'context' => array( 'view', 'edit' ),  
  686. 'readonly' => true,  
  687. ),  
  688. 'description' => array( 
  689. 'description' => __( 'HTML description of the term.' ),  
  690. 'type' => 'string',  
  691. 'context' => array( 'view', 'edit' ),  
  692. ),  
  693. 'link' => array( 
  694. 'description' => __( 'URL of the term.' ),  
  695. 'type' => 'string',  
  696. 'format' => 'uri',  
  697. 'context' => array( 'view', 'embed', 'edit' ),  
  698. 'readonly' => true,  
  699. ),  
  700. 'name' => array( 
  701. 'description' => __( 'HTML title for the term.' ),  
  702. 'type' => 'string',  
  703. 'context' => array( 'view', 'embed', 'edit' ),  
  704. 'arg_options' => array( 
  705. 'sanitize_callback' => 'sanitize_text_field',  
  706. ),  
  707. 'required' => true,  
  708. ),  
  709. 'slug' => array( 
  710. 'description' => __( 'An alphanumeric identifier for the term unique to its type.' ),  
  711. 'type' => 'string',  
  712. 'context' => array( 'view', 'embed', 'edit' ),  
  713. 'arg_options' => array( 
  714. 'sanitize_callback' => array( $this, 'sanitize_slug' ),  
  715. ),  
  716. ),  
  717. 'taxonomy' => array( 
  718. 'description' => __( 'Type attribution for the term.' ),  
  719. 'type' => 'string',  
  720. 'enum' => array_keys( get_taxonomies() ),  
  721. 'context' => array( 'view', 'embed', 'edit' ),  
  722. 'readonly' => true,  
  723. ),  
  724. ),  
  725. ); 
  726.  
  727. $taxonomy = get_taxonomy( $this->taxonomy ); 
  728.  
  729. if ( $taxonomy->hierarchical ) { 
  730. $schema['properties']['parent'] = array( 
  731. 'description' => __( 'The parent term ID.' ),  
  732. 'type' => 'integer',  
  733. 'context' => array( 'view', 'edit' ),  
  734. ); 
  735.  
  736. $schema['properties']['meta'] = $this->meta->get_field_schema(); 
  737.  
  738. return $this->add_additional_fields_schema( $schema ); 
  739.  
  740. /** 
  741. * Retrieves the query params for collections. 
  742. * @since 4.7.0 
  743. * @access public 
  744. * @return array Collection parameters. 
  745. */ 
  746. public function get_collection_params() { 
  747. $query_params = parent::get_collection_params(); 
  748. $taxonomy = get_taxonomy( $this->taxonomy ); 
  749.  
  750. $query_params['context']['default'] = 'view'; 
  751.  
  752. $query_params['exclude'] = array( 
  753. 'description' => __( 'Ensure result set excludes specific IDs.' ),  
  754. 'type' => 'array',  
  755. 'items' => array( 
  756. 'type' => 'integer',  
  757. ),  
  758. 'default' => array(),  
  759. ); 
  760.  
  761. $query_params['include'] = array( 
  762. 'description' => __( 'Limit result set to specific IDs.' ),  
  763. 'type' => 'array',  
  764. 'items' => array( 
  765. 'type' => 'integer',  
  766. ),  
  767. 'default' => array(),  
  768. ); 
  769.  
  770. if ( ! $taxonomy->hierarchical ) { 
  771. $query_params['offset'] = array( 
  772. 'description' => __( 'Offset the result set by a specific number of items.' ),  
  773. 'type' => 'integer',  
  774. ); 
  775.  
  776. $query_params['order'] = array( 
  777. 'description' => __( 'Order sort attribute ascending or descending.' ),  
  778. 'type' => 'string',  
  779. 'default' => 'asc',  
  780. 'enum' => array( 
  781. 'asc',  
  782. 'desc',  
  783. ),  
  784. ); 
  785.  
  786. $query_params['orderby'] = array( 
  787. 'description' => __( 'Sort collection by term attribute.' ),  
  788. 'type' => 'string',  
  789. 'default' => 'name',  
  790. 'enum' => array( 
  791. 'id',  
  792. 'include',  
  793. 'name',  
  794. 'slug',  
  795. 'term_group',  
  796. 'description',  
  797. 'count',  
  798. ),  
  799. ); 
  800.  
  801. $query_params['hide_empty'] = array( 
  802. 'description' => __( 'Whether to hide terms not assigned to any posts.' ),  
  803. 'type' => 'boolean',  
  804. 'default' => false,  
  805. ); 
  806.  
  807. if ( $taxonomy->hierarchical ) { 
  808. $query_params['parent'] = array( 
  809. 'description' => __( 'Limit result set to terms assigned to a specific parent.' ),  
  810. 'type' => 'integer',  
  811. ); 
  812.  
  813. $query_params['post'] = array( 
  814. 'description' => __( 'Limit result set to terms assigned to a specific post.' ),  
  815. 'type' => 'integer',  
  816. 'default' => null,  
  817. ); 
  818.  
  819. $query_params['slug'] = array( 
  820. 'description' => __( 'Limit result set to terms with a specific slug.' ),  
  821. 'type' => 'array',  
  822. 'items' => array( 
  823. 'type' => 'string' 
  824. ),  
  825. ); 
  826.  
  827. /** 
  828. * Filter collection parameters for the terms controller. 
  829. * The dynamic part of the filter `$this->taxonomy` refers to the taxonomy 
  830. * slug for the controller. 
  831. * This filter registers the collection parameter, but does not map the 
  832. * collection parameter to an internal WP_Term_Query parameter. Use the 
  833. * `rest_{$this->taxonomy}_query` filter to set WP_Term_Query parameters. 
  834. * @since 4.7.0 
  835. * @param array $query_params JSON Schema-formatted collection parameters. 
  836. * @param WP_Taxonomy $taxonomy Taxonomy object. 
  837. */ 
  838. return apply_filters( "rest_{$this->taxonomy}_collection_params", $query_params, $taxonomy ); 
  839.  
  840. /** 
  841. * Checks that the taxonomy is valid. 
  842. * @since 4.7.0 
  843. * @access protected 
  844. * @param string $taxonomy Taxonomy to check. 
  845. * @return bool Whether the taxonomy is allowed for REST management. 
  846. */ 
  847. protected function check_is_taxonomy_allowed( $taxonomy ) { 
  848. $taxonomy_obj = get_taxonomy( $taxonomy ); 
  849. if ( $taxonomy_obj && ! empty( $taxonomy_obj->show_in_rest ) ) { 
  850. return true; 
  851. return false;