问题描述

我需要交叉参考两个分类,product_categoriesbrands 。我可以想到的唯一办法就是把所有的帖子都放在一个分类中,然后得到另一个分类中存在的所有帖子:

<?php

  $category = /* Taxonomy object selected from list */;

  // Get all the posts in this query
  $args = array(
    'post_type' => 'product',
    'tax_query' => array(
      array(
        'taxonomy' => $category->taxonomy,
        'field'    => 'slug',
        'terms'    => $category->slug
      )
    )
  );
  $product_items = get_posts( $args );

  // create a blank array ready for the IDs
  $product_items_ids = [];

  // For every post found, populate the array
  foreach ($product_items as $product_item) {
    // Create array from all post IDs in category
    $product_items_ids[] = $product_item->ID;
  }

  // Get all terms in Brand taxonomy that are in the array above
  $brand_items = wp_get_object_terms( $product_items_ids, 'brand' );

  // Output the information needed
  foreach ($brand_items as $brand_item) { ?>
    <li><a href="<?php%20echo%20get_term_link(%20$brand_item->slug,%20%20$brand_item->taxonomy%20);%20?>"> <?php echo $brand_item->name; ?></a></li>
  <?php } ?>

我调用 5-10 次,如果产品列表变得很大,这意味着我正在网站上的每个帖子 10 次加载菜单等。

有没有更有效的方式来做这种类型的查询?


添加注意事项:

第一个查询引用了分配了 $category->taxonomy 分类的帖子。

$category 是由高级自定义字段在管理员中选择的分类对象。对于这个例子,说配件是一个术语,product_cat 是分类法 @PieterGoosen

我需要返回所有与附件条款相关的品牌的条款。

最佳解决思路

我们可以做很多改进代码的性能。让我们先设定一些基准

BENCH MARKS

  • 我正在用这个测试

    • category 分类学术语有 9 个职位

    • 具有 61 个匹配标签的 post_tag 分类法。

使用您当前的代码,我得到以下结果

  • 69 查询在= / – 0.4s

对于这样一个小数据库和测试对象,这是相当昂贵的和大量的查询

OPTIMIZATIONS

我们要做的第一件事就是要查询帖子中的帖子 ID,原因如下

  • 我们不需要任何帖子数据

  • 我们不需要后缓存和后期元缓存更新,我们不需要

  • 显然,只查询帖子 ID 会大大提高性能

只查询 ID 的缺点在于我们也放宽了缓存期限。因为我们不更新术语缓存,这将导致数据库查询的巨大增长。为了解决这个问题,我们将使用 update_object_term_cache 手动更新术语缓存。

到这个时候,只要查询一下,你就获得了 1db 的调用和 0.02s,这并不是那么大,但是在一个巨大的数据库上却有很大的不同。真正的收获将在下一节中介绍

真正的大收获是将术语对象传递给 get_term_link(),而不是术语 ID 。如果术语缓存中没有术语,并将术语 ID 传递给 get_term_link(),而不是从缓存中获取术语对象,get_term_link()将查询数据库以获取术语对象。刚刚测试,这相当于额外的 61 个 db 调用,每个标签一个。想想几百个标签。

我们已经有了术语对象,所以我们可以简单地传递完整的术语对象。你应该永远这样做即使术语对象在缓存中,传递术语 ID 仍然非常缓慢,因为我们仍然必须从缓存中获取术语对象

我已经清理了你的代码。注意,我使用了需要 PHP 5.4+的短数组语法。以下是您的代码的外观

$category       = get_category( 13 ); // JUST FOR TESTING< ADJUST TO YOUR NEEDS

$args = [
    'post_type' => 'product',
    'fields'    => 'ids', // Only query the post ID's, not complete post objects
    'tax_query' => [
        [
            'taxonomy'  => $category->taxonomy,
            'field'     => 'slug',
            'terms'     => $category->slug
        ]
    ]
];
$ids = get_posts( $args );

$links = [];
// Make sure we have ID'saves
if ( $ids ) {
    /**
     * Because we only query post ID's, the post caches are not updated which is
     * good and bad
     *
     * GOOD -> It saves on resources because we do not need post data or post meta data
     * BAD -> We loose the vital term cache, which will result in even more db calls
     *
     * To solve that, we manually update the term cache with update_object_term_cache
     */
    update_object_term_cache( $ids, 'product' );

    $term_names = [];

    foreach ( $ids as $id ) {
        $terms = get_object_term_cache( $id, 'post_tag' );
        foreach ( $terms as $term ) {
            if ( in_array( $term->name, $term_names ) )
                continue;

            $term_names[] = $term->name;

            $links[$term->name] = '<li><a href="'%20.%20get_term_link(%20$term%20)%20.%20'">' . $term->name . '</a></li>';
        }
    }
}

if ( $links ) {
    ksort( $links );
    $link_string = implode( "nt" , $links );
} else {
    $link_string = '';
}

echo $link_string;

现在,我们已经将数量减少到了 0.04 分,这是一个很大的改进。

我们甚至可以进一步,并将结果存储在一瞬间

$category       = get_category( 13 ); // JUST FOR TESTING< ADJUST TO YOUR NEEDS

$link_string    = '';
$transient_name = 'query_' . md5( $category->slug . $category->taxonomy );
if ( false === ( $link_string = get_transient( $transient_name ) ) ) {
    $args = [
        'post_type' => 'product',
        'fields'    => 'ids', // Only query the post ID's, not complete post objects
        'tax_query' => [
            [
                'taxonomy'  => $category->taxonomy,
                'field'     => 'slug',
                'terms'     => $category->slug
            ]
        ]
    ];
    $ids = get_posts( $args );

    $links = [];
    // Make sure we have ID'saves
    if ( $ids ) {
        /**
         * Because we only query post ID's, the post caches are not updated which is
         * good and bad
         *
         * GOOD -> It saves on resources because we do not need post data or post meta data
         * BAD -> We loose the vital term cache, which will result in even more db calls
         *
         * To solve that, we manually update the term cache with update_object_term_cache
         */
        update_object_term_cache( $ids, 'product' );

        $term_names = [];

        foreach ( $ids as $id ) {
            $terms = get_object_term_cache( $id, 'post_tag' );
            foreach ( $terms as $term ) {
                if ( in_array( $term->name, $term_names ) )
                    continue;

                $term_names[] = $term->name;

                $links[$term->name] = '<li><a href="'%20.%20get_term_link(%20$term%20)%20.%20'">' . $term->name . '</a></li>';
            }
        }
    }


    if ( $links ) {
        ksort( $links );
        $link_string = implode( "nt" , $links );
    } else {
        $link_string = '';
    }

    set_transient( $transient_name, $link_string, 7 * DAY_IN_SECONDS );
}

echo $link_string;

这将减少所有内容到 0.002s 中的 2 个查询。随着瞬间的到来,我们只会在发布,更新,删除或取消删除帖子时刷新瞬态。我们将在这里使用 transition_post_status

add_action( 'transition_post_status', function ()
{
    global $wpdb;
    $wpdb->query( "DELETE FROM $wpdb->options WHERE `option_name` LIKE ('_transient%_query_%')" );
    $wpdb->query( "DELETE FROM $wpdb->options WHERE `option_name` LIKE ('_transient_timeout%_query_%')" );
});

参考文献

注:本文内容整合自 Google/Baidu/Bing 辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:薇晓朵技术论坛。