php如何实现点赞取消功能

发布时间:2021-07-19 10:09:12 作者:chen
来源:亿速云 阅读:181
# PHP如何实现点赞取消功能

## 目录
1. [功能需求分析](#功能需求分析)
2. [数据库设计](#数据库设计)
3. [前端交互实现](#前端交互实现)
4. [后端逻辑处理](#后端逻辑处理)
   - [4.1 点赞功能实现](#41-点赞功能实现)
   - [4.2 取消点赞实现](#42-取消点赞实现)
5. [性能优化方案](#性能优化方案)
6. [安全防护措施](#安全防护措施)
7. [完整代码示例](#完整代码示例)
8. [扩展功能建议](#扩展功能建议)

## 功能需求分析

点赞/取消点赞是社交类网站的基础功能,需要满足以下核心需求:

1. 用户可对内容(文章/视频/评论)进行点赞
2. 已点赞用户可取消操作
3. 实时显示点赞数量变化
4. 防止重复点赞和恶意刷赞
5. 区分已点赞/未点赞状态(UI反馈)

技术实现要点:
- 前端通过AJAX实现无刷新交互
- 后端使用PHP+MySQL处理数据
- 需要用户认证系统支持

## 数据库设计

### 方案一:计数器+关系表
```sql
-- 内容主表(示例)
CREATE TABLE `posts` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(255) NOT NULL,
  `content` text NOT NULL,
  `like_count` int(11) DEFAULT 0,
  PRIMARY KEY (`id`)
);

-- 点赞关系表
CREATE TABLE `user_likes` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `user_id` int(11) NOT NULL,
  `post_id` int(11) NOT NULL,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  UNIQUE KEY `user_post` (`user_id`,`post_id`)
);

方案二:纯关系表统计

-- 不维护计数器,每次实时统计
CREATE TABLE `user_likes` (
  `user_id` int(11) NOT NULL,
  `post_id` int(11) NOT NULL,
  `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`user_id`,`post_id`)
);

两种方案对比:

方案 写入性能 读取性能 数据一致性
计数器+关系表 较高(需事务) 极高 需要维护
纯关系表 极高 较低(需COUNT) 自动保证

前端交互实现

HTML结构示例

<div class="post" data-post-id="123">
  <h2>文章标题</h2>
  <div class="like-area">
    <button class="like-btn <?= $hasLiked ? 'active' : '' ?>">
      <i class="icon-thumbs-up"></i>
      <span class="like-count"><?= $likeCount ?></span>
    </button>
  </div>
</div>

JavaScript处理

document.querySelectorAll('.like-btn').forEach(btn => {
  btn.addEventListener('click', async function() {
    const postId = this.closest('.post').dataset.postId;
    const isActive = this.classList.contains('active');
    
    try {
      const response = await fetch('/like.php', {
        method: 'POST',
        headers: {
          'Content-Type': 'application/json'
        },
        body: JSON.stringify({
          post_id: postId,
          action: isActive ? 'unlike' : 'like'
        })
      });
      
      const result = await response.json();
      
      if (result.success) {
        this.classList.toggle('active');
        this.querySelector('.like-count').textContent = result.like_count;
      }
    } catch (error) {
      console.error('操作失败:', error);
    }
  });
});

后端逻辑处理

4.1 点赞功能实现

// like.php
require 'db_connect.php';
session_start();

if (!isset($_SESSION['user_id'])) {
  http_response_code(401);
  die(json_encode(['error' => '请先登录']));
}

$input = json_decode(file_get_contents('php://input'), true);
$postId = intval($input['post_id'] ?? 0);
$action = $input['action'] === 'unlike' ? 'unlike' : 'like';

// 开启事务
$pdo->beginTransaction();

try {
  if ($action === 'like') {
    // 检查是否已点赞
    $stmt = $pdo->prepare("SELECT 1 FROM user_likes WHERE user_id = ? AND post_id = ?");
    $stmt->execute([$_SESSION['user_id'], $postId]);
    
    if ($stmt->fetch()) {
      throw new Exception('您已经点过赞了');
    }
    
    // 插入点赞记录
    $pdo->prepare("INSERT INTO user_likes (user_id, post_id) VALUES (?, ?)")
      ->execute([$_SESSION['user_id'], $postId]);
    
    // 更新计数器
    $pdo->prepare("UPDATE posts SET like_count = like_count + 1 WHERE id = ?")
      ->execute([$postId]);
  } else {
    // 取消点赞逻辑...
  }
  
  // 获取最新点赞数
  $likeCount = $pdo->query("SELECT like_count FROM posts WHERE id = $postId")
    ->fetchColumn();
  
  $pdo->commit();
  
  echo json_encode([
    'success' => true,
    'like_count' => $likeCount
  ]);
} catch (Exception $e) {
  $pdo->rollBack();
  http_response_code(400);
  echo json_encode(['error' => $e->getMessage()]);
}

4.2 取消点赞实现

// 接续上面的try块中的else分支
} else {
  // 检查是否有点赞记录
  $stmt = $pdo->prepare("SELECT 1 FROM user_likes WHERE user_id = ? AND post_id = ?");
  $stmt->execute([$_SESSION['user_id'], $postId]);
  
  if (!$stmt->fetch()) {
    throw new Exception('您尚未点赞');
  }
  
  // 删除点赞记录
  $pdo->prepare("DELETE FROM user_likes WHERE user_id = ? AND post_id = ?")
    ->execute([$_SESSION['user_id'], $postId]);
  
  // 更新计数器
  $pdo->prepare("UPDATE posts SET like_count = GREATEST(0, like_count - 1) WHERE id = ?")
    ->execute([$postId]);
}

性能优化方案

  1. 缓存策略: “`php // 使用Redis缓存热门文章的点赞数 \(redis = new Redis(); \)redis->connect(‘127.0.0.1’, 6379);

\(cacheKey = "post:{\)postId}:likes”;

// 写入时更新缓存 \(redis->set(\)cacheKey, $likeCount, 3600);

// 读取时优先查缓存 \(likeCount = \)redis->get(\(cacheKey); if (\)likeCount === false) { \(likeCount = \)pdo->query(“SELECT like_count…”)->fetchColumn(); \(redis->set(\)cacheKey, $likeCount, 3600); }


2. **批量查询优化**:
   ```sql
   -- 一次查询获取用户是否点赞过多个文章
   SELECT post_id FROM user_likes 
   WHERE user_id = ? AND post_id IN (1, 2, 3, 4);
  1. 延迟更新
    
    // 对于高流量场景,可采用队列异步更新
    $queue->push([
     'type' => 'like',
     'user_id' => $userId,
     'post_id' => $postId,
     'action' => $action
    ]);
    

安全防护措施

  1. CSRF防护

    <input type="hidden" name="csrf_token" value="<?= $_SESSION['csrf_token'] ?>">
    
  2. 频率限制: “`php \(redis->incr("user:{\)_SESSION[‘user_id’]}:like_attempts”); \(redis->expire("user:{\)_SESSION[‘user_id’]}:like_attempts”, 60);

if ($redis->get() > 30) { http_response_code(429); die(‘操作过于频繁’); }


3. **输入验证增强**:
   ```php
   if (!ctype_digit($postId) {
     die('非法参数');
   }
   
   // 检查文章是否存在
   $stmt = $pdo->prepare("SELECT 1 FROM posts WHERE id = ?");
   $stmt->execute([$postId]);
   if (!$stmt->fetch()) {
     die('内容不存在');
   }

完整代码示例

数据库连接 (db_connect.php)

<?php
$host = 'localhost';
$dbname = 'your_database';
$user = 'db_user';
$pass = 'db_password';

try {
  $pdo = new PDO("mysql:host=$host;dbname=$dbname", $user, $pass);
  $pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
  die("数据库连接失败: " . $e->getMessage());
}

前端完整示例 (index.php)

<?php
session_start();
require 'db_connect.php';

// 获取文章列表
$posts = $pdo->query("SELECT id, title, content, like_count FROM posts")->fetchAll();

// 检查用户点赞状态
$userLikes = [];
if (isset($_SESSION['user_id'])) {
  $stmt = $pdo->prepare("SELECT post_id FROM user_likes WHERE user_id = ?");
  $stmt->execute([$_SESSION['user_id']]);
  $userLikes = $stmt->fetchAll(PDO::FETCH_COLUMN);
}
?>
<!DOCTYPE html>
<html>
<!-- 头部内容... -->
<body>
  <?php foreach ($posts as $post): ?>
    <div class="post" data-post-id="<?= $post['id'] ?>">
      <h2><?= htmlspecialchars($post['title']) ?></h2>
      <p><?= nl2br(htmlspecialchars($post['content'])) ?></p>
      <button class="like-btn <?= in_array($post['id'], $userLikes) ? 'active' : '' ?>">
        <i class="icon-thumbs-up"></i>
        <span class="like-count"><?= $post['like_count'] ?></span>
      </button>
    </div>
  <?php endforeach; ?>
  
  <script src="like.js"></script>
</body>
</html>

扩展功能建议

  1. 点赞通知系统

    // 在点赞成功后
    if ($action === 'like') {
     $stmt = $pdo->prepare("INSERT INTO notifications 
       (user_id, type, content_id, sender_id) 
       VALUES (?, 'like', ?, ?)");
     $authorId = $pdo->query("SELECT user_id FROM posts WHERE id = $postId")
       ->fetchColumn();
     $stmt->execute([$authorId, $postId, $_SESSION['user_id']]);
    }
    
  2. 点赞排行榜

    SELECT posts.*, COUNT(user_likes.id) as like_count
    FROM posts
    LEFT JOIN user_likes ON posts.id = user_likes.post_id
    GROUP BY posts.id
    ORDER BY like_count DESC
    LIMIT 10
    
  3. 点赞动画效果

    btn.classList.add('animate');
    setTimeout(() => {
     btn.classList.remove('animate'); 
    }, 1000);
    
  4. 多类型内容支持

    ALTER TABLE user_likes ADD COLUMN content_type ENUM('post','comment','video') NOT NULL;
    

通过以上实现,我们完成了一个健壮的点赞/取消点赞系统。实际项目中可根据具体需求进行调整和扩展。 “`

推荐阅读:
  1. 点赞和取消代码(jquery)
  2. 怎么制作php点赞功能

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

php

上一篇:vue中router-link标签的属性有哪些

下一篇:python中PaddleOCR库的用法

相关阅读

您好,登录后才能下订单哦!

密码登录
登录注册
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》