Make WordPress Core


Ignore:
Timestamp:
09/28/2016 03:54:36 AM (10 years ago)
Author:
boonebgorges
Message:

Taxonomy: Use WP_Term_Query when querying for object terms.

The new 'object_ids' parameter for WP_Term_Query allows queries for
terms that "belong to" a given object. This change makes it possible
to use WP_Term_Query inside of wp_get_object_terms(), rather than
assembling a SQL query.

The refactor has a couple of benefits:

  • Less redundancy.
  • Better consistency in accepted arguments between the term query functions. See #31105.
  • Less redundancy.
  • Object term queries are now cached. The get_object_term_cache() cache remains, and will be a somewhat less fragile secondary cache in front of the query cache (which is subject to frequent invalidation).
  • Less redundancy.

A small breaking change: Previously, if a non-hierarchical taxonomy had
terms that had a non-zero 'parent' (perhaps because of a direct SQL
query), wp_get_object_terms() would respect the 'parent' argument.
This is in contrast to WP_Term_Query and get_terms(), which have
always rejected 'parent' queries for non-hierarchical taxonomies. For
consistency, the behavior of get_terms() is being applied across the
board: passing 'parent' for a non-hierarchical taxonomy will result in
an empty result set (since the cached taxonomy hierarchy will be empty).

Props flixos90, boonebgorges.
See #37198.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/class-wp-term-query.php

    r38500 r38667  
    101101         * @since 4.6.0
    102102         * @since 4.6.0 Introduced 'term_taxonomy_id' parameter.
     103         * @since 4.7.0 Introduced 'object_ids' parameter.
    103104         * @access public
    104105         *
     
    108109         *     @type string|array $taxonomy               Taxonomy name, or array of taxonomies, to which results should
    109110         *                                                be limited.
     111         *     @type int|array    $object_ids             Optional. Object ID, or array of object IDs. Results will be
     112         *                                                limited to terms associated with these objects.
    110113         *     @type string       $orderby                Field(s) to order terms by. Accepts term fields ('name',
    111114         *                                                'slug', 'term_group', 'term_id', 'id', 'description'),
     
    131134         *     @type int          $offset                 The number by which to offset the terms query. Default empty.
    132135         *     @type string       $fields                 Term fields to query for. Accepts 'all' (returns an array of
    133          *                                                complete term objects), 'ids' (returns an array of ids),
    134          *                                                'id=>parent' (returns an associative array with ids as keys,
    135          *                                                parent term IDs as values), 'names' (returns an array of term
    136          *                                                names), 'count' (returns the number of matching terms),
    137          *                                                'id=>name' (returns an associative array with ids as keys,
    138          *                                                term names as values), or 'id=>slug' (returns an associative
    139          *                                                array with ids as keys, term slugs as values). Default 'all'.
     136         *                                                complete term objects), 'all_with_object_id' (returns an
     137         *                                                array of term objects with the 'object_id' param; only works
     138         *                                                when the `$fields` parameter is 'object_ids' ), 'ids'
     139         *                                                (returns an array of ids), 'tt_ids' (returns an array of
     140         *                                                term taxonomy ids), 'id=>parent' (returns an associative
     141         *                                                array with ids as keys, parent term IDs as values), 'names'
     142         *                                                (returns an array of term names), 'count' (returns the number
     143         *                                                of matching terms), 'id=>name' (returns an associative array
     144         *                                                with ids as keys, term names as values), or 'id=>slug'
     145         *                                                (returns an associative array with ids as keys, term slugs
     146         *                                                as values). Default 'all'.
    140147         *     @type bool         $count                  Whether to return a term count (true) or array of term objects
    141148         *                                                (false). Will take precedence over `$fields` if true.
     
    184191                $this->query_var_defaults = array(
    185192                        'taxonomy'               => null,
     193                        'object_ids'             => null,
    186194                        'orderby'                => 'name',
    187195                        'order'                  => 'ASC',
     
    393401                }
    394402
    395                 $orderby = $this->parse_orderby( $this->query_vars['orderby'] );
     403                // 'term_order' is a legal sort order only when joining the relationship table.
     404                $_orderby = $this->query_vars['orderby'];
     405                if ( 'term_order' === $_orderby && empty( $this->query_vars['object_ids'] ) ) {
     406                        $_orderby = 'term_id';
     407                }
     408                $orderby = $this->parse_orderby( $_orderby );
     409
    396410                if ( $orderby ) {
    397411                        $orderby = "ORDER BY $orderby";
     
    508522                }
    509523
     524                if ( ! empty( $args['object_ids'] ) ) {
     525                        $object_ids = $args['object_ids'];
     526                        if ( ! is_array( $object_ids ) ) {
     527                                $object_ids = array( $object_ids );
     528                        }
     529
     530                        $object_ids = implode( ', ', array_map( 'intval', $object_ids ) );
     531                        $this->sql_clauses['where']['object_ids'] = "tr.object_id IN ($object_ids)";
     532                }
     533
     534                /*
     535                 * When querying for object relationships, the 'count > 0' check
     536                 * added by 'hide_empty' is superfluous.
     537                 */
     538                if ( ! empty( $args['object_ids'] ) ) {
     539                        $args['hide_empty'] = false;
     540                }
     541
    510542                if ( '' !== $parent ) {
    511543                        $parent = (int) $parent;
     
    559591                switch ( $args['fields'] ) {
    560592                        case 'all':
     593                        case 'all_with_object_id' :
     594                        case 'tt_ids' :
     595                        case 'slugs' :
    561596                                $selects = array( 't.*', 'tt.*' );
     597                                if ( 'all_with_object_id' === $args['fields'] && ! empty( $args['object_ids'] ) ) {
     598                                        $selects[] = 'tr.object_id';
     599                                }
    562600                                break;
    563601                        case 'ids':
     
    603641                $join .= " INNER JOIN {$this->db->term_taxonomy} AS tt ON t.term_id = tt.term_id";
    604642
     643                if ( ! empty( $this->query_vars['object_ids'] ) ) {
     644                        $join .= " INNER JOIN {$this->db->term_relationships} AS tr ON tr.term_taxonomy_id = tt.term_taxonomy_id";
     645                }
     646
    605647                $where = implode( ' AND ', $this->sql_clauses['where'] );
    606648
     
    658700
    659701                $terms = $this->db->get_results( $this->request );
    660                 if ( 'all' == $_fields ) {
     702                if ( 'all' == $_fields || 'all_with_object_id' === $_fields ) {
    661703                        update_term_cache( $terms );
    662704                }
     
    709751                }
    710752
     753                /*
     754                 * When querying for terms connected to objects, we may get
     755                 * duplicate results. The duplicates should be preserved if
     756                 * `$fields` is 'all_with_object_id', but should otherwise be
     757                 * removed.
     758                 */
     759                if ( ! empty( $args['object_ids'] ) && 'all_with_object_id' != $_fields ) {
     760                        $_tt_ids = $_terms = array();
     761                        foreach ( $terms as $term ) {
     762                                if ( isset( $_tt_ids[ $term->term_id ] ) ) {
     763                                        continue;
     764                                }
     765
     766                                $_tt_ids[ $term->term_id ] = 1;
     767                                $_terms[] = $term;
     768                        }
     769
     770                        $terms = $_terms;
     771                }
     772
    711773                $_terms = array();
    712774                if ( 'id=>parent' == $_fields ) {
     
    716778                } elseif ( 'ids' == $_fields ) {
    717779                        foreach ( $terms as $term ) {
    718                                 $_terms[] = $term->term_id;
     780                                $_terms[] = (int) $term->term_id;
     781                        }
     782                } elseif ( 'tt_ids' == $_fields ) {
     783                        foreach ( $terms as $term ) {
     784                                $_terms[] = (int) $term->term_taxonomy_id;
    719785                        }
    720786                } elseif ( 'names' == $_fields ) {
    721787                        foreach ( $terms as $term ) {
    722788                                $_terms[] = $term->name;
     789                        }
     790                } elseif ( 'slugs' == $_fields ) {
     791                        foreach ( $terms as $term ) {
     792                                $_terms[] = $term->slug;
    723793                        }
    724794                } elseif ( 'id=>name' == $_fields ) {
     
    747817                wp_cache_add( $cache_key, $terms, 'terms', DAY_IN_SECONDS );
    748818
    749                 if ( 'all' === $_fields ) {
     819                if ( 'all' === $_fields || 'all_with_object_id' === $_fields ) {
    750820                        $terms = array_map( 'get_term', $terms );
    751821                }
     
    767837                $_orderby = strtolower( $orderby_raw );
    768838                $maybe_orderby_meta = false;
    769                 if ( 'count' == $_orderby ) {
    770                         $orderby = 'tt.count';
    771                 } elseif ( 'name' == $_orderby ) {
    772                         $orderby = 't.name';
    773                 } elseif ( 'slug' == $_orderby ) {
    774                         $orderby = 't.slug';
     839
     840                if ( in_array( $_orderby, array( 'term_id', 'name', 'slug', 'term_group' ), true ) ) {
     841                        $orderby = "t.$_orderby";
     842                } elseif ( in_array( $_orderby, array( 'count', 'parent', 'taxonomy', 'term_taxonomy_id', 'description' ), true ) ) {
     843                        $orderby = "tt.$_orderby";
     844                } elseif ( 'term_order' === $_orderby ) {
     845                        $orderby = 'tr.term_order';
    775846                } elseif ( 'include' == $_orderby && ! empty( $this->query_vars['include'] ) ) {
    776847                        $include = implode( ',', wp_parse_id_list( $this->query_vars['include'] ) );
    777848                        $orderby = "FIELD( t.term_id, $include )";
    778                 } elseif ( 'term_group' == $_orderby ) {
    779                         $orderby = 't.term_group';
    780                 } elseif ( 'description' == $_orderby ) {
    781                         $orderby = 'tt.description';
    782849                } elseif ( 'none' == $_orderby ) {
    783850                        $orderby = '';
Note: See TracChangeset for help on using the changeset viewer.