pre_get_posts 改主循环,多刷了一次页面才发现分页崩了
需求很简单:首页只显示某个分类的文章。我顺手在 functions.php 里写了段 pre_get_posts,本机看着挺对,上线第二天发现翻到第 2 页就 404。 原因不复杂,但我当时确实没往那想。 没加 is_main_query
需求很简单:首页只显示某个分类的文章。我顺手在 functions.php 里写了段 pre_get_posts,本机看着挺对,上线第二天发现翻到第 2 页就 404。
原因不复杂,但我当时确实没往那想。
没加 is_main_query 会连累到侧栏和分页
// 错误示范,functions.php
add_action( 'pre_get_posts', function ( $query ) {
if ( $query->is_home() ) {
$query->set( 'cat', 12 );
}
} );
看着没毛病。is_home() 也确实只在首页成立。问题在于页面上不止一个查询——侧栏最新文章、相关推荐、主题自带的某个 widget,都可能走同一个 hook。is_home() 是布尔值,它不告诉你「这是不是那个主查询」。
所以条件得写全:
add_action( 'pre_get_posts', function ( $query ) {
if ( is_admin() ) {
return;
}
if ( ! $query->is_main_query() ) {
return;
}
if ( $query->is_home() ) {
$query->set( 'cat', 12 );
}
} );
is_main_query() 是 WP_Query 的实例方法,写在 query 对象上,不是全局函数那种用法。这点容易手滑。
分页 404 的另一个来源:posts_per_page
如果你的需求是「每页显示 8 篇」,光 set posts_per_page 不够。WordPress 算分页总数用的是它自己那套逻辑,你得让它知道总数变了。
我现在习惯这么写:
add_action( 'pre_get_posts', function ( $query ) {
if ( is_admin() || ! $query->is_main_query() ) {
return;
}
if ( $query->is_home() ) {
$query->set( 'cat', 12 );
$query->set( 'posts_per_page', 8 );
}
} );
一般来说这样就够了。但如果主题在模板里自己又调了一次 query_posts(),那你会被打回原形——query_posts() 会覆盖主查询,pre_get_posts 白干。这函数在官方文档里是明确不推荐的,可真有不少老主题还在用。
怎么确认到底哪个查询在捣乱
临时开一下:
// wp-config.php
define( 'SAVEQUERIES', true );
// 模板里想看的地方
global $wpdb;
error_log( print_r( $wpdb->queries, true ) );
日志会告诉你这次请求跑了多少条 SQL、每个查询花了多久、从哪个文件哪一行来的。比猜有用得多。记得用完删掉,这玩意儿在生产环境上挺吃内存的。
哦对,还有一个我踩过的:条件里用 is_front_page() 而不是 is_home()。两者在「静态首页」和「博客首页」的配置下是反的。我没法给你一个统一答案,得看你后台「阅读」设置里选的是哪种,这点确实绕。