WordPress - pre_get_posts 代替页面上的 query_posts [英] WordPress - pre_get_posts in place of query_posts on pages

查看:22
本文介绍了WordPress - pre_get_posts 代替页面上的 query_posts的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我的情况有些复杂,我尽量简明扼要地解释一下.

我目前正在使用 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');

好吧,就这么简单——如果是首页,修改主查询,排除两个类别.

我感到困惑和无法弄清楚的是:-

  1. 自定义页面模板的用例场景.通过我的 query_posts 修改,我可以在 if (have_posts()) 之前放入数组,选择我的页面模板,发布它然后我走.使用 pre_get_posts 我不知道怎么说例如 $query->most-viewed

  2. 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:-

  1. the use case scenario for custom page templates. With my query_posts modification I can just drop in the array before if (have_posts()), select my page template, publish it and away I go. With pre_get_posts I can't figure out how to say for example $query->most-viewed etc

  2. array( '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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆