PHP 仿网易云的评论盖楼
一、简要
第一次做这种设计,当然有许多不足,希望多多指出。
评论盖楼,就是每条评论一个楼层,而楼层里面可以嵌套很多引用的评论,直接上图
A:牛什么牛(见图 Top4)
B回复A:好牛啊。(所以这里就嵌套了A的内容,见图Top3)
C回复B:多谢。(这里同样嵌套B的内容以及B引用的内容,见图Top2)
二、数据库设计
2.1、评论表设计的sql语句
1 CREATE TABLE `comment` ( 2 `id_comment` int(11) NOT NULL AUTO_INCREMENT COMMENT '评论ID', 3 `id_at` int(11) NOT NULL DEFAULT '0' COMMENT '文章Id', 4 `id_wx_from` int(11) NOT NULL DEFAULT '0' COMMENT '发布者的ID', 5 `content` longtext COMMENT '评论内容', 6 `time_create` int(11) NOT NULL DEFAULT '0' COMMENT '加入时间', 7 `path` varchar(255) DEFAULT NULL COMMENT '回复的ID路径:1/2/3/5', 8 `num_praise` int(11) NOT NULL DEFAULT '0' COMMENT '点赞次数', 9 `status` tinyint(4) NOT NULL DEFAULT '0' COMMENT '状态', 10 `re_comment_id` int(11) NOT NULL DEFAULT '0' COMMENT '引用的回复Id', 11 `like_ids` longtext COMMENT '点赞用户Ids', 12 PRIMARY KEY (`id_comment`) 13 ) ENGINE=InnoDB AUTO_INCREMENT=31 DEFAULT CHARSET=utf8;
2.2、评论表的视图
1 CREATE VIEW `view_all_comment` AS 2 select `c`.`id_comment` AS `commentid`,`c`.`id_wx_from` AS `fromid`, 3 `u`.`username` AS `fromname`,`u`.`cover` AS `fromcover`, 4 `c`.`content` AS `content`,`u2`.`id_wx` AS `originid`, 5 `u2`.`username` AS `originname`,`u2`.`cover` AS `origincover`, 6 `c2`.`content` AS `origincontent`, 7 `c`.`time_create` AS `time`, 8 `c`.`status` AS `status`,`c`.`path` AS `path`,`c`.`num_praise` AS `num_praise`,`c`.`like_ids` AS `like_ids`,`p`.`id_at` AS `id_at`,`p`.`title` AS `title` 9 from ((((`comment` `c` 10 left join `wx_user` `u` on((`c`.`id_wx_from` = `u`.`id_wx`))) 11 left join `article` `p` on((`c`.`id_at` = `p`.`id_at`))) 12 left join `comment` `c2` on((`c`.`re_comment_id` = `c2`.`id_comment`))) left join `wx_user` `u2` on((`c2`.`id_wx_from` = `u2`.`id_wx`))) 13 order by `c`.`time_create` ;
视图需要注意的是:分别连接一次评论表和两次用户表,是为了获取评论和被评论的内容、发布者和接受者的用户信息。此处为了方便程序读取信息,数据库设计知识尚浅,不知是否妥当。
三、评论读取接口(PHP+Yii2)
3.1、前端需要的接口格式
1 [{ 2 name: '阿萨德', 3 headPic:require('../../assets/action.jpg'), 4 like: 7633, 5 content: '感谢撒发生的分里卡享哈哈阿萨德里卡多巴拉巴享哈哈阿萨德里卡多巴拉巴享哈哈阿萨德里卡多巴拉巴多巴拉巴拉', 6 time: '2017-06-20', 7 oldComment: [] 8 }, { 9 name: '蒂法', 10 headPic:require('../../assets/action.jpg'), 11 like: 134, 12 content: '感谢楼主分享大事发生', 13 time: '2017-06-20', 14 oldComment: [{ 15 name: 'CHOWCHIHANG', 16 comment: '大夫敢死队风格对方是个豆腐干地方供电所覆盖豆腐干受到广泛的斯蒂芬豆腐干斯蒂芬。' 17 }, { 18 name: 'CHOWCHIHANG', 19 comment: '大夫敢死队风格对方是个豆腐干地方供电所覆盖豆腐干受到广泛的斯蒂芬豆腐干斯蒂芬。' 20 }, { 21 name: 'CHOWCHIHANG', 22 comment: '大夫敢死队风格对方是个豆腐干地方供电所覆盖豆腐干受到广泛的斯蒂芬豆腐干斯蒂芬。' 23 }, { 24 name: 'CHOWCHIHANG', 25 comment: '大夫敢死队风格对方是个豆腐干地方供电所覆盖豆腐干受到广泛的斯蒂芬豆腐干斯蒂芬。' 26 }, { 27 name: 'CHOWCHIHANG', 28 comment: '大夫敢死队风格对方是个豆腐干地方供电所覆盖豆腐干受到广泛的斯蒂芬豆腐干斯蒂芬。' 29 }, { 30 name: 'CHOWCHIHANG', 31 comment: '大夫敢死队风格对方是个豆腐干地方供电所覆盖豆腐干受到广泛的斯蒂芬豆腐干斯蒂芬。' 32 }] 33 }]
3.2、获取全部评论的接口以及数据封装
1 /** 2 * 【根据文章Id获取更多评论(分页获取)】 3 * 20170724 4 * 5 * @param int $id_at 文章Id 6 * @param int $pageIndex 页码 7 * @param int $pageSize 每页条数 8 * @return array 返回的数组 9 * 10 */ 11 public function getMoreCommentsByAtId($id_at, $uid, $pageIndex, $pageSize) 12 { 13 //0、判断数据数据是否有误 14 if(!is_numeric($id_at) || !is_numeric($pageSize) || !is_numeric($pageIndex) || !is_numeric($uid)){ 15 return $this->reErrorMsg(401); 16 } 17 18 //1、读取所有原始数据(分页获取) 19 $rst = (new Query) 20 ->select('commentid as id, fromname as name, fromcover as headPic, num_praise as like, content, time, path, like_ids') 21 ->from('view_all_comment') 22 ->where(['id_at'=>$id_at]) 23 ->andWhere('status IN ('.implode(',', [9,10]).')') 24 ->orderBy('time desc') 25 ->offset($pageSize*($pageIndex -1)) 26 ->limit($pageSize) 27 ->asArray() 28 ->all(); 29 30 //2、处理时间和数据整理 31 foreach ($rst as &$row){ 32 //[ 20170818 验证是否点赞 33 $ids = []; 34 if(!empty($row['like_ids'])) { 35 $ids = explode(',', $row['like_ids']); 36 } 37 38 if(in_array($uid, $ids)){ 39 $row['liked'] = true; 40 }else{ 41 $row['liked'] = false; 42 } 43 unset($row['like_ids']); 44 // 20170818 ] 45 46 47 48 $row['time'] = date('Y-m-d',$row['time']); 49 if(empty($row['path'])){ 50 $row['oldComment'] = [];//回复作者 51 unset($row['path']);//去掉此列 52 continue; 53 } 54 //2.1 回复其他读者,楼层 55 $ids = explode('/',$row['path']);//path => 1/2/3 56 57 //按照IN顺序排序 58 $sql = "SELECT `commentid` AS `id`, `fromname` AS `name`, `content` AS `comment` FROM `view_all_comment` WHERE (`id_at`={$id_at}) AND (`status` IN (".implode(',', [9,10]).")) AND (commentid IN(".implode(',',$ids).")) ORDER BY field(`commentid`, ".implode(',', $ids).")"; 59 $oldComment = $this->findBySql($sql)->asArray()->all(); 60 61 62 63 $row['oldComment'] = $oldComment; 64 unset($row['path']); 65 } 66 67 return $rst; 68 }
四、评论写入接口
4.1 Yii的方式
1 /** 2 * 【发布评论】 3 * 20170725 4 * 20170818 5 * 6 * @return array 7 */ 8 public function actionPostComment(){ 9 $model = new TblComment(); 10 if($model->load(Yii::$app->request->post()) && $model->validate()){ 11 12 13 if( isset($model->re_comment_id) && $model->re_comment_id != 0){ 14 $remodel = TblComment::findOne($model->re_comment_id); 15 if(!$remodel){ 16 return $this->retMsg('401');//保存失败 17 } 18 19 if(empty($remodel->path)){ 20 $model->path = $model->re_comment_id; 21 }else { 22 $model->path = $remodel->path . '/' . $model->re_comment_id;// 1/2/3 23 } 24 } 25 26 27 28 if(!$model->save()){ 29 return $this->retMsg('400');//保存失败 30 } 31 return $this->retMsg('200');//操作成 32 }else{ 33 return $this->retMsg('401');//数据格式错误 34 } 35 36 37 }
说明:发表评论需求的数据:re_comment_id 引用的评论Id,content:评论内容,文章Id,以及用户Id
path表示为引用的楼层,判断是否为空
4.2 TP5的方式
1 /** 2 * 【发布评论】 3 * 20170725 4 * 20170818 5 * 6 * @return array 7 */ 8 public function Post_Comment(){ 9 try{ 10 $data = input();//获取POST实体数据 11 12 13 $model =$this->commentModel;//对应评论表 14 $path = null; //评论引用的楼层 15 $remodel = $this->commentModel->where(['id_comment'=> $data['re_comment_id']])->find(); //获取所引用的评论model 16 17 //处理path,空则直接赋值,否则追加 '/re_comment_id' 18 if(empty($remodel->path)){ 19 $path = $data['re_comment_id']; 20 }else { 21 $path = $remodel->path . '/' . $data['re_comment_id'];// 1/2/3 22 } 23 24 //组装数据 25 $lists[] = [ 26 'id_at' => $data['id_at'], //文章Id 27 'id_wx_from' => $this->uid, //用户Id,后面直接读取session 28 'content' => $data['content'], //评论内容 29 'path'=>$path, //楼层路径 30 'time_create'=> time(), //加入时间 31 'status' => 9, //状态 9 => 未读, 10 => 已读 32 're_comment_id' => $data['re_comment_id'] //引用的评论Id 33 ]; 34 35 if($model->saveAll($lists)){ 36 return $this->retMsg('200');//操作成 37 }else{ 38 return $this->retMsg('400');//保存失败 39 } 40 }catch(Exection $e){ 41 return $this->retMsg('401');//数据格式错误 42 } 43 44 }
生命不息,学习不止