*
* @since 4.6.0
* @since 4.6.0 Introduced 'term_taxonomy_id' parameter.
+ * @since 4.7.0 Introduced 'object_ids' parameter.
* @access public
*
* @param string|array $query {
*
* @type string|array $taxonomy Taxonomy name, or array of taxonomies, to which results should
* be limited.
+ * @type int|array $object_ids Optional. Object ID, or array of object IDs. Results will be
+ * limited to terms associated with these objects.
* @type string $orderby Field(s) to order terms by. Accepts term fields ('name',
* 'slug', 'term_group', 'term_id', 'id', 'description'),
* 'count' for term taxonomy count, 'include' to match the
* positive number. Default ''|0 (all).
* @type int $offset The number by which to offset the terms query. Default empty.
* @type string $fields Term fields to query for. Accepts 'all' (returns an array of
- * complete term objects), 'ids' (returns an array of ids),
- * 'id=>parent' (returns an associative array with ids as keys,
- * parent term IDs as values), 'names' (returns an array of term
- * names), 'count' (returns the number of matching terms),
- * 'id=>name' (returns an associative array with ids as keys,
- * term names as values), or 'id=>slug' (returns an associative
- * array with ids as keys, term slugs as values). Default 'all'.
+ * complete term objects), 'all_with_object_id' (returns an
+ * array of term objects with the 'object_id' param; only works
+ * when the `$fields` parameter is 'object_ids' ), 'ids'
+ * (returns an array of ids), 'tt_ids' (returns an array of
+ * term taxonomy ids), 'id=>parent' (returns an associative
+ * array with ids as keys, parent term IDs as values), 'names'
+ * (returns an array of term names), 'count' (returns the number
+ * of matching terms), 'id=>name' (returns an associative array
+ * with ids as keys, term names as values), or 'id=>slug'
+ * (returns an associative array with ids as keys, term slugs
+ * as values). Default 'all'.
* @type bool $count Whether to return a term count (true) or array of term objects
* (false). Will take precedence over `$fields` if true.
* Default false.
public function __construct( $query = '' ) {
$this->query_var_defaults = array(
'taxonomy' => null,
+ 'object_ids' => null,
'orderby' => 'name',
'order' => 'ASC',
'hide_empty' => true,
'cache_domain' => 'core',
'update_term_meta_cache' => true,
'meta_query' => '',
+ 'meta_key' => '',
+ 'meta_value' => '',
+ 'meta_type' => '',
+ 'meta_compare' => '',
);
if ( ! empty( $query ) ) {
$query['taxonomy'] = $taxonomies;
- /**
- * Filters the terms query arguments.
- *
- * @since 3.1.0
- *
- * @param array $args An array of get_terms() arguments.
- * @param array $taxonomies An array of taxonomies.
- */
- $this->query_vars = apply_filters( 'get_terms_args', $query, $taxonomies );
+ $this->query_vars = $query;
/**
* Fires after term query vars have been parsed.
}
}
- $orderby = $this->parse_orderby( $this->query_vars['orderby'] );
+ // 'term_order' is a legal sort order only when joining the relationship table.
+ $_orderby = $this->query_vars['orderby'];
+ if ( 'term_order' === $_orderby && empty( $this->query_vars['object_ids'] ) ) {
+ $_orderby = 'term_id';
+ }
+ $orderby = $this->parse_orderby( $_orderby );
+
if ( $orderby ) {
$orderby = "ORDER BY $orderby";
}
$this->sql_clauses['where']['description__like'] = $wpdb->prepare( "tt.description LIKE %s", '%' . $wpdb->esc_like( $args['description__like'] ) . '%' );
}
+ if ( ! empty( $args['object_ids'] ) ) {
+ $object_ids = $args['object_ids'];
+ if ( ! is_array( $object_ids ) ) {
+ $object_ids = array( $object_ids );
+ }
+
+ $object_ids = implode( ', ', array_map( 'intval', $object_ids ) );
+ $this->sql_clauses['where']['object_ids'] = "tr.object_id IN ($object_ids)";
+ }
+
+ /*
+ * When querying for object relationships, the 'count > 0' check
+ * added by 'hide_empty' is superfluous.
+ */
+ if ( ! empty( $args['object_ids'] ) ) {
+ $args['hide_empty'] = false;
+ }
+
if ( '' !== $parent ) {
$parent = (int) $parent;
$this->sql_clauses['where']['parent'] = "tt.parent = '$parent'";
$selects = array();
switch ( $args['fields'] ) {
case 'all':
+ case 'all_with_object_id' :
+ case 'tt_ids' :
+ case 'slugs' :
$selects = array( 't.*', 'tt.*' );
+ if ( 'all_with_object_id' === $args['fields'] && ! empty( $args['object_ids'] ) ) {
+ $selects[] = 'tr.object_id';
+ }
break;
case 'ids':
case 'id=>parent':
$join .= " INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id";
- $where = implode( ' AND ', $this->sql_clauses['where'] );
+ if ( ! empty( $this->query_vars['object_ids'] ) ) {
+ $join .= " INNER JOIN {$wpdb->term_relationships} AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id";
+ }
- $pieces = array( 'fields', 'join', 'where', 'distinct', 'orderby', 'order', 'limits' );
+ $where = implode( ' AND ', $this->sql_clauses['where'] );
/**
* Filters the terms query SQL clauses.
* @param array $taxonomies An array of taxonomies.
* @param array $args An array of terms query arguments.
*/
- $clauses = apply_filters( 'terms_clauses', compact( $pieces ), $taxonomies, $args );
+ $clauses = apply_filters( 'terms_clauses', compact( 'fields', 'join', 'where', 'distinct', 'orderby', 'order', 'limits' ), $taxonomies, $args );
$fields = isset( $clauses[ 'fields' ] ) ? $clauses[ 'fields' ] : '';
$join = isset( $clauses[ 'join' ] ) ? $clauses[ 'join' ] : '';
// $args can be anything. Only use the args defined in defaults to compute the key.
$key = md5( serialize( wp_array_slice_assoc( $args, array_keys( $this->query_var_defaults ) ) ) . serialize( $taxonomies ) . $this->request );
- $last_changed = wp_cache_get( 'last_changed', 'terms' );
- if ( ! $last_changed ) {
- $last_changed = microtime();
- wp_cache_set( 'last_changed', $last_changed, 'terms' );
- }
+ $last_changed = wp_cache_get_last_changed( 'terms' );
$cache_key = "get_terms:$key:$last_changed";
$cache = wp_cache_get( $cache_key, 'terms' );
if ( false !== $cache ) {
}
if ( 'count' == $_fields ) {
- return $wpdb->get_var( $this->request );
+ $count = $wpdb->get_var( $this->request );
+ wp_cache_set( $cache_key, $count, 'terms' );
+ return $count;
}
$terms = $wpdb->get_results( $this->request );
- if ( 'all' == $_fields ) {
+ if ( 'all' == $_fields || 'all_with_object_id' === $_fields ) {
update_term_cache( $terms );
}
}
}
+ /*
+ * When querying for terms connected to objects, we may get
+ * duplicate results. The duplicates should be preserved if
+ * `$fields` is 'all_with_object_id', but should otherwise be
+ * removed.
+ */
+ if ( ! empty( $args['object_ids'] ) && 'all_with_object_id' != $_fields ) {
+ $_tt_ids = $_terms = array();
+ foreach ( $terms as $term ) {
+ if ( isset( $_tt_ids[ $term->term_id ] ) ) {
+ continue;
+ }
+
+ $_tt_ids[ $term->term_id ] = 1;
+ $_terms[] = $term;
+ }
+
+ $terms = $_terms;
+ }
+
$_terms = array();
if ( 'id=>parent' == $_fields ) {
foreach ( $terms as $term ) {
}
} elseif ( 'ids' == $_fields ) {
foreach ( $terms as $term ) {
- $_terms[] = $term->term_id;
+ $_terms[] = (int) $term->term_id;
+ }
+ } elseif ( 'tt_ids' == $_fields ) {
+ foreach ( $terms as $term ) {
+ $_terms[] = (int) $term->term_taxonomy_id;
}
} elseif ( 'names' == $_fields ) {
foreach ( $terms as $term ) {
$_terms[] = $term->name;
}
+ } elseif ( 'slugs' == $_fields ) {
+ foreach ( $terms as $term ) {
+ $_terms[] = $term->slug;
+ }
} elseif ( 'id=>name' == $_fields ) {
foreach ( $terms as $term ) {
$_terms[ $term->term_id ] = $term->name;
wp_cache_add( $cache_key, $terms, 'terms', DAY_IN_SECONDS );
- if ( 'all' === $_fields ) {
+ if ( 'all' === $_fields || 'all_with_object_id' === $_fields ) {
$terms = array_map( 'get_term', $terms );
}
protected function parse_orderby( $orderby_raw ) {
$_orderby = strtolower( $orderby_raw );
$maybe_orderby_meta = false;
- if ( 'count' == $_orderby ) {
- $orderby = 'tt.count';
- } elseif ( 'name' == $_orderby ) {
- $orderby = 't.name';
- } elseif ( 'slug' == $_orderby ) {
- $orderby = 't.slug';
+
+ if ( in_array( $_orderby, array( 'term_id', 'name', 'slug', 'term_group' ), true ) ) {
+ $orderby = "t.$_orderby";
+ } elseif ( in_array( $_orderby, array( 'count', 'parent', 'taxonomy', 'term_taxonomy_id', 'description' ), true ) ) {
+ $orderby = "tt.$_orderby";
+ } elseif ( 'term_order' === $_orderby ) {
+ $orderby = 'tr.term_order';
} elseif ( 'include' == $_orderby && ! empty( $this->query_vars['include'] ) ) {
- $include = implode( ',', array_map( 'absint', $this->query_vars['include'] ) );
+ $include = implode( ',', wp_parse_id_list( $this->query_vars['include'] ) );
$orderby = "FIELD( t.term_id, $include )";
- } elseif ( 'term_group' == $_orderby ) {
- $orderby = 't.term_group';
- } elseif ( 'description' == $_orderby ) {
- $orderby = 'tt.description';
} elseif ( 'none' == $_orderby ) {
$orderby = '';
} elseif ( empty( $_orderby ) || 'id' == $_orderby || 'term_id' === $_orderby ) {