问题描述

我昨天读了 @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 辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:薇晓朵技术论坛。