导读:本期聚焦于小伙伴创作的《PHP如何实现视频收藏功能?详解后端逻辑与数据表设计》,敬请观看详情。视频收藏功能看似只是给记录打标记,其实背后涉及用户关系、重复写入控制和统计实时性三个核心问题。若直接往表里插数据而不做唯一约束,很快就会出现同一用户重复收藏同个视频的脏数据。合理的做法是建立用户ID与视频ID的联合唯一索引,配合插入前的存在性判断或数据库层忽略重复写入。读取收藏列表时,要通过左连接拿到视频标题与封面,同时用子查询或关联统计该视频被多少人收藏。本文从数据表结构、收藏与取消接口、列表查询以及防刷限制四个维度,给出一套可落地的PHP实现方案,帮助后端开发者少走弯路。

在内容型网站中,视频收藏是提升用户留存的基础交互。用PHP实现这一功能,重点不在于页面按钮,而在于后端如何设计数据关系、处理并发写入以及高效返回列表。下面从表结构开始,逐步拆解实现逻辑。

一、数据表结构设计

收藏本质是多对多关系:一个用户可收藏多个视频,一个视频也可被多个用户收藏。因此必须单独建一张关联表,而不能在用户表或视频表里加数组字段。关联表只需三个核心字段,外加时间戳用于排序。

建表时务必给 user_id 与 video_id 建立联合唯一索引。这样即使PHP层判断失效,数据库也会拒绝重复插入,从底层杜绝脏数据。下面是MySQL表结构示例:

CREATE TABLE `user_video_favorite` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `user_id` int(11) unsigned NOT NULL COMMENT '用户ID',
  `video_id` int(11) unsigned NOT NULL COMMENT '视频ID',
  `created_at` int(11) unsigned NOT NULL COMMENT '收藏时间',
  PRIMARY KEY (`id`),
  UNIQUE KEY `uk_user_video` (`user_id`,`video_id`),
  KEY `idx_video_id` (`video_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

视频主表可保持简单,至少包含 id、title、cover_url 等字段。统计某视频总收藏数时,不需要在视频表冗余字段,可用 count 查询或定时任务同步,避免每次收藏都更新主表造成锁竞争。

如果业务需要区分收藏夹分类,可再加一个 favorite_group_id 字段,但初期版本建议先不做,降低复杂度。联合唯一索引要调整为 (user_id, video_id, favorite_group_id)。

二、收藏与取消收藏接口

后端接收前端 POST 请求,参数通常包括 user_id 和 video_id。在写入前,应先检查视频是否存在,防止收藏了脏ID。随后尝试插入,并利用数据库唯一索引捕获重复异常,而不是先 select 再 insert,减少一次查询且避免并发竞态。

取消收藏直接用 delete 按 user_id 和 video_id 删除即可。如果前端频繁切换收藏状态,建议提供单个 toggle 接口,根据受影响行数判断当前操作是收藏还是取消,减少接口数量。

<?php
function toggleFavorite($pdo, $userId, $videoId) {
    // 先确认视频存在
    $stmt = $pdo->prepare("SELECT id FROM video WHERE id = ?");
    $stmt->execute([$videoId]);
    if (!$stmt->fetch()) {
        return ['code' => 404, 'msg' => '视频不存在'];
    }

    // 尝试插入,依赖唯一索引防重复
    $stmt = $pdo->prepare(
        "INSERT IGNORE INTO user_video_favorite (user_id, video_id, created_at) VALUES (?, ?, ?)"
    );
    $stmt->execute([$userId, $videoId, time()]);
    if ($stmt->rowCount() > 0) {
        return ['code' => 0, 'msg' => '已收藏', 'status' => 1];
    }

    // 插入失败说明已收藏,执行取消
    $stmt = $pdo->prepare(
        "DELETE FROM user_video_favorite WHERE user_id = ? AND video_id = ?"
    );
    $stmt->execute([$userId, $videoId]);
    return ['code' => 0, 'msg' => '已取消', 'status' => 0];
}
?>

使用 INSERT IGNORE 是此处的技巧:当违反唯一索引时,MySQL 不报错只返回0行影响。相比捕获异常,代码更简洁。若用普通 INSERT 再捕重复错误码 1062,也可行但需处理异常分支。

在高并发场景下,可在 Redis 用 set 缓存用户收藏的 video_id,减轻数据库压力。但缓存与数据库一致性要用延时双删或消息队列处理,新手建议先跑通数据库逻辑再优化。

三、获取用户收藏列表

用户中心展示收藏视频时,要关联视频表取出标题与封面,并按收藏时间倒序。直接用 JOIN 即可,避免先查收藏表再循环查视频表引发 N+1 查询。

列表接口应支持分页,用 LIMIT 和偏移量控制。同时可一并查出每个视频的总收藏数,方便前端展示热度,但总数字段建议用子查询,不要影响主分页性能。

<?php
function getFavoriteList($pdo, $userId, $page = 1, $size = 10) {
    $offset = ($page - 1) * $size;
    $sql = "SELECT v.id, v.title, v.cover_url, f.created_at,
            (SELECT COUNT(*) FROM user_video_favorite WHERE video_id = v.id) AS total_fav
            FROM user_video_favorite f
            INNER JOIN video v ON v.id = f.video_id
            WHERE f.user_id = ?
            ORDER BY f.created_at DESC
            LIMIT ? OFFSET ?";
    $stmt = $pdo->prepare($sql);
    $stmt->execute([$userId, $size, $offset]);
    return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
?>

上面代码中,total_fav 用相关子查询获取,数据量不大时可接受。若视频收藏量到百万级,应改为独立计数表或 Redis 计数器,在收藏取消时同步增减。

前端拿到列表后,可根据 created_at 做相对时间显示,比如“3天前收藏”。后端只返回时间戳,格式化交给客户端更灵活。

四、防刷与权限控制

收藏接口若不加限制,恶意用户可用脚本狂刷,撑爆关联表。基础防护是在网关层对单用户做每秒请求数限流,PHP内部也可记录用户最后操作时间,间隔小于200毫秒直接拒绝。

另外,user_id 不能信任前端传参,必须从登录态(如 JWT 或 session)解析。否则用户A能收藏到用户B名下,引发越权漏洞。下面示例展示从 session 取用户ID:

<?php
session_start();
if (empty($_SESSION['user_id'])) {
    http_response_code(401);
    echo json_encode(['code' => 401, 'msg' => '未登录']);
    exit;
}
$userId = (int)$_SESSION['user_id'];
?>

权限校验应放在所有收藏相关接口的最前面,形成统一中间件。这样新增接口时不会漏掉,也方便后续接入 OAuth 第三方登录。

最后,数据库层面的联合唯一索引不仅是防重复,也是防刷的最后防线。即使限流被绕过,脏数据也不会进表,只是接口返回已收藏,对系统影响可控。

PHP视频收藏MySQL修改时间:2026-08-04 20:39:45

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。