怎样在PHP项目中实现置顶功能?

wen java案例 7

本文目录导读:

怎样在PHP项目中实现置顶功能?

  1. 方案一:使用 is_top 字段 + 双排序(推荐)
  2. 方案二:单独维护 top_list 表(适合复杂场景)
  3. 方案三:前端伪置顶 + 服务端配合
  4. 四种实现方案的对比
  5. 最佳实践建议

在PHP项目中实现置顶功能,通常有数据库标记法排序值法两种主流方案,下面我会给出最实用、可扩展性强的实现方式。


使用 is_top 字段 + 双排序(推荐)

这是最常用且性能较好的方式,适合文章、帖子、产品等列表的置顶。

数据库设计

在原有数据表(如 articlesposts)中添加一个字段:

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 首页

最佳实践建议

  1. 简单项目:直接用 is_top + top_sort 字段,SQL 中 ORDER BY is_top DESC, top_sort DESC, created_at DESC
  2. 多分类项目:使用独立 top_list 表,通过 group_key 区分不同分类的置顶。
  3. 高并发场景:将置顶 ID 列表缓存到 Redis(Set 或 ZSet),查询时先取缓存再查 DB。
  4. 注意分页:如果使用字段标记法,置顶项会始终出现在第一页,需要统计总页数时注意:总记录数 = 置顶数 + 普通条目总数。

如果需要针对具体业务(如文章置顶、商品置顶、多分类置顶)给出更详细的代码示例,可以告诉我你的具体场景。

抱歉,评论功能暂时关闭!