Retrieves the query results.
The return type varies depending on the value passed to $args['fields'].
The following will result in an array of WP_Term objects being returned:
The following will result in a numeric string being returned:
The following will result in an array of text strings being returned:
The following will result in an array of numeric strings being returned:
The following will result in an array of integers being returned:
'count' is passed to $args['fields'].public function get_terms() {
global $wpdb;
$this->parse_query( $this->query_vars );
$args = &$this->query_vars;
// Set up meta_query so it's available to 'pre_get_terms'.
$this->meta_query = new WP_Meta_Query();
$this->meta_query->parse_query_vars( $args );
/**
* Fires before terms are retrieved.
*
* @since 4.6.0
*
* @param WP_Term_Query $query Current instance of WP_Term_Query (passed by reference).
*/
do_action_ref_array( 'pre_get_terms', array( &$this ) );
$taxonomies = (array) $args['taxonomy'];
// Save queries by not crawling the tree in the case of multiple taxes or a flat tax.
$has_hierarchical_tax = false;
if ( $taxonomies ) {
foreach ( $taxonomies as $_tax ) {
if ( is_taxonomy_hierarchical( $_tax ) ) {
$has_hierarchical_tax = true;
}
}
} else {
// When no taxonomies are provided, assume we have to descend the tree.
$has_hierarchical_tax = true;
}
if ( ! $has_hierarchical_tax ) {
$args['hierarchical'] = false;
$args['pad_counts'] = false;
}
// 'parent' overrides 'child_of'.
if ( 0 < (int) $args['parent'] ) {
$args['child_of'] = false;
}
if ( 'all' === $args['get'] ) {
$args['childless'] = false;
$args['child_of'] = 0;
$args['hide_empty'] = 0;
$args['hierarchical'] = false;
$args['pad_counts'] = false;
}
/**
* Filters the terms query arguments.
*
* @since 3.1.0
*
* @param array $args An array of get_terms() arguments.
* @param string[] $taxonomies An array of taxonomy names.
*/
$args = apply_filters( 'get_terms_args', $args, $taxonomies );
// Avoid the query if the queried parent/child_of term has no descendants.
$child_of = $args['child_of'];
$parent = $args['parent'];
if ( $child_of ) {
$_parent = $child_of;
} elseif ( $parent ) {
$_parent = $parent;
} else {
$_parent = false;
}
if ( $_parent ) {
$in_hierarchy = false;
foreach ( $taxonomies as $_tax ) {
$hierarchy = _get_term_hierarchy( $_tax );
if ( isset( $hierarchy[ $_parent ] ) ) {
$in_hierarchy = true;
}
}
if ( ! $in_hierarchy ) {
if ( 'count' === $args['fields'] ) {
return 0;
} else {
$this->terms = array();
return $this->terms;
}
}
}
// '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";
}
$order = $this->parse_order( $this->query_vars['order'] );
if ( $taxonomies ) {
$this->sql_clauses['where']['taxonomy'] =
"tt.taxonomy IN ('" . implode( "', '", array_map( 'esc_sql', $taxonomies ) ) . "')";
}
if ( empty( $args['exclude'] ) ) {
$args['exclude'] = array();
}
if ( empty( $args['include'] ) ) {
$args['include'] = array();
}
$exclude = $args['exclude'];
$exclude_tree = $args['exclude_tree'];
$include = $args['include'];
$inclusions = '';
if ( ! empty( $include ) ) {
$exclude = '';
$exclude_tree = '';
$inclusions = implode( ',', wp_parse_id_list( $include ) );
}
if ( ! empty( $inclusions ) ) {
$this->sql_clauses['where']['inclusions'] = 't.term_id IN ( ' . $inclusions . ' )';
}
$exclusions = array();
if ( ! empty( $exclude_tree ) ) {
$exclude_tree = wp_parse_id_list( $exclude_tree );
$excluded_children = $exclude_tree;
foreach ( $exclude_tree as $extrunk ) {
$excluded_children = array_merge(
$excluded_children,
(array) get_terms(
array(
'taxonomy' => reset( $taxonomies ),
'child_of' => (int) $extrunk,
'fields' => 'ids',
'hide_empty' => 0,
)
)
);
}
$exclusions = array_merge( $excluded_children, $exclusions );
}
if ( ! empty( $exclude ) ) {
$exclusions = array_merge( wp_parse_id_list( $exclude ), $exclusions );
}
// 'childless' terms are those without an entry in the flattened term hierarchy.
$childless = (bool) $args['childless'];
if ( $childless ) {
foreach ( $taxonomies as $_tax ) {
$term_hierarchy = _get_term_hierarchy( $_tax );
$exclusions = array_merge( array_keys( $term_hierarchy ), $exclusions );
}
}
if ( ! empty( $exclusions ) ) {
$exclusions = 't.term_id NOT IN (' . implode( ',', array_map( 'intval', $exclusions ) ) . ')';
} else {
$exclusions = '';
}
/**
* Filters the terms to exclude from the terms query.
*
* @since 2.3.0
*
* @param string $exclusions `NOT IN` clause of the terms query.
* @param array $args An array of terms query arguments.
* @param string[] $taxonomies An array of taxonomy names.
*/
$exclusions = apply_filters( 'list_terms_exclusions', $exclusions, $args, $taxonomies );
if ( ! empty( $exclusions ) ) {
// Strip leading 'AND'. Must do string manipulation here for backward compatibility with filter.
$this->sql_clauses['where']['exclusions'] = preg_replace( '/^\s*AND\s*/', '', $exclusions );
}
if ( '' === $args['name'] ) {
$args['name'] = array();
} else {
$args['name'] = (array) $args['name'];
}
if ( ! empty( $args['name'] ) ) {
$names = $args['name'];
foreach ( $names as &$_name ) {
// `sanitize_term_field()` returns slashed data.
$_name = stripslashes( sanitize_term_field( 'name', $_name, 0, reset( $taxonomies ), 'db' ) );
}
$this->sql_clauses['where']['name'] = "t.name IN ('" . implode( "', '", array_map( 'esc_sql', $names ) ) . "')";
}
if ( '' === $args['slug'] ) {
$args['slug'] = array();
} else {
$args['slug'] = array_map( 'sanitize_title', (array) $args['slug'] );
}
if ( ! empty( $args['slug'] ) ) {
$slug = implode( "', '", $args['slug'] );
$this->sql_clauses['where']['slug'] = "t.slug IN ('" . $slug . "')";
}
if ( '' === $args['term_taxonomy_id'] ) {
$args['term_taxonomy_id'] = array();
} else {
$args['term_taxonomy_id'] = array_map( 'intval', (array) $args['term_taxonomy_id'] );
}
if ( ! empty( $args['term_taxonomy_id'] ) ) {
$tt_ids = implode( ',', $args['term_taxonomy_id'] );
$this->sql_clauses['where']['term_taxonomy_id'] = "tt.term_taxonomy_id IN ({$tt_ids})";
}
if ( ! empty( $args['name__like'] ) ) {
$this->sql_clauses['where']['name__like'] = $wpdb->prepare(
't.name LIKE %s',
'%' . $wpdb->esc_like( $args['name__like'] ) . '%'
);
}
if ( ! empty( $args['description__like'] ) ) {
$this->sql_clauses['where']['description__like'] = $wpdb->prepare(
'tt.description LIKE %s',
'%' . $wpdb->esc_like( $args['description__like'] ) . '%'
);
}
if ( '' === $args['object_ids'] ) {
$args['object_ids'] = array();
} else {
$args['object_ids'] = array_map( 'intval', (array) $args['object_ids'] );
}
if ( ! empty( $args['object_ids'] ) ) {
$object_ids = implode( ', ', $args['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'";
}
$hierarchical = $args['hierarchical'];
if ( 'count' === $args['fields'] ) {
$hierarchical = false;
}
if ( $args['hide_empty'] && ! $hierarchical ) {
$this->sql_clauses['where']['count'] = 'tt.count > 0';
}
$number = $args['number'];
$offset = $args['offset'];
// Don't limit the query results when we have to descend the family tree.
if ( $number && ! $hierarchical && ! $child_of && '' === $parent ) {
if ( $offset ) {
$limits = 'LIMIT ' . $offset . ',' . $number;
} else {
$limits = 'LIMIT ' . $number;
}
} else {
$limits = '';
}
if ( ! empty( $args['search'] ) ) {
$this->sql_clauses['where']['search'] = $this->get_search_sql( $args['search'] );
}
// Meta query support.
$join = '';
$distinct = '';
// Reparse meta_query query_vars, in case they were modified in a 'pre_get_terms' callback.
$this->meta_query->parse_query_vars( $this->query_vars );
$mq_sql = $this->meta_query->get_sql( 'term', 't', 'term_id' );
$meta_clauses = $this->meta_query->get_clauses();
if ( ! empty( $meta_clauses ) ) {
$join .= $mq_sql['join'];
// Strip leading 'AND'.
$this->sql_clauses['where']['meta_query'] = preg_replace( '/^\s*AND\s*/', '', $mq_sql['where'] );
$distinct .= 'DISTINCT';
}
$selects = array();
switch ( $args['fields'] ) {
case 'count':
$orderby = '';
$order = '';
$selects = array( 'COUNT(*)' );
break;
default:
$selects = array( 't.term_id' );
if ( 'all_with_object_id' === $args['fields'] && ! empty( $args['object_ids'] ) ) {
$selects[] = 'tr.object_id';
}
break;
}
$_fields = $args['fields'];
/**
* Filters the fields to select in the terms query.
*
* Field lists modified using this filter will only modify the term fields returned
* by the function when the `$fields` parameter set to 'count' or 'all'. In all other
* cases, the term fields in the results array will be determined by the `$fields`
* parameter alone.
*
* Use of this filter can result in unpredictable behavior, and is not recommended.
*
* @since 2.8.0
*
* @param string[] $selects An array of fields to select for the terms query.
* @param array $args An array of term query arguments.
* @param string[] $taxonomies An array of taxonomy names.
*/
$fields = implode( ', ', apply_filters( 'get_terms_fields', $selects, $args, $taxonomies ) );
$join .= " INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id";
if ( ! empty( $this->query_vars['object_ids'] ) ) {
$join .= " INNER JOIN {$wpdb->term_relationships} AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id";
$distinct = 'DISTINCT';
}
$where = implode( ' AND ', $this->sql_clauses['where'] );
$pieces = array( 'fields', 'join', 'where', 'distinct', 'orderby', 'order', 'limits' );
/**
* Filters the terms query SQL clauses.
*
* @since 3.1.0
*
* @param string[] $clauses {
* Associative array of the clauses for the query.
*
* @type string $fields The SELECT clause of the query.
* @type string $join The JOIN clause of the query.
* @type string $where The WHERE clause of the query.
* @type string $distinct The DISTINCT clause of the query.
* @type string $orderby The ORDER BY clause of the query.
* @type string $order The ORDER clause of the query.
* @type string $limits The LIMIT clause of the query.
* }
* @param string[] $taxonomies An array of taxonomy names.
* @param array $args An array of term query arguments.
*/
$clauses = apply_filters( 'terms_clauses', compact( $pieces ), $taxonomies, $args );
$fields = isset( $clauses['fields'] ) ? $clauses['fields'] : '';
$join = isset( $clauses['join'] ) ? $clauses['join'] : '';
$where = isset( $clauses['where'] ) ? $clauses['where'] : '';
$distinct = isset( $clauses['distinct'] ) ? $clauses['distinct'] : '';
$orderby = isset( $clauses['orderby'] ) ? $clauses['orderby'] : '';
$order = isset( $clauses['order'] ) ? $clauses['order'] : '';
$limits = isset( $clauses['limits'] ) ? $clauses['limits'] : '';
$fields_is_filtered = implode( ', ', $selects ) !== $fields;
if ( $where ) {
$where = "WHERE $where";
}
$this->sql_clauses['select'] = "SELECT $distinct $fields";
$this->sql_clauses['from'] = "FROM $wpdb->terms AS t $join";
$this->sql_clauses['orderby'] = $orderby ? "$orderby $order" : '';
$this->sql_clauses['limits'] = $limits;
// Beginning of the string is on a new line to prevent leading whitespace. See https://core.trac.wordpress.org/ticket/56841.
$this->request =
"{$this->sql_clauses['select']}
{$this->sql_clauses['from']}
{$where}
{$this->sql_clauses['orderby']}
{$this->sql_clauses['limits']}";
$this->terms = null;
/**
* Filters the terms array before the query takes place.
*
* Return a non-null value to bypass WordPress' default term queries.
*
* @since 5.3.0
*
* @param array|null $terms Return an array of term data to short-circuit WP's term query,
* or null to allow WP queries to run normally.
* @param WP_Term_Query $query The WP_Term_Query instance, passed by reference.
*/
$this->terms = apply_filters_ref_array( 'terms_pre_query', array( $this->terms, &$this ) );
if ( null !== $this->terms ) {
return $this->terms;
}
if ( $args['cache_results'] ) {
$cache_key = $this->generate_cache_key( $args, $this->request );
$cache = wp_cache_get( $cache_key, 'term-queries' );
if ( false !== $cache ) {
if ( 'ids' === $_fields ) {
$cache = array_map( 'intval', $cache );
} elseif ( 'count' !== $_fields ) {
if ( ( 'all_with_object_id' === $_fields && ! empty( $args['object_ids'] ) )
|| ( 'all' === $_fields && $args['pad_counts'] || $fields_is_filtered )
) {
$term_ids = wp_list_pluck( $cache, 'term_id' );
} else {
$term_ids = array_map( 'intval', $cache );
}
_prime_term_caches( $term_ids, $args['update_term_meta_cache'] );
$term_objects = $this->populate_terms( $cache );
$cache = $this->format_terms( $term_objects, $_fields );
}
$this->terms = $cache;
return $this->terms;
}
}
if ( 'count' === $_fields ) {
$count = $wpdb->get_var( $this->request ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
if ( $args['cache_results'] ) {
wp_cache_set( $cache_key, $count, 'term-queries' );
}
return $count;
}
$terms = $wpdb->get_results( $this->request ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
if ( empty( $terms ) ) {
if ( $args['cache_results'] ) {
wp_cache_add( $cache_key, array(), 'term-queries' );
}
return array();
}
$term_ids = wp_list_pluck( $terms, 'term_id' );
_prime_term_caches( $term_ids, false );
$term_objects = $this->populate_terms( $terms );
if ( $child_of ) {
foreach ( $taxonomies as $_tax ) {
$children = _get_term_hierarchy( $_tax );
if ( ! empty( $children ) ) {
$term_objects = _get_term_children( $child_of, $term_objects, $_tax );
}
}
}
// Update term counts to include children.
if ( $args['pad_counts'] && 'all' === $_fields ) {
foreach ( $taxonomies as $_tax ) {
_pad_term_counts( $term_objects, $_tax );
}
}
// Make sure we show empty categories that have children.
if ( $hierarchical && $args['hide_empty'] && is_array( $term_objects ) ) {
foreach ( $term_objects as $k => $term ) {
if ( ! $term->count ) {
$children = get_term_children( $term->term_id, $term->taxonomy );
if ( is_array( $children ) ) {
foreach ( $children as $child_id ) {
$child = get_term( $child_id, $term->taxonomy );
if ( $child->count ) {
continue 2;
}
}
}
// It really is empty.
unset( $term_objects[ $k ] );
}
}
}
// Hierarchical queries are not limited, so 'offset' and 'number' must be handled now.
if ( $hierarchical && $number && is_array( $term_objects ) ) {
if ( $offset >= count( $term_objects ) ) {
$term_objects = array();
} else {
$term_objects = array_slice( $term_objects, $offset, $number, true );
}
}
// Prime termmeta cache.
if ( $args['update_term_meta_cache'] ) {
$term_ids = wp_list_pluck( $term_objects, 'term_id' );
wp_lazyload_term_meta( $term_ids );
}
if ( 'all_with_object_id' === $_fields && ! empty( $args['object_ids'] ) ) {
$term_cache = array();
foreach ( $term_objects as $term ) {
$object = new stdClass();
$object->term_id = $term->term_id;
$object->object_id = $term->object_id;
$term_cache[] = $object;
}
} elseif ( 'all' === $_fields && $args['pad_counts'] ) {
$term_cache = array();
foreach ( $term_objects as $term ) {
$object = new stdClass();
$object->term_id = $term->term_id;
$object->count = $term->count;
$term_cache[] = $object;
}
} elseif ( $fields_is_filtered ) {
$term_cache = $term_objects;
} else {
$term_cache = wp_list_pluck( $term_objects, 'term_id' );
}
if ( $args['cache_results'] ) {
wp_cache_add( $cache_key, $term_cache, 'term-queries' );
}
$this->terms = $this->format_terms( $term_objects, $_fields );
return $this->terms;
}
Filters the terms query arguments.
Filters the fields to select in the terms query.
Filters the terms to exclude from the terms query.
Fires before terms are retrieved.
Filters the terms query SQL clauses.
Filters the terms array before the query takes place.
| Version | Description |
|---|---|
| 4.6.0 | Introduced. |
© 2003–2024 WordPress Foundation
Licensed under the GNU GPLv2+ License.
https://developer.wordpress.org/reference/classes/wp_term_query/get_terms