本文目录导读:

在 PHP 项目中实现商品详情点赞功能,核心思路其实很简单:记录哪个用户给哪个商品点过赞,并更新商品的点赞总数,这需要一套前后端配合的流程。
下面我会从数据库设计、后端 PHP 接口、前端交互和防重复点赞这几个方面,给出一个清晰、可落地的实现方案。
数据库设计
你需要两张核心表:
-- 1. 商品表(如果已有,只需添加点赞数字段)
ALTER TABLE `products`
ADD COLUMN `like_count` INT UNSIGNED NOT NULL DEFAULT 0 COMMENT '点赞总数';
-- 2. 点赞记录表(核心:记录谁给什么内容点过赞)
CREATE TABLE `product_likes` (
`id` INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`user_id` INT UNSIGNED NOT NULL COMMENT '用户ID',
`product_id` INT UNSIGNED NOT NULL COMMENT '商品ID',
`created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
-- 防止重复点赞的关键:联合唯一索引
UNIQUE KEY `uk_user_product` (`user_id`, `product_id`),
INDEX `idx_product_id` (`product_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
为什么这样设计?
like_count字段在商品表中,方便直接展示,避免每次统计点赞数时都去 COUNT 记录表,性能更好。product_likes表通过 联合唯一索引 从数据库层面杜绝了同一个用户对同一个商品点赞多于一次。
后端 PHP 接口实现
以下是一个典型的场景:用户点击“点赞”按钮,由 JS 发送 AJAX 请求到 PHP 后端。
接口文件:/api/toggle_like.php
<?php
// 1. 开启会话,获取登录用户信息
session_start();
// 假设用户登录后,用户ID存储在 SESSION 中
$user_id = $_SESSION['user_id'] ?? 0;
$product_id = (int)($_POST['product_id'] ?? 0);
if ($user_id <= 0 || $product_id <= 0) {
echo json_encode(['code' => -1, 'msg' => '参数错误或未登录']);
exit;
}
// 2. 连接数据库(请替换为你的数据库配置)
$pdo = new PDO('mysql:host=localhost;dbname=your_db;charset=utf8mb4', 'root', 'password');
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
try {
$pdo->beginTransaction();
// 3. 核心逻辑:查询是否已点赞
$stmt = $pdo->prepare("SELECT id FROM product_likes WHERE user_id = ? AND product_id = ?");
$stmt->execute([$user_id, $product_id]);
$has_liked = $stmt->fetch();
if ($has_liked) {
// **已点赞:执行取消点赞操作**
// 删除点赞记录
$deleteStmt = $pdo->prepare("DELETE FROM product_likes WHERE user_id = ? AND product_id = ?");
$deleteStmt->execute([$user_id, $product_id]);
// 商品点赞数减1
$updateStmt = $pdo->prepare("UPDATE products SET like_count = like_count - 1 WHERE id = ? AND like_count > 0");
$updateStmt->execute([$product_id]);
$action = 'unlike';
} else {
// **未点赞:执行点赞操作**
// 增加点赞记录
$insertStmt = $pdo->prepare("INSERT INTO product_likes (user_id, product_id) VALUES (?, ?)");
$insertStmt->execute([$user_id, $product_id]);
// 商品点赞数加1
$updateStmt = $pdo->prepare("UPDATE products SET like_count = like_count + 1 WHERE id = ?");
$updateStmt->execute([$product_id]);
$action = 'like';
}
// 4. 查询当前最新的点赞总数
$countStmt = $pdo->prepare("SELECT like_count FROM products WHERE id = ?");
$countStmt->execute([$product_id]);
$current_count = $countStmt->fetchColumn();
$pdo->commit();
// 5. 返回 JSON 数据给前端
echo json_encode([
'code' => 1,
'msg' => '操作成功',
'data' => [
'action' => $action, // 'like' 或 'unlike'
'count' => (int)$current_count
]
]);
} catch (Exception $e) {
$pdo->rollBack();
echo json_encode(['code' => -2, 'msg' => '服务器繁忙,请稍后再试']);
}
?>
要点解释:
- 事务:点赞记录和商品点赞数的增减需要在一个事务里,保证数据一致(要么都成功,要么都失败)。
- 防重复:利用
UNIQUE索引 +INSERT的方式(如果是首次),第二次INSERT会失败,这里用SELECT先判断更直观易懂。 - 原子操作:
UPDATE products SET like_count = like_count + 1是原子操作,即使并发高,也不会导致计数错误。
前端实现
以一个简单的 HTML + JS 为例。
<!-- 商品详情页中的点赞按钮 -->
<div class="like-area">
<button id="likeBtn" data-product-id="<?= $product['id'] ?>" class="like-btn <?= $has_liked ? 'active' : '' ?>">
❤️ <span id="likeCount"><?= $product['like_count'] ?></span>
</button>
</div>
<script>
document.getElementById('likeBtn').addEventListener('click', function() {
const btn = this;
const productId = btn.getAttribute('data-product-id');
// 使用 Fetch API 发送请求
fetch('/api/toggle_like.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: 'product_id=' + productId
})
.then(response => response.json())
.then(data => {
if (data.code === 1) {
// 更新页面显示的点赞数
document.getElementById('likeCount').innerText = data.data.count;
// 切换点赞状态样式(比如高亮)
if (data.data.action === 'like') {
btn.classList.add('active');
} else {
btn.classList.remove('active');
}
} else {
alert(data.msg); // 请先登录”
}
})
.catch(error => {
console.error('Error:', error);
});
});
</script>
关键点:
- 点击按钮后,先发 AJAX 请求给后端,根据后端返回的结果更新界面。不要直接在前端自增减,否则可能和后端数据不同步。
data-product-id把商品 ID 传给 JS。
如何判断用户是否已点赞(预加载状态)
在渲染商品详情页时,PHP 应该查询当前用户是否已经点赞,这样页面加载时就能显示正确的按钮状态(高亮/未高亮)。
// 在商品详情页的 PHP 模块中
$has_liked = false;
if (isset($_SESSION['user_id'])) {
$stmt = $pdo->prepare("SELECT id FROM product_likes WHERE user_id = ? AND product_id = ?");
$stmt->execute([$_SESSION['user_id'], $product_id]);
$has_liked = $stmt->fetch() ? true : false;
}
然后在 HTML 中:
<button id="likeBtn" class="like-btn <?= $has_liked ? 'active' : '' ?>">
一些需要注意的进阶问题
-
性能优化:
- 对于高并发场景,直接操作
like_count字段可能成为瓶颈,可以考虑使用 Redis 缓存点赞数,定期同步到数据库。 - 如果商品点赞操作非常频繁,可以将点赞请求放入 消息队列(如 RabbitMQ、Kafka)进行异步处理。
- 对于高并发场景,直接操作
-
防刷机制:
- 除了利用数据库唯一索引防止重复点赞,还可以在服务端加入 限流 逻辑(同一 IP 每分钟只能对同一商品点赞 5 次)。
- 对接口进行 身份验证(如 JWT 或 Session),防止匿名攻击。
-
用户体验:
- 如果网络有延迟,可以 乐观更新 UI(先变亮并 +1,然后等待后端确认,若失败再回滚),让用户感觉更流畅。
- 加上加载动画或短暂禁用按钮,避免用户快速连点产生多个请求。
-
表设计扩展:
- 如果未来有点踩(dislike)、不同内容类型(文章、评论)等需求,可以在
product_likes表中增加type字段,通过type和target_id来区分。
- 如果未来有点踩(dislike)、不同内容类型(文章、评论)等需求,可以在
点赞功能的核心就是 账号系统 + 记录表 + 原子更新,上面提供的方案虽然是一个基础实现,但已经具备了生产环境可用的核心逻辑(事务、防重复、实时更新),你可以根据项目实际情况,在其基础上增加缓存、限流、异步处理等优化。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播