问题描述

我正在尝试在我的主题中创建一个类别模板,如果用户位于存档的第一页,则会显示静态内容,然后在以下页面上显示类别中的帖子。我基本上是尝试复制 Wired.com 上的类别行为,其中 http://www.wired.com/category/design 是一个着陆页,而/category /design /page /1 显示了类似于期望在类别归档中的反向按时间顺序排列的列表。

这里的关键是,我没有在第一页上显示类别归档中的任何帖子,所以我需要下一页从第一个帖子开始。如果’paged’ 查询 var 为 2,我首先尝试使用 offset and manual pagination 来设置查询的偏移量为 0 。但是当设置为零时,偏移量被忽略,所以我可以做的最好是将偏移量设置为 1,并从该类别中的第二个职位。

这是我添加到 functions.php 中的:

add_action('pre_get_posts', 'category_query_offset', 1 );
function category_query_offset(&$query) {
  // Before anything else, make sure this is the right query
  if (!$query->is_category('news')) {
    return;
  }

  // Next, determine how many posts per page
  $ppp = get_option('posts_per_page');

  //Next, detect and handle pagination
  if ($query->is_paged) {
    // Manually determine page query offset
    $page_offset = (($query->query_vars['paged']-1) * $ppp) - $ppp;
    // Apply adjust page offset
    $query->set('offset', $page_offset );
  }
}

什么会更好,以及它显示的是使用默认分页,以便帖子从第 1 页开始,但为静态内容插入页面 0 。假设’paged’ 为 1,那么’paged’ 为 0,并显示第一页帖子,这样就可以显示静态内容。问题是’paged’ 从不是 1,因为 WordPress 将’paged’ 设置为零,当用户请求第一页。这意味着’paged’ 对于/category /news /page /1 和/category /news 都是 0 。

有没有办法检查用户是否要求/category /news /page /1 而不是/category /news?没有,是否有一种方法来显示从第 2 页开始的类别中的所有帖子?

最佳解决方案

这是一个非常有趣的问题 (我特别为您的方法和研究而升级了) 。这里的大曲线球是查询的第一页:

  • 您无法设置查询以返回第一页上的 0 帖子

  • 通过将每页的页面内容向上移动一页,您将会丢失最后一页,因为查询仍将只具有相同的帖子数量,因此 $max_num_pages 属性仍然保持不变

我们将需要以某种方式”trick” WP_Query 类返回我们的帖子 corectly 与一个页面的偏移,并且还得到正确的页数,以便不松动查询中的最后一页

让我们来看看下面的想法,并尝试将所有内容都放在代码中。在我们做之前,我想在这里提几点

重要笔记:

  • 一切都是未经测试的,所以它可能是错误的。确保在本地进行测试,并打开调试开关

  • 该代码至少需要 PHP 5.3,5.3 以下版本会导致致命错误。请注意,如果仍然使用下面的 PHP 5.5 版本,您应该已经很久以前升级了

  • 修改和滥用您认为适合您的确切需求的代码

光线:

我们需要什么

为了使一切正常,我们将需要以下内容:

  • 正在查看的当前页码

  • posts_per_page 选项在阅读设置中设置

  • 定制 offset

  • 修改查询的 $found_posts 属性以更正 $max_num_pages 属性

分页是 WP_Query 下来一个非常简单的几行代码

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'];
}

基本上发生了什么,一旦偏移被明确设置,paged 参数被忽略。 SQL LIMIT 子句的第一个参数从偏移量重新计算,并将是生成的 SQL 查询中要跳过的帖子数。

从您的问题,显然是将 offset 设置为 0 时,偏移查询失败,这是奇怪的,因为以下检查应该返回 true

if ( isset( $q['offset'] ) && is_numeric( $q['offset'] ) )

0 是一个有效的数字,应该返回 true 。如果您的安装没有,您应该调试问题

为了回到手头的问题,我们将使用相同的逻辑来计算和设置我们的偏移量,以获得第 2 页的帖子 1,并从中分页查询。对于第一页,我们不会更改任何内容,因此假设在第 1 页上的帖子仍然正常,我们稍后将需要”hide”,以便我们不在第 1 页显示它们

add_action( 'pre_get_posts', function ( $q )
{
    if (    !is_admin() // Only target the front end, VERY VERY IMPORTANT
         && $q->is_main_query() // Only target the main query, VERY VERY IMPORTANT
         && $q->is_cateory( 'news' ) // Only target the news category
    ) {
        $current_page = $q->get( 'paged' ); // Get the current page number
        // We will only need to run this from page 2 onwards
        if ( $current_page != 0 ) { // You can also use if ( is_paged() ) {
            // Get the amount of posts per page
            $posts_per_page = get_option( 'posts_per_page' );
            // Recalculate our offset
            $offset = ( ( $current_page - 1) * $posts_per_page ) - $posts_per_page; // This should work on page 2 where it returns 0

            // Set our offset
            $q->set( 'offset', $offset );
        }
    }
});

您应该看到第 2 页的第 1 页上的相同帖子。如前所述,如果没有发生这种情况,is_numeric( 0 )将返回 false(不应该),或者还有另一个 pre_get_posts 操作,也试图设置一个偏移量或者您正在使用 posts_*子句过滤器 (更具体地说是 post_limits 过滤器) 。这将是你需要自己调试的东西。

下一个问题是纠正分页,如前所述,你将是一个简短的页面。为此,我们将需要将 get_option( 'posts_per_page' )的值添加到查询中发现的帖子数量,因为我们将查询量抵消该量。通过这样做,我们有效地将 1 添加到 $max_num_pages 属性。

add_action( 'found_posts', function ( $found_posts, $q )
{
    if (    !is_admin() // Only target the front end, VERY VERY IMPORTANT
         && $q->is_main_query() // Only target the main query, VERY VERY IMPORTANT
         && $q->is_cateory( 'news' ) // Only target the news category
    ) {
        $found_posts = $found_posts + get_option( 'posts_per_page');
    }
}, 10, 2 );

这应该排序所有,除了第一页。

现在全部 (特别为 @ialocin – Yellow Submarine)

这一切都应该进入 functions.php

add_action( 'pre_get_posts', function ( $q )
{
    if (    !is_admin() // Only target the front end, VERY VERY IMPORTANT
         && $q->is_main_query() // Only target the main query, VERY VERY IMPORTANT
         && $q->is_cateory( 'news' ) // Only target the news category
    ) {
        $current_page = $q->get( 'paged' ); // Get the current page number
        // We will only need to run this from page 2 onwards
        if ( $current_page != 0 ) { // You can also use if ( is_paged() ) {
            // Get the amount of posts per page
            $posts_per_page = get_option( 'posts_per_page' );
            // Recalculate our offset
            $offset = ( ( $current_page - 1) * $posts_per_page ) - $posts_per_page; // This should work on page 2 where it returns 0

            // Set our offset
            $q->set( 'offset', $offset );
        }
    }
});

add_filter( 'found_posts', function ( $found_posts, $q )
{
    if (    !is_admin() // Only target the front end, VERY VERY IMPORTANT
         && $q->is_main_query() // Only target the main query, VERY VERY IMPORTANT
         && $q->is_cateory( 'news' ) // Only target the news category
    ) {
        $found_posts = $found_posts + get_option( 'posts_per_page');
    }
    return $found_posts;
}, 10, 2 );

第一页选项

这里有几个选项:

选项 1

我可能会选择这个选项。你想要做的是创建一个 category-news.php(如果还没有这样做) 。这将是每当查看 news 类别时将使用的模板。这个模板很简单

<?php
get_header()

if ( !is_paged() ) { // This is the first page
    get_template_part( 'news', 'special' );
} else { // This is not the first page
    get_template_part( 'news', 'loop' );
}

get_sidebar();
get_footer();

如您所见,我已经包括两个模板部分,news-special.phpnews-loop.php 。现在,两个自定义模板的基础是:

  • news-special.php – > 这个模板部分将是您想要显示在第一页上的任何内容。在这里添加所有您的自定义静态信息。要小心,不要在此模板中调用循环,因为这将显示第一页的帖子。

  • news-loop.php – > 这是我们调用循环的模板。本节将如下所示:

    global $wp_query;
    while ( have_posts() ) {
    the_post();
    
        // Your template tags and markup
    
    }
    

选项 2

使用静态内容创建一个单独的模板,当我们查看 news 类别的第一页时,只需使用 category_template 筛选器即可使用此模板。另外,请确保不要调用此模板中的默认循环。另外,请确保这里的命名约定不会与模板层次结构中的模板名称相冲突

我希望这是有用的。随心所欲地发表评论

EDIT

感谢 OP,WP_Query 类有一个明确的错误,检查 trac ticket #34060 。我发布的代码来自 WordPress v4.4,该版本中修复了错误。

我回到 v4.3 的源代码,那里的 bug 是,当我设置为 offset 的值时,我可以确认 0 被忽略,因为代码只是检查 offset 参数是 empty 。 PHP 中的 0 为空。我不知道这个行为 (错误) 是否仅在 v4.3 或所有以前的版本中发现 (根据机票,该错误在 v4.3 中),但是有一个补丁可以检查这个错误在 trac 门票里。就像我说的,这个 bug 在 v4.4 中一定是固定的

参考文献

注:本文内容整合自 Google/Baidu/Bing 辅助翻译的英文资料结果。如果您对结果不满意,可以加入我们改善翻译效果:薇晓朵技术论坛。