本文目录导读:

在PHP项目中实现用户收藏/取消收藏功能,通常需要以下步骤:
数据库设计
首先创建收藏表:
CREATE TABLE `user_favorites` ( `id` int(11) NOT NULL AUTO_INCREMENT, `user_id` int(11) NOT NULL COMMENT '用户ID', `item_id` int(11) NOT NULL COMMENT '收藏项ID', `item_type` varchar(50) NOT NULL COMMENT '收藏类型(article/product等)', `created_at` datetime DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), UNIQUE KEY `user_item` (`user_id`, `item_id`, `item_type`), INDEX `user_id` (`user_id`), INDEX `item_id` (`item_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
PHP后端实现
收藏/取消收藏接口
<?php
// favorite.php
class FavoriteController {
private $db;
public function __construct($db) {
$this->db = $db;
}
/**
* 切换收藏状态
*/
public function toggleFavorite($userId, $itemId, $itemType = 'article') {
// 检查是否已收藏
$sql = "SELECT id FROM user_favorites
WHERE user_id = ? AND item_id = ? AND item_type = ?";
$stmt = $this->db->prepare($sql);
$stmt->execute([$userId, $itemId, $itemType]);
$result = $stmt->fetch();
if ($result) {
// 已收藏,取消收藏
return $this->removeFavorite($result['id']);
} else {
// 未收藏,添加收藏
return $this->addFavorite($userId, $itemId, $itemType);
}
}
/**
* 添加收藏
*/
private function addFavorite($userId, $itemId, $itemType) {
try {
$sql = "INSERT INTO user_favorites (user_id, item_id, item_type)
VALUES (?, ?, ?)";
$stmt = $this->db->prepare($sql);
$stmt->execute([$userId, $itemId, $itemType]);
return [
'success' => true,
'action' => 'favorited',
'message' => '收藏成功'
];
} catch (PDOException $e) {
return [
'success' => false,
'message' => '收藏失败:' . $e->getMessage()
];
}
}
/**
* 取消收藏
*/
private function removeFavorite($favoriteId) {
try {
$sql = "DELETE FROM user_favorites WHERE id = ?";
$stmt = $this->db->prepare($sql);
$stmt->execute([$favoriteId]);
return [
'success' => true,
'action' => 'unfavorited',
'message' => '取消收藏成功'
];
} catch (PDOException $e) {
return [
'success' => false,
'message' => '取消收藏失败:' . $e->getMessage()
];
}
}
/**
* 检查是否已收藏
*/
public function isFavorited($userId, $itemId, $itemType) {
$sql = "SELECT id FROM user_favorites
WHERE user_id = ? AND item_id = ? AND item_type = ?";
$stmt = $this->db->prepare($sql);
$stmt->execute([$userId, $itemId, $itemType]);
return $stmt->rowCount() > 0;
}
/**
* 获取用户收藏列表
*/
public function getUserFavorites($userId, $itemType = null, $page = 1, $limit = 20) {
$offset = ($page - 1) * $limit;
$where = "user_id = ?";
$params = [$userId];
if ($itemType) {
$where .= " AND item_type = ?";
$params[] = $itemType;
}
$sql = "SELECT * FROM user_favorites
WHERE {$where}
ORDER BY created_at DESC
LIMIT ? OFFSET ?";
$params[] = $limit;
$params[] = $offset;
$stmt = $this->db->prepare($sql);
$stmt->execute($params);
return $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}
API路由处理
<?php
// api/favorite.php
header('Content-Type: application/json');
session_start();
require_once 'config/database.php';
require_once 'FavoriteController.php';
// 验证用户登录
if (!isset($_SESSION['user_id'])) {
echo json_encode(['success' => false, 'message' => '请先登录']);
exit;
}
$userId = $_SESSION['user_id'];
$action = $_POST['action'] ?? 'toggle';
$itemId = $_POST['item_id'] ?? 0;
$itemType = $_POST['item_type'] ?? 'article';
$db = getDatabaseConnection();
$favoriteController = new FavoriteController($db);
switch ($action) {
case 'toggle':
$result = $favoriteController->toggleFavorite($userId, $itemId, $itemType);
break;
case 'check':
$isFavorited = $favoriteController->isFavorited($userId, $itemId, $itemType);
$result = ['success' => true, 'is_favorited' => $isFavorited];
break;
default:
$result = ['success' => false, 'message' => '无效的操作'];
}
echo json_encode($result);
前端实现
HTML
<button class="favorite-btn" data-id="123" data-type="article">
<span class="heart-icon">♡</span>
<span class="favorite-text">收藏</span>
</button>
JavaScript (原生)
// 收藏功能
document.querySelectorAll('.favorite-btn').forEach(btn => {
btn.addEventListener('click', async function(e) {
e.preventDefault();
const itemId = this.dataset.id;
const itemType = this.dataset.type;
const userId = document.body.dataset.userId;
if (!userId) {
alert('请先登录');
window.location.href = '/login.php';
return;
}
try {
const formData = new FormData();
formData.append('action', 'toggle');
formData.append('item_id', itemId);
formData.append('item_type', itemType);
const response = await fetch('/api/favorite.php', {
method: 'POST',
body: formData
});
const result = await response.json();
if (result.success) {
// 切换收藏按钮状态
this.classList.toggle('favorited');
const heartIcon = this.querySelector('.heart-icon');
const favoriteText = this.querySelector('.favorite-text');
if (result.action === 'favorited') {
heartIcon.textContent = '♥';
favoriteText.textContent = '已收藏';
} else {
heartIcon.textContent = '♡';
favoriteText.textContent = '收藏';
}
// 可选:显示提示信息
showToast(result.message);
} else {
alert(result.message);
}
} catch (error) {
console.error('Error:', error);
alert('操作失败,请重试');
}
});
});
// Toast提示
function showToast(message) {
const toast = document.createElement('div');
toast.className = 'toast';
toast.textContent = message;
document.body.appendChild(toast);
setTimeout(() => {
toast.remove();
}, 2000);
}
使用 jQuery 的简化版本
// 收藏切换
$('.favorite-btn').click(function() {
const $btn = $(this);
const itemId = $btn.data('id');
const itemType = $btn.data('type');
// 检查登录状态
if (!$('body').data('userId')) {
alert('请先登录');
return;
}
$.ajax({
url: '/api/favorite.php',
method: 'POST',
data: {
action: 'toggle',
item_id: itemId,
item_type: itemType
},
success: function(response) {
if (response.success) {
$btn.toggleClass('favorited');
if (response.action === 'favorited') {
$btn.find('.heart-icon').text('♥');
$btn.find('.favorite-text').text('已收藏');
} else {
$btn.find('.heart-icon').text('♡');
$btn.find('.favorite-text').text('收藏');
}
// 显示通知
showNotification(response.message);
}
},
error: function() {
alert('操作失败,请重试');
}
});
});
CSS样式
.favorite-btn {
display: inline-flex;
align-items: center;
padding: 8px 16px;
border: 1px solid #ddd;
background: white;
cursor: pointer;
transition: all 0.3s ease;
}
.favorite-btn:hover {
border-color: #e74c3c;
color: #e74c3c;
}
.favorite-btn.favorited {
background: #e74c3c;
color: white;
border-color: #e74c3c;
}
.heart-icon {
margin-right: 5px;
font-size: 18px;
}
.toast {
position: fixed;
bottom: 20px;
left: 50%;
transform: translateX(-50%);
background: rgba(0,0,0,0.8);
color: white;
padding: 10px 20px;
border-radius: 4px;
z-index: 1000;
animation: fadeInOut 2s ease;
}
@keyframes fadeInOut {
0% { opacity: 0; }
10% { opacity: 1; }
90% { opacity: 1; }
100% { opacity: 0; }
}
使用示例
<?php
// 文章页面
$isFavorited = $favoriteController->isFavorited($currentUser['id'], $article['id'], 'article');
?>
<button class="favorite-btn <?php echo $isFavorited ? 'favorited' : ''; ?>"
data-id="<?php echo $article['id']; ?>"
data-type="article">
<span class="heart-icon"><?php echo $isFavorited ? '♥' : '♡'; ?></span>
<span class="favorite-text"><?php echo $isFavorited ? '已收藏' : '收藏'; ?></span>
</button>
这个方案实现了:
- ✅ 收藏/取消收藏切换
- ✅ 防止重复收藏(数据库唯一索引)
- ✅ 检查收藏状态
- ✅ 前端即时反馈
- ✅ 用户登录验证
- ✅ 分页获取收藏列表
你可以根据项目需求进行调整和扩展。