一 序
现在网上有很多第三方评论插件,例如畅言,多说之类的,虽然用起来很简单,但是每次加载页面都需要加载他们的JS,如果他们的服务器不稳定,就会加载很慢,影响网站的加载速度,而且数据文件放在第三方那里也不安全,自己对用户的控制力也会很低,因此自己动手制作一套评论系统比使用第三方插件会舒服很多,别的话我就不提了,下面开始文章正文:
二 评论模板
评论模板是博主自己写的一套模板,代码放在了我的GitHub上面,效果就是本博客评论页内容,喜欢的话可以直接去下载,顺便给个star,蟹蟹~
GitHub传送门 : 留言模板
三 MySQL数据表设计
//cid 主键ID , pid 父级ID , article_aid 评论文章ID , date 评论时间戳 , website 评论者博客地址 , nickname 评论者昵称
//content 评论内容 , portrait 评论者头像ID , email 评论者邮箱 , add 评论者物理地址 , ip 评论者IP , isp 评论者运营商
//area 评论者地域 , ding 评论内容获赞数量 , cai 评论内容被踩数量 , dip 赞IP集合 , cip 踩IP集合
四 展示出评论列表的PHP方法
属性与构造方法
1 2 3 4 5 6 7 8 9 | class Message{ public $db; public $ip; public function __construct(){ $ this ->db = new Comment(); $ this ->ip = $ this ->getIP(); } } |
getIP方法 获取访问者真实客户端IP
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 | public function getIP(){ static $realip; if (isset($_SERVER)){ if (isset($_SERVER[ "HTTP_X_FORWARDED_FOR" ])){ $realip = $_SERVER[ "HTTP_X_FORWARDED_FOR" ]; } else if (isset($_SERVER[ "HTTP_CLIENT_IP" ])){ $realip = $_SERVER[ "HTTP_CLIENT_IP" ]; } else { $realip = $_SERVER[ "REMOTE_ADDR" ]; } } else { if (getenv( "HTTP_X_FORWARDED_FOR" )){ $realip = getenv( "HTTP_X_FORWARDED_FOR" ); } else if (getenv( "HTTP_CLIENT_IP" )){ $realip = getenv( "HTTP_CLIENT_IP" ); } else { $realip = getenv( "REMOTE_ADDR" ); } } return $realip; } |
1 2 3 4 5 6 | public function index(){ $listData = $ this ->showLists(); view:: with ( 'htmlStr' ,$listData[ 'htmlStr' ])-> with ( 'count' ,$listData[ 'count' ]) -> with ( 'pageStr' ,$listData[ 'pageStr' ])-> with ( 'clientIP' ,$ this ->ip); return view(); } |
先不管showLists方法,来看看一条评论的样式 :
需要达到以上样式 , PHP需要的数组数据的形式就必须是这样 :
那么知道了需要得到以上形式的数组 , 就有思路处理从MySQL查询出的数组数据了 , 在双重foreach中重组即可 :
resetArr方法 查询出评论表中所有评论数据并重组
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public function resetArr($aid,$page=0){ //查询出comment表中当前文章的所有评论 $arr = Db::query( "SELECT * FROM comment WHERE article_aid=$aid" ); if ($arr){ //重组数组 foreach($arr as $k=>$v){ if ($v[ 'pid' ] > 0 ){ foreach($arr as $c=>$d){ if ($v[ 'pid' ] == $d[ 'cid' ]){ $arr[$k][ 'father' ][] = $d; } } } } //倒序排列 因为是最新的评论在最上面显示 krsort($arr); //截取5条 分页显示 $arr = array_slice($arr,$page*5,5); } else { return false ; } return $arr; } |
有了重组好的数据就可以在showLists中调用这个方法得到数据 , 再调用另一个方法进行拼接HTML代码了:
showLists方法相当于一个总开关 , 调用重组数据的方法后再调用拼接HTML代码的方法 , 并且统计出评论条数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | public function showLists(){ //首先判断地址栏是否有$aid 如果没有文章ID 那么就可以知道是评论页 if ($_GET[ 'aid' ]){ $aid = $_GET[ 'aid' ]; } else { $aid = 0; } //调用resetArr方法 得到已经处理好的评论列表数据 $arr = $ this ->resetArr($aid); //默认分页HTML字符串为空 $pageStr = '' ; if ($arr){ //调用resetHTML方法 得到已经处理好的评论列表HTML字符串 $htmlStr = $ this ->resetHtml($arr,$aid); //根据cid查询出该文章的评论条数 $count = Db::query( "SELECT count(cid) as num FROM comment WHERE article_aid=$aid" ); $count = $count[0][ 'num' ]; //判断评论条数是否大于5 如果多与5条即可拼接字符串 if ($count > 5){ $pageStr = '<nav><ul class="pagination" aid="' . $aid . '">' ; $pageStr .= '<li class="disabled"><span>上一页</span></li><li class="active"><span>1</span></li>' ; for ($i=1;$i<ceil($count/5);$i++){ $pageStr .= '<li class="pageNum"><span>' . ($i+1) . '</span></li>' ; }; $pageStr .= '<li class="nextPage"><span>下一页</span></li></ul>' ; } } else { $htmlStr = '' ; $count = 0; } return [ 'htmlStr' =>$htmlStr, 'count' =>$count, 'pageStr' =>$pageStr]; } |
restHtml方法 , 作用就是将上面展示的数组配合html代码 拼接成一个完整的评论列表
具体的思路该怎么做 首先先来看一下最终需要的HTML代码结构 :
由上图可以清晰地看出HTML代码结构 , .LCSub就是每条评论 , .FRMain就是该评论的楼上每层的评论
所以restHtml方法首先需要使用foreach循环出每条评论 , 然后再在foreach中调用另一个递归方法 , 拼接嵌套的.FRMain :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 | public function resetHtml($arr,$aid){ //因为最后需要在循环中拼接,所以首先声明htmlStr为一个空字符串, $htmlStr = '' ; foreach($arr as $k=>$v){ $htmlStr .= '<div class="LCSub"><div class="CPortrait"><a href="' . $v[ 'website' ] . '" class="CPLink">' ; $htmlStr .= '<img src="./resource/message/images/' . $v[ 'portrait' ] . '.png" pid="' . $v[ 'portrait' ] . '" class="PortImg"></a></div>' ; $htmlStr .= '<div class="ContMsg"><div class="UserInfo"><span class="MsgTime">' . date( 'Y-m-d H:i:s' ,$v[ 'date' ]) . '</span>' ; $htmlStr .= '<span class="Nickname"><a href="' . $v[ 'website' ] . '" class="NnLink">' . $v[ 'nickname' ] . '</a>' ; $htmlStr .= '</span><span class="UserAdd">[' . $v[ 'add' ] . '网友]</span></div>' ; //在这里判断$v中是否有父级评论,意为判断这条评论是不是回复楼上的评论 if ($v[ 'father' ]){ //如果是回复楼上 那么就需要拼接出嵌套的楼上的评论 则调用connectHtml方法,这是一个递归方法 $htmlStr .= '<div class="fatherReply">' . $ this ->connectHtml($v[ 'father' ], true ,$aid) . '</div>' ; } $htmlStr .= '<div class="CommentInfo">' . $v[ 'content' ] . '</div><div class="CommentBtn" pid="' . $v[ 'cid' ] . '">' ; //判断此IP是否有过点赞行为 $caiClass = $ this ->getCaiClass($v[ 'cip' ]); $dingClass = $ this ->getDingClass($v[ 'dip' ]); $htmlStr .= $caiClass . '<span class="CaiCount">' . $v[ 'cai' ] . '</span><i class="iconCai"></i></div>' ; $htmlStr .= $dingClass . '<span class="dingCount">' . $v[ 'ding' ] . '</span><i class="iconDing"></i></div>' ; $htmlStr .= '<span class="CBReply" sid="1" aid="' . $aid . '">回复</span></div></div></div>' ; } return $htmlStr; } |
//可以看到我在循环中调用了connectHtml方法 , 这是一个递归方法 , 需要传入3个参数 , 参数1是$v['father'] , 参数2是布尔值
//因为这是一个递归函数 , 它不仅在foreach中被调用 , 还需要自调用 , 所以参数2用一个布尔值判断是外部的foreach调用还是自调用 , 这里选择true为外部调用
connectHtml方法 , 作用是拼接嵌套的HTML字符串
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 | public function connectHtml($arr,$staticState,$aid){ //声明静态变量$htmlStr 用于保存拼接的html字符串 避免递归调用时被重置 static $htmlStr = '' ; //声明静态变量$floor 用于保存楼层号 避免递归调用时被重置 static $floor = 0; //判断形参staticState,如果是外部foreach调用则重置已保存的$htmlStr与$floor的值 if ($staticState){ $htmlStr = '' ; $floor = 0; } //在foreach循环中拼接html字符串 foreach($arr as $k=>$v){ $htmlStr .= '<div class="FRMain">' ; //判断$v中是否存在父级评论 如果存在则自调用 形成嵌套形式 if ($v[ 'father' ]){ //传入3个参数,第二个参数为false 表示自调用 避免2个静态变量的值被重置 $ this ->connectHtml($v[ 'father' ], false ,$aid); } //第一层结束后 楼层号自增 1 $floor++; $htmlStr .= '<div class="RMFloor"><div class="UserInfo"><span class="MsgTime floorNum"> ' . $floor . '</span>' ; $htmlStr .= '<span class="Nickname"><a href="" class="NnLink">' . $v[ 'nickname' ] . '</a></span><span class="UserAdd">[' . $v[ 'add' ] . '网友]</span>' ; $htmlStr .= '</div><div class="CommentInfo">' . $v[ 'content' ] . '</div><div class="CommentBtn CommentsBtn" pid="' . $v[ 'cid' ] . '">' ; //判断该条评论是否被踩 $caiClass = $ this ->getCaiClass($v[ 'cip' ]); //判断该条评论是否被赞 $dingClass = $ this ->getDingClass($v[ 'dip' ]); $htmlStr .= $caiClass . '<span class="CaiCount">' . $v[ 'cai' ] . '</span><i class="iconCai"></i></div>' ; $htmlStr .= $dingClass . '<span class="dingCount">' . $v[ 'ding' ] . '</span><i class="iconDing"></i></div>' ; $htmlStr .= '<span class="CBReply" sid="1" aid="' . $aid . '">回复</span></div></div></div>' ; } return $htmlStr; } |
不过还有一个小细节 , 就是需要判断每条评论是否被赞、踩 , 做法很简单 , 就是通过2个方法处理 , 然后给出div的class样式就可以显示出是否被赞、踩
getCaiClass , getDingClass 这2个方法分别需要传入cip与dip也就是数据库保存的ip集合
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public function getCaiClass($cip){ if ($cip && in_array($ this ->ip,explode( '|' ,$cip))){ $caiClass = '<div class="Cai_active">' ; } else { $caiClass = '<div class="CBCai">' ; } return $caiClass; } public function getDingClass($dip){ if ($dip && in_array($ this ->ip,explode( '|' ,$dip))){ $dingClass = '<div class="Ding_active">' ; } else { $dingClass = '<div class="CBDing">' ; } return $dingClass; } |
经过以上这么多个方法 一个完整的评论列表就可以被展示出来了 最后只需要在html页面相应的位置echo出$count , $htmlStr , $pageStr就大工告成啦 !
五 AJAX异步添加评论
评论列表可以展示出来后 下面的工作就是需要通过JQ , AJAX来添加评论了
submit提交后触发的事件(表单验证部分未展示)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 | $.post(toAjaxComment,{nickname:subName,email:subEmail,website:subUrl,content:subArea,pid:pid,aid:aid,portrait:porid,page:page}, function (res){ if (res){ var apComment = function (){ //清空输入栏的信息 This.siblings( '.CPersonal' ).find( '.InfoNn' ).val( '' ); This.siblings( '.CPersonal' ).find( '.InfoEm' ).val( '' ); This.siblings( '.CPersonal' ).find( '.InfoWs' ).val( '' ); This.siblings( '.CContent' ).children().val( '' ); //声明一个变量为1 用于改变评论条数的值 var cmCount = 1; //判断当前是否有评论 , 如果存在则评论数量+1 if ($( '.TCNumber' ).text()){ cmCount = parseInt($( '.TCNumber' ).text()) + 1; } //判断当前页面是否为第一页 为第一页则调用pageDis方法于pageShow方法将页面刷新为第一页评论 if (page == 0){ pageDis($( '.prevPage' ).next()); pageShow(res); } else { //判断当前页面是否存在评论 不存在则删除提示条 并且新增评论主体部分.CMain 将新增评论插入主体中 if ($( '.emptyTips' ).length > 0){ $( '.emptyTips' ).remove(); var str = '<div class="CMain"><div class="CMTitle"><div class="MTContent">评论</div>' ; str += '<div class="MTCount">共计<span class="TCNumber">1</span>条评论</div></div>' ; str += '<div class="CMLists"><div class="MLTitle"><i class="LTBg"></i>最新评论</div>' ; str += '<div class="MLContents">' + res + '</div></div></div>' ; $( '.CHeader' ).after(str); } else { //判断当前评论是对文章的直接评论还是回复楼上的评论 , 后者隐藏回复栏 , 将滚动条滚动至最顶部 if (This.parent().siblings( '.CommentBtn' ).length > 0){ This.parent().prev().children( '.CBReply' ).text( '回复' ).attr( 'aid' ,1); This.parent().stop().slideUp(300); $( 'html,body' ).animate({scrollTop:$( '.CMTitle' ).offset().top + "px" },100); } //判断评论数量是否大于5 , 大于5则将当前页面最后一条评论删除 if (cmCount > 5){ $( '.LCSub' ).last().fadeOut(300, function (){ $( '.LCSub' ).last().remove(); }); //得出当前应有分页页数 var listNum = Math.ceil(cmCount/5); //判断当前评论分页代码是否存在 if ($( '.pagination' ).length == 0){ //不存在 则拼接字符串 将分页代码添加上去 var pageStr = '<ul class="pagination">' ; pageStr += '<li class="disabled"><span>上一页</span></li><li class="active"><span>1</span></li>' ; for ( var i=1;i<listNum;i++){ pageStr += '<li class="pageNum"><span>' + (i+1) + '</span></li>' ; } pageStr += '<li class="nextPage"><span>下一页</span> </li></span></listnum;i++){></ul>' ; $( '#Comment' ).after(pageStr); } else { //存在 并且评论数量已达到最大页数则将页码数+1 var maxList = parseInt($( '.nextPage' ).prev().text()) + 1; if (listNum == maxList){ var listStr = '<li class="pageNum"><span>' + maxList + '</span> </li>' ; $( '.nextPage' ).before(listStr); } } } $( '.MLContents' ).prepend(res); } } $( '.TCNumber' ).text(cmCount); //Ajax完成后还原该属性 使用户可继续发送评论 $( 'body' ).attr( 'sid' ,1); This.text( '发布评论' ); }; //当以上代码执行完毕后 执行AJAX发送邮件通知博主 , 评论者 , 被回复者 $.when(apComment()).done( function (){ $.post(toAjaxMail,{nickname:subName,email:subEmail,pid:pid,aid:aid}, function (res){ }, 'json' ) }) |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | public function AjaxComment(){ if (IS_AJAX){ //调用getTBIP方法 通过淘宝接口获取IP地址 $ipData = $ this ->getTBIP($ this ->ip); //通过Comment模型中的add方法 将post中的数据添加进数据库 $cid = $ this ->db->add($ipData); if ($cid){ //判断当前页面是否是第一页 不是第一页则传入页码1 让其返回第一页的数据 if ($_POST[ 'page' ] == 0){ $arr = $ this ->resetArr($_POST[ 'aid' ],$_POST[ 'page' ]); } else { //如果是第一页 则只截取第一条评论 在前端页面添加进去即可 $data = $ this ->resetArr($_POST[ 'aid' ]); $arr[0] = $data[0]; } //调用resetHTML方法 拼接出HTML字符串并返回给前台 $htmlStr = $ this ->resetHtml($arr,$_POST[ 'aid' ]); echo json_encode($htmlStr, true );die; } else { echo false ;die; } } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | public function getTBIP($ip){ $data = @file_get_contents( "http://ip.taobao.com/service/getIpInfo.php?ip=" .$ip); $data = json_decode($data, true ); $data = $data[ 'data' ]; $reldata = [ 'ip' =>$data[ 'ip' ], 'isp' =>$data[ 'isp' ], 'area' =>$data[ 'area' ] ]; if (strpos($data[ 'region' ], '市' )){ $reldata[ 'add' ] = $data[ 'country' ] . $data[ 'region' ]; } else { $reldata[ 'add' ] = $data[ 'country' ] . $data[ 'region' ] . $data[ 'city' ]; } return $reldata; } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | public function add($ipData){ $data = $_POST; $Model = new Comment(); $Model[ 'nickname' ] = htmlspecialchars($data[ 'nickname' ]); $Model[ 'pid' ] = $data[ 'pid' ]; $Model[ 'article_aid' ] = $data[ 'aid' ]; $Model[ 'date' ] = time(); $Model[ 'website' ] = $data[ 'website' ]; $Model[ 'content' ] = htmlspecialchars($data[ 'content' ]); $Model[ 'portrait' ] = $data[ 'portrait' ]; $Model[ 'email' ] = $data[ 'email' ]; $Model[ 'add' ] = $ipData[ 'add' ]; $Model[ 'area' ] = $ipData[ 'area' ]; $Model[ 'ip' ] = $ipData[ 'ip' ]; $Model[ 'isp' ] = $ipData[ 'isp' ]; return $Model->save(); } |
//因为框架原因 , 我使用了HDPHP内置的发送Email的方法 , 是被封装好了的方法 , 不同的框架使用方法也不一样 , 所以就不贴出来了
六 分页功能
分页列表点击事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | $( 'body' ).on( 'click' , '.pageNum' , function (){ var page = parseInt($( this ).children().text()); pageDis($( this )); pageChg(page); }); $( 'body' ).on( 'click' , '.prevPage' , function (){ var page = parseInt($( '.active' ).prev().children().text()); pageDis($( '.active' ).prev()); pageChg(page); }); $( 'body' ).on( 'click' , '.nextPage' , function (){ var page = parseInt($( '.active' ).next().children().text()); pageDis($( '.active' ).next()); pageChg(page); }); |
PageDis函数的作用是根据点击的页码 Div做出相应地改变class样式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | function pageDis(atPage){ $( '.active' ).attr( 'class' , 'pageNum' ); atPage.attr( 'class' , 'active' ); var ppgLen = atPage.prev( '.prevPage' ).length; var npgLen = atPage.next( '.nextPage' ).length; var prevPg = $( '.pagination' ).children().first(); var nextPg = $( '.pagination' ).children().last(); if (ppgLen == 1){ prevPg.attr( 'class' , 'disabled' ); } else { prevPg.attr( 'class' , 'prevPage' ); } if (npgLen == 1){ nextPg.attr( 'class' , 'disabled' ); } else { nextPg.attr( 'class' , 'nextPage' ); } } |
1 2 3 4 5 6 | function pageChg(page){ var aid = $( '.pagination' ).attr( 'aid' ); $.post(toAjaxPaging,{aid:aid,page:page - 1}, function (res){ pageShow(res); }, 'json' ); } |
1 2 3 4 5 6 7 8 9 | public function AjaxPaging(){ if (IS_AJAX){ //调用resetArr方法,传入页码 , 得到该页面数据 $arr = $ this ->resetArr($_POST[ 'aid' ],$_POST[ 'page' ]); //调用resetHtml方法 , 得到拼接后的HTML字符串并输出给前台 $htmlStr = $ this ->resetHtml($arr,$_POST[ 'aid' ]); echo json_encode($htmlStr, true );die; } } |
pageShow函数 作用是实现一个简单的淡出淡入效果 将评论列表内容切换
1 2 3 4 5 6 | function pageShow(res){ $( '.LCSub' ).fadeOut(300, function (){ $( 'html,body' ).animate({scrollTop:$( '.CMTitle' ).offset().top + "px" },100); $( '.MLContents' ).html(res); }) } |
七 顶、踩功能
顶与踩按钮的点击事件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 | $( 'body' ).on( 'click' , '.CBDing' , function (){ var This = $( this ); var cid = This.parent().attr( 'pid' ); //防止用户多次点击请求 var ajaxState = This.attr( 'sid' ); if (!ajaxState){ ajaxState = 1; } if (ajaxState == 1){ This.attr( 'sid' ,0); $.post(toAjaxLikes,{cid:cid,ip:clientIP}, function (res){ if (res){ $( ".CommentBtn[pid=" +cid+ "]" ).children( '.CBDing' ).removeClass( 'CBDing' ).addClass( 'Ding_active' ); $( ".CommentBtn[pid=" +cid+ "]" ).find( '.dingCount' ).text(res); This.attr( 'sid' ,1); } }, 'json' ) } else { return false ; } }); $( 'body' ).on( 'click' , '.CBCai' , function (){ var This = $( this ); var cid = This.parent().attr( 'pid' ); var ajaxState = This.attr( 'sid' ); //防止用户多次点击请求 if (!ajaxState){ ajaxState = 1; } if (ajaxState == 1){ This.attr( 'sid' ,0); $.post(toAjaxBoo,{cid:cid,ip:clientIP}, function (res){ if (res){ $( ".CommentBtn[pid=" +cid+ "]" ).children( '.CBCai' ).removeClass( 'CBCai' ).addClass( 'Cai_active' ); $( ".CommentBtn[pid=" +cid+ "]" ).find( '.CaiCount' ).text(res); This.attr( 'sid' ,1); } }, 'json' ) } else { return false ; } }); |
被请求的顶与踩的PHP方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | public function AjaxLikes(){ if (IS_AJAX){ //查询出comment表中赞过该条评论的IP集合 $data = Db::table( 'comment' )->where( "cid" ,$_POST[ 'cid' ])->get([ 'ding' , 'dip' ]); $ding = $data[0][ 'ding' ] + 1; $odip = $data[0][ 'dip' ]; //判断此评论是被否被赞过 if ($ding == 1){ $dip = $_POST[ 'ip' ]; } else { //判断此IP是否在已赞的IP集合中 if (in_array($_POST[ 'ip' ],explode( '|' ,$odip))){ echo false ;die; } else { $dip = $odip . '|' . $_POST[ 'ip' ]; } } //将此IP添加至已赞的IP集合中 $res = Db::table( 'comment' )->where( "cid" ,$_POST[ 'cid' ])->update([ 'ding' =>$ding, 'dip' =>$dip]); if ($res == 1){ echo $ding; } else { echo false ; } } } public function AjaxBoo(){ if (IS_AJAX){ //查询出comment表中踩过该条评论的IP集合 $data = Db::table( 'comment' )->where( "cid" ,$_POST[ 'cid' ])->get([ 'cai' , 'cip' ]); $cai = $data[0][ 'cai' ] + 1; $ocip = $data[0][ 'cip' ]; //判断此评论是被否被踩过 if ($cai == 1){ $cip = $_POST[ 'ip' ]; } else { //判断此IP是否在已赞的IP集合中 if (in_array($_POST[ 'ip' ],explode( '|' ,$ocip))){ echo false ;die; } else { $cip = $ocip . '|' . $_POST[ 'ip' ]; } } //将此IP添加至已赞的IP集合中 $res = Db::table( 'comment' )->where( "cid" ,$_POST[ 'cid' ])->update([ 'cai' =>$cai, 'cip' =>$cip]); if ($res == 1){ echo $cai; } else { echo false ; } } } |
本项目GitHub传送门 : 盖楼回复留言板
结语 : 至此 , 所有JS代码与PHP代码都已逐步经过分析 , 制作一个无限嵌套回复的留言板也终于可以大功告成啦 , 快去动手尝试吧~
