Make WordPress Core


Ignore:
Timestamp:
09/28/2016 03:54:36 AM (7 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.