問題描述

我昨天讀了 @nacin’s You don’t know Query,併發了一下查詢兔子洞。在昨天之前,我 (錯誤地) 使用 query_posts()來滿足我所有的查詢需求。現在我對使用 WP_Query()有點聰明,但仍然有一些灰色的地方。

我認為我確信:

如果我在頁面上的任何位置進行附加循環 – 在側邊欄中,在頁腳中,任何類型的 「相關帖子」 等 – 我想使用 WP_Query()。我可以在一個頁面上重複使用,而不會有任何傷害。 (對?) 。

我不知道什麼

  1. 何時使用 @nacin’s pre_get_postsWP_Query()?我現在應該使用 pre_get_posts 嗎?
  2. 當我想修改一個模板頁面中的循環 – 讓我説我想修改一個分類存檔頁面 – 我要刪除 if have_posts : while have_posts : the_post 部件,並寫我自己的 WP_Query()?還是使用 pre_get_posts 在我的 functions.php 文件中修改輸出?

l

我想從中抽出的長話短説規則是:

  1. 切勿使用 query_posts
  2. 在單個頁面上運行多個查詢時,請使用 WP_Query()
  3. 修改循環時,請執行此操作__________________。

感謝任何智慧

特里

ps:我已經看到和閲讀:When should you use WP_Query vs query_posts() vs get_posts()? 哪個增加了另一個維度 – get_posts 。但是根本不處理 pre_get_posts

最佳解決方案

你是對的説:

Never use query_posts anymore

pre_get_posts

 pre_get_posts 是一個過濾器,用於更改任何查詢。最常用於更改’main query’:

add_action('pre_get_posts','wpse50761_alter_query');
function wpse50761_alter_query($query){

      if( $query->is_main_query() ){
        //Do something to main query
      }
}

(我也會檢查 is_admin()是否返回 false – 儘管這可能是多餘的。) 主要查詢顯示在您的模板中:

if( have_posts() ):
    while( have_posts() ): the_post();
       //The loop
    endwhile;
endif;

如果您覺得需要編輯此循環 – 請使用 pre_get_posts 。即如果您試圖使用 query_posts() – 請改用 pre_get_posts

WP_Query

主要查詢是 WP_Query object 的一個重要實例。例如,WordPress 使用它來決定要使用哪個模板,並且傳入 url 的任何參數 (例如分頁) 都被引導到 WP_Query 對象的實例中。

對於輔助循環 (例如,在 side-bars 或’related posts’ 列表中),您將需要創建自己的 WP_Query 對象的單獨實例。例如。

$my_secondary_loop = new WP_Query(...);
if( $my_secondary_loop->have_posts() ):
    while( $my_secondary_loop->have_posts() ): $my_secondary_loop->the_post();
       //The secondary loop
    endwhile;
endif;
wp_reset_postdata();

注意 wp_reset_postdata(); – 這是因為輔助循環將覆蓋標識’current post’ 的全局 $post 變量。這實質上將其重新設置為我們正在使用的 $post

get_posts()

這實際上是 WP_Query 對象的單獨實例的包裝器。這返回一個 post 對象數組。上述循環中使用的方法已不再適用於您。這不是一個’Loop’,只是一個 post 對象的數組。

<ul>
<?php
global $post;
$args = array( 'numberposts' => 5, 'offset'=> 1, 'category' => 1 );
$myposts = get_posts( $args );
foreach( $myposts as $post ) :  setup_postdata($post); ?>
    <li><a href="<?php%20the_permalink();%20?>"><?php the_title(); ?></a></li>
<?php endforeach; wp_reset_postdata(); ?>
</ul>

回答你的問題

  1. 使用 pre_get_posts 更改主查詢。對模板頁面中的二級循環使用單獨的 WP_Query 對象 (方法 2) 。
  2. 如果要更改主循環的查詢,請使用 pre_get_posts

次佳解決方案

循環有兩種不同的上下文:

  • 基於 URL 請求發生的主循環,並在加載模板之前處理
  • 以任何其他方式發生的次級循環,從模板文件或其他方式調用

query_posts()的問題是它是次循環,試圖成為主要的,並且慘敗。因此忘記它存在。

修改主循環

  • 不要使用 query_posts()
  • 使用 pre_get_posts 過濾器與 $query->is_main_query()檢查
  • 交替使用 request 過濾器 (有點太粗糙,以上是更好)

運行二級循環

使用幾乎可互換的 new WP_Queryget_posts()(後者是前者的薄包裝) 。

要清理

如果您使用 query_posts()或直接與全局 $wp_query 混淆,請使用 wp_reset_query(),因此您幾乎不需要。

如果您使用 the_post()setup_postdata()或使用全局 $post,需要恢復 post-related 的初始狀態,請使用 wp_reset_postdata()

第三種解決方案

有使用 query_posts($query)的合法方案,例如:

  1. 您想要顯示一個頁面上的帖子或 custom-post-type 帖子列表 (使用頁面模板)
  2. 你想讓這些帖子的分頁工作

現在為什麼要在頁面上顯示它,而不是使用歸檔模板?

  1. 管理員 (您的客户?) 更直觀 – 他們可以在’Pages’ 中看到該頁面
  2. 最好將它添加到菜單 (沒有頁面,他們必須直接添加 url)
  3. 如果您想在模板上顯示其他內容 (文字,貼子縮略圖或任何自定義元數據內容),您可以輕鬆地從頁面中獲取該內容 (對客户而言也是如此) 。看看是否使用歸檔模板,您需要對其他內容進行硬編碼,或者使用主題/插件選項 (這使得客户不那麼直觀)

這是一個簡化的示例代碼 (這將在您的頁面模板上 – 例如 page-page-of-posts.php):

/**
 * Template Name: Page of Posts
 */

while(have_posts()) { // original main loop - page content
  the_post();
  the_title(); // title of the page
  the_content(); // content of the page
  // etc...
}

// now we display list of our custom-post-type posts

// first obtain pagination parametres
$paged = 1;
if(get_query_var('paged')) {
  $paged = get_query_var('paged');
} elseif(get_query_var('page')) {
  $paged = get_query_var('page');
}

// query posts and replace the main query (page) with this one (so the pagination works)
query_posts(array('post_type' => 'my_post_type', 'post_status' => 'publish', 'paged' => $paged));

// pagination
next_posts_link();
previous_posts_link();

// loop
while(have_posts()) {
  the_post();
  the_title(); // your custom-post-type post's title
  the_content(); // // your custom-post-type post's content
}

wp_reset_query(); // sets the main query (global $wp_query) to the original page query (it obtains it from global $wp_the_query variable) and resets the post data

// So, now we can display the page-related content again (if we wish so)
while(have_posts()) { // original main loop - page content
  the_post();
  the_title(); // title of the page
  the_content(); // content of the page
  // etc...
}

現在,要清楚的是,我們可以避免在這裏使用 query_posts(),而是使用 WP_Query,就像這樣:

// ...

global $wp_query;
$wp_query = new WP_Query(array('your query vars here')); // sets the new custom query as a main query

// your custom-post-type loop here

wp_reset_query();

// ...

但是,為什麼我們有這樣一個很好的小功能呢?

第四種方案

我從 functions.php 修改 WordPress 查詢:

//for "IS_PAGE" conditions, pre_get_posts doesn't work (it's WORDPRESS behaviour)
//so, inside `add_action('wp....`  use `add_filter(posts_where.....`
//OR     modify  "PAGE" query directly into  template file
add_action( 'pre_get_posts', 'myf88' );

function myf88($query) {
    if ( ! is_admin() && $query->is_main_query() )  {
        if (  $query->is_category ) {
            $query->set( 'post_type', array( 'post', 'page', 'my_postType' ) );
            add_filter( 'posts_where' , 'MyFilterFunction_1' );
        }
    }
}
function MyFilterFunction_1($where) {
    global $wpdb;
    $where .= " AND ({$wpdb->posts}.post_name NOT LIKE 'Journal%')";
    return $where;
}

參考文獻

注:本文內容整合自 Google/Baidu/Bing 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。