[ Index ] |
PHP Cross Reference of BackPress |
[Summary view] [Print] [Text view]
1 <?php 2 // Last sync [WP11537] - Refactored into a class based on wp-includes/taxonomy.php 3 4 /** 5 * Taxonomy API 6 * 7 * @package WordPress 8 * @subpackage Taxonomy 9 * @since 2.3.0 10 */ 11 12 /** 13 * WordPress Taxonomy based off of WordPress revision 8782. 14 * 15 * @since 2.3.0 16 */ 17 class WP_Taxonomy { 18 /** 19 * Stores the database. 20 * 21 * @var unknown_type 22 */ 23 var $db; 24 var $taxonomies = array(); 25 26 function WP_Taxonomy( &$db ) { 27 self::__construct( $db ); 28 register_shutdown_function( array(&$this, '__destruct') ); 29 } 30 31 /** 32 * PHP5 constructor - Assigns the database to an attribute of the class. 33 * 34 * @param unknown_type $db 35 */ 36 function __construct( &$db ) { 37 $this->db =& $db; 38 } 39 40 /** 41 * Does nothing. 42 * 43 * @package BackPress 44 * @subpackage Taxonomy 45 */ 46 function __destruct() { 47 } 48 49 /** 50 * Return all of the taxonomy names that are of $object_type. 51 * 52 * It appears that this function can be used to find all of the names inside of 53 * $this->taxonomies variable. 54 * 55 * <code><?php $taxonomies = $this->get_object_taxonomies('post'); ?></code> Should 56 * result in <code>Array('category', 'post_tag')</code> 57 * 58 * @package WordPress 59 * @subpackage Taxonomy 60 * @since 2.3.0 61 * 62 * @uses $this->taxonomies 63 * 64 * @param array|string|object $object_type Name of the type of taxonomy object, or an object (row from posts) 65 * @return array The names of all taxonomy of $object_type. 66 */ 67 function get_object_taxonomies($object_type) { 68 $object_type = (array) $object_type; 69 70 // WP DIFF 71 $taxonomies = array(); 72 foreach ( (array) $this->taxonomies as $taxonomy ) { 73 if ( array_intersect($object_type, (array) $taxonomy->object_type) ) 74 $taxonomies[] = $taxonomy->name; 75 } 76 77 return $taxonomies; 78 } 79 80 /** 81 * Retrieves the taxonomy object of $taxonomy. 82 * 83 * The get_taxonomy function will first check that the parameter string given 84 * is a taxonomy object and if it is, it will return it. 85 * 86 * @package WordPress 87 * @subpackage Taxonomy 88 * @since 2.3.0 89 * 90 * @uses $this->taxonomies 91 * @uses $this->is_taxonomy() Checks whether taxonomy exists 92 * 93 * @param string $taxonomy Name of taxonomy object to return 94 * @return object|bool The Taxonomy Object or false if $taxonomy doesn't exist 95 */ 96 function get_taxonomy( $taxonomy ) { 97 if ( !$this->is_taxonomy($taxonomy) ) 98 return false; 99 100 return $this->taxonomies[$taxonomy]; 101 } 102 103 /** 104 * Checks that the taxonomy name exists. 105 * 106 * @package WordPress 107 * @subpackage Taxonomy 108 * @since 2.3.0 109 * 110 * @uses $this->taxonomies 111 * 112 * @param string $taxonomy Name of taxonomy object 113 * @return bool Whether the taxonomy exists or not. 114 */ 115 function is_taxonomy( $taxonomy ) { 116 return isset($this->taxonomies[$taxonomy]); 117 } 118 119 /** 120 * Whether the taxonomy object is hierarchical. 121 * 122 * Checks to make sure that the taxonomy is an object first. Then Gets the 123 * object, and finally returns the hierarchical value in the object. 124 * 125 * A false return value might also mean that the taxonomy does not exist. 126 * 127 * @package WordPress 128 * @subpackage Taxonomy 129 * @since 2.3.0 130 * 131 * @uses $this->is_taxonomy() Checks whether taxonomy exists 132 * @uses $this->get_taxonomy() Used to get the taxonomy object 133 * 134 * @param string $taxonomy Name of taxonomy object 135 * @return bool Whether the taxonomy is hierarchical 136 */ 137 function is_taxonomy_hierarchical($taxonomy) { 138 if ( !$this->is_taxonomy($taxonomy) ) 139 return false; 140 141 $taxonomy = $this->get_taxonomy($taxonomy); 142 return $taxonomy->hierarchical; 143 } 144 145 /** 146 * Create or modify a taxonomy object. Do not use before init. 147 * 148 * A simple function for creating or modifying a taxonomy object based on the 149 * parameters given. The function will accept an array (third optional 150 * parameter), along with strings for the taxonomy name and another string for 151 * the object type. 152 * 153 * The function keeps a default set, allowing for the $args to be optional but 154 * allow the other functions to still work. It is possible to overwrite the 155 * default set, which contains two keys: hierarchical and update_count_callback. 156 * 157 * Nothing is returned, so expect error maybe or use is_taxonomy() to check 158 * whether taxonomy exists. 159 * 160 * Optional $args contents: 161 * 162 * hierarachical - has some defined purpose at other parts of the API and is a 163 * boolean value. 164 * 165 * update_count_callback - works much like a hook, in that it will be called 166 * when the count is updated. 167 * 168 * @package WordPress 169 * @subpackage Taxonomy 170 * @since 2.3.0 171 * @uses $this->taxonomies Inserts new taxonomy object into the list 172 * 173 * @param string $taxonomy Name of taxonomy object 174 * @param string $object_type Name of the object type for the taxonomy object. 175 * @param array|string $args See above description for the two keys values. 176 */ 177 function register_taxonomy( $taxonomy, $object_type, $args = array() ) { 178 $defaults = array('hierarchical' => false, 'update_count_callback' => ''); 179 $args = wp_parse_args($args, $defaults); 180 181 $args['name'] = $taxonomy; 182 $args['object_type'] = $object_type; 183 $this->taxonomies[$taxonomy] = (object) $args; 184 } 185 186 // 187 // Term API 188 // 189 190 /** 191 * Retrieve object_ids of valid taxonomy and term. 192 * 193 * The strings of $taxonomies must exist before this function will continue. On 194 * failure of finding a valid taxonomy, it will return an WP_Error class, kind 195 * of like Exceptions in PHP 5, except you can't catch them. Even so, you can 196 * still test for the WP_Error class and get the error message. 197 * 198 * The $terms aren't checked the same as $taxonomies, but still need to exist 199 * for $object_ids to be returned. 200 * 201 * It is possible to change the order that object_ids is returned by either 202 * using PHP sort family functions or using the database by using $args with 203 * either ASC or DESC array. The value should be in the key named 'order'. 204 * 205 * @package WordPress 206 * @subpackage Taxonomy 207 * @since 2.3.0 208 * 209 * @uses wp_parse_args() Creates an array from string $args. 210 * 211 * @param string|array $terms String of term or array of string values of terms that will be used 212 * @param string|array $taxonomies String of taxonomy name or Array of string values of taxonomy names 213 * @param array|string $args Change the order of the object_ids, either ASC or DESC 214 * @return WP_Error|array If the taxonomy does not exist, then WP_Error will be returned. On success 215 * the array can be empty meaning that there are no $object_ids found or it will return the $object_ids found. 216 */ 217 function get_objects_in_term( $terms, $taxonomies, $args = null ) { 218 if ( !is_array($terms) ) 219 $terms = array($terms); 220 221 if ( !is_array($taxonomies) ) 222 $taxonomies = array($taxonomies); 223 224 foreach ( (array) $taxonomies as $taxonomy ) { 225 if ( !$this->is_taxonomy($taxonomy) ) 226 return new WP_Error('invalid_taxonomy', __('Invalid Taxonomy')); 227 } 228 229 $defaults = array('order' => 'ASC', 'field' => 'term_id'); 230 $args = wp_parse_args( $args, $defaults ); 231 extract($args, EXTR_SKIP); 232 233 if ( 'tt_id' == $field ) 234 $field = 'tt.term_taxonomy_id'; 235 else 236 $field = 'tt.term_id'; 237 238 $order = ( 'desc' == strtolower($order) ) ? 'DESC' : 'ASC'; 239 240 $terms = array_map('intval', $terms); 241 242 $taxonomies = "'" . implode("', '", $taxonomies) . "'"; 243 $terms = "'" . implode("', '", $terms) . "'"; 244 245 $object_ids = $this->db->get_col("SELECT tr.object_id FROM {$this->db->term_relationships} AS tr INNER JOIN {$this->db->term_taxonomy} AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy IN ($taxonomies) AND $field IN ($terms) ORDER BY tr.object_id $order"); 246 247 if ( ! $object_ids ) 248 return array(); 249 250 return $object_ids; 251 } 252 253 /** 254 * Get all Term data from database by Term ID. 255 * 256 * The usage of the get_term function is to apply filters to a term object. It 257 * is possible to get a term object from the database before applying the 258 * filters. 259 * 260 * $term ID must be part of $taxonomy, to get from the database. Failure, might 261 * be able to be captured by the hooks. Failure would be the same value as $this->db 262 * returns for the get_row method. 263 * 264 * There are two hooks, one is specifically for each term, named 'get_term', and 265 * the second is for the taxonomy name, 'term_$taxonomy'. Both hooks gets the 266 * term object, and the taxonomy name as parameters. Both hooks are expected to 267 * return a Term object. 268 * 269 * 'get_term' hook - Takes two parameters the term Object and the taxonomy name. 270 * Must return term object. Used in get_term() as a catch-all filter for every 271 * $term. 272 * 273 * 'get_$taxonomy' hook - Takes two parameters the term Object and the taxonomy 274 * name. Must return term object. $taxonomy will be the taxonomy name, so for 275 * example, if 'category', it would be 'get_category' as the filter name. Useful 276 * for custom taxonomies or plugging into default taxonomies. 277 * 278 * @package WordPress 279 * @subpackage Taxonomy 280 * @since 2.3.0 281 * 282 * @uses $this->sanitize_term() Cleanses the term based on $filter context before returning. 283 * @see $this->sanitize_term_field() The $context param lists the available values for get_term_by() $filter param. 284 * 285 * @param int|object $term If integer, will get from database. If object will apply filters and return $term. 286 * @param string $taxonomy Taxonomy name that $term is part of. 287 * @param string $output Constant OBJECT, ARRAY_A, or ARRAY_N 288 * @param string $filter Optional, default is raw or no WordPress defined filter will applied. 289 * @return mixed|null|WP_Error Term Row from database. Will return null if $term is empty. If taxonomy does not 290 * exist then WP_Error will be returned. 291 */ 292 function &get_term($term, $taxonomy, $output = OBJECT, $filter = 'raw') { 293 if ( empty($term) ) { 294 $error = new WP_Error('invalid_term', __('Empty Term')); 295 return $error; 296 } 297 298 if ( !$this->is_taxonomy($taxonomy) ) { 299 $error = new WP_Error('invalid_taxonomy', __('Invalid Taxonomy')); 300 return $error; 301 } 302 303 if ( is_object($term) ) { 304 wp_cache_add($term->term_id, $term, $taxonomy); 305 wp_cache_add($term->term_taxonomy_id, $term->term_id, "$taxonomy:tt_id" ); 306 $_term = $term; 307 } else { 308 $term = (int) $term; 309 if ( ! $_term = wp_cache_get($term, $taxonomy) ) { 310 $_term = $this->db->get_row( $this->db->prepare( "SELECT t.*, tt.* FROM {$this->db->terms} AS t INNER JOIN {$this->db->term_taxonomy} AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy = %s AND t.term_id = %s LIMIT 1", $taxonomy, $term) ); 311 wp_cache_add($term, $_term, $taxonomy); 312 wp_cache_add($_term->term_taxonomy_id, $_term->term_id, "$taxonomy:tt_id" ); 313 } 314 } 315 316 $_term = apply_filters('get_term', $_term, $taxonomy); 317 $_term = apply_filters("get_$taxonomy", $_term, $taxonomy); 318 $_term = $this->sanitize_term($_term, $taxonomy, $filter); 319 320 backpress_convert_object( $_term, $output ); 321 322 return $_term; 323 } 324 325 /** 326 * Get all Term data from database by Term field and data. 327 * 328 * Warning: $value is not escaped for 'name' $field. You must do it yourself, if 329 * required. 330 * 331 * The default $field is 'id', therefore it is possible to also use null for 332 * field, but not recommended that you do so. 333 * 334 * If $value does not exist, the return value will be false. If $taxonomy exists 335 * and $field and $value combinations exist, the Term will be returned. 336 * 337 * @package WordPress 338 * @subpackage Taxonomy 339 * @since 2.3.0 340 * 341 * @uses $this->sanitize_term() Cleanses the term based on $filter context before returning. 342 * @see $this->sanitize_term_field() The $context param lists the available values for get_term_by() $filter param. 343 * 344 * @param string $field Either 'slug', 'name', 'id', or 'tt_id' 345 * @param string|int $value Search for this term value 346 * @param string $taxonomy Taxonomy Name 347 * @param string $output Constant OBJECT, ARRAY_A, or ARRAY_N 348 * @param string $filter Optional, default is raw or no WordPress defined filter will applied. 349 * @return mixed Term Row from database. Will return false if $taxonomy does not exist or $term was not found. 350 */ 351 function get_term_by($field, $value, $taxonomy, $output = OBJECT, $filter = 'raw') { 352 if ( !$this->is_taxonomy($taxonomy) ) 353 return false; 354 355 if ( 'slug' == $field ) { 356 $field = 't.slug'; 357 $value = $this->sanitize_term_slug($value, $taxonomy); 358 if ( empty($value) ) 359 return false; 360 } else if ( 'name' == $field ) { 361 // Assume already escaped 362 $field = 't.name'; 363 } else if ( 'tt_id' == $field ) { 364 $field = 'tt.term_taxonomy_id'; 365 $value = (int) $value; 366 if ( $_term_id = wp_cache_get( $value, "$taxonomy:tt_id" ) ) 367 return $this->get_term( $_term_id, $taxonomy, $output, $filter ); 368 } else { 369 $field = 't.term_id'; 370 $value = (int) $value; 371 } 372 373 $term = $this->db->get_row( $this->db->prepare( "SELECT t.*, tt.* FROM {$this->db->terms} AS t INNER JOIN {$this->db->term_taxonomy} AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy = %s AND $field = %s LIMIT 1", $taxonomy, $value) ); 374 if ( !$term ) 375 return false; 376 377 wp_cache_add($term->term_id, $term, $taxonomy); 378 wp_cache_add($term->term_taxonomy_id, $term->term_id, "$taxonomy:tt_id" ); 379 380 $term = $this->sanitize_term($term, $taxonomy, $filter); 381 382 backpress_convert_object( $term, $output ); 383 384 return $term; 385 } 386 387 /** 388 * Merge all term children into a single array of their IDs. 389 * 390 * This recursive function will merge all of the children of $term into the same 391 * array of term IDs. Only useful for taxonomies which are hierarchical. 392 * 393 * Will return an empty array if $term does not exist in $taxonomy. 394 * 395 * @package WordPress 396 * @subpackage Taxonomy 397 * @since 2.3.0 398 * 399 * @uses $this->_get_term_hierarchy() 400 * @uses $this->get_term_children() Used to get the children of both $taxonomy and the parent $term 401 * 402 * @param string $term ID of Term to get children 403 * @param string $taxonomy Taxonomy Name 404 * @return array|WP_Error List of Term Objects. WP_Error returned if $taxonomy does not exist 405 */ 406 function get_term_children( $term_id, $taxonomy ) { 407 if ( !$this->is_taxonomy($taxonomy) ) 408 return new WP_Error('invalid_taxonomy', __('Invalid Taxonomy')); 409 410 $term_id = intval( $term_id ); 411 412 $terms = $this->_get_term_hierarchy($taxonomy); 413 414 if ( ! isset($terms[$term_id]) ) 415 return array(); 416 417 $children = $terms[$term_id]; 418 419 foreach ( (array) $terms[$term_id] as $child ) { 420 if ( isset($terms[$child]) ) 421 $children = array_merge($children, $this->get_term_children($child, $taxonomy)); 422 } 423 424 return $children; 425 } 426 427 /** 428 * Get sanitized Term field. 429 * 430 * Does checks for $term, based on the $taxonomy. The function is for contextual 431 * reasons and for simplicity of usage. See sanitize_term_field() for more 432 * information. 433 * 434 * @package WordPress 435 * @subpackage Taxonomy 436 * @since 2.3.0 437 * 438 * @uses $this->sanitize_term_field() Passes the return value in sanitize_term_field on success. 439 * 440 * @param string $field Term field to fetch 441 * @param int $term Term ID 442 * @param string $taxonomy Taxonomy Name 443 * @param string $context Optional, default is display. Look at sanitize_term_field() for available options. 444 * @return mixed Will return an empty string if $term is not an object or if $field is not set in $term. 445 */ 446 function get_term_field( $field, $term, $taxonomy, $context = 'display' ) { 447 $term = (int) $term; 448 $term = $this->get_term( $term, $taxonomy ); 449 if ( is_wp_error($term) ) 450 return $term; 451 452 if ( !is_object($term) ) 453 return ''; 454 455 if ( !isset($term->$field) ) 456 return ''; 457 458 return $this->sanitize_term_field($field, $term->$field, $term->term_id, $taxonomy, $context); 459 } 460 461 /** 462 * Sanitizes Term for editing. 463 * 464 * Return value is sanitize_term() and usage is for sanitizing the term for 465 * editing. Function is for contextual and simplicity. 466 * 467 * @package WordPress 468 * @subpackage Taxonomy 469 * @since 2.3.0 470 * 471 * @uses $this->sanitize_term() Passes the return value on success 472 * 473 * @param int|object $id Term ID or Object 474 * @param string $taxonomy Taxonomy Name 475 * @return mixed|null|WP_Error Will return empty string if $term is not an object. 476 */ 477 function get_term_to_edit( $id, $taxonomy ) { 478 $term = $this->get_term( $id, $taxonomy ); 479 480 if ( is_wp_error($term) ) 481 return $term; 482 483 if ( !is_object($term) ) 484 return ''; 485 486 return $this->sanitize_term($term, $taxonomy, 'edit'); 487 } 488 489 /** 490 * Retrieve the terms in a given taxonomy or list of taxonomies. 491 * 492 * You can fully inject any customizations to the query before it is sent, as 493 * well as control the output with a filter. 494 * 495 * The 'get_terms' filter will be called when the cache has the term and will 496 * pass the found term along with the array of $taxonomies and array of $args. 497 * This filter is also called before the array of terms is passed and will pass 498 * the array of terms, along with the $taxonomies and $args. 499 * 500 * The 'list_terms_exclusions' filter passes the compiled exclusions along with 501 * the $args. 502 * 503 * The 'get_terms_orderby' filter passes the ORDER BY clause for the query 504 * along with the $args array. 505 * 506 * The 'get_terms_fields' filter passes the fields for the SELECT query 507 * along with the $args array. 508 * 509 * The list of arguments that $args can contain, which will overwrite the defaults: 510 * 511 * orderby - Default is 'name'. Can be name, count, term_group, slug or nothing 512 * (will use term_id), Passing a custom value other than these will cause it to 513 * order based on the custom value. 514 * 515 * order - Default is ASC. Can use DESC. 516 * 517 * hide_empty - Default is true. Will not return empty terms, which means 518 * terms whose count is 0 according to the given taxonomy. 519 * 520 * exclude - Default is an empty string. A comma- or space-delimited string 521 * of term ids to exclude from the return array. If 'include' is non-empty, 522 * 'exclude' is ignored. 523 * 524 * include - Default is an empty string. A comma- or space-delimited string 525 * of term ids to include in the return array. 526 * 527 * number - The maximum number of terms to return. Default is empty. 528 * 529 * offset - The number by which to offset the terms query. 530 * 531 * fields - Default is 'all', which returns an array of term objects. 532 * If 'fields' is 'ids' or 'names', returns an array of 533 * integers or strings, respectively. 534 * 535 * slug - Returns terms whose "slug" matches this value. Default is empty string. 536 * 537 * hierarchical - Whether to include terms that have non-empty descendants 538 * (even if 'hide_empty' is set to true). 539 * 540 * search - Returned terms' names will contain the value of 'search', 541 * case-insensitive. Default is an empty string. 542 * 543 * name__like - Returned terms' names will begin with the value of 'name__like', 544 * case-insensitive. Default is empty string. 545 * 546 * The argument 'pad_counts', if set to true will include the quantity of a term's 547 * children in the quantity of each term's "count" object variable. 548 * 549 * The 'get' argument, if set to 'all' instead of its default empty string, 550 * returns terms regardless of ancestry or whether the terms are empty. 551 * 552 * The 'child_of' argument, when used, should be set to the integer of a term ID. Its default 553 * is 0. If set to a non-zero value, all returned terms will be descendants 554 * of that term according to the given taxonomy. Hence 'child_of' is set to 0 555 * if more than one taxonomy is passed in $taxonomies, because multiple taxonomies 556 * make term ancestry ambiguous. 557 * 558 * The 'parent' argument, when used, should be set to the integer of a term ID. Its default is 559 * the empty string '', which has a different meaning from the integer 0. 560 * If set to an integer value, all returned terms will have as an immediate 561 * ancestor the term whose ID is specified by that integer according to the given taxonomy. 562 * The 'parent' argument is different from 'child_of' in that a term X is considered a 'parent' 563 * of term Y only if term X is the father of term Y, not its grandfather or great-grandfather, etc. 564 * 565 * @package WordPress 566 * @subpackage Taxonomy 567 * @since 2.3.0 568 * 569 * @uses wp_parse_args() Merges the defaults with those defined by $args and allows for strings. 570 * 571 * @param string|array Taxonomy name or list of Taxonomy names 572 * @param string|array $args The values of what to search for when returning terms 573 * @return array|WP_Error List of Term Objects and their children. Will return WP_Error, if any of $taxonomies do not exist. 574 */ 575 function &get_terms($taxonomies, $args = '') { 576 $empty_array = array(); 577 578 $single_taxonomy = false; 579 if ( !is_array($taxonomies) ) { 580 $single_taxonomy = true; 581 $taxonomies = array($taxonomies); 582 } 583 584 foreach ( (array) $taxonomies as $taxonomy ) { 585 if ( ! $this->is_taxonomy($taxonomy) ) { 586 $error = new WP_Error('invalid_taxonomy', __('Invalid Taxonomy')); 587 return $error; 588 } 589 } 590 591 $in_taxonomies = "'" . implode("', '", $taxonomies) . "'"; 592 593 $defaults = array('orderby' => 'name', 'order' => 'ASC', 594 'hide_empty' => true, 'exclude' => '', 'exclude_tree' => '', 'include' => '', 595 'number' => '', 'fields' => 'all', 'slug' => '', 'parent' => '', 596 'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 597 'pad_counts' => false, 'offset' => '', 'search' => ''); 598 $args = wp_parse_args( $args, $defaults ); 599 $args['number'] = absint( $args['number'] ); 600 $args['offset'] = absint( $args['offset'] ); 601 if ( !$single_taxonomy || !$this->is_taxonomy_hierarchical($taxonomies[0]) || 602 '' !== $args['parent'] ) { 603 $args['child_of'] = 0; 604 $args['hierarchical'] = false; 605 $args['pad_counts'] = false; 606 } 607 608 if ( 'all' == $args['get'] ) { 609 $args['child_of'] = 0; 610 $args['hide_empty'] = 0; 611 $args['hierarchical'] = false; 612 $args['pad_counts'] = false; 613 } 614 extract($args, EXTR_SKIP); 615 616 if ( $child_of ) { 617 $hierarchy = $this->_get_term_hierarchy($taxonomies[0]); 618 if ( !isset($hierarchy[$child_of]) ) 619 return $empty_array; 620 } 621 622 if ( $parent ) { 623 $hierarchy = $this->_get_term_hierarchy($taxonomies[0]); 624 if ( !isset($hierarchy[$parent]) ) 625 return $empty_array; 626 } 627 628 // $args can be whatever, only use the args defined in defaults to compute the key 629 $filter_key = ( has_filter('list_terms_exclusions') ) ? serialize($GLOBALS['wp_filter']['list_terms_exclusions']) : ''; 630 $key = md5( serialize( compact(array_keys($defaults)) ) . serialize( $taxonomies ) . $filter_key ); 631 $last_changed = wp_cache_get('last_changed', 'terms'); 632 if ( !$last_changed ) { 633 $last_changed = time(); 634 wp_cache_set('last_changed', $last_changed, 'terms'); 635 } 636 $cache_key = "get_terms:$key:$last_changed"; 637 $cache = wp_cache_get( $cache_key, 'terms' ); 638 if ( false !== $cache ) { 639 $cache = apply_filters('get_terms', $cache, $taxonomies, $args); 640 return $cache; 641 } 642 643 $_orderby = strtolower($orderby); 644 if ( 'count' == $_orderby ) 645 $orderby = 'tt.count'; 646 else if ( 'name' == $_orderby ) 647 $orderby = 't.name'; 648 else if ( 'slug' == $_orderby ) 649 $orderby = 't.slug'; 650 else if ( 'term_group' == $_orderby ) 651 $orderby = 't.term_group'; 652 elseif ( empty($_orderby) || 'id' == $_orderby ) 653 $orderby = 't.term_id'; 654 655 $orderby = apply_filters( 'get_terms_orderby', $orderby, $args ); 656 657 $where = ''; 658 $inclusions = ''; 659 if ( !empty($include) ) { 660 $exclude = ''; 661 $exclude_tree = ''; 662 $interms = preg_split('/[\s,]+/',$include); 663 if ( count($interms) ) { 664 foreach ( (array) $interms as $interm ) { 665 if (empty($inclusions)) 666 $inclusions = ' AND ( t.term_id = ' . intval($interm) . ' '; 667 else 668 $inclusions .= ' OR t.term_id = ' . intval($interm) . ' '; 669 } 670 } 671 } 672 673 if ( !empty($inclusions) ) 674 $inclusions .= ')'; 675 $where .= $inclusions; 676 677 $exclusions = ''; 678 if ( ! empty( $exclude_tree ) ) { 679 $excluded_trunks = preg_split('/[\s,]+/',$exclude_tree); 680 foreach( (array) $excluded_trunks as $extrunk ) { 681 $excluded_children = (array) $this->get_terms($taxonomies[0], array('child_of' => intval($extrunk), 'fields' => 'ids')); 682 $excluded_children[] = $extrunk; 683 foreach( (array) $excluded_children as $exterm ) { 684 if ( empty($exclusions) ) 685 $exclusions = ' AND ( t.term_id <> ' . intval($exterm) . ' '; 686 else 687 $exclusions .= ' AND t.term_id <> ' . intval($exterm) . ' '; 688 689 } 690 } 691 } 692 if ( !empty($exclude) ) { 693 $exterms = preg_split('/[\s,]+/',$exclude); 694 if ( count($exterms) ) { 695 foreach ( (array) $exterms as $exterm ) { 696 if ( empty($exclusions) ) 697 $exclusions = ' AND ( t.term_id <> ' . intval($exterm) . ' '; 698 else 699 $exclusions .= ' AND t.term_id <> ' . intval($exterm) . ' '; 700 } 701 } 702 } 703 704 if ( !empty($exclusions) ) 705 $exclusions .= ')'; 706 $exclusions = apply_filters('list_terms_exclusions', $exclusions, $args ); 707 $where .= $exclusions; 708 709 if ( !empty($slug) ) { 710 $slug = $this->sanitize_term_slug($slug); 711 $where .= " AND t.slug = '$slug'"; 712 } 713 714 if ( !empty($name__like) ) 715 $where .= " AND t.name LIKE '{$name__like}%'"; 716 717 if ( '' !== $parent ) { 718 $parent = (int) $parent; 719 $where .= " AND tt.parent = '$parent'"; 720 } 721 722 if ( $hide_empty && !$hierarchical ) 723 $where .= ' AND tt.count > 0'; 724 725 // don't limit the query results when we have to descend the family tree 726 if ( ! empty($number) && ! $hierarchical && empty( $child_of ) && '' === $parent ) { 727 if( $offset ) 728 $limit = 'LIMIT ' . $offset . ',' . $number; 729 else 730 $limit = 'LIMIT ' . $number; 731 732 } else 733 $limit = ''; 734 735 if ( !empty($search) ) { 736 $search = like_escape($search); 737 $where .= " AND (t.name LIKE '%$search%')"; 738 } 739 740 if ( !in_array( $fields, array( 'all', 'ids', 'names', 'tt_ids' ) ) ) 741 $fields = 'all'; 742 743 $selects = array(); 744 if ( 'all' == $fields ) 745 $selects = array('t.*', 'tt.*'); 746 else if ( 'ids' == $fields ) 747 $selects = array('t.term_id', 'tt.parent', 'tt.count'); 748 else if ( 'names' == $fields ) 749 $selects = array('t.term_id', 'tt.parent', 'tt.count', 't.name'); 750 $select_this = implode(', ', apply_filters( 'get_terms_fields', $selects, $args )); 751 752 $query = "SELECT $select_this FROM {$this->db->terms} AS t INNER JOIN {$this->db->term_taxonomy} AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy IN ($in_taxonomies) $where ORDER BY $orderby $order $limit"; 753 754 $terms = $this->db->get_results($query); 755 if ( 'all' == $fields ) { 756 $this->update_term_cache($terms); 757 } 758 759 if ( empty($terms) ) { 760 wp_cache_add( $cache_key, array(), 'terms' ); 761 $terms = apply_filters('get_terms', array(), $taxonomies, $args); 762 return $terms; 763 } 764 765 if ( $child_of || $hierarchical ) { 766 $children = $this->_get_term_hierarchy($taxonomies[0]); 767 if ( ! empty($children) ) 768 $terms = & $this->_get_term_children($child_of, $terms, $taxonomies[0]); 769 } 770 771 // Update term counts to include children. 772 if ( $pad_counts ) 773 $this->_pad_term_counts($terms, $taxonomies[0]); 774 775 // Make sure we show empty categories that have children. 776 if ( $hierarchical && $hide_empty && is_array($terms) ) { 777 foreach ( $terms as $k => $term ) { 778 if ( ! $term->count ) { 779 $children = $this->_get_term_children($term->term_id, $terms, $taxonomies[0]); 780 if( is_array($children) ) 781 foreach ( $children as $child ) 782 if ( $child->count ) 783 continue 2; 784 785 // It really is empty 786 unset($terms[$k]); 787 } 788 } 789 } 790 reset ( $terms ); 791 792 $_terms = array(); 793 if ( 'ids' == $fields ) { 794 while ( $term = array_shift($terms) ) 795 $_terms[] = $term->term_id; 796 $terms = $_terms; 797 } elseif ( 'names' == $fields ) { 798 while ( $term = array_shift($terms) ) 799 $_terms[] = $term->name; 800 $terms = $_terms; 801 } 802 803 if ( 0 < $number && intval(@count($terms)) > $number ) { 804 $terms = array_slice($terms, $offset, $number); 805 } 806 807 wp_cache_add( $cache_key, $terms, 'terms' ); 808 809 $terms = apply_filters('get_terms', $terms, $taxonomies, $args); 810 return $terms; 811 } 812 813 /** 814 * Check if Term exists. 815 * 816 * Returns the index of a defined term, or 0 (false) if the term doesn't exist. 817 * 818 * @package WordPress 819 * @subpackage Taxonomy 820 * @since 2.3.0 821 * 822 * @param int|string $term The term to check 823 * @param string $taxonomy The taxonomy name to use 824 * @param int $parent ID of parent term under which to confine the exists search. 825 * @return mixed Get the term id or Term Object, if exists. 826 */ 827 function is_term($term, $taxonomy = '', $parent = 0) { 828 $select = "SELECT term_id FROM {$this->db->terms} as t WHERE "; 829 $tax_select = "SELECT tt.term_id, tt.term_taxonomy_id FROM {$this->db->terms} AS t INNER JOIN {$this->db->term_taxonomy} as tt ON tt.term_id = t.term_id WHERE "; 830 831 if ( is_int($term) ) { 832 if ( 0 == $term ) 833 return 0; 834 $where = 't.term_id = %d'; 835 if ( !empty($taxonomy) ) 836 return $this->db->get_row( $this->db->prepare( $tax_select . $where . " AND tt.taxonomy = %s", $term, $taxonomy ), ARRAY_A ); 837 else 838 return $this->db->get_var( $this->db->prepare( $select . $where, $term ) ); 839 } 840 841 $term = trim( stripslashes( $term ) ); 842 843 if ( '' === $slug = $this->sanitize_term_slug($term) ) 844 return 0; 845 846 $where = 't.slug = %s'; 847 $else_where = 't.name = %s'; 848 $where_fields = array($slug); 849 $else_where_fields = array($term); 850 if ( !empty($taxonomy) ) { 851 $parent = (int) $parent; 852 if ( $parent > 0 ) { 853 $where_fields[] = $parent; 854 $else_where_fields[] = $parent; 855 $where .= ' AND tt.parent = %d'; 856 $else_where .= ' AND tt.parent = %d'; 857 } 858 859 $where_fields[] = $taxonomy; 860 $else_where_fields[] = $taxonomy; 861 862 if ( $result = $this->db->get_row( $this->db->prepare("SELECT tt.term_id, tt.term_taxonomy_id FROM {$this->db->terms} AS t INNER JOIN {$this->db->term_taxonomy} as tt ON tt.term_id = t.term_id WHERE $where AND tt.taxonomy = %s", $where_fields), ARRAY_A) ) 863 return $result; 864 865 return $this->db->get_row( $this->db->prepare("SELECT tt.term_id, tt.term_taxonomy_id FROM {$this->db->terms} AS t INNER JOIN {$this->db->term_taxonomy} as tt ON tt.term_id = t.term_id WHERE $else_where AND tt.taxonomy = %s", $else_where_fields), ARRAY_A); 866 } 867 868 if ( $result = $this->db->get_var( $this->db->prepare("SELECT term_id FROM {$this->db->terms} as t WHERE $where", $where_fields) ) ) 869 return $result; 870 871 return $this->db->get_var( $this->db->prepare("SELECT term_id FROM {$this->db->terms} as t WHERE $else_where", $else_where_fields) ); 872 } 873 874 function sanitize_term_slug( $title, $taxonomy = '', $term_id = 0 ) { 875 return apply_filters( 'pre_term_slug', $title, $taxonomy, $term_id ); 876 } 877 878 function format_to_edit( $text ) { 879 return format_to_edit( $text ); 880 } 881 882 /** 883 * Sanitize Term all fields 884 * 885 * Relies on sanitize_term_field() to sanitize the term. The difference 886 * is that this function will sanitize <strong>all</strong> fields. The 887 * context is based on sanitize_term_field(). 888 * 889 * The $term is expected to be either an array or an object. 890 * 891 * @package WordPress 892 * @subpackage Taxonomy 893 * @since 2.3.0 894 * 895 * @uses $this->sanitize_term_field Used to sanitize all fields in a term 896 * 897 * @param array|object $term The term to check 898 * @param string $taxonomy The taxonomy name to use 899 * @param string $context Default is 'display'. 900 * @return array|object Term with all fields sanitized 901 */ 902 function sanitize_term($term, $taxonomy, $context = 'display') { 903 904 if ( 'raw' == $context ) 905 return $term; 906 907 $fields = array('term_id', 'name', 'description', 'slug', 'count', 'parent', 'term_group'); 908 909 $do_object = false; 910 if ( is_object($term) ) 911 $do_object = true; 912 913 $term_id = $do_object ? $term->term_id : (isset($term['term_id']) ? $term['term_id'] : 0); 914 915 foreach ( (array) $fields as $field ) { 916 if ( $do_object ) { 917 if ( isset($term->$field) ) 918 $term->$field = $this->sanitize_term_field($field, $term->$field, $term_id, $taxonomy, $context); 919 } else { 920 if ( isset($term[$field]) ) 921 $term[$field] = $this->sanitize_term_field($field, $term[$field], $term_id, $taxonomy, $context); 922 } 923 } 924 925 if ( $do_object ) 926 $term->filter = $context; 927 else 928 $term['filter'] = $context; 929 930 return $term; 931 } 932 933 /** 934 * Cleanse the field value in the term based on the context. 935 * 936 * Passing a term field value through the function should be assumed to have 937 * cleansed the value for whatever context the term field is going to be used. 938 * 939 * If no context or an unsupported context is given, then default filters will 940 * be applied. 941 * 942 * There are enough filters for each context to support a custom filtering 943 * without creating your own filter function. Simply create a function that 944 * hooks into the filter you need. 945 * 946 * @package WordPress 947 * @subpackage Taxonomy 948 * @since 2.3.0 949 * 950 * @param string $field Term field to sanitize 951 * @param string $value Search for this term value 952 * @param int $term_id Term ID 953 * @param string $taxonomy Taxonomy Name 954 * @param string $context Either edit, db, display, attribute, or js. 955 * @return mixed sanitized field 956 */ 957 function sanitize_term_field($field, $value, $term_id, $taxonomy, $context) { 958 if ( 'parent' == $field || 'term_id' == $field || 'count' == $field || 'term_group' == $field ) { 959 $value = (int) $value; 960 if ( $value < 0 ) 961 $value = 0; 962 } 963 964 if ( 'raw' == $context ) 965 return $value; 966 967 if ( 'edit' == $context ) { 968 $value = apply_filters("edit_term_$field", $value, $term_id, $taxonomy); 969 $value = apply_filters("edit_$taxonomy}_$field", $value, $term_id); 970 if ( 'description' == $field ) 971 $value = $this->format_to_edit($value); 972 else 973 $value = esc_attr($value); 974 } else if ( 'db' == $context ) { 975 $value = apply_filters("pre_term_$field", $value, $taxonomy); 976 $value = apply_filters("pre_$taxonomy}_$field", $value); 977 // WP DIFF 978 } else if ( 'rss' == $context ) { 979 $value = apply_filters("term_$field}_rss", $value, $taxonomy); 980 $value = apply_filters("$taxonomy}_$field}_rss", $value); 981 } else { 982 // Use display filters by default. 983 $value = apply_filters("term_$field", $value, $term_id, $taxonomy, $context); 984 $value = apply_filters("$taxonomy}_$field", $value, $term_id, $context); 985 } 986 987 if ( 'attribute' == $context ) 988 $value = esc_attr($value); 989 else if ( 'js' == $context ) 990 $value = esc_js($value); 991 992 return $value; 993 } 994 995 /** 996 * Count how many terms are in Taxonomy. 997 * 998 * Default $args is 'ignore_empty' which can be <code>'ignore_empty=true'</code> 999 * or <code>array('ignore_empty' => true);</code>. 1000 * 1001 * @package WordPress 1002 * @subpackage Taxonomy 1003 * @since 2.3.0 1004 * 1005 * @uses wp_parse_args() Turns strings into arrays and merges defaults into an array. 1006 * 1007 * @param string $taxonomy Taxonomy name 1008 * @param array|string $args Overwrite defaults 1009 * @return int How many terms are in $taxonomy 1010 */ 1011 function count_terms( $taxonomy, $args = array() ) { 1012 $defaults = array('ignore_empty' => false); 1013 $args = wp_parse_args($args, $defaults); 1014 extract($args, EXTR_SKIP); 1015 1016 $where = ''; 1017 if ( $ignore_empty ) 1018 $where = 'AND count > 0'; 1019 1020 return $this->db->get_var( $this->db->prepare( "SELECT COUNT(*) FROM {$this->db->term_taxonomy} WHERE taxonomy = %s $where", $taxonomy ) ); 1021 } 1022 1023 /** 1024 * Will unlink the term from the taxonomy. 1025 * 1026 * Will remove the term's relationship to the taxonomy, not the term or taxonomy 1027 * itself. The term and taxonomy will still exist. Will require the term's 1028 * object ID to perform the operation. 1029 * 1030 * @package WordPress 1031 * @subpackage Taxonomy 1032 * @since 2.3.0 1033 * 1034 * @param int $object_id The term Object Id that refers to the term 1035 * @param string|array $taxonomy List of Taxonomy Names or single Taxonomy name. 1036 */ 1037 function delete_object_term_relationships( $object_id, $taxonomies ) { 1038 $object_id = (int) $object_id; 1039 1040 if ( !is_array($taxonomies) ) 1041 $taxonomies = array($taxonomies); 1042 1043 foreach ( (array) $taxonomies as $taxonomy ) { 1044 $terms = $this->get_object_terms($object_id, $taxonomy, array('fields' => 'tt_ids')); 1045 $in_terms = "'" . implode("', '", $terms) . "'"; 1046 $this->db->query( $this->db->prepare( "DELETE FROM {$this->db->term_relationships} WHERE object_id = %d AND term_taxonomy_id IN ($in_terms)", $object_id ) ); 1047 $this->update_term_count($terms, $taxonomy); 1048 } 1049 } 1050 1051 /** 1052 * Removes a term from the database. 1053 * 1054 * If the term is a parent of other terms, then the children will be updated to 1055 * that term's parent. 1056 * 1057 * The $args 'default' will only override the terms found, if there is only one 1058 * term found. Any other and the found terms are used. 1059 * 1060 * The $args 'force_default' will force the term supplied as default to be 1061 * assigned even if the object was not going to be termless 1062 * @package WordPress 1063 * @subpackage Taxonomy 1064 * @since 2.3.0 1065 * 1066 * @uses do_action() Calls both 'delete_term' and 'delete_$taxonomy' action 1067 * hooks, passing term object, term id. 'delete_term' gets an additional 1068 * parameter with the $taxonomy parameter. 1069 * 1070 * @param int $term Term ID 1071 * @param string $taxonomy Taxonomy Name 1072 * @param array|string $args Optional. Change 'default' term id and override found term ids. 1073 * @return bool|WP_Error Returns false if not term; true if completes delete action. 1074 */ 1075 function delete_term( $term, $taxonomy, $args = array() ) { 1076 $term = (int) $term; 1077 1078 if ( ! $ids = $this->is_term($term, $taxonomy) ) 1079 return false; 1080 if ( is_wp_error( $ids ) ) 1081 return $ids; 1082 1083 $tt_id = $ids['term_taxonomy_id']; 1084 1085 $defaults = array(); 1086 $args = wp_parse_args($args, $defaults); 1087 extract($args, EXTR_SKIP); 1088 1089 if ( isset($default) ) { 1090 $default = (int) $default; 1091 if ( !$this->is_term($default, $taxonomy) ) 1092 unset($default); 1093 } 1094 1095 // Update children to point to new parent 1096 if ( $this->is_taxonomy_hierarchical($taxonomy) ) { 1097 $term_obj = $this->get_term($term, $taxonomy); 1098 if ( is_wp_error( $term_obj ) ) 1099 return $term_obj; 1100 $parent = $term_obj->parent; 1101 1102 $this->db->update( $this->db->term_taxonomy, compact( 'parent' ), array( 'parent' => $term_obj->term_id ) + compact( 'taxonomy' ) ); 1103 } 1104 1105 $objects = $this->db->get_col( $this->db->prepare( "SELECT object_id FROM {$this->db->term_relationships} WHERE term_taxonomy_id = %d", $tt_id ) ); 1106 1107 foreach ( (array) $objects as $object ) { 1108 $terms = $this->get_object_terms($object, $taxonomy, array('fields' => 'ids', 'orderby' => 'none')); 1109 if ( 1 == count($terms) && isset($default) ) { 1110 $terms = array($default); 1111 } else { 1112 $terms = array_diff($terms, array($term)); 1113 if (isset($default) && isset($force_default) && $force_default) 1114 $terms = array_merge($terms, array($default)); 1115 } 1116 $terms = array_map('intval', $terms); 1117 $this->set_object_terms($object, $terms, $taxonomy); 1118 } 1119 1120 $this->db->query( $this->db->prepare( "DELETE FROM {$this->db->term_taxonomy} WHERE term_taxonomy_id = %d", $tt_id ) ); 1121 1122 // Delete the term if no taxonomies use it. 1123 if ( !$this->db->get_var( $this->db->prepare( "SELECT COUNT(*) FROM {$this->db->term_taxonomy} WHERE term_id = %d", $term) ) ) 1124 $this->db->query( $this->db->prepare( "DELETE FROM {$this->db->terms} WHERE term_id = %d", $term) ); 1125 1126 $this->clean_term_cache($term, $taxonomy); 1127 1128 do_action('delete_term', $term, $tt_id, $taxonomy); 1129 do_action("delete_$taxonomy", $term, $tt_id); 1130 1131 return true; 1132 } 1133 1134 /** 1135 * Retrieves the terms associated with the given object(s), in the supplied taxonomies. 1136 * 1137 * The following information has to do the $args parameter and for what can be 1138 * contained in the string or array of that parameter, if it exists. 1139 * 1140 * The first argument is called, 'orderby' and has the default value of 'name'. 1141 * The other value that is supported is 'count'. 1142 * 1143 * The second argument is called, 'order' and has the default value of 'ASC'. 1144 * The only other value that will be acceptable is 'DESC'. 1145 * 1146 * The final argument supported is called, 'fields' and has the default value of 1147 * 'all'. There are multiple other options that can be used instead. Supported 1148 * values are as follows: 'all', 'ids', 'names', and finally 1149 * 'all_with_object_id'. 1150 * 1151 * The fields argument also decides what will be returned. If 'all' or 1152 * 'all_with_object_id' is choosen or the default kept intact, then all matching 1153 * terms objects will be returned. If either 'ids' or 'names' is used, then an 1154 * array of all matching term ids or term names will be returned respectively. 1155 * 1156 * @package WordPress 1157 * @subpackage Taxonomy 1158 * @since 2.3.0 1159 * 1160 * @param int|array $object_id The id of the object(s) to retrieve. 1161 * @param string|array $taxonomies The taxonomies to retrieve terms from. 1162 * @param array|string $args Change what is returned 1163 * @return array|WP_Error The requested term data or empty array if no terms found. WP_Error if $taxonomy does not exist. 1164 */ 1165 function get_object_terms($object_ids, $taxonomies, $args = array()) { 1166 if ( !is_array($taxonomies) ) 1167 $taxonomies = array($taxonomies); 1168 1169 foreach ( (array) $taxonomies as $taxonomy ) { 1170 if ( !$this->is_taxonomy($taxonomy) ) 1171 return new WP_Error('invalid_taxonomy', __('Invalid Taxonomy')); 1172 } 1173 1174 if ( !is_array($object_ids) ) 1175 $object_ids = array($object_ids); 1176 $object_ids = array_map('intval', $object_ids); 1177 1178 $defaults = array('orderby' => 'name', 'order' => 'ASC', 'fields' => 'all'); 1179 $args = wp_parse_args( $args, $defaults ); 1180 1181 $terms = array(); 1182 if ( count($taxonomies) > 1 ) { 1183 foreach ( $taxonomies as $index => $taxonomy ) { 1184 $t = $this->get_taxonomy($taxonomy); 1185 if ( isset($t->args) && is_array($t->args) && $args != array_merge($args, $t->args) ) { 1186 unset($taxonomies[$index]); 1187 $terms = array_merge($terms, $this->get_object_terms($object_ids, $taxonomy, array_merge($args, $t->args))); 1188 } 1189 } 1190 } else { 1191 $t = $this->get_taxonomy($taxonomies[0]); 1192 if ( isset($t->args) && is_array($t->args) ) 1193 $args = array_merge($args, $t->args); 1194 } 1195 1196 extract($args, EXTR_SKIP); 1197 1198 if ( 'count' == $orderby ) 1199 $orderby = 'tt.count'; 1200 else if ( 'name' == $orderby ) 1201 $orderby = 't.name'; 1202 else if ( 'slug' == $orderby ) 1203 $orderby = 't.slug'; 1204 else if ( 'term_group' == $orderby ) 1205 $orderby = 't.term_group'; 1206 else if ( 'term_order' == $orderby ) 1207 $orderby = 'tr.term_order'; 1208 else if ( 'none' == $orderby ) { 1209 $orderby = ''; 1210 $order = ''; 1211 } else { 1212 $orderby = 't.term_id'; 1213 } 1214 1215 // tt_ids queries can only be none or tr.term_taxonomy_id 1216 if ( ('tt_ids' == $fields) && !empty($orderby) ) 1217 $orderby = 'tr.term_taxonomy_id'; 1218 1219 if ( !empty($orderby) ) 1220 $orderby = "ORDER BY $orderby"; 1221 1222 $taxonomies = "'" . implode("', '", $taxonomies) . "'"; 1223 $object_ids = implode(', ', $object_ids); 1224 1225 $select_this = ''; 1226 if ( 'all' == $fields ) 1227 $select_this = 't.*, tt.*'; 1228 else if ( 'ids' == $fields ) 1229 $select_this = 't.term_id'; 1230 else if ( 'names' == $fields ) 1231 $select_this = 't.name'; 1232 else if ( 'all_with_object_id' == $fields ) 1233 $select_this = 't.*, tt.*, tr.object_id'; 1234 1235 $query = "SELECT $select_this FROM {$this->db->terms} AS t INNER JOIN {$this->db->term_taxonomy} AS tt ON tt.term_id = t.term_id INNER JOIN {$this->db->term_relationships} AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tt.taxonomy IN ($taxonomies) AND tr.object_id IN ($object_ids) $orderby $order"; 1236 1237 if ( 'all' == $fields || 'all_with_object_id' == $fields ) { 1238 $terms = array_merge($terms, $this->db->get_results($query)); 1239 $this->update_term_cache($terms); 1240 } else if ( 'ids' == $fields || 'names' == $fields ) { 1241 $terms = array_merge($terms, $this->db->get_col($query)); 1242 } else if ( 'tt_ids' == $fields ) { 1243 $terms = $this->db->get_col("SELECT tr.term_taxonomy_id FROM {$this->db->term_relationships} AS tr INNER JOIN {$this->db->term_taxonomy} AS tt ON tr.term_taxonomy_id = tt.term_taxonomy_id WHERE tr.object_id IN ($object_ids) AND tt.taxonomy IN ($taxonomies) $orderby $order"); 1244 } 1245 1246 if ( ! $terms ) 1247 $terms = array(); 1248 1249 return apply_filters('wp_get_object_terms', $terms, $object_ids, $taxonomies, $args); 1250 } 1251 1252 /** 1253 * Adds a new term to the database. Optionally marks it as an alias of an existing term. 1254 * 1255 * Error handling is assigned for the nonexistance of the $taxonomy and $term 1256 * parameters before inserting. If both the term id and taxonomy exist 1257 * previously, then an array will be returned that contains the term id and the 1258 * contents of what is returned. The keys of the array are 'term_id' and 1259 * 'term_taxonomy_id' containing numeric values. 1260 * 1261 * It is assumed that the term does not yet exist or the above will apply. The 1262 * term will be first added to the term table and then related to the taxonomy 1263 * if everything is well. If everything is correct, then several actions will be 1264 * run prior to a filter and then several actions will be run after the filter 1265 * is run. 1266 * 1267 * The arguments decide how the term is handled based on the $args parameter. 1268 * The following is a list of the available overrides and the defaults. 1269 * 1270 * 'alias_of'. There is no default, but if added, expected is the slug that the 1271 * term will be an alias of. Expected to be a string. 1272 * 1273 * 'description'. There is no default. If exists, will be added to the database 1274 * along with the term. Expected to be a string. 1275 * 1276 * 'parent'. Expected to be numeric and default is 0 (zero). Will assign value 1277 * of 'parent' to the term. 1278 * 1279 * 'slug'. Expected to be a string. There is no default. 1280 * 1281 * If 'slug' argument exists then the slug will be checked to see if it is not 1282 * a valid term. If that check succeeds (it is not a valid term), then it is 1283 * added and the term id is given. If it fails, then a check is made to whether 1284 * the taxonomy is hierarchical and the parent argument is not empty. If the 1285 * second check succeeds, the term will be inserted and the term id will be 1286 * given. 1287 * 1288 * @package WordPress 1289 * @subpackage Taxonomy 1290 * @since 2.3.0 1291 * 1292 * @uses do_action() Calls 'create_term' hook with the term id and taxonomy id as parameters. 1293 * @uses do_action() Calls 'create_$taxonomy' hook with term id and taxonomy id as parameters. 1294 * @uses apply_filters() Calls 'term_id_filter' hook with term id and taxonomy id as parameters. 1295 * @uses do_action() Calls 'created_term' hook with the term id and taxonomy id as parameters. 1296 * @uses do_action() Calls 'created_$taxonomy' hook with term id and taxonomy id as parameters. 1297 * 1298 * @param int|string $term The term to add or update. 1299 * @param string $taxonomy The taxonomy to which to add the term 1300 * @param array|string $args Change the values of the inserted term 1301 * @return array|WP_Error The Term ID and Term Taxonomy ID 1302 */ 1303 function insert_term( $term, $taxonomy, $args = array() ) { 1304 if ( !$this->is_taxonomy($taxonomy) ) 1305 return new WP_Error('invalid_taxonomy', __('Invalid taxonomy')); 1306 1307 if ( is_int($term) && 0 == $term ) 1308 return new WP_Error('invalid_term_id', __('Invalid term ID')); 1309 1310 if ( '' == trim($term) ) 1311 return new WP_Error('empty_term_name', __('A name is required for this term')); 1312 1313 $defaults = array( 'alias_of' => '', 'description' => '', 'parent' => 0, 'slug' => ''); 1314 $args = wp_parse_args($args, $defaults); 1315 $args['name'] = $term; 1316 $args['taxonomy'] = $taxonomy; 1317 $args = $this->sanitize_term($args, $taxonomy, 'db'); 1318 extract($args, EXTR_SKIP); 1319 1320 // expected_slashed ($name) 1321 $name = stripslashes($name); 1322 $description = stripslashes($description); 1323 1324 if ( empty($slug) ) 1325 $slug = $this->sanitize_term_slug($name, $taxonomy); 1326 1327 $term_group = 0; 1328 if ( $alias_of ) { 1329 $alias = $this->db->get_row( $this->db->prepare( "SELECT term_id, term_group FROM {$this->db->terms} WHERE slug = %s", $alias_of) ); 1330 if ( $alias->term_group ) { 1331 // The alias we want is already in a group, so let's use that one. 1332 $term_group = $alias->term_group; 1333 } else { 1334 // The alias isn't in a group, so let's create a new one and firstly add the alias term to it. 1335 $term_group = $this->db->get_var("SELECT MAX(term_group) FROM {$this->db->terms}") + 1; 1336 $this->db->query( $this->db->prepare( "UPDATE {$this->db->terms} SET term_group = %d WHERE term_id = %d", $term_group, $alias->term_id ) ); 1337 } 1338 } 1339 1340 if ( ! $term_id = $this->is_term($slug) ) { 1341 if ( false === $this->db->insert( $this->db->terms, compact( 'name', 'slug', 'term_group' ) ) ) 1342 return new WP_Error('db_insert_error', __('Could not insert term into the database'), $this->db->last_error); 1343 $term_id = (int) $this->db->insert_id; 1344 } else if ( $this->is_taxonomy_hierarchical($taxonomy) && !empty($parent) ) { 1345 // If the taxonomy supports hierarchy and the term has a parent, make the slug unique 1346 // by incorporating parent slugs. 1347 $slug = $this->unique_term_slug($slug, (object) $args); 1348 if ( false === $this->db->insert( $this->db->terms, compact( 'name', 'slug', 'term_group' ) ) ) 1349 return new WP_Error('db_insert_error', __('Could not insert term into the database'), $this->db->last_error); 1350 $term_id = (int) $this->db->insert_id; 1351 } 1352 1353 if ( empty($slug) ) { 1354 $slug = $this->sanitize_term_slug($slug, $taxonomy, $term_id); 1355 $this->db->update( $this->db->terms, compact( 'slug' ), compact( 'term_id' ) ); 1356 } 1357 1358 $tt_id = $this->db->get_var( $this->db->prepare( "SELECT tt.term_taxonomy_id FROM {$this->db->term_taxonomy} AS tt INNER JOIN {$this->db->terms} AS t ON tt.term_id = t.term_id WHERE tt.taxonomy = %s AND t.term_id = %d", $taxonomy, $term_id ) ); 1359 1360 if ( !empty($tt_id) ) 1361 return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id); 1362 1363 $this->db->insert( $this->db->term_taxonomy, compact( 'term_id', 'taxonomy', 'description', 'parent') + array( 'count' => 0 ) ); 1364 $tt_id = (int) $this->db->insert_id; 1365 1366 do_action("create_term", $term_id, $tt_id); 1367 do_action("create_$taxonomy", $term_id, $tt_id); 1368 1369 $term_id = apply_filters('term_id_filter', $term_id, $tt_id); 1370 1371 $this->clean_term_cache($term_id, $taxonomy); 1372 1373 do_action("created_term", $term_id, $tt_id); 1374 do_action("created_$taxonomy", $term_id, $tt_id); 1375 1376 return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id); 1377 } 1378 1379 /** 1380 * Create Term and Taxonomy Relationships. 1381 * 1382 * Relates an object (post, link etc) to a term and taxonomy type. Creates the 1383 * term and taxonomy relationship if it doesn't already exist. Creates a term if 1384 * it doesn't exist (using the slug). 1385 * 1386 * A relationship means that the term is grouped in or belongs to the taxonomy. 1387 * A term has no meaning until it is given context by defining which taxonomy it 1388 * exists under. 1389 * 1390 * @package WordPress 1391 * @subpackage Taxonomy 1392 * @since 2.3.0 1393 * 1394 * @param int $object_id The object to relate to. 1395 * @param array|int|string $term The slug or id of the term, will replace all existing 1396 * related terms in this taxonomy. 1397 * @param array|string $taxonomy The context in which to relate the term to the object. 1398 * @param bool $append If false will delete difference of terms. 1399 * @return array|WP_Error Affected Term IDs 1400 */ 1401 function set_object_terms($object_id, $terms, $taxonomy, $append = false) { 1402 $object_id = (int) $object_id; 1403 1404 if ( !$this->is_taxonomy($taxonomy) ) 1405 return new WP_Error('invalid_taxonomy', __('Invalid Taxonomy')); 1406 1407 if ( !is_array($terms) ) 1408 $terms = array($terms); 1409 1410 if ( ! $append ) 1411 $old_tt_ids = $this->get_object_terms($object_id, $taxonomy, array('fields' => 'tt_ids', 'orderby' => 'none')); 1412 1413 $tt_ids = array(); 1414 $term_ids = array(); 1415 1416 foreach ( (array) $terms as $term ) { 1417 if ( !strlen(trim($term)) ) 1418 continue; 1419 1420 if ( !$id = $this->is_term($term, $taxonomy) ) 1421 $id = $this->insert_term($term, $taxonomy); 1422 if ( is_wp_error($id) ) 1423 return $id; 1424 $term_ids[] = $id['term_id']; 1425 $id = $id['term_taxonomy_id']; 1426 $tt_ids[] = $id; 1427 1428 if ( $this->db->get_var( $this->db->prepare( "SELECT term_taxonomy_id FROM {$this->db->term_relationships} WHERE object_id = %d AND term_taxonomy_id = %d", $object_id, $id ) ) ) 1429 continue; 1430 $this->db->insert( $this->db->term_relationships, array( 'object_id' => $object_id, 'term_taxonomy_id' => $id ) ); 1431 } 1432 1433 $this->update_term_count($tt_ids, $taxonomy); 1434 1435 if ( ! $append ) { 1436 $delete_terms = array_diff($old_tt_ids, $tt_ids); 1437 if ( $delete_terms ) { 1438 $in_delete_terms = "'" . implode("', '", $delete_terms) . "'"; 1439 $this->db->query( $this->db->prepare("DELETE FROM {$this->db->term_relationships} WHERE object_id = %d AND term_taxonomy_id IN ($in_delete_terms)", $object_id) ); 1440 $this->update_term_count($delete_terms, $taxonomy); 1441 } 1442 } 1443 1444 $t = $this->get_taxonomy($taxonomy); 1445 if ( ! $append && isset($t->sort) && $t->sort ) { 1446 $values = array(); 1447 $term_order = 0; 1448 $final_tt_ids = $this->get_object_terms($object_id, $taxonomy, 'fields=tt_ids'); 1449 foreach ( $tt_ids as $tt_id ) 1450 if ( in_array($tt_id, $final_tt_ids) ) 1451 $values[] = $this->db->prepare( "(%d, %d, %d)", $object_id, $tt_id, ++$term_order); 1452 if ( $values ) 1453 $this->db->query("INSERT INTO {$this->db->term_relationships} (object_id, term_taxonomy_id, term_order) VALUES " . join(',', $values) . " ON DUPLICATE KEY UPDATE term_order = VALUES(term_order)"); 1454 } 1455 1456 do_action('set_object_terms', $object_id, $terms, $tt_ids, $taxonomy, $append); 1457 return $tt_ids; 1458 } 1459 1460 /** 1461 * Will make slug unique, if it isn't already. 1462 * 1463 * The $slug has to be unique global to every taxonomy, meaning that one 1464 * taxonomy term can't have a matching slug with another taxonomy term. Each 1465 * slug has to be globally unique for every taxonomy. 1466 * 1467 * The way this works is that if the taxonomy that the term belongs to is 1468 * heirarchical and has a parent, it will append that parent to the $slug. 1469 * 1470 * If that still doesn't return an unique slug, then it try to append a number 1471 * until it finds a number that is truely unique. 1472 * 1473 * The only purpose for $term is for appending a parent, if one exists. 1474 * 1475 * @package WordPress 1476 * @subpackage Taxonomy 1477 * @since 2.3.0 1478 * 1479 * @param string $slug The string that will be tried for a unique slug 1480 * @param object $term The term object that the $slug will belong too 1481 * @return string Will return a true unique slug. 1482 */ 1483 function unique_term_slug($slug, $term) { 1484 // If the taxonomy supports hierarchy and the term has a parent, make the slug unique 1485 // by incorporating parent slugs. 1486 if ( $this->is_taxonomy_hierarchical($term->taxonomy) && !empty($term->parent) ) { 1487 $the_parent = $term->parent; 1488 while ( ! empty($the_parent) ) { 1489 $parent_term = $this->get_term($the_parent, $term->taxonomy); 1490 if ( is_wp_error($parent_term) || empty($parent_term) ) 1491 break; 1492 $slug .= '-' . $parent_term->slug; 1493 if ( empty($parent_term->parent) ) 1494 break; 1495 $the_parent = $parent_term->parent; 1496 } 1497 } 1498 1499 // If we didn't get a unique slug, try appending a number to make it unique. 1500 if ( !empty($args['term_id']) ) 1501 $query = $this->db->prepare( "SELECT slug FROM {$this->db->terms} WHERE slug = %s AND term_id != %d", $slug, $args['term_id'] ); 1502 else 1503 $query = $this->db->prepare( "SELECT slug FROM {$this->db->terms} WHERE slug = %s", $slug ); 1504 1505 if ( $this->db->get_var( $query ) ) { 1506 $num = 2; 1507 do { 1508 $alt_slug = $slug . "-$num"; 1509 $num++; 1510 $slug_check = $this->db->get_var( $this->db->prepare( "SELECT slug FROM {$this->db->terms} WHERE slug = %s", $alt_slug ) ); 1511 } while ( $slug_check ); 1512 $slug = $alt_slug; 1513 } 1514 1515 return $slug; 1516 } 1517 1518 /** 1519 * Update term based on arguments provided. 1520 * 1521 * The $args will indiscriminately override all values with the same field name. 1522 * Care must be taken to not override important information need to update or 1523 * update will fail (or perhaps create a new term, neither would be acceptable). 1524 * 1525 * Defaults will set 'alias_of', 'description', 'parent', and 'slug' if not 1526 * defined in $args already. 1527 * 1528 * 'alias_of' will create a term group, if it doesn't already exist, and update 1529 * it for the $term. 1530 * 1531 * If the 'slug' argument in $args is missing, then the 'name' in $args will be 1532 * used. It should also be noted that if you set 'slug' and it isn't unique then 1533 * a WP_Error will be passed back. If you don't pass any slug, then a unique one 1534 * will be created for you. 1535 * 1536 * For what can be overrode in $args, check the term scheme can contain and stay 1537 * away from the term keys. 1538 * 1539 * @package WordPress 1540 * @subpackage Taxonomy 1541 * @since 2.3.0 1542 * 1543 * @uses do_action() Will call both 'edit_term' and 'edit_$taxonomy' twice. 1544 * @uses apply_filters() Will call the 'term_id_filter' filter and pass the term 1545 * id and taxonomy id. 1546 * 1547 * @param int $term_id The ID of the term 1548 * @param string $taxonomy The context in which to relate the term to the object. 1549 * @param array|string $args Overwrite term field values 1550 * @return array|WP_Error Returns Term ID and Taxonomy Term ID 1551 */ 1552 function update_term( $term_id, $taxonomy, $args = array() ) { 1553 if ( !$this->is_taxonomy($taxonomy) ) 1554 return new WP_Error('invalid_taxonomy', __('Invalid taxonomy')); 1555 1556 $term_id = (int) $term_id; 1557 1558 // First, get all of the original args 1559 $term = $this->get_term($term_id, $taxonomy, ARRAY_A); 1560 1561 if ( is_wp_error( $term ) ) 1562 return $term; 1563 1564 // Merge old and new args with new args overwriting old ones. 1565 $args = array_merge($term, $args); 1566 1567 $defaults = array( 'alias_of' => '', 'description' => '', 'parent' => 0, 'slug' => ''); 1568 $args = wp_parse_args($args, $defaults); 1569 $args = $this->sanitize_term($args, $taxonomy, 'db'); 1570 extract($args, EXTR_SKIP); 1571 1572 // expected_slashed ($name) 1573 $name = stripslashes($name); 1574 $description = stripslashes($description); 1575 1576 if ( '' == trim($name) ) 1577 return new WP_Error('empty_term_name', __('A name is required for this term')); 1578 1579 $empty_slug = false; 1580 if ( empty($slug) ) { 1581 $empty_slug = true; 1582 $slug = $this->sanitize_term_slug($name, $taxonomy, $term_id); 1583 } 1584 1585 if ( $alias_of ) { 1586 $alias = $this->db->get_row( $this->db->prepare( "SELECT term_id, term_group FROM {$this->db->terms} WHERE slug = %s", $alias_of) ); 1587 if ( $alias->term_group ) { 1588 // The alias we want is already in a group, so let's use that one. 1589 $term_group = $alias->term_group; 1590 } else { 1591 // The alias isn't in a group, so let's create a new one and firstly add the alias term to it. 1592 $term_group = $this->db->get_var("SELECT MAX(term_group) FROM {$this->db->terms}") + 1; 1593 $this->db->update( $this->db->terms, compact('term_group'), array( 'term_id' => $alias->term_id ) ); 1594 } 1595 } 1596 1597 // Check for duplicate slug 1598 $id = $this->db->get_var( $this->db->prepare( "SELECT term_id FROM {$this->db->terms} WHERE slug = %s", $slug ) ); 1599 if ( $id && ($id != $term_id) ) { 1600 // If an empty slug was passed or the parent changed, reset the slug to something unique. 1601 // Otherwise, bail. 1602 if ( $empty_slug || ( $parent != $term->parent) ) 1603 $slug = $this->unique_term_slug($slug, (object) $args); 1604 else 1605 return new WP_Error('duplicate_term_slug', sprintf(__('The slug “%s” is already in use by another term'), $slug)); 1606 } 1607 1608 $this->db->update($this->db->terms, compact( 'name', 'slug', 'term_group' ), compact( 'term_id' ) ); 1609 1610 if ( empty($slug) ) { 1611 $slug = $this->sanitize_term_slug($name, $taxonomy, $term_id); 1612 $this->db->update( $this->db->terms, compact( 'slug' ), compact( 'term_id' ) ); 1613 } 1614 1615 $tt_id = $this->db->get_var( $this->db->prepare( "SELECT tt.term_taxonomy_id FROM {$this->db->term_taxonomy} AS tt INNER JOIN {$this->db->terms} AS t ON tt.term_id = t.term_id WHERE tt.taxonomy = %s AND t.term_id = %d", $taxonomy, $term_id) ); 1616 1617 $this->db->update( $this->db->term_taxonomy, compact( 'term_id', 'taxonomy', 'description', 'parent' ), array( 'term_taxonomy_id' => $tt_id ) ); 1618 1619 do_action("edit_term", $term_id, $tt_id); 1620 do_action("edit_$taxonomy", $term_id, $tt_id); 1621 1622 $term_id = apply_filters('term_id_filter', $term_id, $tt_id); 1623 1624 $this->clean_term_cache($term_id, $taxonomy); 1625 1626 do_action("edited_term", $term_id, $tt_id); 1627 do_action("edited_$taxonomy", $term_id, $tt_id); 1628 1629 return array('term_id' => $term_id, 'term_taxonomy_id' => $tt_id); 1630 } 1631 1632 /** 1633 * Enable or disable term counting. 1634 * 1635 * @since 2.5.0 1636 * 1637 * @param bool $defer Optional. Enable if true, disable if false. 1638 * @return bool Whether term counting is enabled or disabled. 1639 */ 1640 function defer_term_counting($defer=NULL) { 1641 static $_defer = false; 1642 1643 if ( is_bool($defer) ) { 1644 $_defer = $defer; 1645 // flush any deferred counts 1646 if ( !$defer ) 1647 $this->update_term_count( NULL, NULL, true ); 1648 } 1649 1650 return $_defer; 1651 } 1652 1653 /** 1654 * Updates the amount of terms in taxonomy. 1655 * 1656 * If there is a taxonomy callback applied, then it will be called for updating 1657 * the count. 1658 * 1659 * The default action is to count what the amount of terms have the relationship 1660 * of term ID. Once that is done, then update the database. 1661 * 1662 * @package WordPress 1663 * @subpackage Taxonomy 1664 * @since 2.3.0 1665 * @uses $this->db 1666 * 1667 * @param int|array $terms The ID of the terms 1668 * @param string $taxonomy The context of the term. 1669 * @return bool If no terms will return false, and if successful will return true. 1670 */ 1671 function update_term_count( $terms, $taxonomy, $do_deferred=false ) { 1672 static $_deferred = array(); 1673 1674 if ( $do_deferred ) { 1675 foreach ( (array) array_keys($_deferred) as $tax ) { 1676 $this->update_term_count_now( $_deferred[$tax], $tax ); 1677 unset( $_deferred[$tax] ); 1678 } 1679 } 1680 1681 if ( empty($terms) ) 1682 return false; 1683 1684 if ( !is_array($terms) ) 1685 $terms = array($terms); 1686 1687 if ( $this->defer_term_counting() ) { 1688 if ( !isset($_deferred[$taxonomy]) ) 1689 $_deferred[$taxonomy] = array(); 1690 $_deferred[$taxonomy] = array_unique( array_merge($_deferred[$taxonomy], $terms) ); 1691 return true; 1692 } 1693 1694 return $this->update_term_count_now( $terms, $taxonomy ); 1695 } 1696 1697 /** 1698 * Perform term count update immediately. 1699 * 1700 * @since 2.5.0 1701 * 1702 * @param array $terms The term_taxonomy_id of terms to update. 1703 * @param string $taxonomy The context of the term. 1704 * @return bool Always true when complete. 1705 */ 1706 function update_term_count_now( $terms, $taxonomy ) { 1707 $terms = array_map('intval', $terms); 1708 1709 $taxonomy = $this->get_taxonomy($taxonomy); 1710 if ( !empty($taxonomy->update_count_callback) ) { 1711 call_user_func($taxonomy->update_count_callback, $terms); 1712 } else { 1713 // Default count updater 1714 foreach ( (array) $terms as $term ) { 1715 $count = $this->db->get_var( $this->db->prepare( "SELECT COUNT(*) FROM {$this->db->term_relationships} WHERE term_taxonomy_id = %d", $term) ); 1716 $this->db->update( $this->db->term_taxonomy, compact( 'count' ), array( 'term_taxonomy_id' => $term ) ); 1717 } 1718 1719 } 1720 1721 $this->clean_term_cache($terms); 1722 1723 return true; 1724 } 1725 1726 // 1727 // Cache 1728 // 1729 1730 /** 1731 * Removes the taxonomy relationship to terms from the cache. 1732 * 1733 * Will remove the entire taxonomy relationship containing term $object_id. The 1734 * term IDs have to exist within the taxonomy $object_type for the deletion to 1735 * take place. 1736 * 1737 * @package WordPress 1738 * @subpackage Taxonomy 1739 * @since 2.3 1740 * 1741 * @see $this->get_object_taxonomies() for more on $object_type 1742 * @uses do_action() Will call action hook named, 'clean_object_term_cache' after completion. 1743 * Passes, function params in same order. 1744 * 1745 * @param int|array $object_ids Single or list of term object ID(s) 1746 * @param string $object_type The taxonomy object type 1747 */ 1748 function clean_object_term_cache($object_ids, $object_type) { 1749 if ( !is_array($object_ids) ) 1750 $object_ids = array($object_ids); 1751 1752 foreach ( $object_ids as $id ) 1753 foreach ( $this->get_object_taxonomies($object_type) as $taxonomy ) 1754 wp_cache_delete($id, "{$taxonomy}_relationships"); 1755 1756 do_action('clean_object_term_cache', $object_ids, $object_type); 1757 } 1758 1759 /** 1760 * Will remove all of the term ids from the cache. 1761 * 1762 * @package WordPress 1763 * @subpackage Taxonomy 1764 * @since 2.3.0 1765 * 1766 * @param int|array $ids Single or list of Term IDs 1767 * @param string $taxonomy Can be empty and will assume tt_ids, else will use for context. 1768 */ 1769 function clean_term_cache($ids, $taxonomy = '') { 1770 static $cleaned = array(); 1771 1772 if ( !is_array($ids) ) 1773 $ids = array($ids); 1774 1775 $taxonomies = array(); 1776 // If no taxonomy, assume tt_ids. 1777 if ( empty($taxonomy) ) { 1778 $tt_ids = implode( ',', array_map( 'intval', $ids ) ); 1779 $terms = $this->db->get_results("SELECT term_id, term_taxonomy_id, taxonomy FROM {$this->db->term_taxonomy} WHERE term_taxonomy_id IN ($tt_ids)"); 1780 foreach ( (array) $terms as $term ) { 1781 $taxonomies[] = $term->taxonomy; 1782 wp_cache_delete($term->term_id, $term->taxonomy); 1783 wp_cache_delete($term->term_taxonomy_id, "{$term->taxonomy}:tt_id"); 1784 } 1785 $taxonomies = array_unique($taxonomies); 1786 } else { 1787 $tt_ids = implode( ',', array_map( 'intval', $ids ) ); 1788 $terms = $this->db->get_results("SELECT term_id, term_taxonomy_id FROM {$this->db->term_taxonomy} WHERE term_id IN ($tt_ids)"); 1789 foreach ( (array) $terms as $term ) { 1790 wp_cache_delete($term->term_id, $taxonomy); 1791 wp_cache_delete($term->term_taxonomy_id, "$taxonomy:tt_id"); 1792 } 1793 $taxonomies = array($taxonomy); 1794 } 1795 1796 foreach ( $taxonomies as $taxonomy ) { 1797 if ( isset($cleaned[$taxonomy]) ) 1798 continue; 1799 $cleaned[$taxonomy] = true; 1800 wp_cache_delete('all_ids', $taxonomy); 1801 wp_cache_delete('get', $taxonomy); 1802 $this->delete_children_cache($taxonomy); 1803 } 1804 1805 wp_cache_delete('get_terms', 'terms'); 1806 1807 do_action('clean_term_cache', $ids, $taxonomy); 1808 } 1809 1810 /** 1811 * Retrieves the taxonomy relationship to the term object id. 1812 * 1813 * @package WordPress 1814 * @subpackage Taxonomy 1815 * @since 2.3.0 1816 * 1817 * @uses wp_cache_get() Retrieves taxonomy relationship from cache 1818 * 1819 * @param int|array $id Term object ID 1820 * @param string $taxonomy Taxonomy Name 1821 * @return bool|array Empty array if $terms found, but not $taxonomy. False if nothing is in cache for $taxonomy and $id. 1822 */ 1823 function &get_object_term_cache($id, $taxonomy) { 1824 $cache = wp_cache_get($id, "{$taxonomy}_relationships"); 1825 return $cache; 1826 } 1827 1828 /** 1829 * Updates the cache for Term ID(s). 1830 * 1831 * Will only update the cache for terms not already cached. 1832 * 1833 * The $object_ids expects that the ids be separated by commas, if it is a 1834 * string. 1835 * 1836 * It should be noted that update_object_term_cache() is very time extensive. It 1837 * is advised that the function is not called very often or at least not for a 1838 * lot of terms that exist in a lot of taxonomies. The amount of time increases 1839 * for each term and it also increases for each taxonomy the term belongs to. 1840 * 1841 * @package WordPress 1842 * @subpackage Taxonomy 1843 * @since 2.3.0 1844 * @uses $this->get_object_terms() Used to get terms from the database to update 1845 * 1846 * @param string|array $object_ids Single or list of term object ID(s) 1847 * @param string $object_type The taxonomy object type 1848 * @return null|bool Null value is given with empty $object_ids. False if 1849 */ 1850 function update_object_term_cache($object_ids, $object_type) { 1851 if ( empty($object_ids) ) 1852 return; 1853 1854 if ( !is_array($object_ids) ) 1855 $object_ids = explode(',', $object_ids); 1856 1857 $object_ids = array_map('intval', $object_ids); 1858 1859 $taxonomies = $this->get_object_taxonomies($object_type); 1860 1861 $ids = array(); 1862 foreach ( (array) $object_ids as $id ) { 1863 foreach ( $taxonomies as $taxonomy ) { 1864 if ( false === wp_cache_get($id, "{$taxonomy}_relationships") ) { 1865 $ids[] = $id; 1866 break; 1867 } 1868 } 1869 } 1870 1871 if ( empty( $ids ) ) 1872 return false; 1873 1874 $terms = $this->get_object_terms($ids, $taxonomies, 'fields=all_with_object_id'); 1875 1876 $object_terms = array(); 1877 foreach ( (array) $terms as $term ) 1878 $object_terms[$term->object_id][$term->taxonomy][$term->term_id] = $term; 1879 1880 foreach ( $ids as $id ) { 1881 foreach ( $taxonomies as $taxonomy ) { 1882 if ( ! isset($object_terms[$id][$taxonomy]) ) { 1883 if ( !isset($object_terms[$id]) ) 1884 $object_terms[$id] = array(); 1885 $object_terms[$id][$taxonomy] = array(); 1886 } 1887 } 1888 } 1889 1890 foreach ( $object_terms as $id => $value ) { 1891 foreach ( $value as $taxonomy => $terms ) { 1892 wp_cache_set($id, $terms, "{$taxonomy}_relationships"); 1893 } 1894 } 1895 } 1896 1897 /** 1898 * Updates Terms to Taxonomy in cache. 1899 * 1900 * @package WordPress 1901 * @subpackage Taxonomy 1902 * @since 2.3.0 1903 * 1904 * @param array $terms List of Term objects to change 1905 * @param string $taxonomy Optional. Update Term to this taxonomy in cache 1906 */ 1907 function update_term_cache($terms, $taxonomy = '') { 1908 foreach ( (array) $terms as $term ) { 1909 $term_taxonomy = $taxonomy; 1910 if ( empty($term_taxonomy) ) 1911 $term_taxonomy = $term->taxonomy; 1912 1913 wp_cache_add($term->term_id, $term, $term_taxonomy); 1914 wp_cache_add($term->term_taxonomy_id, $term->term_id, "$term_taxonomy:tt_id"); 1915 } 1916 } 1917 1918 // 1919 // Private 1920 // 1921 1922 /** 1923 * Retrieves children of taxonomy as Term IDs. 1924 * 1925 * @package WordPress 1926 * @subpackage Taxonomy 1927 * @access private 1928 * @since 2.3.0 1929 * 1930 * @uses backpress_update_option() Stores all of the children in "$taxonomy_children" 1931 * option. That is the name of the taxonomy, immediately followed by '_children'. 1932 * 1933 * @param string $taxonomy Taxonomy Name 1934 * @return array Empty if $taxonomy isn't hierarachical or returns children as Term IDs. 1935 */ 1936 function _get_term_hierarchy($taxonomy) { 1937 if ( !$this->is_taxonomy_hierarchical($taxonomy) ) 1938 return array(); 1939 $children = $this->get_children_cache($taxonomy); 1940 if ( is_array($children) ) 1941 return $children; 1942 1943 $children = array(); 1944 $terms = $this->get_terms($taxonomy, 'get=all'); 1945 foreach ( $terms as $term ) { 1946 if ( $term->parent > 0 ) 1947 $children[$term->parent][] = $term->term_id; 1948 } 1949 $this->set_children_cache($taxonomy, $children); 1950 1951 return $children; 1952 } 1953 1954 /** 1955 * Get the subset of $terms that are descendants of $term_id. 1956 * 1957 * If $terms is an array of objects, then _get_term_children returns an array of objects. 1958 * If $terms is an array of IDs, then _get_term_children returns an array of IDs. 1959 * 1960 * @package WordPress 1961 * @subpackage Taxonomy 1962 * @access private 1963 * @since 2.3.0 1964 * 1965 * @param int $term_id The ancestor term: all returned terms should be descendants of $term_id. 1966 * @param array $terms The set of terms---either an array of term objects or term IDs---from which those that are descendants of $term_id will be chosen. 1967 * @param string $taxonomy The taxonomy which determines the hierarchy of the terms. 1968 * @return array The subset of $terms that are descendants of $term_id. 1969 */ 1970 function &_get_term_children($term_id, $terms, $taxonomy) { 1971 $empty_array = array(); 1972 if ( empty($terms) ) 1973 return $empty_array; 1974 1975 $term_list = array(); 1976 $has_children = $this->_get_term_hierarchy($taxonomy); 1977 1978 if ( ( 0 != $term_id ) && ! isset($has_children[$term_id]) ) 1979 return $empty_array; 1980 1981 foreach ( (array) $terms as $term ) { 1982 $use_id = false; 1983 if ( !is_object($term) ) { 1984 $term = $this->get_term($term, $taxonomy); 1985 if ( is_wp_error( $term ) ) 1986 return $term; 1987 $use_id = true; 1988 } 1989 1990 if ( $term->term_id == $term_id ) 1991 continue; 1992 1993 if ( $term->parent == $term_id ) { 1994 if ( $use_id ) 1995 $term_list[] = $term->term_id; 1996 else 1997 $term_list[] = $term; 1998 1999 if ( !isset($has_children[$term->term_id]) ) 2000 continue; 2001 2002 if ( $children = $this->_get_term_children($term->term_id, $terms, $taxonomy) ) 2003 $term_list = array_merge($term_list, $children); 2004 } 2005 } 2006 2007 return $term_list; 2008 } 2009 2010 /** 2011 * Add count of children to parent count. 2012 * 2013 * Recalculates term counts by including items from child terms. Assumes all 2014 * relevant children are already in the $terms argument. 2015 * 2016 * @package WordPress 2017 * @subpackage Taxonomy 2018 * @access private 2019 * @since 2.3 2020 * 2021 * @param array $terms List of Term IDs 2022 * @param string $taxonomy Term Context 2023 * @return null Will break from function if conditions are not met. 2024 */ 2025 function _pad_term_counts(&$terms, $taxonomy) { 2026 return; 2027 } 2028 2029 /** 2030 * Determine if the given object is associated with any of the given terms. 2031 * 2032 * The given terms are checked against the object's terms' term_ids, names and slugs. 2033 * Terms given as integers will only be checked against the object's terms' term_ids. 2034 * If no terms are given, determines if object is associated with any terms in the given taxonomy. 2035 * 2036 * @since 2.7.0 2037 * @uses WP_Taxonomy::get_object_term_cache() 2038 * @uses WP_Taxonomy::get_object_terms() 2039 * 2040 * @param int $object_id. ID of the object (post ID, link ID, ...) 2041 * @param string $taxonomy. Single taxonomy name 2042 * @param int|string|array $terms Optional. Term term_id, name, slug or array of said 2043 * @return bool|WP_Error. WP_Error on input error. 2044 */ 2045 function is_object_in_term( $object_id, $taxonomy, $terms = null ) { 2046 if ( !$object_id = (int) $object_id ) 2047 return new WP_Error( 'invalid_object', __( 'Invalid object ID' ) ); 2048 2049 $object_terms = $this->get_object_term_cache( $object_id, $taxonomy ); 2050 if ( empty( $object_terms ) ) 2051 $object_terms = $this->get_object_terms( $object_id, $taxonomy ); 2052 2053 if ( is_wp_error( $object_terms ) ) 2054 return $object_terms; 2055 if ( empty( $object_terms ) ) 2056 return false; 2057 if ( empty( $terms ) ) 2058 return ( !empty( $object_terms ) ); 2059 2060 $terms = (array) $terms; 2061 2062 if ( $ints = array_filter( $terms, 'is_int' ) ) 2063 $strs = array_diff( $terms, $ints ); 2064 else 2065 $strs =& $terms; 2066 2067 foreach ( $object_terms as $object_term ) { 2068 if ( $ints && in_array( $object_term->term_id, $ints ) ) return true; // If int, check against term_id 2069 if ( $strs ) { 2070 if ( in_array( $object_term->term_id, $strs ) ) return true; 2071 if ( in_array( $object_term->name, $strs ) ) return true; 2072 if ( in_array( $object_term->slug, $strs ) ) return true; 2073 } 2074 } 2075 2076 return false; 2077 } 2078 2079 function get_children_cache( $taxonomy ) { return false; } 2080 function set_children_cache( $taxonomy, $children ) {} 2081 function delete_children_cache( $taxonomy ) {} 2082 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Thu Dec 26 01:01:01 2024 | Cross-referenced by PHPXref 0.7.1 |