AJAX+PHP+MySQL制作盖楼嵌套、无限回复留言板

现在网上有很多第三方评论插件,例如畅言,多说之类的,虽然用起来很简单,但是每次加载页面都需要加载他们的JS,如果他们的服务器不稳定,就会加载很慢,影响网站的加载速度,而且数据文件放在第三方那里也不安全,自己对用户的控制力也会很低,因此自己动手制作一套评论系统比使用第三方插件会舒服很多,别的话我就不提了,下面开始文章正文:

一 评论模板

评论模板是博主自己写的一套模板,代码放在了我的GitHub上面,效果就是本博客评论页内容,喜欢的话可以直接去下载,顺便给个star,蟹蟹~

GitHub传送门 :    留言模板

二 MySQL数据表设计


//cid 主键ID , pid 父级IDarticle_aid 评论文章IDdate 评论时间戳 , 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();
    }
}
//在构造方法中将2个属性分别接受 实例化Comment模型类评论者IP

###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;
}
###入口index方法 调用showLists方法 分配变量至HTML

?
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();
}
//listData接受的数据第一个是 拼接好的列表HTML字符串 , 第二个是 评论条数 , 第三个就是 拼接好的分页列表的HTML字符串

先不管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;
}
//resetArr方法中需要传入2个参数 aid文章ID , page分页页码

有了重组好的数据就可以在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个方法的处理 , 就可以得到上图样式的HTML代码

不过还有一个小细节 , 就是需要判断每条评论是否被赞、踩 , 做法很简单 , 就是通过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')
})
###PHP中被AJAX请求的添加评论方法

?
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;
        }
    }
}
###getTBIP方法 请求淘宝接口 , 返回IP物理地址、运营商等信息

?
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;
}
###Comment模型类add方法

?
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();
}
//不同框架的添加方法略有不同 , 但是思路都是一样的 , 添加之前需要将content , nickname字段使用htmlspecialchars函数转译 , 防止SQL注入

//因为框架原因 , 我使用了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);
});
//以上3个事件中都调用了PageDis、PageChg2个函数

###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');
    }
}
###PageChg函数的作用则是通过AJAX请求PHP , 得到当前分页的数据后将HTML代码添加至页面

?
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');
}
###PHP 分页方法

?
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;
    }
}
//可以看到再次调用了resetArrresetHtml方法 可以说这2个方法是整个message类的核心

###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);
    })
}
//在AJAX添加评论时 , 如果当前评论页面不是第一页 , 那么也会调用此函数 , 将评论列表内容切换

五 顶、踩功能

###顶与踩按钮的点击事件

?
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代码都已逐步经过分析 , 制作一个无限嵌套回复的留言板也终于可以大功告成啦 , 快去动手尝试吧~

发布评论
还没有评论,快来抢沙发吧!
󰀿
󰀿