本文约 5,159 字,预计阅读 13 分钟。文中命令与配置均经过实测,可直接参考执行。
引言:文章”消失”了,其实还在
后台”所有文章”页面显示 12 篇,但站点前台明明有 500 篇。或者搜索某篇文章,搜不到。或者点”已发布”筛选,数字对不上。
这类问题几乎不会真的丢数据——wp_posts 里的记录都在,出问题的是后台列表的查询构造过程。理解这个过程,六类异常都有明确解法。
原理分析:后台列表是怎么查出来的
后台文章列表由 WP_List_Table 的子类 WP_Posts_List_Table 渲染,数据来自一次 WP_Query:
$query = new WP_Query( array(
'post_type' => 'post',
'post_status' => 'any', // 或 all
'posts_per_page' => 20, // 来自用户"显示选项"
'paged' => $paged,
's' => $search, // 搜索关键词
'cat' => $cat, // 分类筛选
'orderby' => 'date',
'order' => 'DESC',
) );
这条查询会被四个东西修改:
- 用户偏好:
wp_usermeta里的edit_post_per_page和manageedit-postcolumnshidden(每页条数和隐藏列); pre_get_posts钩子:主题或插件可能在这里偷偷加了条件;posts_where/posts_join等过滤器:插件做权限控制、语言过滤时常用;- 数据库状态:
post_status、post_type、post_parent的值是否干净。
所以异常基本都能归到这四类的某一类。
保姆级实操
异常一:数量明显不对(有 500 篇只显示 12 篇)
原因 A:每页条数被改小了
右上角”显示选项” → 每页条数。也可能被代码改过:
wp user meta get 1 edit_post_per_page
wp user meta update 1 edit_post_per_page 20
原因 B:某个插件用 pre_get_posts 加了限制
这是最常见的根因。排查方法——临时把插件全停了看是否恢复:
wp plugin list --status=active --field=name
# 逐个停用测试,或者一次性停掉全部
wp plugin deactivate --all
wp plugin activate 某插件
找到元凶后查它的代码:
grep -rn "pre_get_posts" wp-content/plugins/某插件/
典型的有问题的写法(忘了判断 is_main_query 和 is_admin):
// 错误写法:把前台的筛选条件也应用到了后台
add_action( 'pre_get_posts', function( $q ) {
$q->set( 'posts_per_page', 12 ); // 后台也被限制了!
} );
// 正确写法
add_action( 'pre_get_posts', function( $q ) {
if ( is_admin() || ! $q->is_main_query() ) {
return;
}
$q->set( 'posts_per_page', 12 );
} );
原因 C:多语言插件按语言过滤
WPML/Polylang 在后台只显示当前语言的文章。切换语言选择器(后台顶栏)就能看到全部。
异常二:筛选”已发布”的数量对不上
顶部的”全部 / 已发布 / 草稿 / 回收站”数字来自独立统计:
SELECT post_status, COUNT(*) FROM wp_posts
WHERE post_type = 'post' GROUP BY post_status;
对比后台显示的数字。如果对不上,通常是数据库里有脏状态值(比如 post_status 是 inherit 的附件混进来了,或者被插件写成了奇怪的值)。
修复:
-- 查看所有状态
SELECT DISTINCT post_status FROM wp_posts;
-- 把异常状态改回来(示例)
UPDATE wp_posts SET post_status = 'publish'
WHERE post_status = 'some-weird-status';
异常三:某些列不显示(比如没有”分类””标签”列)
这是用户偏好,不是故障。右上角”显示选项”勾选即可。如果是所有用户都不显示,检查代码:
// 强制显示某些列
add_filter( 'manage_edit-post_columns', function( $columns ) {
$columns['categories'] = '分类';
$columns['tags'] = '标签';
return $columns;
} );
异常四:点开列表直接报错
常见报错及原因:
报错 1:Fatal error: Allowed memory size exhausted
文章太多 + 插件吃内存。临时提高限制:
// wp-config.php
define( 'WP_MEMORY_LIMIT', '256M' );
define( 'WP_MAX_MEMORY_LIMIT', '512M' );
但根治要减少每页条数(改成 10 或 20)和找出吃内存的插件。
报错 2:WordPress database error ... Table 'wp_xxx.wp_posts' doesn't exist
表前缀不匹配。检查 wp-config.php 的 $table_prefix:
wp config get table_prefix
# 看实际表
wp db query "SHOW TABLES;"
报错 3:500 错误
看日志:
tail -50 wp-content/debug.log
tail -50 /var/log/php8.2-fpm.log
异常五:搜索不到明明存在的文章
后台搜索只搜 post_title、post_content、post_excerpt,不搜自定义字段和分类名。
另外两个常见原因:
- 文章状态是草稿/待审,而搜索时默认筛选的是”全部”——这个通常没问题;
- 搜索插件改写了搜索逻辑(比如装了 Relevanssi 或 SearchWP),把后台搜索也接管了。检查:
bash
grep -rn "is_search" wp-content/plugins/ | grep -i "admin"
要让后台搜索支持自定义字段:
add_filter( 'posts_search', function( $search, $q ) {
global $wpdb;
if ( ! is_admin() || ! $q->is_main_query() || empty( $q->get('s') ) ) {
return $search;
}
$term = '%' . $wpdb->esc_like( $q->get('s') ) . '%';
$search .= $wpdb->prepare(
" OR EXISTS (SELECT 1 FROM {$wpdb->postmeta} pm
WHERE pm.post_id = {$wpdb->posts}.ID
AND pm.meta_value LIKE %s) ",
$term
);
return $search;
}, 10, 2 );
异常六:分页错乱 / 点第二页是空
通常是 posts_per_page 和分页参数冲突,或者插件修改了 offset。
// 检查当前查询参数
add_action( 'admin_notices', function() {
if ( ! isset($_GET['post_type']) || 'post' !== $_GET['post_type'] ) return;
global $wp_query;
echo '<pre>per_page=' . $wp_query->get('posts_per_page') . ' paged=' . $wp_query->get('paged') . ' found=' . $wp_query->found_posts . '</pre>';
} );
注意:offset 参数会让 paged 失效,这是 WordPress 的已知行为。插件里如果用了 offset,分页就会错乱。
通用修复:重置后台列表偏好
用户的列表设置存在 wp_usermeta 里,删掉就能恢复默认:
wp user meta delete 1 manageedit-postcolumnshidden
wp user meta delete 1 edit_post_per_page
wp user meta list 1 --keys=manageedit-postcolumnshidden
常见踩坑与失败案例
- 案例一:某站后台文章列表只显示 12 篇,翻遍了设置找不到原因,最后发现是主题的作者为了首页”最新文章”模块写了
pre_get_posts,没有判断is_admin(),把后台列表也限制成 12 篇了。这是教科书级的错误。 - 案例二:迁移后后台列表报错
Table doesn't exist。原因是新站的$table_prefix是wp_mm_,而wp-config.php里还写着wp_。用wp config get table_prefix一查就出来了。 - 内存耗尽:文章 2000 篇 + 每页 999 条 + 多个 SEO 插件同时跑,后台列表必崩。把每页条数改成 20。
- 多语言站”少了一半文章”:这是正常的,Polylang 默认按语言过滤。切换语言或开启”显示所有语言”。
post_status脏数据:某插件把状态写成了Publish(大写 P),导致统计和筛选都找不到它。SQL 里统一大小写:
sql
UPDATE wp_posts SET post_status = LOWER(post_status)
WHERE post_status <> LOWER(post_status);
- 回收站文章占用计数:后台”全部”数量包含回收站。30 天后自动清空,或手工
wp post delete $(wp post list --post_status=trash --format=ids) --force。 - 列表页加载极慢:参见 WordPress 后台卡顿排查,根因通常是同一个插件。
总结
后台文章列表异常几乎不会真的丢数据,问题都在查询构造环节。排查顺序:先看 SELECT post_status, COUNT(*) 确认数据库真实数量 → 再查 edit_post_per_page 用户偏好 → 然后停插件排查 pre_get_posts 误伤(这是最高频根因,写法上必须判断 is_admin() 和 is_main_query())→ 最后看表前缀和内存限制。开发者请务必记住那条铁律:任何 pre_get_posts 修改都要先判断 is_admin() 和 is_main_query(),否则一定会在某个时刻把后台搞乱。
