如何修复自定义循环的分页?


122

我已经在模板文件/自定义页面模板中添加了自定义/辅助查询;如何使WordPress使用我的自定义查询进行分页,而不是使用主查询循环的分页?

附录

我已经通过修改了主循环查询query_posts()。为什么分页不起作用,如何解决?

Answers:


215

问题

默认情况下,在任何给定的上下文中,WordPress都会使用主查询来确定分页。主查询对象存储在$wp_query全局对象中,该对象也用于输出主查询循环:

if ( have_posts() ) : while ( have_posts() ) : the_post();

当您使用自定义查询,您可以创建一个完全独立的查询对象:

$custom_query = new WP_Query( $custom_query_args );

该查询通过一个完全独立的循环输出:

if ( $custom_query->have_posts() ) : 
    while ( $custom_query->have_posts() ) : 
        $custom_query->the_post();

但是,分页模板标签,其中包括previous_posts_link()next_posts_link()posts_nav_link(),和paginate_links(),根据他们的输出主要查询对象$wp_query。该主查询可以分页,也可以不分页。例如,如果当前上下文是自定义页面模板,则主要$wp_query对象将仅包含一个帖子 -自定义页面模板所分配到的页面ID的帖子

如果当前上下文是某种存档索引,则主要内容$wp_query可能包含足以引起分页的帖子,这将导致问题的下一部分:对于主要$wp_query对象,WordPress将paged 基于以下内容将参数传递给查询:pagedURL查询变量。提取查询后,该paged参数将用于确定要返回的一组分页帖子。如果单击了显示的分页链接,并加载了下一页,则您的自定义查询将无法知道分页已更改

解决方案

将正确的分页参数传递给自定义查询

假设自定义查询使用args数组:

$custom_query_args = array(
    // Custom query parameters go here
);

您将需要将正确的paged参数传递给数组。您可以通过以下方法获取用于确定当前页面的URL查询变量get_query_var()

get_query_var( 'paged' );

然后,您可以将该参数附加到自定义查询args数组中:

$custom_query_args['paged'] = get_query_var( 'paged' ) 
    ? get_query_var( 'paged' ) 
    : 1;

注意:如果您的页面是静态首页,请确保使用page而不是,paged因为静态首页使用pagepaged。这是静态首页应具有的功能

$custom_query_args['paged'] = get_query_var( 'page' ) 
    ? get_query_var( 'page' ) 
    : 1;

现在,当获取自定义查询时,将返回正确的分页帖子集。

将自定义查询对象用于分页功能

为了使分页功能产生正确的输出-即相对于自定义查询的上一个/下一个/页面链接-需要强制WordPress识别自定义查询。这需要一点“ hack”:用$wp_query自定义查询对象替换主对象$custom_query

破解主要查询对象

  1. 备份主查询对象: $temp_query = $wp_query
  2. 空主查询对象: $wp_query = NULL;
  3. 将自定义查询交换到主查询对象中: $wp_query = $custom_query;

    $temp_query = $wp_query;
    $wp_query   = NULL;
    $wp_query   = $custom_query;
    

必须在调用任何分页功能之前完成此“ hack”操作

重置主查询对象

一旦输出了分页功能,就重置主查询对象:

$wp_query = NULL;
$wp_query = $temp_query;

分页功能修复

previous_posts_link()无论分页如何,该功能都将正常工作。它仅确定当前页面,然后输出的链接page - 1。但是,需要修复才能next_posts_link()正确输出。这是因为next_posts_link()使用max_num_pages参数:

<?php next_posts_link( $label , $max_pages ); ?>

与其他查询参数一样,默认情况下,该函数将max_num_pages用于主$wp_query对象。为了强制next_posts_link()考虑$custom_query对象,您需要将传递max_num_pages给函数。您可以从$custom_query对象中获取此值$custom_query->max_num_pages

<?php next_posts_link( 'Older Posts' , $custom_query->max_num_pages ); ?>

全部放在一起

以下是具有适当功能的分页功能的自定义查询循环的基本结构:

// Define custom query parameters
$custom_query_args = array( /* Parameters go here */ );

// Get current page and append to custom query parameters array
$custom_query_args['paged'] = get_query_var( 'paged' ) ? get_query_var( 'paged' ) : 1;

// Instantiate custom query
$custom_query = new WP_Query( $custom_query_args );

// Pagination fix
$temp_query = $wp_query;
$wp_query   = NULL;
$wp_query   = $custom_query;

// Output custom query loop
if ( $custom_query->have_posts() ) :
    while ( $custom_query->have_posts() ) :
        $custom_query->the_post();
        // Loop output goes here
    endwhile;
endif;
// Reset postdata
wp_reset_postdata();

// Custom query loop pagination
previous_posts_link( 'Older Posts' );
next_posts_link( 'Newer Posts', $custom_query->max_num_pages );

// Reset main query object
$wp_query = NULL;
$wp_query = $temp_query;

附录:那query_posts()呢?

query_posts() 用于次级回路

如果您要query_posts()用于输出自定义循环,而不是通过实例化自定义查询的单独对象WP_Query(),那么您就是_doing_it_wrong(),并且会遇到一些问题(其中至少有分页问题)。解决这些问题的第一步将是将对不当使用的呼叫转换query_posts()为正确的WP_Query()呼叫。

使用query_posts()修改主循环

如果您只想修改主循环查询的参数(例如更改每页的帖子数或排除类别),则可能会想使用query_posts()。但是你还是不应该。使用时query_posts(),您将强制WordPress 替换主查询对象。(WordPress实际上进行了第二次查询,并覆盖了$wp_query。)问题是,它在更新分页的过程中为时太晚。

解决方案是通过pre_get_posts钩子提取帖子之前过滤主查询

而不是将其添加到类别模板文件(category.php):

query_posts( array(
    'posts_per_page' => 5
) );

将以下内容添加到functions.php

function wpse120407_pre_get_posts( $query ) {
    // Test for category archive index
    // and ensure that the query is the main query
    // and not a secondary query (such as a nav menu
    // or recent posts widget output, etc.
    if ( is_category() && $query->is_main_query() ) {
        // Modify posts per page
        $query->set( 'posts_per_page', 5 ); 
    }
}
add_action( 'pre_get_posts', 'wpse120407_pre_get_posts' );

无需将其添加到博客文章索引模板文件(home.php):

query_posts( array(
    'cat' => '-5'
) );

将以下内容添加到functions.php

function wpse120407_pre_get_posts( $query ) {
    // Test for main blog posts index
    // and ensure that the query is the main query
    // and not a secondary query (such as a nav menu
    // or recent posts widget output, etc.
    if ( is_home() && $query->is_main_query() ) {
        // Exclude category ID 5
        $query->set( 'category__not_in', array( 5 ) ); 
    }
}
add_action( 'pre_get_posts', 'wpse120407_pre_get_posts' );

这样,WordPress将$wp_query在确定分页时使用已修改的对象,而无需修改模板。

什么时候使用什么功能

研究这个问题,并回答这个问题和答案,以了解如何以及何时使用WP_Querypre_get_postsquery_posts()


31
抄本中的愿望页面可能是如此完整。
Pieter Goosen 2014年

芯片,你让我开心!
tepkenvannkorn

1
芯片,您总是可以节省很多时间!如果只有google会在我疯狂搜索之前给您更高的答案(针对googlers的标注);)谢谢。
Sagive SEO 2014年

使用您的示例,直到我使用了if-else块(而不是?:有条件的)在此页面的下面找到该主题之前,该页面无法工作themeforest.net/forums/thread/…,非常奇怪。否则,这个答案教给我很多东西。
2014年

2
很好的答案-一件事,我在ajax调用中运行下一个/上一个帖子链接功能时遇到问题-只是不会-在快速浏览后发现全局paged没有更新(观察到与admin-有关ajax.php环境),所以我添加了它: global $paged; $paged = $custom_query_args['paged']; 并且起作用了:)
acSlater 2014年

21

我将此代码用于带有分页的自定义循环:

<?php
if ( get_query_var('paged') ) {
    $paged = get_query_var('paged');
} elseif ( get_query_var('page') ) { // 'page' is used instead of 'paged' on Static Front Page
    $paged = get_query_var('page');
} else {
    $paged = 1;
}

$custom_query_args = array(
    'post_type' => 'post', 
    'posts_per_page' => get_option('posts_per_page'),
    'paged' => $paged,
    'post_status' => 'publish',
    'ignore_sticky_posts' => true,
    //'category_name' => 'custom-cat',
    'order' => 'DESC', // 'ASC'
    'orderby' => 'date' // modified | title | name | ID | rand
);
$custom_query = new WP_Query( $custom_query_args );

if ( $custom_query->have_posts() ) :
    while( $custom_query->have_posts() ) : $custom_query->the_post(); ?>

        <article <?php post_class(); ?>>
            <h3><a href="<?php the_permalink(); ?>"><?php the_title(); ?></a></h3>
            <small><?php the_time('F jS, Y') ?> by <?php the_author_posts_link() ?></small>
            <div><?php the_excerpt(); ?></div>
        </article>

    <?php
    endwhile;
    ?>

    <?php if ($custom_query->max_num_pages > 1) : // custom pagination  ?>
        <?php
        $orig_query = $wp_query; // fix for pagination to work
        $wp_query = $custom_query;
        ?>
        <nav class="prev-next-posts">
            <div class="prev-posts-link">
                <?php echo get_next_posts_link( 'Older Entries', $custom_query->max_num_pages ); ?>
            </div>
            <div class="next-posts-link">
                <?php echo get_previous_posts_link( 'Newer Entries' ); ?>
            </div>
        </nav>
        <?php
        $wp_query = $orig_query; // fix for pagination to work
        ?>
    <?php endif; ?>

<?php
    wp_reset_postdata(); // reset the query 
else:
    echo '<p>'.__('Sorry, no posts matched your criteria.').'</p>';
endif;
?>

资源:


1
另一个不错的教程,带有以下答案的变体:callmenick.com/post/custom-wordpress-loop-with-pagination
mrwweb

5

一如既往的酷。作为对此的附录,请考虑以下情况:您正在使用连接到页面的全局页面模板作为“介绍文字”,并在其后跟要分页的子查询。

如上文所述,使用paginate_links()并使用大多数默认值(并假设已启用漂亮的永久链接),您的分页链接将默认设置mysite.ca/page-slug/page/#为可爱,但会引发404错误,因为WordPress不知道该特定URL结构,实际上寻找“ page”的子页面,即“ page-slug”的子页面。

这里的技巧是插入一个巧妙的重写规则,该规则仅适用于接受该/page/#/结构并将其重写为WordPress可以理解的查询字符串的特定“伪存档页面”页面mysite.ca/?pagename=page-slug&paged=#。请注意,pagenamepaged不要注意(和namepage这确实使我悲伤了HOURS,在此激励了这个答案!)。

这是重定向规则:

add_rewrite_rule( "page-slug/page/([0-9]{1,})/?$", 'index.php?pagename=page-slug&paged=$matches[1]', "top" );

与往常一样,在更改重写规则时,请记住通过访问管理后端中的“设置”>“永久链接” 来刷新永久链接。

如果您有多个页面将以这种方式运行(例如,在处理多种自定义帖子类型时),则可能要避免为每个页面段创建新的重写规则。我们可以编写一个更通用的正则表达式,该表达式可用于您标识的任何页面信息。

下面是一种方法:

function wpse_120407_pseudo_archive_rewrite(){
    // Add the slugs of the pages that are using a Global Template to simulate being an "archive" page
    $pseudo_archive_pages = array(
        "all-movies",
        "all-actors"
    );

    $slug_clause = implode( "|", $pseudo_archive_pages );
    add_rewrite_rule( "($slug_clause)/page/([0-9]{1,})/?$", 'index.php?pagename=$matches[1]&paged=$matches[2]', "top" );
}
add_action( 'init', 'wpse_120407_pseudo_archive_rewrite' );

缺点/警告

这种方法的缺点是让Page slug硬编码,这使我有些p嘴。如果管理员更改了该伪归档页面的页面标签,那么您将敬酒-重写规则将不再匹配,您将获得可怕的404。

我不确定是否可以考虑此方法的解决方法,但是如果是全局页面模板以某种方式触发了重写规则,那将是很好的。有一天,如果没有其他人能破解那个特定的螺母,我可能会重新审视这个答案。


1
您可以挂钩保存,检查页面是否在meta键下包含存档模板_wp_page_template,然后添加另一个重写和刷新规则。
米洛

2

我已经通过修改了主循环查询query_posts()。为什么分页不起作用,如何解决?

好的答案创建的芯片今天需要修改。在主查询执行之后的
一段时间内,我们拥有的$wp_the_query变量应等于$wp_query全局变量。

这就是为什么这部分来自芯片的答案:

破解主要查询对象

不再需要。我们可以通过创建临时变量来忘记这一部分。

// Pagination fix
$temp_query = $wp_query;
$wp_query   = NULL;
$wp_query   = $custom_query;

现在,我们可以致电:

$wp_query   = $wp_the_query;

甚至更好,我们可以致电:

wp_reset_query();

Chip概述的所有其他内容均会保留。在查询重置部分之后,您可以调用分页函数,f($wp_query)它们取决于$wp_query全局。


为了进一步改善分页机制并赋予query_posts功能更多的自由,我创建了以下可能的改进:

https://core.trac.wordpress.org/ticket/39483


1
global $wp_query;
        $paged = get_query_var('paged', 1);

    $args = array( 
        'post_type' => '{your_post_type_name}',
        'meta_query' => array('{add your meta query argument if need}'),  
        'orderby' => 'modified',
        'order' => 'DESC',
        'posts_per_page' => 20,
        'paged' => $paged 
    );
    $query = new WP_Query($args);

    if($query->have_posts()):
        while ($query->have_posts()) : $query->the_post();
            //add your code here
        endwhile;
        wp_reset_query();

        //manage pagination based on custom Query.
        $GLOBALS['wp_query']->max_num_pages = $query->max_num_pages;
        the_posts_pagination(array(
            'mid_size' => 1,
            'prev_text' => __('Previous page', 'patelextensions'),
            'next_text' => __('Next page', 'patelextensions'),
            'before_page_number' => '<span class="meta-nav screen-reader-text">' . __('Page', 'patelextensions') . ' </span>',
        ));
    else:
    ?>
        <div class="container text-center"><?php echo _d('Result not found','30'); ?></div>
    <?php
        endif;
    ?>
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.