我的畢設項目的評論模塊原來是采用多說插件完成的,但是我現在希望能夠自己管理評論內容,所以自己開始寫評論模塊。具體準備采用與簡書下評論類似的結構,即一級評論直接顯示在文章下方,而二三級評論顯示在一級評論的下方,如下圖所示:
我覺得這可以說是無限極分類的一種運用,準確的說是子孫樹的運用,進行子孫樹分類后,循環輸出內容,形成評論(不懂無限極分類的朋友可以去看看我的文章無限極分類原理與實現)。
當然,真正完成無限極回復評論的功能還有其他的要點,下面就來說說我是怎么完成無限極評論的。
數據庫設計
首先,就是數據表的設計。如果是論壇系統,可以將評論的數據分割成兩個表,一個表存儲評論的信息,包括發布帖子的用戶id或回復的用戶id,回復的帖子id,回復的時間等;另一個表,存儲評論的內容,包括帖子的主題和回復的內容。
我完成的是文章的評論模塊,就沒分成兩個表了,直接將評論的內容與信息放在一起,如下面結構:
| 列名 | 列類型 | 列屬性 | 說明 |
|------|--------|------- |
| comm_id| INT | UNSIGNED PRIMARY KEY AUTO_INCREMENT | 主鍵
| user_id | INT | UNSIGNED NOT NULL DEFAULT 0 | 用戶id
| parent_id | INT |UNSIGNED NOT NULL DEFAULT 0| 評論的父級
| artcile_id | INT |UNSIGNED NOT NULL DEFAULT 0| 評論的文章id
| comm_cont | TEXT | | 評論的內容
| comm_time | INT | UNSIGNED NOT NULL DEFAULT 0 | 評論發布的時間
SQL語句:
CREATE TABLE comment (
comm_id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
user_id INT UNSIGNED NOT NULL DEFAULT 0 ,
parent_id INT UNSIGNED NOT NULL DEFAULT 0 ,
article_id INT UNSIGNED NOT NULL DEFAULT 0 ,
comm_cont TEXT,
comm_time INT UNSIGNED NOT NULL DEFAULT 0
) ENGINE=MYISAM CHARSET=UTF8 ;
這種結構是完成無限極回復的基礎,也能夠很明顯的看出取出的數據能很好的進行無限極分類。
評論的結構分析
完成一個評論模塊是很容易的,評論放入數據庫,然后取回放置在html中,同樣能完成評論模塊。但是,這種結構是很雜亂,無序的。要想完成如簡書評論一樣的評論模塊,就得采用特殊的方式。
那么,就要好好觀察一下簡書評論的結構了。
結合上面的數據表結構,可以推斷一下,從數據表中取出且無限極分類后的數據,它的結構應該是這樣的:
array (
array(一級評論,
child=>array(
二級評論,
三級評論
)
),
array (
一級評論 ,
child=>array(
)
……
為什么這么說?你可以很明顯的看出,二級、三級評論被包裹在一級評論中,而二級、三級評論是平行關系;因此,二級、三級評論是一級評論的子孫節點,而二級、三級評論是平行節點,不存在父子關系。
因此,可以得出,分類后的數據有且只有一個子孫節點,多級評論無論是不是對一級評論進行回復,只要是處于一級評論的范圍,那么它的父節點必然是一級評論。
那么,二級、三級回復中的@某某某
又是怎么實現的?其實,我在這里困擾了很長時間。我期望用對表的自身連接來完成,但是不行,破壞了上面所述的結構。最后,我從簡書請求的json數據中得到了答案,請看簡書評論的json數據:
上傳后有點模糊,各位同學可以借助火狐或者谷歌瀏覽器上面的插件觀察JSON數據。
重點觀察compiled_content
字段,可以推斷它是將@某某某
直接存儲到數據庫中的。這樣問題就解決了,同時觀察json數據,也能夠驗證我上面所述的結構是正確的。
具體實現
分析完結構就來說說具體怎么來完成無限極回復。第一步,一級評論形成,這個簡單,直接將評論存儲即可
而二級評論要利用到js,點擊評論時,獲取一級評論的用戶名,并保存,在發布回復時注意將其和評論的內容結合在一起發往后臺:
// replyUser 即 被回復的用戶名 @xxxx
var content = $('#reply').val.split(replyUser)[1];
var userlink = '<a href="#" class="xxx" target="_blank" >' + replyUser + '</a>';
var comm_cont = encodeURIComponent(userlink+content);
那么在數據庫中表現如下:
之后就是一個關鍵點,利用子孫樹進行分類,分類函數如下:
/**
* @param $data array 數據
* @param $parent string 父級元素的名稱 如 parent_id
* @param $son string 子級元素的名稱 如 comm_id
* @param $pid int 父級元素的id 實際上傳遞元素的主鍵
* @return array
*/
function getSubTree($data , $parent , $son , $pid = 0) {
$tmp = array();
foreach ($data as $key => $value) {
if($value[$parent] == $pid) {
$value['child'] = getSubTree($data , $parent , $son , $value[$son]);
$tmp[] = $value;
}
}
return $tmp;
}
如此分類后,數據結構發生變化,大致如下:
擁有這種結構就能很好去完成評論了,如下
<?php foreach($tree as $key=>$val) ?>
<div class="comm_list" >
<h2><?php echo $val['user_name'];?></h2>
<p><?php echo $val['comm_cont'] ?></p>
<!-- 其他信息 -->
<div class="comm_reply">
<?php if(!empty($val['child'])) { ?>
<?php foreach($val['child'] as $k=>$v) ?>
<div class="reply_list" >
<h2><?php echo $v['user_name'];?></h2>
<p><?php echo $v['comm_cont'] ?></p>
<!-- 其他信息 -->
</div>
<?php }}?>
</div>
</div>
<?php } ?>
同時,形成的回復樣式如下所示:
這樣類簡書評論結構的無限極回復就完成了。
PS
這只是評論的一種形式,如還有一種類樓梯式的結構,這種實現則更為簡單一點,如下圖:
這種結構就很容易完成,只要存儲數據庫中的parent_id完全等于你回復的comm_id,在經過如下無限極分類,就能夠完成。
/**
* 子孫樹
*/
function getSubTree($data , $parent , $son , $pid = 0, $lev = 0) {
$tmp = array();
foreach ($data as $key => $value) {
if($value[$parent] == $pid) {
$value['lev'] = $lev;
$tmp[] = $value;
$tmp = array_merge($tmp , getSonTree($data , $parent , $son , $value[$son] , $lev+1));
}
}
return $tmp;
}
這種無限極子孫樹分類和前面給出的子孫樹分類不同,分類后不會將子類包裹在child中,而是形成等級,最高級為0,依次往下。
例如:一級評論的comm_id=1,parent_id=0,那么二級評論的comm_id=2,parent_id=1;三級評論的comm_id=3,parent_id=2;
分類最后形成如下結構
array(
array('comm_id'=>1,parent_id=>0,art_id=>1,'lev'=>0) ,
array('comm_id'=>2,parent_id=>1,art_id=>1,'lev'=>1),
array('comm_id'=>3,parent_id=>2,art_id=>1,'lev'=>2),
array('comm_id'=>4,parent_id=>3,art_id=>1,'lev'=>3),
array('comm_id'=>5,parent_id=>2,art_id=>1,'lev'=>2)
);
然后直接循環輸出,并將lev作為屬性打印在html中,最后利用js讀取lev,并根據不同的等級分配不同的margin-left即可,它會隨著margin的不同而排列在不同的位置,如下:
// html中
<?php foreach($tree as $key=>$val) {?>
<div class="comm_list" lev="<?php echo $val['lev']?>">
……
</div>
<?php } ?>
// js中
$('div.comm_list').css('margin-left' , 20 * lev);
END