PHP项目怎么实现商品详情点赞?

wen PHP项目 29

本文目录导读:

PHP项目怎么实现商品详情点赞?

  1. 数据库设计
  2. 后端 PHP 接口实现
  3. 前端实现
  4. 如何判断用户是否已点赞(预加载状态)
  5. 一些需要注意的进阶问题

在 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' : '' ?>">

一些需要注意的进阶问题

  1. 性能优化

    • 对于高并发场景,直接操作 like_count 字段可能成为瓶颈,可以考虑使用 Redis 缓存点赞数,定期同步到数据库。
    • 如果商品点赞操作非常频繁,可以将点赞请求放入 消息队列(如 RabbitMQ、Kafka)进行异步处理。
  2. 防刷机制

    • 除了利用数据库唯一索引防止重复点赞,还可以在服务端加入 限流 逻辑(同一 IP 每分钟只能对同一商品点赞 5 次)。
    • 对接口进行 身份验证(如 JWT 或 Session),防止匿名攻击。
  3. 用户体验

    • 如果网络有延迟,可以 乐观更新 UI(先变亮并 +1,然后等待后端确认,若失败再回滚),让用户感觉更流畅。
    • 加上加载动画或短暂禁用按钮,避免用户快速连点产生多个请求。
  4. 表设计扩展

    • 如果未来有点踩(dislike)、不同内容类型(文章、评论)等需求,可以在 product_likes 表中增加 type 字段,通过 typetarget_id 来区分。

点赞功能的核心就是 账号系统 + 记录表 + 原子更新,上面提供的方案虽然是一个基础实现,但已经具备了生产环境可用的核心逻辑(事务、防重复、实时更新),你可以根据项目实际情况,在其基础上增加缓存、限流、异步处理等优化。

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