EP109. “点赞实时更新与删除赞的服务端逻辑”
🔒 登录后可标记已读📌 并入 EP108 的提醒:下一步给 .like-box 加 data-like 属性时,如果当前用户还没点过赞,$existQuery->posts[0]->ID 这个写法会因为数组里没有第 0 项而报 PHP 警告(跟很早以前 pageBanner() 函数遇到的「访问不存在的数组项」是同一类问题)。这一讲先照原样写,PHP 警告会先出现,官方会在后面一讲提醒用 isset() 包一层解决,这篇笔记先如实记录当前阶段的代码状态。
这一讲把点赞功能的最后两块拼图补完:① 点赞/取消点赞成功后,心形图标和数字实时更新(不用手动刷新页面);② 实现 deleteLike() 的真正业务逻辑——同样要做权限校验:只有「点赞记录的创建者本人」才能删除这条记录,且要确认目标 ID 真的是一条 like 记录。
涉及文件
wp-content/themes/fictional-university-theme/src/modules/Like.js(修改)wp-content/themes/fictional-university-theme/single-professor.php(修改)wp-content/themes/fictional-university-theme/inc/like-route.php(修改)
代码实现
single-professor.php:给 .like-box 新增 data-like 属性,记录当前用户对这位教授的点赞记录 ID(这里的写法在还没点赞时会触发 PHP 警告,属于本讲遗留、下一讲才修的已知问题):
<span class="like-box" data-like="<?php echo $existQuery->posts[0]->ID; ?>" data-professor="<?php the_ID(); ?>" data-exists="<?php echo $existStatus; ?>">
src/modules/Like.js:ourClickDispatcher 改用 .attr() 而不是 .data() 读取实时属性值(jQuery 的 .data() 只在页面加载时缓存一次,之后属性值再怎么变都不会反映到 .data() 的返回结果里,必须用 .attr() 才能拿到最新值):
ourClickDispatcher(e) {
var currentLikeBox = $(e.target).closest(".like-box")
if (currentLikeBox.attr("data-exists") == "yes") { // 修改:.data() → .attr()
this.deleteLike(currentLikeBox)
} else {
this.createLike(currentLikeBox)
}
}
createLike() 成功后实时更新界面:
createLike(currentLikeBox) {
$.ajax({
beforeSend: xhr => {
xhr.setRequestHeader("X-WP-Nonce", universityData.nonce)
},
url: universityData.root_url + "/wp-json/university/v1/manageLike",
type: "POST",
data: { "professorId": currentLikeBox.data("professor") },
success: response => {
currentLikeBox.attr("data-exists", "yes")
var likeCount = parseInt(currentLikeBox.find(".like-count").html(), 10)
likeCount++
currentLikeBox.find(".like-count").html(likeCount)
currentLikeBox.attr("data-like", response) // response 是刚创建的 like 文章 ID
console.log(response)
},
error: response => {
console.log(response)
}
})
}
deleteLike():请求带上要删除的 like 记录 ID + nonce,成功后做相反方向的界面更新:
deleteLike(currentLikeBox) {
$.ajax({
beforeSend: xhr => {
xhr.setRequestHeader("X-WP-Nonce", universityData.nonce)
},
url: universityData.root_url + "/wp-json/university/v1/manageLike",
data: { "like": currentLikeBox.attr("data-like") },
type: "DELETE",
success: response => {
currentLikeBox.attr("data-exists", "no")
var likeCount = parseInt(currentLikeBox.find(".like-count").html(), 10)
likeCount--
currentLikeBox.find(".like-count").html(likeCount)
currentLikeBox.attr("data-like", "")
console.log(response)
},
error: response => {
console.log(response)
}
})
}
inc/like-route.php:deleteLike() 实现真正的删除逻辑,含权限校验:
// wp-content/themes/fictional-university-theme/inc/like-route.php
function deleteLike($data) {
$likeId = sanitize_text_field($data['like']);
if (get_current_user_id() == get_post_field('post_author', $likeId) AND get_post_type($likeId) == 'like') {
wp_delete_post($likeId, true);
return 'Congrats, like deleted.';
} else {
die("You do not have permission to delete that.");
}
}
关键改动点:
parseInt(字符串, 10):从页面读出的赞数原本是字符串(HTML 文本),要先转成数字才能做加减运算,第二个参数10指定十进制createLike()成功后,用 REST 端点返回的新文章 ID(也就是wp_insert_post()的返回值)更新data-like属性——这样即使用户不刷新页面、连续点赞/取消,JS 也始终知道「当前这条点赞记录的 ID 是多少」,可以在下次点击时正确删除deleteLike($likeId, true)的第二个参数true表示跳过回收站、直接彻底删除——因为点赞记录本身没有「恢复」的意义,不需要走回收站流程deleteLike()的权限校验用了两个条件:get_current_user_id() == get_post_field('post_author', $likeId)(只有点赞记录的创建者才能删自己的记录,防止恶意用户删除别人的点赞)+get_post_type($likeId) == 'like'(确认这个 ID 对应的确实是一条 like 记录,不是被人拿来传了博客文章或其他文章的 ID 妄图删除).data()vs.attr()的关键差异:jQuery 的.data()方法只在第一次访问时读取一次data-*属性并缓存,之后即使用 JS 改了属性的实际值,.data()还是会返回最初缓存的旧值;.attr()每次调用都会读取元素当前的真实属性值——这个功能里赞的状态会不断被 JS 动态改写,所以必须全部换成.attr()才能让「连续点击切换点赞/取消赞」正常工作
[截图:前台点击心形图标后,图标从空心变实心、赞数实时 +1 的效果(无需刷新页面)]
Hook / Function 速查
| 名称 | 类型 | 用途 |
|---|---|---|
parseInt(str, radix) | JS 内建函数 | 把字符串解析成整数,radix 指定进制(人类习惯用 10) |
get_post_field($field, $post_id) | WP 内建 function | 获取指定文章的某个原生字段值(如 post_author),不需要先建 WP_Query |
wp_delete_post($post_id, $force_delete) | WP 内建 function | 删除文章,$force_delete 传 true 时跳过回收站直接永久删除 |
.attr(name) | jQuery 方法 | 读取元素当前的真实 HTML 属性值(每次都重新读取,不缓存) |
.data(name) | jQuery 方法 | 读取 data-* 属性,但只在首次访问时读取并缓存,之后不会感知属性值的变化 |
常见坑
- 用
.data()读取会被 JS 动态修改的属性——.data()的缓存机制会导致读到的永远是页面加载时的旧值,凡是「状态会被 JS 自己改写、且需要马上读到最新值」的场景都应该用.attr() deleteLike()只校验「是不是 like 类型」不校验「是不是自己创建的」——会让任何登录用户都能删除别人的点赞记录,两个条件必须同时满足- 数字加减前忘记
parseInt()——JavaScript 里字符串跟数字做++/--运算不会报错,但结果往往不是预期的数字运算(容易出现字符串拼接式的诡异结果),一定要先转换类型
延伸 / 后续讲座会用到
下一讲会先给一份「jQuery-free 版 Like.js」补充选修内容(同时会正式落实 EP108/EP110 提到的 isset() 修复);再往后课程会转向新的主题:网站正式上线部署相关的准备工作。
Sources
Udemy:
- Become a WordPress Developer: Unlocking Power With Code — Section 20, EP109(含 EP108 提醒并入)