一、简要
第一次做这种设计,当然有许多不足,希望多多指出。
评论盖楼,就是每条评论一个楼层,而楼层里面可以嵌套很多引用的评论,直接上图
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/356 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/323 }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(); //获取所引用的评论model16 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/322 }23 24 //组装数据25 $lists[] = [26 'id_at' => $data['id_at'], //文章Id27 'id_wx_from' => $this->uid, //用户Id,后面直接读取session28 'content' => $data['content'], //评论内容29 'path'=>$path, //楼层路径30 'time_create'=> time(), //加入时间31 'status' => 9, //状态 9 => 未读, 10 => 已读32 're_comment_id' => $data['re_comment_id'] //引用的评论Id33 ];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 }