問題描述
我昨天讀了 @nacin’s You don’t know Query,併發了一下查詢兔子洞。在昨天之前,我 (錯誤地) 使用 query_posts()來滿足我所有的查詢需求。現在我對使用 WP_Query()有點聰明,但仍然有一些灰色的地方。
我認為我確信:
如果我在頁面上的任何位置進行附加循環 – 在側邊欄中,在頁腳中,任何類型的 「相關帖子」 等 – 我想使用 WP_Query()。我可以在一個頁面上重複使用,而不會有任何傷害。 (對?) 。
我不知道什麼
- 何時使用 @nacin’s
pre_get_posts與WP_Query()?我現在應該使用pre_get_posts嗎? - 當我想修改一個模板頁面中的循環 – 讓我説我想修改一個分類存檔頁面 – 我要刪除
if have_posts : while have_posts : the_post部件,並寫我自己的WP_Query()?還是使用pre_get_posts在我的 functions.php 文件中修改輸出?
l
我想從中抽出的長話短説規則是:
- 切勿使用
query_posts - 在單個頁面上運行多個查詢時,請使用
WP_Query() - 修改循環時,請執行此操作__________________。
感謝任何智慧
特里
ps:我已經看到和閲讀:When should you use WP_Query vs query_posts() vs get_posts()? 哪個增加了另一個維度 – get_posts 。但是根本不處理 pre_get_posts 。
最佳解決方案
你是對的説:
Never use
query_postsanymore
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>
回答你的問題
- 使用
pre_get_posts更改主查詢。對模板頁面中的二級循環使用單獨的WP_Query對象 (方法 2) 。 - 如果要更改主循環的查詢,請使用
pre_get_posts。
次佳解決方案
循環有兩種不同的上下文:
- 基於 URL 請求發生的主循環,並在加載模板之前處理
- 以任何其他方式發生的次級循環,從模板文件或其他方式調用
query_posts()的問題是它是次循環,試圖成為主要的,並且慘敗。因此忘記它存在。
修改主循環
- 不要使用
query_posts() - 使用
pre_get_posts過濾器與$query->is_main_query()檢查 - 交替使用
request過濾器 (有點太粗糙,以上是更好)
運行二級循環
使用幾乎可互換的 new WP_Query 或 get_posts()(後者是前者的薄包裝) 。
要清理
如果您使用 query_posts()或直接與全局 $wp_query 混淆,請使用 wp_reset_query(),因此您幾乎不需要。
如果您使用 the_post()或 setup_postdata()或使用全局 $post,需要恢復 post-related 的初始狀態,請使用 wp_reset_postdata()。
第三種解決方案
有使用 query_posts($query)的合法方案,例如:
- 您想要顯示一個頁面上的帖子或 custom-post-type 帖子列表 (使用頁面模板)
- 你想讓這些帖子的分頁工作
現在為什麼要在頁面上顯示它,而不是使用歸檔模板?
- 管理員 (您的客户?) 更直觀 – 他們可以在’Pages’ 中看到該頁面
- 最好將它添加到菜單 (沒有頁面,他們必須直接添加 url)
- 如果您想在模板上顯示其他內容 (文字,貼子縮略圖或任何自定義元數據內容),您可以輕鬆地從頁面中獲取該內容 (對客户而言也是如此) 。看看是否使用歸檔模板,您需要對其他內容進行硬編碼,或者使用主題/插件選項 (這使得客户不那麼直觀)
這是一個簡化的示例代碼 (這將在您的頁面模板上 – 例如 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 輔助翻譯的英文資料結果。如果您對結果不滿意,可以加入我們改善翻譯效果:薇曉朵技術論壇。