PHP项目如何实现收藏点赞功能?

wen PHP项目 10

本文目录导读:

PHP项目如何实现收藏点赞功能?

  1. 数据库设计
  2. 点赞功能实现
  3. 收藏功能实现
  4. 控制器层实现
  5. 前端实现
  6. 性能优化建议
  7. 安全注意事项

我来为你详细介绍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;
    }
}

这个方案完整实现了收藏和点赞功能,包含了数据库设计、后端逻辑、前端交互以及性能优化等核心要素,你可以根据实际需求进行调整和扩展。

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