本文目录导读:

在PHP项目中实现置顶功能,通常有数据库标记法和排序值法两种主流方案,下面我会给出最实用、可扩展性强的实现方式。
使用 is_top 字段 + 双排序(推荐)
这是最常用且性能较好的方式,适合文章、帖子、产品等列表的置顶。
数据库设计
在原有数据表(如 articles、posts)中添加一个字段:
ALTER TABLE `articles` ADD COLUMN `is_top` TINYINT(1) NOT NULL DEFAULT 0 COMMENT '是否置顶:0=否,1=是', ADD COLUMN `top_sort` INT NOT NULL DEFAULT 0 COMMENT '置顶排序值(越大越靠前)', ADD INDEX `idx_top_list` (`is_top`, `top_sort`);
is_top:标记是否置顶。top_sort:当有多个置顶项时,控制它们之间的顺序(如 100、200 等,方便插入新置顶)。
PHP 查询代码
// 获取列表时,置顶的始终排在前面,按 top_sort 降序,再按正常排序
$sql = "SELECT * FROM articles
ORDER BY is_top DESC, top_sort DESC, created_at DESC
LIMIT 20";
$result = $db->query($sql);
原理:is_top DESC 保证置顶的(1)排在非置顶(0)前面。top_sort DESC 让置顶内部可排序,created_at DESC 作为普通排序依据。
置顶/取消置顶操作
// 置顶某篇文章
function setTop($articleId, $sortValue = 100) {
$sql = "UPDATE articles SET is_top = 1, top_sort = ? WHERE id = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$sortValue, $articleId]);
}
// 取消置顶
function unsetTop($articleId) {
$sql = "UPDATE articles SET is_top = 0, top_sort = 0 WHERE id = ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$articleId]);
}
单独维护 top_list 表(适合复杂场景)
如果置顶逻辑复杂(如不同分类有不同置顶、置顶有时间限制),推荐用独立表。
新建置顶表
CREATE TABLE `top_list` (
`id` INT AUTO_INCREMENT PRIMARY KEY,
`item_type` VARCHAR(50) NOT NULL COMMENT '类型:article, product, topic',
`item_id` INT NOT NULL COMMENT '对应业务表的ID',
`group_key` VARCHAR(100) DEFAULT NULL COMMENT '分组键(如分类ID)',
`sort_val` INT NOT NULL DEFAULT 0 COMMENT '排序值',
`created_at` DATETIME DEFAULT CURRENT_TIMESTAMP,
`expire_at` DATETIME DEFAULT NULL COMMENT '置顶过期时间',
UNIQUE KEY `uk_item` (`item_type`, `item_id`)
);
查询逻辑(使用 UNION)
// 先查询置顶项
$topIds = getTopItemIds('article', $categoryId); // 返回 [101, 205, 33]
// 获取置顶记录
$topItems = [];
if (!empty($topIds)) {
$placeholders = implode(',', array_fill(0, count($topIds), '?'));
$sql = "SELECT * FROM articles WHERE id IN ($placeholders)";
$stmt = $pdo->prepare($sql);
$stmt->execute($topIds);
$topItems = $stmt->fetchAll();
// 按 top_list 的 sort_val 顺序重新排序
$order = array_flip($topIds);
usort($topItems, fn($a, $b) => $order[$a['id']] <=> $order[$b['id']]);
}
// 获取非置顶记录(分页时注意排除)
$excludeIds = $topIds ?: [0];
$placeholders = implode(',', array_fill(0, count($excludeIds), '?'));
$sql = "SELECT * FROM articles
WHERE id NOT IN ($placeholders)
ORDER BY created_at DESC
LIMIT 20";
$stmt = $pdo->prepare($sql);
$stmt->execute($excludeIds);
$normalItems = $stmt->fetchAll();
// 合并结果
$list = array_merge($topItems, $normalItems);
前端伪置顶 + 服务端配合
适合实时性要求不高且数据量小的场景(如个人博客、小型CMS)。
后端返回所有数据时标记置顶
// 查询全部数据(不考虑分页),或者分页查询后手动合并
$sql = "SELECT *, is_top FROM articles ORDER BY is_top DESC, created_at DESC";
$result = $query->fetchAll();
// 返回给前端时,前端根据 is_top 字段决定展示样式
return json_encode([
'list' => $result,
'top_ids' => array_column(array_filter($result, fn($v) => $v['is_top'] == 1), 'id')
]);
前端代码(Vue 示例)
<template>
<div v-for="item in sortedList" :key="item.id" :class="{ 'top-item': item.is_top }">
{{ item.title }}
</div>
</template>
<script>
export default {
computed: {
sortedList() {
return this.list.sort((a, b) => b.is_top - a.is_top || b.created_at - a.created_at);
}
},
data() {
return { list: [] };
}
}
</script>
四种实现方案的对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 字段标记 + 双排序 | 简单高效,SQL 内置 | 管理多分类置顶时需扩展 | 单表业务,置顶数量少 |
| 独立置顶表 | 灵活,支持多类型+过期 | 查询需 UNION 或多次查询 | 多业务表、分组置顶、有时效性 |
| 前端伪置顶 | 后端改动小,适合轻量项目 | 分页困难,大数据量性能差 | 个人博客、后台管理 |
| Redis 缓存置顶列表 | 性能极高,适合高并发 | 需额外维护缓存一致性 | 新闻门户、BBS 首页 |
最佳实践建议
- 简单项目:直接用
is_top + top_sort字段,SQL 中ORDER BY is_top DESC, top_sort DESC, created_at DESC。 - 多分类项目:使用独立
top_list表,通过group_key区分不同分类的置顶。 - 高并发场景:将置顶 ID 列表缓存到 Redis(Set 或 ZSet),查询时先取缓存再查 DB。
- 注意分页:如果使用字段标记法,置顶项会始终出现在第一页,需要统计总页数时注意:总记录数 = 置顶数 + 普通条目总数。
如果需要针对具体业务(如文章置顶、商品置顶、多分类置顶)给出更详细的代码示例,可以告诉我你的具体场景。
版权声明:足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
足球直播
足球直播
足球直播
足球直播
足球直播
中超直播
中超直播
中超直播
中超直播
中超直播
英超直播
英超直播
英超直播
英超直播
英超直播
德甲直播
德甲直播
德甲直播
德甲直播
德甲直播
西甲直播
西甲直播
西甲直播
西甲直播
西甲直播
意甲直播
意甲直播
意甲直播
意甲直播
意甲直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播