現如今,不管是哪種型別的應用,評論區都少不了。從工具類的到媒體資訊流類的,評論留言都是最基本的互動環節。比如抖音短視訊下,針對視訊每個使用者都可以發表自己的觀點;而針對使用者的評論,其他的使用者又可以對其進行評論,依次回覆下去。
那麼,一個視訊的評論回覆的表如何設計?功能如何實現呢?如標題,這裡是用一張表完成,但是在我完成功能後發現拆成兩個張更合適(評論表和回覆表),這個後面已經改了,最後也會說一下。
頁面上展示是,視訊下分頁展示第一級的評論列表,評論下的評論是進行摺疊,點選「檢視全部」分頁顯示所有層級的評論。一張表的設計下,評論下的評論下……的評論是通過關聯上一級主鍵的,也就是遞迴的方式。但是下面的是隻要是對評論進行評論都放在第一級的評論下,而遞迴是樹形結構。雖然也能通過對樹形結構資料處理拉伸到二季下,但是在底下評論特別多的情況就會體驗特別差。所以針對這個我對錶格加了一個麵包屑欄位,表結構如下,然後再分次完成評論資料的如何新增,按頁面方式查詢的。
CREATE TABLE `short_video_comment` ( `id` int(11) NOT NULL AUTO_INCREMENT, `pid` int(11) DEFAULT '0' COMMENT '父級ID', `crumbs` json DEFAULT NULL COMMENT '麵包屑', `video_id` int(11) DEFAULT '0', `user_id` int(11) DEFAULT '0', `commented_user_id` int(11) DEFAULT '0' COMMENT '被評論者使用者ID', `is_pubisher` tinyint(1) DEFAULT '0' COMMENT '是否作者', `content` varchar(255) DEFAULT '' COMMENT '評論', `state` int(1) DEFAULT '1' COMMENT '1. 顯示 0. 隱藏', `like_count` int(11) DEFAULT '0' COMMENT '點贊數', `create_time` int(11) DEFAULT '0' COMMENT '建立時間', `delete_time` int(11) DEFAULT '0' COMMENT '刪除時間', `update_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新時間', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=282 DEFAULT CHARSET=utf8mb4 COMMENT='使用者評論記錄表';
如上圖的 「crumbs」 欄位是一個陣列方式的json,也就是新增時會記錄當前評論的關係鏈,從最頂層ID到最近的上一級。而實現分頁顯示第二級下的所有評論,只需要把 「crumbs」 索引為1的pid作為查詢條件就能找到該id下的所有評論,如下是查詢二級評論ID為285的所有評論列表。
而對於關於crumbs如何存入和誰回覆誰,就只需要在評論新增的時候,對上一級評論ID進行遞迴查詢所有上級ID放入 "crumbs", 被回覆者就更好辦了,查詢上一級評論的評論者ID(user_id),放入當前評論記錄的 "commented_user_id",展示的時候只需要關聯一下使用者資訊表就可以了,接下來就是業務程式碼演示了。
▲評論新增
public static function commentAdd($videoInfo, $userId, $content, $commentInfo = []) { $commentId = 0; $videoId = $videoInfo['id'] ?? 0; $publisherId = $videoInfo['user_id'] ?? 0; try { $crumbs = [0]; if ($commentInfo) { $pid = $commentInfo['id'] ?? 0; $insert['commented_user_id'] = $commentInfo['user_id'] ?? 0; $insert['pid'] = $pid; $crumbs = ShortVideoComment::getCrumbs($pid); } $insert['video_id'] = $videoId; $insert['user_id'] = $userId; $insert['is_pubisher'] = $userId == $publisherId ? 1 : 0; $insert['content'] = $content; $insert['state'] = ShortVideoComment::STATE['SHOW']; $insert['create_time'] = time(); $insert['crumbs'] = json_encode($crumbs); $commentId = ShortVideoComment::insertGetId($insert); if ($commentId && $videoId) { $map[] = ['id', '=', $videoId]; ShortVideoModel::where($map)->setInc('comment_count'); $userId && self::setRating(6, $userId, $videoId); } } catch (\Exception $e) { $preFileName = str::snake(__FUNCTION__); $path = self::getClassName(); write_log("msg:" . $e->getMessage(), $preFileName . "_error", $path); } return $commentId; }
▲獲取麵包屑
public static function getCrumbs($cateId = 0, &$ids = []) { $idArr = array_merge((array)$cateId, $ids); $info = self::where('id', $cateId)->find(); if ($info['pid'] != 0) { $idArr = self::getCrumbs($info['pid'], $idArr); } else { array_unshift($idArr, 0); } return $idArr; }
▲資料查詢
public static function getList($map, $page = 1, $size = 30, $pid = 0) { $where[] = ['delete_time', '=', 0]; $where[] = ['state', '=', self::STATE['SHOW']]; $map = array_merge($where, $map); $field = ['id,crumbs,pid,video_id,user_id,commented_user_id,content']; $queryObj = self::field($field) ->with([ 'user' => function ($query) { $query->withField('id, nickname, avatar, mobile'); }, 'commented_user' => function ($query) { $query->withField('id, nickname, avatar, mobile'); } ]) ->where($map); $pid && $queryObj->whereRaw("JSON_EXTRACT(`crumbs` ,'$[1]') = $pid"); $list = $queryObj->page($page, $size) ->order('like_count desc') ->select(); return $list; }
以上是初版時候根據業務設計的表格,後來的評論區完全仿照某音,所以也就對錶格進行了拆分。分成了評論表和回覆表,只要是對評論進行評論就是回覆,這樣在後面資料龐大的時候,效能會更好一點。但是如何是前期設計的就是一張表,而迭代的時候也要有某音評論的效果,可以作為解決方法嘗試滴。