WordPress.org

Make WordPress Core


Ignore:
Timestamp:
09/25/2015 03:58:59 AM (5 years ago)
Author:
boonebgorges
Message:

Introduce metadata for taxonomy terms.

Adds a new table to the database schema (wp_termmeta), and a set of
*_term_meta() API functions. get_terms() and wp_get_object_terms()
now also support 'meta_query' parameters, with syntax identical to other
uses of WP_Meta_Query.

When fetching terms via get_terms() or wp_get_object_terms(), metadata for
matched terms is preloaded into the cache by default. Disable this behavior
by setting the new $update_term_meta_cache paramater to false.

To maximize performance, within WP_Query loops, the termmeta cache is *not*
primed by default. Instead, we use a lazy-loading technique: metadata for all
terms belonging to posts in the loop is loaded into the cache the first time
that get_term_meta() is called within the loop.

Props boonebgorges, sirzooro.
See #10142.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/wp-includes/taxonomy-functions.php

    r34516 r34529  
    958958 * @since 4.2.0 Introduced 'name' and 'childless' parameters.
    959959 * @since 4.4.0 Introduced the ability to pass 'term_id' as an alias of 'id' for the `orderby` parameter.
     960 *              Introduced the 'meta_query' and 'update_term_meta_cache' parameters.
    960961 *
    961962 * @global wpdb  $wpdb WordPress database abstraction object.
     
    10141015 *     @type string       $cache_domain      Unique cache key to be produced when this query is stored in an
    10151016 *                                           object cache. Default is 'core'.
     1017 *     @type bool         $update_term_meta_cache Whether to prime meta caches for matched terms. Default true.
     1018 *     @type array        $meta_query             Meta query clauses to limit retrieved terms by.
     1019 *                                                See `WP_Meta_Query`. Default empty.
    10161020 * }
    10171021 * @return array|int|WP_Error List of Term Objects and their children. Will return WP_Error, if any of $taxonomies
     
    10371041        'number' => '', 'fields' => 'all', 'name' => '', 'slug' => '', 'parent' => '', 'childless' => false,
    10381042        'hierarchical' => true, 'child_of' => 0, 'get' => '', 'name__like' => '', 'description__like' => '',
    1039         'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core' );
     1043        'pad_counts' => false, 'offset' => '', 'search' => '', 'cache_domain' => 'core',
     1044        'update_term_meta_cache' => true, 'meta_query' => '' );
    10401045    $args = wp_parse_args( $args, $defaults );
    10411046    $args['number'] = absint( $args['number'] );
     
    12951300        $like = '%' . $wpdb->esc_like( $args['search'] ) . '%';
    12961301        $where .= $wpdb->prepare( ' AND ((t.name LIKE %s) OR (t.slug LIKE %s))', $like, $like );
     1302    }
     1303
     1304    // Meta query support.
     1305    $join = '';
     1306    if ( ! empty( $args['meta_query'] ) ) {
     1307        $mquery = new WP_Meta_Query( $args['meta_query'] );
     1308        $mq_sql = $mquery->get_sql( 'term', 't', 'term_id' );
     1309
     1310        $join  .= $mq_sql['join'];
     1311        $where .= $mq_sql['where'];
    12971312    }
    12981313
     
    13421357    $fields = implode( ', ', apply_filters( 'get_terms_fields', $selects, $args, $taxonomies ) );
    13431358
    1344     $join = "INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id";
     1359    $join .= " INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id";
    13451360
    13461361    $pieces = array( 'fields', 'join', 'where', 'orderby', 'order', 'limits' );
     
    13731388    if ( 'all' == $_fields ) {
    13741389        update_term_cache( $terms );
     1390    }
     1391
     1392    // Prime termmeta cache.
     1393    if ( $args['update_term_meta_cache'] ) {
     1394        $term_ids = wp_list_pluck( $terms, 'term_id' );
     1395        update_termmeta_cache( $term_ids );
    13751396    }
    13761397
     
    14561477
    14571478/**
     1479 * Adds metadata to a term.
     1480 *
     1481 * @since 4.4.0
     1482 *
     1483 * @param int    $term_id    Term ID.
     1484 * @param string $meta_key   Metadata name.
     1485 * @param mixed  $meta_value Metadata value.
     1486 * @param bool   $unique     Optional. Whether to bail if an entry with the same key is found for the term.
     1487 *                           Default false.
     1488 * @return int|bool Meta ID on success, false on failure.
     1489 */
     1490function add_term_meta( $term_id, $meta_key, $meta_value, $unique = false ) {
     1491    return add_metadata( 'term', $term_id, $meta_key, $meta_value, $unique );
     1492}
     1493
     1494/**
     1495 * Removes metadata matching criteria from a term.
     1496 *
     1497 * @since 4.4.0
     1498 *
     1499 * @param int    $term_id    Term ID.
     1500 * @param string $meta_key   Metadata name.
     1501 * @param mixed  $meta_value Optional. Metadata value. If provided, rows will only be removed that match the value.
     1502 * @return bool True on success, false on failure.
     1503 */
     1504function delete_term_meta( $term_id, $meta_key, $meta_value = '' ) {
     1505    return delete_metadata( 'term', $term_id, $meta_key, $meta_value );
     1506}
     1507
     1508/**
     1509 * Retrieves metadata for a term.
     1510 *
     1511 * @since 4.4.0
     1512 *
     1513 * @param int    $term_id Term ID.
     1514 * @param string $key     Optional. The meta key to retrieve. If no key is provided, fetches all metadata for the term.
     1515 * @param bool   $single  Whether to return a single value. If false, an array of all values matching the
     1516 *                        `$term_id`/`$key` pair will be returned. Default: false.
     1517 * @return mixed If `$single` is false, an array of metadata values. If `$single` is true, a single metadata value.
     1518 */
     1519function get_term_meta( $term_id, $key = '', $single = false ) {
     1520    return get_metadata( 'term', $term_id, $key, $single );
     1521}
     1522
     1523/**
     1524 * Updates term metadata.
     1525 *
     1526 * Use the `$prev_value` parameter to differentiate between meta fields with the same key and term ID.
     1527 *
     1528 * If the meta field for the term does not exist, it will be added.
     1529 *
     1530 * @since 4.4.0
     1531 *
     1532 * @param int    $term_id    Term ID.
     1533 * @param string $meta_key   Metadata key.
     1534 * @param mixed  $meta_value Metadata value.
     1535 * @param mixed  $prev_value Optional. Previous value to check before removing.
     1536 * @return int|bool Meta ID if the key didn't previously exist. True on successful update. False on failure.
     1537 */
     1538function update_term_meta( $term_id, $meta_key, $meta_value, $prev_value = '' ) {
     1539    return update_metadata( 'term', $term_id, $meta_key, $meta_value, $prev_value );
     1540}
     1541
     1542/**
     1543 * Updates metadata cache for list of term IDs.
     1544 *
     1545 * Performs SQL query to retrieve all metadata for the terms matching `$term_ids` and stores them in the cache.
     1546 * Subsequent calls to `get_term_meta()` will not need to query the database.
     1547 *
     1548 * @since 4.4.0
     1549 *
     1550 * @param array $term_ids List of term IDs.
     1551 * @return array|false Returns false if there is nothing to update. Returns an array of metadata on success.
     1552 */
     1553function update_termmeta_cache( $term_ids ) {
     1554    return update_meta_cache( 'term', $term_ids );
     1555}
     1556
     1557/**
     1558 * Lazy-loads termmeta when inside of a `WP_Query` loop.
     1559 *
     1560 * As a rule, term queries (`get_terms()` and `wp_get_object_terms()`) prime the metadata cache for matched terms by
     1561 * default. However, this can cause a slight performance penalty, especially when that metadata is not actually used.
     1562 * In the context of a `WP_Query` loop, we're able to avoid this potential penalty. `update_object_term_cache()`,
     1563 * called from `update_post_caches()`, does not 'update_term_meta_cache'. Instead, the first time `get_term_meta()` is
     1564 * called from within a `WP_Query` loop, the current function detects the fact, and then primes the metadata cache for
     1565 * all terms attached to all posts in the loop, with a single database query.
     1566 *
     1567 * @since 4.4.0
     1568 *
     1569 * @param null $check   The `$check` param passed from the 'pre_term_metadata' hook.
     1570 * @param int  $term_id ID of the term whose metadata is being cached.
     1571 * @return null In order not to short-circuit `get_metadata()`.
     1572 */
     1573function wp_lazyload_term_meta( $check, $term_id ) {
     1574    global $wp_query;
     1575
     1576    if ( $wp_query instanceof WP_Query && ! empty( $wp_query->posts ) && $wp_query->get( 'update_post_term_cache' ) ) {
     1577        // We can only lazyload if the entire post object is present.
     1578        $posts = array();
     1579        foreach ( $wp_query->posts as $post ) {
     1580            if ( $post instanceof WP_Post ) {
     1581                $posts[] = $post;
     1582            }
     1583        }
     1584
     1585        if ( empty( $posts ) ) {
     1586            return;
     1587        }
     1588
     1589        // Fetch cached term_ids for each post. Keyed by term_id for faster lookup.
     1590        $term_ids = array();
     1591        foreach ( $posts as $post ) {
     1592            $taxonomies = get_object_taxonomies( $post->post_type );
     1593            foreach ( $taxonomies as $taxonomy ) {
     1594                // No extra queries. Term cache should already be primed by 'update_post_term_cache'.
     1595                $terms = get_object_term_cache( $post->ID, $taxonomy );
     1596                if ( false !== $terms ) {
     1597                    foreach ( $terms as $term ) {
     1598                        if ( ! isset( $term_ids[ $term->term_id ] ) ) {
     1599                            $term_ids[ $term->term_id ] = 1;
     1600                        }
     1601                    }
     1602                }
     1603            }
     1604        }
     1605
     1606        if ( $term_ids ) {
     1607            update_termmeta_cache( array_keys( $term_ids ) );
     1608        }
     1609    }
     1610
     1611    return $check;
     1612}
     1613
     1614/**
    14581615 * Check if Term exists.
    14591616 *
     
    20122169 * @since 4.2.0 Added support for 'taxonomy', 'parent', and 'term_taxonomy_id' values of `$orderby`.
    20132170 *              Introduced `$parent` argument.
     2171 * @since 4.4.0 Introduced `$meta_query` and `$update_term_meta_cache` arguments.
    20142172 *
    20152173 * @global wpdb $wpdb WordPress database abstraction object.
     
    20272185 *                           of strings.
    20282186 *     @type int    $parent  Optional. Limit results to the direct children of a given term ID.
     2187 *     @type bool   $update_term_meta_cache Whether to prime termmeta cache for matched terms. Only applies when
     2188 *                                          `$fields` is 'all', 'all_with_object_id', or 'term_id'. Default true.
     2189 *     @type array  $meta_query             Meta query clauses to limit retrieved terms by. See `WP_Meta_Query`.
     2190 *                                          Default empty.
    20292191 * }
    20302192 * @return array|WP_Error The requested term data or empty array if no terms found.
     
    20542216        'fields'  => 'all',
    20552217        'parent'  => '',
     2218        'update_term_meta_cache' => true,
     2219        'meta_query' => '',
    20562220    );
    20572221    $args = wp_parse_args( $args, $defaults );
     
    21272291    }
    21282292
     2293    // Meta query support.
     2294    $meta_query_join = '';
     2295    if ( ! empty( $args['meta_query'] ) ) {
     2296        $mquery = new WP_Meta_Query( $args['meta_query'] );
     2297        $mq_sql = $mquery->get_sql( 'term', 't', 'term_id' );
     2298
     2299        $meta_query_join .= $mq_sql['join'];
     2300
     2301        // Strip leading AND.
     2302        $where[] = preg_replace( '/^\s*AND/', '', $mq_sql['where'] );
     2303    }
     2304
    21292305    $where = implode( ' AND ', $where );
    21302306
    2131     $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 WHERE $where $orderby $order";
     2307    $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";
    21322308
    21332309    $objects = false;
     
    21522328            $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.
    21532329        }
     2330    }
     2331
     2332    // Update termmeta cache, if necessary.
     2333    if ( $args['update_term_meta_cache'] && ( 'all' === $fields || 'all_with_object_ids' === $fields || 'term_id' === $fields ) ) {
     2334        if ( 'term_id' === $fields ) {
     2335            $term_ids = $fields;
     2336        } else {
     2337            $term_ids = wp_list_pluck( $terms, 'term_id' );
     2338        }
     2339
     2340        update_termmeta_cache( $term_ids );
    21542341    }
    21552342
     
    32893476        'fields' => 'all_with_object_id',
    32903477        'orderby' => 'none',
     3478        'update_term_meta_cache' => false,
    32913479    ) );
    32923480
Note: See TracChangeset for help on using the changeset viewer.