需求:精选置顶 + 不重复
Bricks Facebook 群里有用户求助:他想在 Bricks 的查询结果里同时显示 5 个 CPT(资源类文章),要求:
- 查询结果前 2 条必须是不同 CPT 里最新的、被标记为 “Featured”(基于 ACF 的是/否字段)的 2 篇文章;
- 其余结果按日期顺序继续排列,不能重复这 2 篇精选文章;
- 精选文章永远至少 2 篇,但只有最新的 2 篇置顶,第 3、4、5 篇精选就按正常顺序出现在结果里;
- 结果要用 Bricks 的筛选功能,所以不能用两个查询循环拼。
这类“精选置顶”需求在内容站里很常见:编辑希望把重点内容顶到列表最前面,其余按时间自然排序。难点在于“置顶的同时不能重复出现”,用两个查询循环拼的话,精选文章会同时在两个循环里出现,而且 Bricks 的 Query Filters 筛选器只作用于单个循环,无法跨循环工作。
一个查询循环、一个 PHP 查询就能解决。
核心思路:取 ID 再合并
方案分三步:
- 取精选:查最新 2 篇
featured字段为 1 的文章,只要 ID。 - 取其余:查全部文章,用
post__not_in排除刚才那 2 篇精选,只要 ID。 - 合并:把精选 ID 数组放在前面、其余 ID 接在后面,用
post__in+orderby => post__in让 Bricks 严格按这个顺序输出。
为什么要“只要 ID”?因为两个查询的排序规则不同(精选按时间取前 2,其余按时间全量),直接在 SQL 层用一条查询做“先精选后其余”很麻烦,而分两次取 ID 再合并,逻辑清晰、容易维护,还能顺便去重。fields => 'ids' 让 get_posts() 只返回 ID 数组,内存占用极小。
完整 PHP 查询代码
在 Query Loop 的 PHP 查询编辑器(Query editor)里粘贴:
// 定义你的文章类型 - 按实际 CPT slug 调整
$post_types = ['resource']; // 或 ['type1', 'type2', 'type3', 'type4', 'type5']
// 第 1 步:取最新 2 篇精选文章
$featured_args = [
'post_type' => $post_types,
'posts_per_page' => 2,
'orderby' => 'date',
'order' => 'DESC',
'meta_query' => [
[
'key' => 'featured', // 换成你的 ACF 字段名
'value' => '1',
'compare' => '='
]
],
'fields' => 'ids',
'no_found_rows' => true,
];
$featured_post_ids = get_posts( $featured_args );
// 第 2 步:取其余文章(排除 2 篇精选)
$other_args = [
'post_type' => $post_types,
'posts_per_page' => 100, // 按预计文章总数调整
'orderby' => 'date',
'order' => 'DESC',
'fields' => 'ids',
'no_found_rows' => true,
];
// 找到精选就排除掉
if ( ! empty( $featured_post_ids ) ) {
$other_args['post__not_in'] = $featured_post_ids;
}
$other_post_ids = get_posts( $other_args );
// 第 3 步:合并数组 - 精选在前,其余在后
$final_post_ids = array_merge( $featured_post_ids, $other_post_ids );
// 没有文章时返回空结果
if ( empty( $final_post_ids ) ) {
return ['post__in' => [0]];
}
// 第 4 步:返回查询参数,保持顺序
return [
'post_type' => $post_types,
'posts_per_page' => -1, // 全部显示,或设数字用于分页
'post__in' => $final_post_ids,
'orderby' => 'post__in', // 保持我们设定的顺序
'no_found_rows' => true,
];
使用说明与坑
- 把
$post_types数组换成你自己的 CPT slug;多 CPT 直接列在数组里。 - 把
'featured'换成你的 ACF 是/否字段名。ACF 是/否字段勾选后存储值为1。 posts_per_page => 100是“其余文章”的上限,按站点规模调大即可。orderby => 'post__in'是整段代码的灵魂:WordPress 默认会按日期重排post__in的结果,必须显式声明保持数组顺序。no_found_rows => true能省一次 COUNT 查询,循环不需要分页计数时记得带上。- 空结果处理:
['post__in' => [0]]是 WordPress 里“返回空”的惯用写法,因为post__in传空数组会被忽略导致返回全部文章,塞一个不存在的 ID 0 就能安全地得到空结果。
最后提醒一点:这段代码放在 Bricks 的 PHP 查询编辑器里时,记得点 Sign code(签名代码) 按钮保存,否则代码不会生效。签名是 Bricks 对 PHP 查询的安全确认机制,防止未经验证的代码直接执行。如果编辑时报“代码未签名”的错误,回到查询编辑器重新粘贴再签名一次即可。
性能方面也放心:两个 get_posts 都带了 no_found_rows => true 和 fields => 'ids',不查总数、只取 ID,几十篇文章的站点毫秒级完成,不会拖慢页面。真正的大列表(几千篇)才需要考虑缓存,一般内容站用不到。
小结:“精选置顶 + 去重”这类需求,用两个 get_posts 分别取 ID 再合并,比写复杂 meta_query 排序直观得多。因为全程只用一个查询循环,Bricks 的筛选(Query Filters)功能也能正常叠加,不会冲突。学会了这个模式,类似“置顶 + 常规排序”的组合需求都能照搬——比如“置顶新品 + 其余按热度”,把第一个查询的排序条件换一下就行。