thinkphp中实现关注功能本质是user表与follow表的多对多关联。需建follow表含user_id、followed_id、created_at字段,加联合唯一索引;user模型用belongstomany正确定义followings和followers关系;关注/取消操作须事务+存在性判断;分页查询应显式指定字段避免n+1。

关注功能本质是两张表的关联操作
ThinkPHP 里实现关注,核心不是调用某个“关注组件”,而是建好 user 表和 follow(或 user_follow)表,再用模型关系把它们串起来。常见错误是直接在 user 表加个 followed_ids 字段存 JSON,这会导致查询慢、无法索引、事务难保证。
-
follow表至少要有user_id(谁关注)、followed_id(关注谁)、created_at - 必须给这两列加联合唯一索引:
UNIQUE KEY `user_followed` (`user_id`,`followed_id`),否则同一对用户可能重复关注 - 别用
hasOne或belongsTo去关联关注关系——这是多对多,要用belongsToMany,哪怕逻辑上“关注”看起来像单向动作
用 belongsToMany 正确声明关注/被关注关系
一个用户既能关注别人,也能被别人关注,所以要在 User 模型里定义两组关系。很多人只写了一边,导致查“我关注了谁”可以,但查“谁关注了我”就绕弯子甚至查不出来。
- 关注别人:用
belongsToMany('User', 'follow', 'user_id', 'followed_id'),建议命名为followings - 被别人关注:用
belongsToMany('User', 'follow', 'followed_id', 'user_id'),建议命名为followers - 注意中间表名
'follow'必须和实际表名一致;字段顺序错一位,关联就全乱——user_id是“当前模型的 ID 所在字段”,不是“对方的 ID” - 如果用的是 ThinkPHP 6.3+,推荐开启
withSum或withCount预载入数量,避免 N+1:比如User::withCount('followings', 'followers')->find($id)
关注/取消关注操作必须用事务 + 条件判断
前端点一下“关注”,后端不能只执行一条 insert。用户可能手抖连点,或网络重发请求,导致重复插入——而数据库靠唯一索引报错 SQLSTATE[23000]: Integrity constraint violation 不是友好体验,也不利于日志追踪。
- 用
Db::transaction()包裹整个操作 - 先
Follow::where(['user_id' => $uid, 'followed_id' => $targetId])->find()查是否存在,再决定 insert 还是 delete - 不要依赖
insertGetId()后判断返回 0 来识别失败——它不捕获唯一键冲突,得 catchPDOException并检查$e->getCode() === '23000' - 操作成功后,记得清除相关缓存,比如
cache('user_'.$uid.'_followings'),否则下次查还是旧数据
分页查关注列表时小心 with 的性能陷阱
查“张三关注了哪些人”,用 User::find($uid)->followings()->paginate(20) 看似简单,但默认会加载所有关联字段,如果 User 模型里定义了大量 with 关联(比如头像、等级、签名),每条关注记录都会触发额外查询。
- 显式指定需要的字段:
followings()->field('id,nickname,avatar')->paginate(20) - 避免在关联定义里写
->with(['profile', 'level']),这种预加载会在每次查关注列表时无差别执行 - 如果要显示“是否已关注”状态(比如在被关注者列表里标出“已关注”),别在循环里反复查
Follow::where(...)->count(),改用一次Follow::where('user_id', $uid)->column('followed_id')拿出 ID 数组,PHP 层 in_array 判断
belongsToMany 四个参数顺序配反了——这三个地方一错,后面所有功能都飘在半空。php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











