Ticket #37198: 37198.diff
File 37198.diff, 20.4 KB (added by , 8 years ago) |
---|
-
src/wp-includes/class-wp-term-query.php
93 93 * 94 94 * @since 4.6.0 95 95 * @since 4.6.0 Introduced 'term_taxonomy_id' parameter. 96 * @since 4.7.0 Introduced 'object_ids' parameter. 96 97 * @access public 97 98 * 98 99 * @param string|array $query { … … 100 101 * 101 102 * @type string|array $taxonomy Taxonomy name, or array of taxonomies, to which results should 102 103 * be limited. 104 * @type int|array $object_ids Object ID, or array of object IDs, to which results should be 105 * limited. 103 106 * @type string $orderby Field(s) to order terms by. Accepts term fields ('name', 104 107 * 'slug', 'term_group', 'term_id', 'id', 'description'), 105 108 * 'count' for term taxonomy count, 'include' to match the … … 124 127 * @type int $offset The number by which to offset the terms query. Default empty. 125 128 * @type string $fields Term fields to query for. Accepts 'all' (returns an array of 126 129 * complete term objects), 'ids' (returns an array of ids), 127 * 'id=>parent' (returns an associative array with ids as keys, 130 * 'tt_ids' (returns an array of term taxonomy ids), 'id=>parent' 131 * (returns an associative array with ids as keys, 128 132 * parent term IDs as values), 'names' (returns an array of term 129 133 * names), 'count' (returns the number of matching terms), 130 134 * 'id=>name' (returns an associative array with ids as keys, 131 135 * term names as values), or 'id=>slug' (returns an associative 132 * array with ids as keys, term slugs as values). Default 'all'. 136 * array with ids as keys, term slugs as values). Also accepts 137 * 'all_with_object_id' when the 'object_ids' argument is passed. 138 * Default 'all'. 133 139 * @type bool $count Whether to return a term count (true) or array of term objects 134 140 * (false). Will take precedence over `$fields` if true. 135 141 * Default false. … … 174 180 public function __construct( $query = '' ) { 175 181 $this->query_var_defaults = array( 176 182 'taxonomy' => null, 183 'object_ids' => null, 177 184 'orderby' => 'name', 178 185 'order' => 'ASC', 179 186 'hide_empty' => true, … … 219 226 $query = $this->query_vars; 220 227 } 221 228 229 // Ensures that taxonomy and object_ids are arrays. 230 if ( isset( $query['taxonomy'] ) && ! is_array( $query['taxonomy'] ) ) { 231 $query['taxonomy'] = (array) $query['taxonomy']; 232 } 233 if ( isset( $query['object_ids'] ) && ! is_array( $query['object_ids'] ) ) { 234 $query['object_ids'] = (array) $query['object_ids']; 235 } 236 222 237 $taxonomies = isset( $query['taxonomy'] ) ? $query['taxonomy'] : null; 223 238 224 239 /** … … 368 383 $_parent = false; 369 384 } 370 385 371 if ( $_parent ) { 386 // Verification of term hierarchy must be skipped for `wp_get_object_terms()`. 387 if ( $_parent && empty( $args['object_ids'] ) ) { 372 388 $in_hierarchy = false; 373 389 foreach ( $taxonomies as $_tax ) { 374 390 $hierarchy = _get_term_hierarchy( $_tax ); … … 494 510 $this->sql_clauses['where']['description__like'] = $wpdb->prepare( "tt.description LIKE %s", '%' . $wpdb->esc_like( $args['description__like'] ) . '%' ); 495 511 } 496 512 513 if ( ! empty( $args['object_ids'] ) ) { 514 $object_ids = implode( ', ', $args['object_ids'] ); 515 516 $this->sql_clauses['where']['object_ids'] = "tr.object_id IN ($object_ids)"; 517 } 518 497 519 if ( '' !== $parent ) { 498 520 $parent = (int) $parent; 499 521 $this->sql_clauses['where']['parent'] = "tt.parent = '$parent'"; … … 521 543 $limits = ''; 522 544 } 523 545 524 525 546 if ( ! empty( $args['search'] ) ) { 526 547 $this->sql_clauses['where']['search'] = $this->get_search_sql( $args['search'] ); 527 548 } … … 545 566 $selects = array(); 546 567 switch ( $args['fields'] ) { 547 568 case 'all': 569 case 'all_with_object_id': 548 570 $selects = array( 't.*', 'tt.*' ); 571 if ( 'all_with_object_id' === $args['fields'] && ! empty( $args['object_ids'] ) ) { 572 $selects[] = 'tr.object_id'; 573 } 549 574 break; 550 575 case 'ids': 551 576 case 'id=>parent': 552 577 $selects = array( 't.term_id', 'tt.parent', 'tt.count', 'tt.taxonomy' ); 553 578 break; 579 case 'tt_ids': 580 $selects = array( 't.term_id', 'tt.parent', 'tt.count', 'tt.taxonomy', 'tt.term_taxonomy_id' ); 581 break; 554 582 case 'names': 555 583 $selects = array( 't.term_id', 'tt.parent', 'tt.count', 't.name', 'tt.taxonomy' ); 556 584 break; 585 case 'slugs': 586 $selects = array( 't.term_id', 'tt.parent', 'tt.count', 't.slug', 'tt.taxonomy' ); 587 break; 557 588 case 'count': 558 589 $orderby = ''; 559 590 $order = ''; … … 589 620 590 621 $join .= " INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id"; 591 622 623 if ( ! empty( $this->query_vars['object_ids'] ) ) { 624 $join .= " INNER JOIN $wpdb->term_relationships AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id"; 625 } 626 592 627 $where = implode( ' AND ', $this->sql_clauses['where'] ); 593 628 594 629 $pieces = array( 'fields', 'join', 'where', 'distinct', 'orderby', 'order', 'limits' ); … … 633 668 $cache_key = "get_terms:$key:$last_changed"; 634 669 $cache = wp_cache_get( $cache_key, 'terms' ); 635 670 if ( false !== $cache ) { 636 if ( 'all' === $_fields ) { 637 $cache = array_map( 'get_term', $cache ); 638 } 671 $cache = $this->post_process_terms( $cache, $args ); 639 672 640 673 return $cache; 641 674 } … … 645 678 } 646 679 647 680 $terms = $wpdb->get_results( $this->request ); 648 if ( 'all' == $_fields ) {681 if ( 'all' == $_fields || 'all_with_object_id' == $_fields ) { 649 682 update_term_cache( $terms ); 650 683 } 651 684 … … 705 738 foreach ( $terms as $term ) { 706 739 $_terms[] = $term->term_id; 707 740 } 741 } elseif ( 'tt_ids' == $_fields ) { 742 foreach ( $terms as $term ) { 743 $_terms[] = $term->term_taxonomy_id; 744 } 708 745 } elseif ( 'names' == $_fields ) { 709 746 foreach ( $terms as $term ) { 710 747 $_terms[] = $term->name; 711 748 } 749 } elseif ( 'slugs' == $_fields ) { 750 foreach ( $terms as $term ) { 751 $_terms[] = $term->slug; 752 } 712 753 } elseif ( 'id=>name' == $_fields ) { 713 754 foreach ( $terms as $term ) { 714 755 $_terms[ $term->term_id ] = $term->name; … … 719 760 } 720 761 } 721 762 763 // Removes duplicates if the object ID should not be included. 764 if ( ! empty( $args['object_ids'] ) && 'all' == $_fields ) { 765 $_tt_ids = array(); 766 foreach ( $terms as $term ) { 767 if ( in_array( $term->term_taxonomy_id, $_tt_ids ) ) { 768 continue; 769 } 770 771 $_tt_ids[] = $term->term_taxonomy_id; 772 $_terms[] = $term; 773 } 774 $terms = $_terms; 775 } 776 722 777 if ( ! empty( $_terms ) ) { 723 778 $terms = $_terms; 724 779 } 725 780 781 if ( ! empty( $args['object_ids'] ) && 'all' != $_fields && 'all_with_object_id' != $_fields ) { 782 $terms = array_values( array_unique( $terms ) ); 783 } 784 726 785 // Hierarchical queries are not limited, so 'offset' and 'number' must be handled now. 727 786 if ( $hierarchical && $number && is_array( $terms ) ) { 728 787 if ( $offset >= count( $terms ) ) { … … 734 793 735 794 wp_cache_add( $cache_key, $terms, 'terms', DAY_IN_SECONDS ); 736 795 737 if ( 'all' === $_fields ) { 738 $terms = array_map( 'get_term', $terms ); 739 } 796 $terms = $this->post_process_terms( $terms, $args ); 740 797 741 798 $this->terms = $terms; 742 799 return $this->terms; … … 756 813 protected function parse_orderby( $orderby_raw ) { 757 814 $_orderby = strtolower( $orderby_raw ); 758 815 $maybe_orderby_meta = false; 759 if ( 'count' == $_orderby ) { 760 $orderby = 'tt.count'; 761 } elseif ( 'name' == $_orderby ) { 762 $orderby = 't.name'; 763 } elseif ( 'slug' == $_orderby ) { 764 $orderby = 't.slug'; 816 817 // Support full inputs including the table shortcut for backwards compatibility. 818 if ( false !== strpos( $_orderby, '.' ) ) { 819 $_orderby = explode( '.', $_orderby )[1]; 820 } 821 822 if ( in_array( $_orderby, array( 'term_id', 'name', 'slug', 'term_group' ) ) ) { 823 $orderby = "t.$_orderby"; 824 } elseif ( in_array( $_orderby, array( 'count', 'parent', 'taxonomy', 'term_taxonomy_id', 'description' ) ) ) { 825 $orderby = "tt.$_orderby"; 826 } elseif ( ! empty( $this->query_vars['object_ids'] ) && 'term_order' == $_orderby ) { 827 $orderby = 'tr.term_order'; 765 828 } elseif ( 'include' == $_orderby && ! empty( $this->query_vars['include'] ) ) { 766 829 $include = implode( ',', array_map( 'absint', $this->query_vars['include'] ) ); 767 830 $orderby = "FIELD( t.term_id, $include )"; 768 } elseif ( 'term_group' == $_orderby ) {769 $orderby = 't.term_group';770 } elseif ( 'description' == $_orderby ) {771 $orderby = 'tt.description';772 831 } elseif ( 'none' == $_orderby ) { 773 832 $orderby = ''; 774 } elseif ( empty( $_orderby ) || 'id' == $_orderby || 'term_id' === $_orderby) {833 } elseif ( empty( $_orderby ) || 'id' == $_orderby ) { 775 834 $orderby = 't.term_id'; 776 835 } else { 777 836 $orderby = 't.name'; … … 901 960 902 961 return $wpdb->prepare( '((t.name LIKE %s) OR (t.slug LIKE %s))', $like, $like ); 903 962 } 963 964 /** 965 * Post-processes terms. 966 * 967 * This function is called both after retrieving terms and getting terms from cache. 968 * It ensures proper return values depending on the query vars. 969 * 970 * `wp_get_object_terms()` expects sanitized terms, so this logic is executed here. 971 * 972 * @since 4.7.0 973 * @access private 974 * 975 * @param array $terms List of terms to preprocess. 976 * @param array $args Array of arguments. 977 * @return stdClass The raw term object. 978 */ 979 private function post_process_terms( $terms, $args ) { 980 if ( 'all' === $args['fields'] || 'all_with_object_id' === $args['fields'] ) { 981 if ( ! empty( $args['object_ids'] ) ) { 982 $object_id_index = array(); 983 foreach ( $terms as $key => $term ) { 984 $terms[ $key ] = sanitize_term( $term, $term->taxonomy, 'raw' ); 985 if ( isset( $terms[ $key ]->object_id ) ) { 986 $object_id_index[ $key ] = $terms[ $key ]->object_id; 987 } 988 } 989 } 990 991 $terms = array_map( 'get_term', $terms ); 992 993 if ( ! empty( $args['object_ids'] ) ) { 994 foreach ( $terms as $key => $term ) { 995 if ( isset( $object_id_index[ $key ] ) ) { 996 $terms[ $key ]->object_id = $object_id_index[ $key ]; 997 } 998 } 999 } 1000 } elseif ( 'count' !== $args['fields'] && ! empty( $args['object_ids'] ) ) { 1001 foreach ( $terms as $key => $term ) { 1002 $field = 'ids' == $args['fields'] ? 'term_id' : ( 'tt_ids' == $args['fields'] ? 'term_taxonomy_id' : 'name' ); 1003 $taxonomy = ! empty( $args['taxonomy'] ) ? $args['taxonomy'][0] : ''; 1004 $terms[ $key ] = sanitize_term_field( $field, $term, $term, $taxonomy, 'raw' ); 1005 } 1006 } 1007 1008 return $terms; 1009 } 904 1010 } -
src/wp-includes/taxonomy.php
1940 1940 * Introduced `$parent` argument. 1941 1941 * @since 4.4.0 Introduced `$meta_query` and `$update_term_meta_cache` arguments. When `$fields` is 'all' or 1942 1942 * 'all_with_object_id', an array of `WP_Term` objects will be returned. 1943 * @since 4.7.0 The function now uses `WP_Term_Query` via `get_terms()`. 1943 1944 * 1945 * @see get_terms() 1946 * 1944 1947 * @global wpdb $wpdb WordPress database abstraction object. 1945 1948 * 1946 1949 * @param int|array $object_ids The ID(s) of the object(s) to retrieve. 1947 1950 * @param string|array $taxonomies The taxonomies to retrieve terms from. 1948 * @param array|string $args { 1949 * Array of arguments. 1950 * @type string $orderby Field by which results should be sorted. Accepts 'name', 'count', 'slug', 1951 * 'term_group', 'term_order', 'taxonomy', 'parent', or 'term_taxonomy_id'. 1952 * Default 'name'. 1953 * @type string $order Sort order. Accepts 'ASC' or 'DESC'. Default 'ASC'. 1954 * @type string $fields Fields to return for matched terms. Accepts 'all', 'ids', 'names', and 1955 * 'all_with_object_id'. Note that 'all' or 'all_with_object_id' will result 1956 * in an array of term objects being returned, 'ids' will return an array of 1957 * integers, and 'names' an array of strings. 1958 * @type int $parent Optional. Limit results to the direct children of a given term ID. 1959 * @type bool $update_term_meta_cache Whether to prime termmeta cache for matched terms. Only applies when 1960 * `$fields` is 'all', 'all_with_object_id', or 'term_id'. Default true. 1961 * @type array $meta_query Meta query clauses to limit retrieved terms by. See `WP_Meta_Query`. 1962 * Default empty. 1963 * } 1951 * @param array|string $args Array of arguments. For a list of supported arguments, see `get_terms()`. 1964 1952 * @return array|WP_Error The requested term data or empty array if no terms found. 1965 1953 * WP_Error if any of the $taxonomies don't exist. 1966 1954 */ … … 1967 1955 function wp_get_object_terms($object_ids, $taxonomies, $args = array()) { 1968 1956 global $wpdb; 1969 1957 1970 if ( empty( $object_ids ) || empty( $taxonomies ) ) 1958 if ( empty( $object_ids ) || empty( $taxonomies ) ) { 1971 1959 return array(); 1960 } 1972 1961 1973 if ( !is_array($taxonomies) ) 1974 $taxonomies = array($taxonomies); 1962 if ( ! is_array( $taxonomies ) ) { 1963 $taxonomies = array( $taxonomies ); 1964 } 1975 1965 1976 1966 foreach ( $taxonomies as $taxonomy ) { 1977 if ( ! taxonomy_exists($taxonomy) ) 1978 return new WP_Error('invalid_taxonomy', __('Invalid taxonomy')); 1967 if ( ! taxonomy_exists( $taxonomy ) ) { 1968 return new WP_Error( 'invalid_taxonomy', __( 'Invalid taxonomy' ) ); 1969 } 1979 1970 } 1980 1971 1981 if ( !is_array($object_ids) ) 1982 $object_ids = array($object_ids); 1983 $object_ids = array_map('intval', $object_ids); 1972 // For backwards compatibility, the orderby key defaults to the term ID. 1973 $args = wp_parse_args( $args, array( 'orderby' => 'id' ) ); 1984 1974 1985 $defaults = array( 1986 'orderby' => 'name', 1987 'order' => 'ASC', 1988 'fields' => 'all', 1989 'parent' => '', 1990 'update_term_meta_cache' => true, 1991 'meta_query' => '', 1992 ); 1993 $args = wp_parse_args( $args, $defaults ); 1975 $args['taxonomy'] = $taxonomies; 1976 $args['object_ids'] = $object_ids; 1994 1977 1995 $terms = array(); 1996 if ( count($taxonomies) > 1 ) { 1997 foreach ( $taxonomies as $index => $taxonomy ) { 1998 $t = get_taxonomy($taxonomy); 1999 if ( isset($t->args) && is_array($t->args) && $args != array_merge($args, $t->args) ) { 2000 unset($taxonomies[$index]); 2001 $terms = array_merge($terms, wp_get_object_terms($object_ids, $taxonomy, array_merge($args, $t->args))); 2002 } 2003 } 2004 } else { 2005 $t = get_taxonomy($taxonomies[0]); 2006 if ( isset($t->args) && is_array($t->args) ) 2007 $args = array_merge($args, $t->args); 2008 } 1978 $terms = get_terms( $args ); 2009 1979 2010 $orderby = $args['orderby']; 2011 $order = $args['order']; 2012 $fields = $args['fields']; 2013 2014 if ( in_array( $orderby, array( 'term_id', 'name', 'slug', 'term_group' ) ) ) { 2015 $orderby = "t.$orderby"; 2016 } elseif ( in_array( $orderby, array( 'count', 'parent', 'taxonomy', 'term_taxonomy_id' ) ) ) { 2017 $orderby = "tt.$orderby"; 2018 } elseif ( 'term_order' === $orderby ) { 2019 $orderby = 'tr.term_order'; 2020 } elseif ( 'none' === $orderby ) { 2021 $orderby = ''; 2022 $order = ''; 2023 } else { 2024 $orderby = 't.term_id'; 1980 if ( ! is_array( $object_ids ) ) { 1981 $object_ids = array( $object_ids ); 2025 1982 } 1983 $object_ids = array_map( 'intval', $object_ids ); 2026 1984 2027 // tt_ids queries can only be none or tr.term_taxonomy_id2028 if ( ('tt_ids' == $fields) && !empty($orderby) )2029 $orderby = 'tr.term_taxonomy_id';2030 2031 if ( !empty($orderby) )2032 $orderby = "ORDER BY $orderby";2033 2034 $order = strtoupper( $order );2035 if ( '' !== $order && ! in_array( $order, array( 'ASC', 'DESC' ) ) )2036 $order = 'ASC';2037 2038 1985 $taxonomy_array = $taxonomies; 2039 1986 $object_id_array = $object_ids; 2040 $taxonomies = "'" . implode("', '", array_map( 'esc_sql', $taxonomies ) ) . "'";2041 $object_ids = implode(', ', $object_ids);2042 1987 2043 $select_this = ''; 2044 if ( 'all' == $fields ) { 2045 $select_this = 't.*, tt.*'; 2046 } elseif ( 'ids' == $fields ) { 2047 $select_this = 't.term_id'; 2048 } elseif ( 'names' == $fields ) { 2049 $select_this = 't.name'; 2050 } elseif ( 'slugs' == $fields ) { 2051 $select_this = 't.slug'; 2052 } elseif ( 'all_with_object_id' == $fields ) { 2053 $select_this = 't.*, tt.*, tr.object_id'; 2054 } 1988 // These modifications are here for backwards compatibility. 1989 $taxonomies = "'" . implode( "', '", array_map( 'esc_sql', $taxonomies ) ) . "'"; 1990 $object_ids = implode( ', ', $object_ids ); 2055 1991 2056 $where = array(2057 "tt.taxonomy IN ($taxonomies)",2058 "tr.object_id IN ($object_ids)",2059 );2060 2061 if ( '' !== $args['parent'] ) {2062 $where[] = $wpdb->prepare( 'tt.parent = %d', $args['parent'] );2063 }2064 2065 // Meta query support.2066 $meta_query_join = '';2067 if ( ! empty( $args['meta_query'] ) ) {2068 $mquery = new WP_Meta_Query( $args['meta_query'] );2069 $mq_sql = $mquery->get_sql( 'term', 't', 'term_id' );2070 2071 $meta_query_join .= $mq_sql['join'];2072 2073 // Strip leading AND.2074 $where[] = preg_replace( '/^\s*AND/', '', $mq_sql['where'] );2075 }2076 2077 $where = implode( ' AND ', $where );2078 2079 $query = "SELECT $select_this FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON tt.term_id = t.term_id INNER JOIN $wpdb->term_relationships AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id $meta_query_join WHERE $where $orderby $order";2080 2081 $objects = false;2082 if ( 'all' == $fields || 'all_with_object_id' == $fields ) {2083 $_terms = $wpdb->get_results( $query );2084 $object_id_index = array();2085 foreach ( $_terms as $key => $term ) {2086 $term = sanitize_term( $term, $taxonomy, 'raw' );2087 $_terms[ $key ] = $term;2088 2089 if ( isset( $term->object_id ) ) {2090 $object_id_index[ $key ] = $term->object_id;2091 }2092 }2093 2094 update_term_cache( $_terms );2095 $_terms = array_map( 'get_term', $_terms );2096 2097 // Re-add the object_id data, which is lost when fetching terms from cache.2098 if ( 'all_with_object_id' === $fields ) {2099 foreach ( $_terms as $key => $_term ) {2100 if ( isset( $object_id_index[ $key ] ) ) {2101 $_term->object_id = $object_id_index[ $key ];2102 }2103 }2104 }2105 2106 $terms = array_merge( $terms, $_terms );2107 $objects = true;2108 2109 } elseif ( 'ids' == $fields || 'names' == $fields || 'slugs' == $fields ) {2110 $_terms = $wpdb->get_col( $query );2111 $_field = ( 'ids' == $fields ) ? 'term_id' : 'name';2112 foreach ( $_terms as $key => $term ) {2113 $_terms[$key] = sanitize_term_field( $_field, $term, $term, $taxonomy, 'raw' );2114 }2115 $terms = array_merge( $terms, $_terms );2116 } elseif ( 'tt_ids' == $fields ) {2117 $terms = $wpdb->get_col("SELECT tr.term_taxonomy_id FROM $wpdb->term_relationships AS tr INNER JOIN $wpdb->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");2118 foreach ( $terms as $key => $tt_id ) {2119 $terms[$key] = sanitize_term_field( 'term_taxonomy_id', $tt_id, 0, $taxonomy, 'raw' ); // 0 should be the term id, however is not needed when using raw context.2120 }2121 }2122 2123 // Update termmeta cache, if necessary.2124 if ( $args['update_term_meta_cache'] && ( 'all' === $fields || 'all_with_object_id' === $fields || 'ids' === $fields ) ) {2125 if ( 'ids' === $fields ) {2126 $term_ids = $terms;2127 } else {2128 $term_ids = wp_list_pluck( $terms, 'term_id' );2129 }2130 2131 update_termmeta_cache( $term_ids );2132 }2133 2134 if ( ! $terms ) {2135 $terms = array();2136 } elseif ( $objects && 'all_with_object_id' !== $fields ) {2137 $_tt_ids = array();2138 $_terms = array();2139 foreach ( $terms as $term ) {2140 if ( in_array( $term->term_taxonomy_id, $_tt_ids ) ) {2141 continue;2142 }2143 2144 $_tt_ids[] = $term->term_taxonomy_id;2145 $_terms[] = $term;2146 }2147 $terms = $_terms;2148 } elseif ( ! $objects ) {2149 $terms = array_values( array_unique( $terms ) );2150 }2151 2152 1992 /** 2153 1993 * Filters the terms for a given object or objects. 2154 1994 *