問題描述

我需要交叉參考兩個分類,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 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。