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