CodeIgniter4点赞点踩功能:如何限制单用户仅可投一票
问题根因
现有代码存在3处逻辑缺陷,导致点赞、点踩可同时生效:
- 执行点踩逻辑时,仅判断用户是否已点赞,未清除用户对同一张图片已存在的点踩记录;执行点赞逻辑时同理,未清除已存在的点赞记录,会直接生成两条独立的投票记录
- 取消点赞、取消点踩的删除语句缺失
username、viewkey过滤条件,执行时会删除整张action表中对应类型的所有记录,属于严重逻辑漏洞 - 代码中
unset($image->setUserLiked)、unset($image->userDisliked)属于无效写法,unset仅可删除对象属性,无法删除对象方法,不过该问题不影响核心投票逻辑,仅会产生冗余代码和PHP运行警告。
修复代码
核心调整思路:用户对单张图片的点赞/点踩记录最多仅保留1条,执行新的投票操作前先清空同用户同图片下的所有历史赞/踩记录,所有删除、更新操作必须补全用户、图片维度的过滤条件,避免误操作全表数据。
将原switch语句中dislike、undislike、like、unlike四个分支替换为以下代码即可:
case 'dislike': $username = session()->get('username'); $viewkey = $request['viewkey']; // 清除当前用户对该图片的所有历史赞/踩记录 $this->builder() ->whereIn('action', [0, 1]) ->where(['username' => $username, 'viewkey' => $viewkey]) ->delete(); // 写入新的点踩记录 $this->builder()->insert([ 'action' => 0, 'username' => $username, 'viewkey' => $viewkey, 'modified_at' => $request['modified_at'], ]); break; case 'undislike': // 补全过滤条件,仅删除当前用户对当前图片的点踩记录 $this->builder() ->where([ 'action' => 0, 'username' => session()->get('username'), 'viewkey' => $request['viewkey'] ]) ->delete(); break; case 'like': $username = session()->get('username'); $viewkey = $request['viewkey']; // 清除当前用户对该图片的所有历史赞/踩记录 $this->builder() ->whereIn('action', [0, 1]) ->where(['username' => $username, 'viewkey' => $viewkey]) ->delete(); // 写入新的点赞记录 $this->builder()->insert([ 'action' => 1, 'username' => $username, 'viewkey' => $viewkey, 'modified_at' => $request['modified_at'], ]); break; case 'unlike': // 补全过滤条件,仅删除当前用户对当前图片的点赞记录 $this->builder() ->where([ 'action' => 1, 'username' => session()->get('username'), 'viewkey' => $request['viewkey'] ]) ->delete(); break;
额外加固建议
- 给action表添加联合唯一索引
UNIQUE KEY user_vote_unique (username, viewkey, action),从数据库层面杜绝同用户同图片同操作类型的重复记录生成,即使代码逻辑出现疏漏也不会产生脏数据 - 清理代码中所有无效的unset对象方法/属性语句,避免产生不必要的PHP警告
- 浏览、收藏分支的更新、删除操作也需要补全
username和viewkey过滤条件,避免出现误删全表数据的问题
内容的提问来源于stack exchange,提问作者Programming_Squirrel
相关产品推荐
相关产品推荐

