WordPress - pre_get_posts 代替页面上的 query_posts [英] WordPress - pre_get_posts in place of query_posts on pages
问题描述
我的情况有些复杂,我尽量简明扼要地解释一下.
我目前正在使用 query_posts
来修改我网站上自定义页面上的主要查询,据我所知效果很好,但我已经读过使用 query_posts 是不好的做法出于多种不同的原因.
那么,您可能会问,为什么我使用 query_posts
而不是创建 WP_Query
对象?
这是因为我使用的是无限滚动插件,无限滚动不能很好地与 WP_query 配合使用,但是当您简单地使用 query_posts 修改主查询时,它绝对可以正常工作.例如,使用无限滚动 + WP_query(主要关注点)无法进行分页.
在一页上,我正在修改查询以获取查看次数最多的帖子.
<?php $paged = get_query_var( 'paged' ) ?get_query_var('分页'): 1;?><?php query_posts( array( 'meta_key' => 'wpb_post_views_count', 'orderby' => 'meta_value_num', 'order' => 'DESC' , 'paged' => $paged, );?><?php if (have_posts()) : ?><?php while ( have_posts() ) : the_post() ?><?php if ( has_post_format( 'video' )) {get_template_part('视频发布');}elseif(has_post_format('图像')){get_template_part('图片发布');} 别的 {get_template_part('标准帖子');}?><?php endwhile;?><?php endif;?>
所以经过大量阅读后,我发现我修改主查询的另一个选项是使用 pre_get_posts
,尽管我有点不确定如何去做.
以这个为例:-
function textdomain_exclude_category( $query ) {如果 ( $query->is_home() && $query->is_main_query() ) {$query->set('cat', '-1,-2');}}add_action('pre_get_posts', 'textdomain_exclude_category');
好吧,就这么简单——如果是首页,修改主查询,排除两个类别.
我感到困惑和无法弄清楚的是:-
自定义页面模板的用例场景.通过我的
query_posts
修改,我可以在if (have_posts())
之前放入数组,选择我的页面模板,发布它然后我走.使用pre_get_posts
我不知道怎么说例如$query->most-viewed
等array( 'meta_key' => 'wpb_post_views_count', 'orderby' => 'meta_value_num', 'order' => 'DESC' , 'paged' => $paged,) );
我到底是怎么用 pre_get_posts
做到这一点的,并确保它是分页的,即.适用于无限滚动?在我看到的所有 pre_get_posts
示例中,都没有数组.
如何使用 pre_get_posts
钩子通过自定义页面模板显示页面上的帖子列表?
我一直在玩 pre_get_posts
钩子,这是一个想法
第 1 步:
用 slug 创建一个名为 Show 的页面:
example.com/show
第 2 步:
创建自定义页面模板:
tpl_show.php
位于当前主题目录中.
第 3 步:
我们构造以下pre_get_posts
动作回调:
function b2e_pre_get_posts( $query ){$target_page = 'show';//根据您的需要编辑if ( !is_admin()//仅前端&&$query->is_main_query()//仅主查询&&$target_page === $query->get( 'pagename' )//只匹配页面名){//修改 query_vars:$query->set('post_type', 'post');//覆盖'post_type'$query->set('pagename', null );//覆盖页面名称"$query->set('posts_per_page', 10);$query->set('meta_key', 'wpb_post_views_count');$query->set('orderby', 'meta_value_num');$query->set('order', 'DESC');//支持分页$query->is_singular = 0;//自定义页面模板add_filter('template_include', 'b2e_template_include', 99);}}add_action('pre_get_posts', 'b2e_pre_get_posts');
哪里
function b2e_template_include( $template ){$target_tpl = 'tpl_show.php';//根据您的需要编辑remove_filter('template_include', 'b2e_template_include', 99);$new_template = locate_template( array( $target_tpl ) );如果(!空($new_template))$template = $new_template;;返回 $template;}
这也应该给我们分页:
example.com/show/page/2example.com/show/page/3
等
注意事项
我根据@PieterGoosen 的建议更新了答案并删除了查询对象部分修改,因为它可以例如打破他的设置的面包屑.
还删除了 pre_get_posts
钩子中的 is_page()
检查,因为在某些情况下它可能仍然会产生一些不规则性.原因是查询对象并不总是可用.这正在研究中,参见例如
很有趣WP_Query
中的 分页 取决于未设置 nopaging
并且当前页面不是 singular(来自4.4 source)>
//分页if ( empty($q['nopaging']) && !$this->is_singular ) {$page = absint($q['paged']);如果 ( !$page )$页= 1;//如果提供了 'offset',它优先于 'paged'.if ( isset( $q['offset'] ) && is_numeric( $q['offset'] ) ) {$q['offset'] = absint( $q['offset'] );$pgstrt = $q['offset'] .', ';} 别的 {$pgstrt = absint( ( $page - 1 ) * $q['posts_per_page'] ) .', ';}$limits = 'LIMIT' .$pgstrt .$q['posts_per_page'];}
我们可以看到生成的 SQL 查询的 LIMIT
部分在条件检查范围内.这就解释了为什么我们要修改上面的 is_singular
属性.
我们可以使用其他过滤器/钩子,但在这里我们使用了 OP 提到的 pre_get_posts
.
希望对您有所帮助.
My situation is somewhat complex, I'll try to explain it as succinctly as possible.
I'm currently using query_posts
to modify the main query on custom pages on my site, which as far as I can tell works quite well, though I've read that using query_posts is bad practice for a number of different reasons.
So, why am I using query_posts
and not creating a WP_Query
object you may ask?
It's because I'm using the infinite-scroll plugin, infinite-scroll doesn't play nice with WP_query, but it works absolutely fine when you simply modify the main query with query_posts. For example, pagination doesn't work using infinite scroll + WP_query (main concern).
On one page, I'm modifying the query to get most viewed posts.
<?php $paged = get_query_var( 'paged' ) ? get_query_var( 'paged' ) : 1; ?>
<?php query_posts( array( 'meta_key' => 'wpb_post_views_count', 'orderby' => 'meta_value_num', 'order' => 'DESC' , 'paged' => $paged, ) ); ?>
<?php if (have_posts()) : ?>
<?php while ( have_posts() ) : the_post() ?>
<?php if ( has_post_format( 'video' )) {
get_template_part( 'video-post' );
}elseif ( has_post_format( 'image' )) {
get_template_part( 'image-post' );
} else {
get_template_part( 'standard-post' );
}
?>
<?php endwhile;?>
<?php endif; ?>
So after a lot of reading I gather that my other option to modify the main query is using pre_get_posts
, though I'm somewhat unsure as to how to go about this.
Take this for example:-
function textdomain_exclude_category( $query ) {
if ( $query->is_home() && $query->is_main_query() ) {
$query->set( 'cat', '-1,-2' );
}
}
add_action( 'pre_get_posts', 'textdomain_exclude_category' );
Alright, so simple enough - if it's the home page, modify the main query and exclude two categories.
What I'm confused about and can't figure out is:-
the use case scenario for custom page templates. With my
query_posts
modification I can just drop in the array beforeif (have_posts())
, select my page template, publish it and away I go. Withpre_get_posts
I can't figure out how to say for example$query->most-viewed
etcarray( 'meta_key' => 'wpb_post_views_count', 'orderby' => 'meta_value_num', 'order' => 'DESC' , 'paged' => $paged, ) );
How the heck do I do that with pre_get_posts
and make sure it's paginated, ie. works with infinite scroll? In all the examples I've seen with pre_get_posts
there's no arrays.
How to use the pre_get_posts
hook to display list of posts on a page, through a custom page template?
I've been playing with the pre_get_posts
hook and here's one idea
Step #1:
Ceate a page called for example Show with the slug:
example.com/show
Step #2:
Create a custom page template:
tpl_show.php
located in the current theme directory.
Step #3:
We construct the following pre_get_posts
action callback:
function b2e_pre_get_posts( $query )
{
$target_page = 'show'; // EDIT to your needs
if ( ! is_admin() // front-end only
&& $query->is_main_query() // main query only
&& $target_page === $query->get( 'pagename' ) // matching pagename only
) {
// modify query_vars:
$query->set( 'post_type', 'post' ); // override 'post_type'
$query->set( 'pagename', null ); // override 'pagename'
$query->set( 'posts_per_page', 10 );
$query->set( 'meta_key', 'wpb_post_views_count' );
$query->set( 'orderby', 'meta_value_num' );
$query->set( 'order', 'DESC' );
// Support for paging
$query->is_singular = 0;
// custom page template
add_filter( 'template_include', 'b2e_template_include', 99 );
}
}
add_action( 'pre_get_posts', 'b2e_pre_get_posts' );
where
function b2e_template_include( $template )
{
$target_tpl = 'tpl_show.php'; // EDIT to your needs
remove_filter( 'template_include', 'b2e_template_include', 99 );
$new_template = locate_template( array( $target_tpl ) );
if ( ! empty( $new_template ) )
$template = $new_template; ;
return $template;
}
This should also give us pagination:
example.com/show/page/2
example.com/show/page/3
etc.
Notes
I updated the answer and removed the query-object part modification, based on the suggestion from @PieterGoosen, since it could e.g. break the breadcrumbs on his setup.
Also removed the is_page()
check within the pre_get_posts
hook, since it might still give some irregularities in some cases. The reason is that the query-object is not always available. This is being worked on, see e.g. #27015. There are workarounds possible if we want to use the is_page()
or is_front_page()
.
I constructed the following table, just to get a better overview of some of the properties and query varaiables of the main WP_Query
object, for a given slug:
It's interesting to note that the pagination in WP_Query
depends on the nopaging
not being set and the current page not being singular (from the 4.4 source):
// Paging
if ( empty($q['nopaging']) && !$this->is_singular ) {
$page = absint($q['paged']);
if ( !$page )
$page = 1;
// If 'offset' is provided, it takes precedence over 'paged'.
if ( isset( $q['offset'] ) && is_numeric( $q['offset'] ) ) {
$q['offset'] = absint( $q['offset'] );
$pgstrt = $q['offset'] . ', ';
} else {
$pgstrt = absint( ( $page - 1 ) * $q['posts_per_page'] ) . ', ';
}
$limits = 'LIMIT ' . $pgstrt . $q['posts_per_page'];
}
where we can see that the LIMIT
part of the generated SQL query is within the conditional check. This explains why we modify the is_singular
property above.
We could have used other filter/hooks, but here we used pre_get_posts
as mentioned by the OP.
Hope this help.
这篇关于WordPress - pre_get_posts 代替页面上的 query_posts的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!