PHP项目怎样实现资讯收藏功能?

wen PHP项目 54

本文目录导读:

PHP项目怎样实现资讯收藏功能?

  1. 数据库设计
  2. 后端API逻辑 (PHP示例)
  3. 前端交互 (JavaScript + HTML示例)
  4. 性能优化与注意事项

在PHP项目中实现资讯收藏功能,核心业务逻辑通常是:用户对自己感兴趣的资讯文章进行标记(收藏/取消收藏),并在个人中心查看已收藏列表

下面我会从数据库设计、后端API逻辑、前端交互 三个主要方面,结合代码示例,为你梳理一个标准且可靠的实现方案。

数据库设计

收藏功能通常是多对多关系(一个用户可以收藏多篇文章,一篇文章可以被多个用户收藏),建议设计一张独立的favorites表。

-- 收藏表
CREATE TABLE `favorites` (
    `id` int(11) NOT NULL AUTO_INCREMENT,
    `user_id` int(11) NOT NULL COMMENT '用户ID',
    `article_id` int(11) NOT NULL COMMENT '资讯文章ID',
    `created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '收藏时间',
    PRIMARY KEY (`id`),
    -- 关键:建立唯一索引,防止重复收藏
    UNIQUE KEY `uk_user_article` (`user_id`, `article_id`),
    KEY `idx_user_id` (`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户收藏表';
-- 假设已有的资讯文章表 (用于关联查询)
CREATE TABLE `articles` (
    `id` int(11) NOT NULL AUTO_INCREMENT, varchar(255) NOT NULL,
    `content` text,
    `published_at` datetime DEFAULT NULL,
    PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

设计要点

  1. UNIQUE KEY uk_user_article (user_id, article_id):这是防止重复收藏的关键,依赖数据库约束比每次操作前都查询一次更可靠。
  2. created_at:记录收藏时间,便于按时间排序展示列表。

后端API逻辑 (PHP示例)

这里以常见的MVC框架(如ThinkPHP、Laravel)或原生代码为例,核心逻辑是通用的,假设你使用PDO操作数据库。

切换收藏状态 (Toggle) 接口

最常用的方式是:如果已经收藏,则取消收藏;如果未收藏,则添加收藏

<?php
// 示例: toggleFavorite.php
// 假设已经通过 session/jwt 获取到了当前用户ID
$userId = $_SESSION['user_id'] ?? 0;
$articleId = intval($_POST['article_id'] ?? 0);
if ($userId <= 0 || $articleId <= 0) {
    echo json_encode(['code' => 0, 'msg' => '参数错误或未登录']);
    exit;
}
// 1. 连接数据库 (使用PDO示例)
$pdo = new PDO('mysql:host=localhost;dbname=your_db;charset=utf8mb4', 'root', 'password');
// 2. 检查该用户是否已收藏该文章
$stmt = $pdo->prepare("SELECT id FROM favorites WHERE user_id = ? AND article_id = ?");
$stmt->execute([$userId, $articleId]);
$favorite = $stmt->fetch();
if ($favorite) {
    // 3. 如果已存在,则执行删除(取消收藏)
    $deleteStmt = $pdo->prepare("DELETE FROM favorites WHERE user_id = ? AND article_id = ?");
    $deleteStmt->execute([$userId, $articleId]);
    $isFavorited = false;
    $msg = '取消收藏成功';
} else {
    // 4. 如果不存在,则执行插入(添加收藏)
    $insertStmt = $pdo->prepare("INSERT INTO favorites (user_id, article_id) VALUES (?, ?)");
    $insertStmt->execute([$userId, $articleId]);
    $isFavorited = true;
    $msg = '收藏成功';
}
// 5. 返回JSON结果给前端
echo json_encode([
    'code' => 1,
    'msg' => $msg,
    'data' => [
        'is_favorited' => $isFavorited // 前端可以根据这个值切换按钮样式
    ]
]);

获取用户收藏列表

<?php
// 示例: getFavorites.php
$userId = $_SESSION['user_id'] ?? 0;
$page = intval($_GET['page'] ?? 1);
$pageSize = 10;
$offset = ($page - 1) * $pageSize;
if ($userId <= 0) {
    echo json_encode(['code' => 0, 'msg' => '未登录']);
    exit;
}
$pdo = new PDO('mysql:host=localhost;dbname=your_db;charset=utf8mb4', 'root', 'password');
// 连表查询:根据收藏表关联文章表,获取文章标题、时间等信息
$sql = "SELECT a.id, a.title, a.published_at, f.created_at as favorite_time
        FROM favorites f
        INNER JOIN articles a ON f.article_id = a.id
        WHERE f.user_id = ?
        ORDER BY f.created_at DESC
        LIMIT ? OFFSET ?";
$stmt = $pdo->prepare($sql);
$stmt->execute([$userId, $pageSize, $offset]);
$favorites = $stmt->fetchAll(PDO::FETCH_ASSOC);
// 可选:获取总条数用于分页
$countStmt = $pdo->prepare("SELECT COUNT(*) FROM favorites WHERE user_id = ?");
$countStmt->execute([$userId]);
$total = $countStmt->fetchColumn();
echo json_encode([
    'code' => 1,
    'data' => [
        'list' => $favorites,
        'total' => $total,
        'page' => $page
    ]
]);

检查单篇文章的收藏状态(辅助接口)

通常在文章详情页,需要判断当前用户是否收藏了这篇文章,用于初始化收藏按钮的状态。

<?php
// 示例: checkFavorite.php
$userId = $_SESSION['user_id'] ?? 0;
$articleId = intval($_GET['article_id'] ?? 0);
$pdo = new PDO('...');
$stmt = $pdo->prepare("SELECT id FROM favorites WHERE user_id = ? AND article_id = ?");
$stmt->execute([$userId, $articleId]);
$isFavorited = (bool)$stmt->fetch();
echo json_encode(['code' => 1, 'data' => ['is_favorited' => $isFavorited]]);

前端交互 (JavaScript + HTML示例)

假设用户点击收藏按钮时触发切换操作。

HTML部分

<!-- 文章详情页 -->
<button id="favoriteBtn" data-article-id="123">
    <span id="favIcon">☆</span>
    <span id="favText">收藏</span>
</button>

JavaScript部分 (使用Fetch API)

const favoriteBtn = document.getElementById('favoriteBtn');
const articleId = favoriteBtn.dataset.articleId;
// 1. 页面加载时,检查该文章是否已被用户收藏
fetch(`checkFavorite.php?article_id=${articleId}`)
    .then(res => res.json())
    .then(data => {
        if (data.code === 1 && data.data.is_favorited) {
            updateFavoriteBtn(true); // 更新按钮为已收藏状态
        }
    });
// 2. 绑定点击事件,触发切换
favoriteBtn.addEventListener('click', function() {
    const formData = new FormData();
    formData.append('article_id', articleId);
    fetch('toggleFavorite.php', {
        method: 'POST',
        body: formData
    })
    .then(res => res.json())
    .then(data => {
        if (data.code === 1) {
            // 根据返回的 is_favorited 状态更新UI
            updateFavoriteBtn(data.data.is_favorited);
            // 可以显示一个提示消息
            alert(data.msg);
        } else {
            alert(data.msg || '操作失败,请重试');
            // 如果是未登录,会跳转登录页
        }
    })
    .catch(err => {
        console.error('Error:', err);
        alert('网络错误');
    });
});
// 辅助函数:更新按钮样式和文本
function updateFavoriteBtn(isFavorited) {
    const icon = document.getElementById('favIcon');
    const text = document.getElementById('favText');
    if (isFavorited) {
        icon.innerHTML = '★'; // 实心星
        text.innerHTML = '已收藏';
        favoriteBtn.classList.add('active'); // 添加高亮样式
    } else {
        icon.innerHTML = '☆'; // 空心星
        text.innerHTML = '收藏';
        favoriteBtn.classList.remove('active');
    }
}

性能优化与注意事项

  1. 防重复点击:前端在点击后应立即禁用按钮,等待接口返回后再恢复,防止用户快速点击发送多个请求。

    // 在点击事件开始时禁用按钮
    favoriteBtn.disabled = true;
    fetch(...).then(...).finally(() => {
        favoriteBtn.disabled = false;
    });
  2. 用户ID安全:永远不要从客户端获取user_id,必须在服务端通过Session或JWT验证。

  3. 缓存与计数:如果文章需要显示“收藏数”,可以在文章表articles中加一个favorites_count字段,每次用户收藏/取消收藏时,通过事务更新该字段,但要注意并发问题,可以使用UPDATE articles SET favorites_count = favorites_count + 1 WHERE id = ?这种原子操作。

  4. 大表处理:如果用户量非常大(百万级),可以考虑使用Redis的SortedSet来存储某个用户的所有收藏ID,查询列表时走缓存,但写入时双写数据库和缓存,并处理好一致性。

  5. 安全性:在toggleFavorite接口中,最好验证article_id对应的文章是否存在,防止用户收藏不存在的文章。

实现收藏功能的关键点是:

  • 数据库:建立(user_id, article_id)的唯一联合索引,防止重复。
  • 后端:提供切换接口(增删一体)和列表查询接口。
  • 前端:处理好按钮状态的切换(点击后立即禁用防止重复请求),并根据接口返回的is_favorited真实状态更新UI。

按照这个标准方案实现,收藏功能就可以稳定、高效地运行了。

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