bbs/掲示板/ビルを建てる技術実現(PHP)

51324 ワード

2015年3月5日14:36:44
更新:2015年7月18日16:33:23土曜日
ターゲットとして、ネットワークのような建物を建てる機能を実現しますが、投稿を繰り返し表示しません.
テストアドレス:一般的な実装 結合js実装
効果:
*    //1 

**    //1      ***    //1       ****    //1       (     ...) *****    //..... ******    *******    ********    *********    **********    ***********    ************    *    //2  **    //2      *    //3  **    //....
       >>>   :2015030319

|-47 : @   ~     ! [2015030319] <    >

|-|-52 : @47    [2015030319] <    >

|-|-|-53 : @52    [2015030319] <    >

|-|-|-|-55 : @53    [2015030511] <    >

|-|-|-|-|-56 : @55    [2015030511] <    >

|-|-|-|-|-|-57 : @56    [2015030511] <    >

|-|-|-|-|-|-|-58 : @57    [2015030511] <    >

|-|-|-|-|-|-|-|-60 : @58    [2015030511] <    >

|-|-|-|-|-|-|-|-|-61 : @60    [2015030511] <    >

|-|-|-|-|-|-|-|-|-|-62 : @61    [2015030511] <    >

|-|-|-|-|-|-|-|-|-|-|-63 : @62    [2015030511] <    >

|-|-|-|-|-|-|-|-|-|-|-|-64 : @63    [2015030511] <    >

|-|-|-|-|-|-|-|-|-66 : @60    [2015-03-06-16] <    >

|-|-|-|-|-|-|-59 : @57    [2015030511] <    >

|-|-|-|-|-|-67 : @56    ~ [2015-03-06-16] <    >

|-|-|-|-|-|-|-68 : @67   ~ [2015-03-06-16] <    >

|-|-|-54 : @52    [2015030511] <    >

|-48 : @    [2015030319] <    >

|-|-51 : @48    [2015030319] <    >

|-49 : @    [2015030319] <    >

|-|-50 : @49    [2015030319] <    >

実装ロジック:
1.記憶、データベース(MYSQL)を大きな構造体配列とし、各記録を一つの構造体として、親帖情報、子帖情報、兄弟帖情報を記録する
2.表示原理,回复帖在网上表示时也是独有一行的,只比楼主的帖子多了多少インデント,所以我把所有的帖子(子回帖,孙子回帖....脳补网易盖楼)都看起来是不同インデント的普通帖子
3.データの表示
某一贴のすべての回帖,子回帖,孙子回帖....メモリに一度に読み込む(欠点、キャッシュで解決可能)
方法1:再帰(マルチフォークツリー遍歴)で投稿を1次元配列に並べ替え、順番に表示します(ネストされたループを回避します)
方法2:同じように1次元配列のidソートを生成しますが、挿入ソート方法を使用します(再帰的に非効率だそうです)
方法3(推奨):2つのステップに分けて、あるページの20個の1級返信をユーザーに取得し、クライアントはajax非同期でサブ返信を補完する.
4.「並べ替え」の際に2つの配列が生成され、
1つの中には投稿のidしかなく、ループに使われています.順番は1階→1階のすべてのレス→2階→2階のすべてのレスです...
もう1つは具体的な投稿内容などの情報です
 
実装の詳細:
1.データベース:
id
rootid
fatherid
next_brotherid
first_childid
last_childid
level
inttime
strtime
content
本帖id
首帖id
父帖id
次の兄弟帖id
第一条レスid
最後の返信のid
本帖深さ(第几层回复)
投稿タイムスタンプ
投稿文字時間(便利な時間軸統計)
投稿の内容
 
2.データを入庫し、データベースをチェーンテーブルとして使用する.
 1     //  /   /       

 2     public function addRoot($content = '  ')

 3     {

 4         $a = array(

 5             'rootid' => 0,

 6             'fatherid' => 0,

 7             'next_brotherid' => 0,

 8             'first_childid' => 0,

 9             'level' => 0,

10             'content' => $content

11             );

12 

13         $inttime = time();

14         $strtime = date('YmdH', $inttime);

15 

16         $a['inttime'] = $inttime;

17         $a['strtime'] = $strtime;

18 

19         $insert_id = $this->getlink('tiezi')->insert($a);

20     }

21 

22     //   

23     public function addReplay($fatherid, $content = '  ')

24     {

25         $where = "id={$fatherid}";

26         $r = $this->getlink('tiezi')->selectOne($where);

27 

28         $id = $r['id'];

29         $rootid = $r['rootid'];

30         $first_childid = $r['first_childid'];

31         $last_childid = $r['last_childid'];

32         $level = $r['level'];

33 

34         $a = array(

35             'fatherid' => $fatherid,

36             'next_brotherid' => 0,

37             'first_childid' => 0,

38             'content' => $content

39             );

40 

41         //       (level == 0)

42         $a['rootid'] = $level ? $rootid : $id;

43 

44         $inttime = time();

45         $strtime = date('YmdH', $inttime);

46 

47         $a['level'] = ++$level;

48         $a['inttime'] = $inttime;

49         $a['strtime'] = $strtime;

50 

51         $insert_id = $this->getlink('tiezi')->insert($a);

52 

53         //        ,    ,         

54         if (!$first_childid) {

55             $where = "id = {$id}";

56             $b = array(

57                 'first_childid' => $insert_id

58                 );

59             $this->getlink('tiezi')->update($b, $where);

60         }

61 

62         //           ,              

63         if ($last_childid) {

64             //        ,          next_brotherid

65             $where = "id = {$last_childid}";

66             $c = array(

67                 'next_brotherid' => $insert_id

68                 );

69             $this->getlink('tiezi')->update($c, $where);

70 

71         }

72         //     last_childid   

73         $where = "id = {$id}";

74         $c = array(

75             'last_childid' => $insert_id

76             );

77         $this->getlink('tiezi')->update($c, $where);

78     }

挿入するたびにいくつかのsql文を実行することに注意してください.
併発量が大きい場合は、1.キュー; 2.msyqlのauto_の代わりにredisでidを統一的に生成するincrement; 3.取引
3.投稿データを取得して「ソート」
3.1再帰ソート
 1     //      

 2     public function getTieziDetail($rootid)

 3     {

 4         $this->rootid = $rootid;

 5         //      ,          

 6         $fields = 'first_childid';

 7         $where = 'id = '.$rootid;

 8         $root = $this->getlink('tiezi')->selectOne($where);

 9         $first_childid = $root['first_childid'];

10 

11         //        

12         $where = 'rootid = '.$rootid;

13         $this->tieziList = $this->getlink('tiezi')->find($where, '', '', '', 'id');// id  

14         // $this->tieziList[$rootid] = $root;

15         

16         $this->rv($this->tieziList[$first_childid]);

17         // $this->rv($root);

18 

19         return array(

20             'tiezi' => $this->tieziList,

21             'sort' => $this->sort

22             );

23     }

24 

25     //    /    

26     public function rv($node)

27     {

28         $this->sort[$node['id']] = $node['id']; //      id

29         

30         if ($node['first_childid'] && empty($this->sort[$node['first_childid']])) { //     ,           

31             $this->rv($this->tieziList[$node['first_childid']]);

32         } elseif ($node['next_brotherid']) {//      ,       

33             $this->rv($this->tieziList[$node['next_brotherid']]);

34         } elseif ($this->tieziList[$node['fatherid']]['next_brotherid']) {//    ,     ,       ,       ,               (   )

35             // $fatherid = $node['fatherid'];

36             // $next_brotherid_of_father = $this->tieziList[$fatherid]['next_brotherid'];

37             // $this->rv($this->tieziList[$next_brotherid_of_father]); //             

38             $this->rv($this->tieziList[$this->tieziList[$node['fatherid']]['next_brotherid']]);

39         } elseif ($node['fatherid'] != $this->rootid) { //         ,      ,           

40             $this->rv($this->tieziList[$node['fatherid']]);

41         }

42 

43         return;

44     }

3.2ソートの挿入
 1 //      

 2     public function getTieziDetail($rootid)

 3     {

 4         $this->rootid = $rootid;

 5         //      ,          

 6         // $fields = 'id first_childid content strtime';

 7         $where = 'id = '.$rootid;

 8         $root = $this->getlink('tiezi')->selectOne($where);

 9         $first_childid = $root['first_childid'];

10 

11         //        

12         $where = 'rootid = '.$rootid;

13         $order = 'id';

14         $this->tieziList = $this->getlink('tiezi')->find($where, '', $order, '', 'id');// id  

15         

16         // $this->rv1($this->tieziList[$first_childid]);

17         $this->rv($root);

18         $this->tieziList[$rootid] = $root;

19         unset($this->sort[0]);

20 

21         return array(

22             'tiezi' => $this->tieziList,

23             'root' => $root,

24             'sort' => $this->sort

25             );

26     }

27 

28     //      (  )

29     //     ,                    ,           

30     public function rv($root)

31     {

32         $this->sort[] = $root['id'];

33         $this->sort[] = $root['first_childid'];

34         $this->sort[] = $root['last_childid'];

35 

36         foreach ($this->tieziList as $currentid => $v) {

37             $currentid_key = array_search($currentid, $this->sort); //            sort              

38             // if ($currentid_key) { //           $this->sort 

39                 $first_childid = $v['first_childid'];

40                 $last_childid = $v['last_childid'];

41                 $next_brotherid = $v['next_brotherid'];

42 

43                 //                

44                 if ($first_childid && ($first_childid != $this->sort[$currentid_key+1])) { //           sort ,   

45                     array_splice($this->sort, $currentid_key + 1, 0, $first_childid);

46                     if ($last_childid && ($last_childid != $first_childid)) { //       ,first_childid  == last_childid

47                         array_splice($this->sort, $currentid_key + 2, 0, $last_childid); //         

48                     }

49                 }

50 

51                 //      

52                 if ($next_brotherid) { //     

53                     $next_brotherid_key = array_search($next_brotherid, $this->sort);

54                     if (!$next_brotherid_key) { //

55                         if ($last_childid) {

56                             $last_childid_key = array_search($last_childid, $this->sort);

57                             array_splice($this->sort, $last_childid_key + 1, 0, $next_brotherid); //                    

58                         } elseif ($first_childid) {

59                             array_splice($this->sort, $currentid_key + 2, 0, $next_brotherid); //                   

60                         } else {

61                             array_splice($this->sort, $currentid_key + 1, 0, $next_brotherid); //                

62                         }

63                     }

64                 }

65             // }

66         }

67     }

htmlによると、以上の2つの方法は、ある投稿のすべての返信を一度に読み取ることであり、欠陥になります.
 1 <html>

 2 <head>

 3     <meta charset="utf-8">

 4 </head>

 5     <body>

 6         <?php

 7             echo $root['content'], ' >>>    '.$root['id'].'   :', $root['strtime'], '<hr>';

 8             $i = 0;

 9             foreach ($sort as $v) {

10                 for($i=0; $i < $tiezi[$v]['level']; ++$i){

11                     echo '|-';

12                 }

13                 $tmp_id = $tiezi[$v]['id'];

14                 $tmp_rootid = $tiezi[$v]['rootid'];

15                 echo $tmp_id.' : @'. $tiezi[$tiezi[$v]['fatherid']]['id']. ' ' .$tiezi[$v]['content'].' ['.$tiezi[$v]['strtime']."] <a href='{$controllerUrl}/bbs_replay?id={$tmp_id}&rootid={$tmp_rootid}'><    ></a><br>";

16             } 

17         ?>

18     </body>

19 </html>

 
3.3ルート順の巡回(すべての返信をマルチフォークツリーと見なし、投稿はこのツリーの踵ノードであり、データベースを循環して読み取ることがあり、気になる場合は3.4方法を使用する)
 1 //     

 2     // 1.           ,       ,            

 3     // 2.            ,         ,             

 4     // 3.        ,      ,    1,2       

 5     // 4.         ,              

 6     // 5.    ,           ,    ,    1,2,3     ,         4   ,      

 7     public function getAllReplaysByRootFirst($id)

 8     {

 9         $where = "id={$id}";

10         $current = $this->getlink('tiezi')->selectOne($where);

11 

12         $replay = []; //       

13         $stack = []; //     

14         $tmp = []; //       

15 

16         if (!empty($current['first_childid'])) {

17             //      $stack      ,                ,    do...while

18             do {

19                 if (empty($current['stack'])) { //           

20                     $replay[] = $current;

21                     if (!empty($current['first_childid'])) { //     ,   current       ,      

22                         $current['stack'] = 1; 

23                         $stack[] = $current;

24 

25                         $where = "id={$current['first_childid']}";

26                         $current = $this->getlink('tiezi')->selectOne($where);

27                     } elseif (!empty($current['next_brotherid'])) { //       ,        ,   

28                         $where = "id={$current['next_brotherid']}";

29                         $current = $this->getlink('tiezi')->selectOne($where);

30                     } else {

31                         $current = array_pop($stack);

32                     }

33                 } else { //    (  )   ,                

34                     if (!empty($current['next_brotherid'])) { //       ,        ,   

35                         $where = "id={$current['next_brotherid']}";

36                         $current = $this->getlink('tiezi')->selectOne($where);

37                     } else {

38                         $current = array_pop($stack);

39                     }

40                 }

41                 

42             } while (!empty($stack));

43         }

44 

45         return $replay;

46     }

3.4実際に即して、大多数の招待状の返答はただ1階だけあって、めったにビルを建てる情況が発生しなくて、網易のようにちょうどビルを建てる機能を出す時を除いて、あの時間は100数階の深さまで覆うようです
2つのステップに分けて歩きます.
第一歩、サービス側は一度に「すべて」の「一級」の返事を取得し、サブ返事を取得しない(ビルを建てる返事)
ステップ2では、クライアントでajaxループを介して各投稿のサブ返信を非同期で要求し(方法3.3)、domを動的に書き込み、すべての返信を改善する
1     //      ,                

2     public function getLv1Replays($rootid)

3     {

4         $where = "rootid = {$rootid} and level = 1";

5         return $this->getlink('tiezi')->select($where);

6     }

このようなメリットまたは理由は、
1.「すべて」の1級回答を得るわけではありません.現実には必ずページがあるからです.1ページあたりの標準は20本、50本以上を支え、50本を超えています.退職を考えて、このような製品と混同して、IQに気をつけてください.
2.ajaxは非同期で、コールバックに基づいて、ある返信に多くのサブ返信があれば、その返信のすべてのサブ返信を完全に取得してから他のデータを取得するとは言わない.
欠点は次のとおりです.
1.もしネットのスピードが遅いならば、カードの现象が现れて、NND、ネットはよくなくてどんなアルゴリズムも糞で、考えません;
2.1レベルの返信を表示してから、すべてのサブ返信を表示します.今のハードウェアはすべて強くて、瞬間的なことは考えなくてもいいです.
デモ:angular.js + bbs
 
まとめ:
一つの複雑な機能の実現は、いくつかのステップに分けて完成したほうがいい.一歩で完成するとは思わないでください.そうすれば、多くの脳細胞が死んでから機能を完成する方法を考え出すことができ、効率は高くありません.
例:
いくつかの良い文字列マッチングアルゴリズムは、例えば、文字列の移動の長さを計算し、保存してから、比較文字列を使用します.
画像の中の1つの閉じた線内の画素をすべて統一色に染めて、まず1行ごとに画像をスキャンして、つながっている画素の条を記録して、それから染色します
 
ps.の中のいくつかのgetlink()などの操作データベースの関数は私が自分で書いたPHPフレーム内の関数で、特にここで宣伝して、ハハハ