本文目录导读:

我来为你详细介绍PHP项目中实现收藏/点赞功能的完整方案。
数据库设计
基础表结构
-- 用户表 (已有)
CREATE TABLE `users` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`username` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
表 (已有,如文章、视频等)
CREATE TABLE `articles` (
`id` int(11) NOT NULL AUTO_INCREMENT, varchar(200) NOT NULL,
`content` text,
PRIMARY KEY (`id`)
) ENGINE=InnoDB;
-- 点赞记录表
CREATE TABLE `likes` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL COMMENT '用户ID',
`target_id` int(11) NOT NULL COMMENT '目标内容ID',
`target_type` varchar(20) NOT NULL COMMENT '目标类型: article, video, comment',
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_user_target` (`user_id`, `target_id`, `target_type`),
KEY `idx_target` (`target_id`, `target_type`)
) ENGINE=InnoDB;
-- 收藏记录表
CREATE TABLE `favorites` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) NOT NULL COMMENT '用户ID',
`target_id` int(11) NOT NULL COMMENT '目标内容ID',
`target_type` varchar(20) NOT NULL COMMENT '目标类型',
`created_at` timestamp DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (`id`),
UNIQUE KEY `uk_user_target` (`user_id`, `target_id`, `target_type`),
KEY `idx_target` (`target_id`, `target_type`)
) ENGINE=InnoDB;
点赞功能实现
Service层 - LIKE操作
<?php
class LikeService {
private $db;
public function __construct($db) {
$this->db = $db;
}
/**
* 切换点赞状态
* @param int $userId 用户ID
* @param int $targetId 目标ID
* @param string $targetType 目标类型
* @return array
*/
public function toggleLike($userId, $targetId, $targetType) {
try {
// 检查是否已点赞
$hasLiked = $this->checkLike($userId, $targetId, $targetType);
if ($hasLiked) {
// 取消点赞
$this->removeLike($userId, $targetId, $targetType);
$liked = false;
} else {
// 添加点赞
$this->addLike($userId, $targetId, $targetType);
$liked = true;
}
// 获取最新的点赞数
$count = $this->getLikeCount($targetId, $targetType);
return [
'success' => true,
'liked' => $liked,
'count' => $count
];
} catch (Exception $e) {
return [
'success' => false,
'message' => $e->getMessage()
];
}
}
/**
* 添加点赞
*/
private function addLike($userId, $targetId, $targetType) {
$sql = "INSERT INTO likes (user_id, target_id, target_type)
VALUES (:user_id, :target_id, :target_type)";
$stmt = $this->db->prepare($sql);
$stmt->execute([
':user_id' => $userId,
':target_id' => $targetId,
':target_type' => $targetType
]);
// 更新内容表的点赞数(如果存在)
$this->updateCount($targetId, $targetType, 'like', 1);
}
/**
* 取消点赞
*/
private function removeLike($userId, $targetId, $targetType) {
$sql = "DELETE FROM likes
WHERE user_id = :user_id
AND target_id = :target_id
AND target_type = :target_type";
$stmt = $this->db->prepare($sql);
$stmt->execute([
':user_id' => $userId,
':target_id' => $targetId,
':target_type' => $targetType
]);
// 更新内容表的点赞数
$this->updateCount($targetId, $targetType, 'like', -1);
}
/**
* 检查是否已点赞
*/
public function checkLike($userId, $targetId, $targetType) {
$sql = "SELECT id FROM likes
WHERE user_id = :user_id
AND target_id = :target_id
AND target_type = :target_type";
$stmt = $this->db->prepare($sql);
$stmt->execute([
':user_id' => $userId,
':target_id' => $targetId,
':target_type' => $targetType
]);
return $stmt->fetch() ? true : false;
}
/**
* 获取点赞数
*/
public function getLikeCount($targetId, $targetType) {
$sql = "SELECT COUNT(*) as count FROM likes
WHERE target_id = :target_id
AND target_type = :target_type";
$stmt = $this->db->prepare($sql);
$stmt->execute([
':target_id' => $targetId,
':target_type' => $targetType
]);
$result = $stmt->fetch();
return $result['count'];
}
/**
* 更新内容表的计数(可选)
*/
private function updateCount($targetId, $targetType, $type, $value) {
// 根据目标类型更新不同表
switch ($targetType) {
case 'article':
$column = $type === 'like' ? 'like_count' : 'favorite_count';
$sql = "UPDATE articles SET {$column} = {$column} + :value WHERE id = :id";
break;
// 可以添加其他类型
}
if (isset($sql)) {
$stmt = $this->db->prepare($sql);
$stmt->execute([
':value' => $value,
':id' => $targetId
]);
}
}
}
收藏功能实现
<?php
class FavoriteService {
private $db;
public function __construct($db) {
$this->db = $db;
}
/**
* 切换收藏状态
*/
public function toggleFavorite($userId, $targetId, $targetType) {
try {
$hasFavorited = $this->checkFavorite($userId, $targetId, $targetType);
if ($hasFavorited) {
$this->removeFavorite($userId, $targetId, $targetType);
$favorited = false;
} else {
$this->addFavorite($userId, $targetId, $targetType);
$favorited = true;
}
$count = $this->getFavoriteCount($targetId, $targetType);
return [
'success' => true,
'favorited' => $favorited,
'count' => $count
];
} catch (Exception $e) {
return [
'success' => false,
'message' => $e->getMessage()
];
}
}
// 添加收藏
private function addFavorite($userId, $targetId, $targetType) {
$sql = "INSERT INTO favorites (user_id, target_id, target_type)
VALUES (:user_id, :target_id, :target_type)";
$stmt = $this->db->prepare($sql);
return $stmt->execute([
':user_id' => $userId,
':target_id' => $targetId,
':target_type' => $targetType
]);
}
// 取消收藏
private function removeFavorite($userId, $targetId, $targetType) {
$sql = "DELETE FROM favorites
WHERE user_id = :user_id
AND target_id = :target_id
AND target_type = :target_type";
$stmt = $this->db->prepare($sql);
return $stmt->execute([
':user_id' => $userId,
':target_id' => $targetId,
':target_type' => $targetType
]);
}
// 检查是否已收藏
public function checkFavorite($userId, $targetId, $targetType) {
$sql = "SELECT id FROM favorites
WHERE user_id = :user_id
AND target_id = :target_id
AND target_type = :target_type";
$stmt = $this->db->prepare($sql);
$stmt->execute([
':user_id' => $userId,
':target_id' => $targetId,
':target_type' => $targetType
]);
return $stmt->fetch() ? true : false;
}
// 获取收藏数
public function getFavoriteCount($targetId, $targetType) {
$sql = "SELECT COUNT(*) as count FROM favorites
WHERE target_id = :target_id
AND target_type = :target_type";
$stmt = $this->db->prepare($sql);
$stmt->execute([
':target_id' => $targetId,
':target_type' => $targetType
]);
$result = $stmt->fetch();
return $result['count'];
}
// 获取用户收藏列表
public function getUserFavorites($userId, $targetType, $page = 1, $limit = 10) {
$offset = ($page - 1) * $limit;
$sql = "SELECT f.*, a.title, a.created_at
FROM favorites f
JOIN articles a ON f.target_id = a.id
WHERE f.user_id = :user_id
AND f.target_type = :target_type
ORDER BY f.created_at DESC
LIMIT :limit OFFSET :offset";
$stmt = $this->db->prepare($sql);
$stmt->bindParam(':user_id', $userId, PDO::PARAM_INT);
$stmt->bindParam(':target_type', $targetType, PDO::PARAM_STR);
$stmt->bindParam(':limit', $limit, PDO::PARAM_INT);
$stmt->bindParam(':offset', $offset, PDO::PARAM_INT);
$stmt->execute();
return $stmt->fetchAll();
}
}
控制器层实现
<?php
class ActionController {
private $likeService;
private $favoriteService;
private $auth; // 用户认证服务
public function __construct() {
$db = new PDO("mysql:host=localhost;dbname=test", "root", "");
$this->likeService = new LikeService($db);
$this->favoriteService = new FavoriteService($db);
$this->auth = new AuthService();
}
/**
* AJAX处理点赞请求
*/
public function handleLike() {
// 检查用户登录
$user = $this->auth->getCurrentUser();
if (!$user) {
return json_encode(['success' => false, 'message' => '请先登录']);
}
// 验证输入
$targetId = intval($_POST['target_id']);
$targetType = in_array($_POST['target_type'], ['article', 'video'])
? $_POST['target_type'] : 'article';
if (!$targetId) {
return json_encode(['success' => false, 'message' => '参数错误']);
}
// 执行点赞操作
$result = $this->likeService->toggleLike($user['id'], $targetId, $targetType);
return json_encode($result);
}
/**
* AJAX处理收藏请求
*/
public function handleFavorite() {
$user = $this->auth->getCurrentUser();
if (!$user) {
return json_encode(['success' => false, 'message' => '请先登录']);
}
$targetId = intval($_POST['target_id']);
$targetType = in_array($_POST['target_type'], ['article', 'video'])
? $_POST['target_type'] : 'article';
if (!$targetId) {
return json_encode(['success' => false, 'message' => '参数错误']);
}
$result = $this->favoriteService->toggleFavorite($user['id'], $targetId, $targetType);
return json_encode($result);
}
}
前端实现
HTML结构
<!-- 点赞按钮 -->
<div class="action-bar">
<button class="btn-like" data-id="123" data-type="article">
<i class="icon-like"></i>
<span class="count">99</span>
</button>
<!-- 收藏按钮 -->
<button class="btn-favorite" data-id="123" data-type="article">
<i class="icon-star"></i>
<span class="count">56</span>
</button>
</div>
JavaScript交互
// 封装AJAX请求
class ActionHandler {
constructor() {
this.initEvents();
}
initEvents() {
// 点赞事件
document.querySelectorAll('.btn-like').forEach(btn => {
btn.addEventListener('click', (e) => this.handleLike(e));
});
// 收藏事件
document.querySelectorAll('.btn-favorite').forEach(btn => {
btn.addEventListener('click', (e) => this.handleFavorite(e));
});
}
async handleLike(event) {
const btn = event.currentTarget;
const targetId = btn.dataset.id;
const targetType = btn.dataset.type;
// 防重复点击
if (btn.classList.contains('loading')) return;
btn.classList.add('loading');
try {
const formData = new FormData();
formData.append('target_id', targetId);
formData.append('target_type', targetType);
const response = await fetch('/api/like', {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.success) {
// 更新UI
btn.classList.toggle('active');
btn.querySelector('.count').textContent = result.count;
// 动画效果
btn.querySelector('i').classList.add('animated');
setTimeout(() => {
btn.querySelector('i').classList.remove('animated');
}, 500);
} else {
alert(result.message || '操作失败');
}
} catch (error) {
console.error('Error:', error);
alert('网络错误,请稍后重试');
} finally {
btn.classList.remove('loading');
}
}
async handleFavorite(event) {
// 类似点赞逻辑
// ...
}
}
// 初始化
new ActionHandler();
性能优化建议
使用Redis缓存
class LikeServiceWithCache extends LikeService {
private $redis;
public function __construct($db, $redis) {
parent::__construct($db);
$this->redis = $redis;
}
public function getLikeCountWithCache($targetId, $targetType) {
$cacheKey = "likes:{$targetType}:{$targetId}";
// 尝试从缓存获取
$count = $this->redis->get($cacheKey);
if ($count === false) {
// 缓存未命中,从数据库获取
$count = parent::getLikeCount($targetId, $targetType);
$this->redis->setex($cacheKey, 3600, $count); // 缓存1小时
}
return $count;
}
}
批量查询优化
// 批量获取多个内容的点赞状态
public function getBatchLikeStatus($userId, $targetIds, $targetType) {
if (empty($targetIds)) return [];
$placeholders = implode(',', array_fill(0, count($targetIds), '?'));
$sql = "SELECT target_id FROM likes
WHERE user_id = ?
AND target_id IN ($placeholders)
AND target_type = ?";
$params = array_merge([$userId], $targetIds, [$targetType]);
$stmt = $this->db->prepare($sql);
$stmt->execute($params);
$likedIds = $stmt->fetchAll(PDO::FETCH_COLUMN);
return array_fill_keys($likedIds, true);
}
安全注意事项
// 1. 防止SQL注入(使用预处理语句)
// 2. 验证用户权限
// 3. 防止重复操作(数据库唯一索引)
// 4. 限制操作频率
class RateLimiter {
private $redis;
private $maxRequests = 10; // 每分钟最多10次
private $timeWindow = 60; // 60秒
public function isAllowed($userId, $action) {
$key = "rate_limit:{$action}:{$userId}";
$current = $this->redis->get($key) ?: 0;
if ($current >= $this->maxRequests) {
return false;
}
$this->redis->multi();
$this->redis->incr($key);
$this->redis->expire($key, $this->timeWindow);
$this->redis->exec();
return true;
}
}
这个方案完整实现了收藏和点赞功能,包含了数据库设计、后端逻辑、前端交互以及性能优化等核心要素,你可以根据实际需求进行调整和扩展。