2011-04-05 72 views
16

對於個人項目,我需要使用PHP和MySQL構建論壇。我不可能使用已經構建的論壇包(如phpBB)。在PHP/MySQL中處理未讀文章

我目前正在研究構建這樣一個應用程序所需的邏輯,但這已經過了漫長的一天,我正在爲處理未讀帖子的用戶而努力。一個解決辦法我是有一個單獨的表基本上包含所有崗位ID和用戶ID,以確定它們是否已閱讀:

tbl_userReadPosts: user_id, post_id, read_timestamp

顯然,如果一個用戶的ID出現在該表中,我們知道他們已經閱讀了這篇文章。這很好,除非我們每天都有帖子(這在提議的系統中是可能的),以及用戶的數量。如果不是幾個小時,這張桌子在幾天之內就會變得很大。

另一種選擇是將用戶的上次活動作爲時間戳記進行跟蹤,然後檢索在上次活動更新後所做的所有帖子。這在理論上是有效的,但假設用戶正在撰寫一篇非常長的文章,同時還有幾位成員開始新的主題或回覆其他主題的文章。當用戶提交他的新帖子時,他的最後一次活動將被更新,因此與此期間所做的不匹配。

有沒有人有這方面的經驗,你是如何解決它的?

我已經在phpBB中檢查過,似乎系統會爲每個用戶分配一個自定義會話,並在此基礎上工作,但文檔對於未處理帖子的處理方式非常稀疏。

一如既往地感激地收到的想法和意見。

回答

6

對不起,我只有第二個答案。您絕對不希望將讀取的信息存儲在數據庫中,正如您已經推斷的那樣,該表格將變得非常龐大。

之間的東西你已經建議:存儲用戶最後的活動,並結合存儲他們在cookie中看到的信息,以確定他們已經讀過哪些線程/帖子。

這會將存儲卸載到客戶端Cookie,效率更高。

+0

我給所有的回答者提供了一個+1的輸入。我喜歡你的建議,約翰。我會玩這個方法(或者相反)。謝謝! – BenM 2011-04-05 20:09:34

7

一個擁有所有user_id和post_ids的表是一個壞主意,因爲它以指數級增長。想象一下,如果您的論壇解決方案增長到一百萬個帖子和五萬個用戶。現在你有500億條記錄。這將是一個問題。

訣竅就是像上面說的那樣使用表格,但它只保存自登錄以來已閱讀的帖子,即發佈在上次登錄和此登錄之間發佈的帖子。

上次登錄之前發佈的所有帖子均視爲已讀。

IE,我上次登錄於2011年4月3日,然後我今天登錄。 2011年4月3日之前發佈的所有帖子都被視爲已閱讀(他們對我而言並不陌生)。在2011年4月3日到現在之間的所有帖子都是未讀的,除非在閱讀表中可以看到。每次登錄時,讀取的表格都會被刷新。

這樣,您的閱讀文章表不應超過每個成員幾百條記錄。

3

而不是每個職位*用戶都有一個新行,您可以在用戶表中包含一個字段,該字段包含用戶已讀過的post-ID的逗號分隔字符串。

顯然,用戶不需要知道2年前有未讀帖子,所以您只顯示過去24小時內發佈的帖子的「新帖子」,並且不在逗號分隔的字符串中。

你也可以用會話變量或cookie解決這個問題。

2

該方法爲每個forumID單獨存儲最近訪問的postID

它不像一個單獨跟蹤每篇文章的解決方案那麼細緻,但是它縮小了每個用戶需要存儲的數據量,並且仍然提供了一種體面的方式來跟蹤用戶的查看歷史記錄。

<?php 
    session_start(); 
    //error_reporting(E_ALL); 

    // debug: clear session 
    if (isset($_GET['reset'])) { unset($_SESSION['activity']); } 

    // sample data: db table with your forum ids 
    $forums = array(
     // forumID  forumTitle 
      '1'  => 'Public Chat', 
      '2'  => 'Member Area', 
      '3'  => 'Moderator Mayhem' 
    ); 

    // sample data: db table with your forum posts 
    $posts = array(
     // postID     forumID  postTitle 
      '12345' => array( 'fID'=>'1', 'title'=>'Hello World'), 
      '12346' => array( 'fID'=>'3', 'title'=>'I hate you all'), 
      '12347' => array( 'fID'=>'1', 'title'=>'Greetings!'), 
      '12348' => array( 'fID'=>'2', 'title'=>'Car thread'), 
      '12349' => array( 'fID'=>'1', 'title'=>'I like turtles!'), 
      '12350' => array( 'fID'=>'2', 'title'=>'Food thread'), 
      '12351' => array( 'fID'=>'3', 'title'=>'FR33 V1AGR4'), 
      '12352' => array( 'fID'=>'3', 'title'=>'CAPSLOCK IS AWESOME!!!!!!!!'), 
      '12353' => array( 'fID'=>'2', 'title'=>'Funny pictures thread'), 
    ); 

    // sample data: db table with the last read post from each forum 
    $userhist = array(
     // forumID  postID 
      '1'  => '12344', 
      '2'  => '12350', 
      '3'  => '12346' 
    ); 

    // reference for shorter code 
    $s = &$_SESSION['activity']; 

    // store user's history into session 
    if (!isset($s)) { $s = $userhist; } 

    // mark forum as read 
    if (isset($_GET['mark'])) { 
     $mid = (int)$_GET['mark']; 
     if (array_key_exists($mid, $forums)) { 
      // sets the last read post to the last entry in $posts 
      $s[$mid] = array_search(end($posts), $posts); 
     } 
     // mark all forums as read 
     elseif ($mid == 0) { 
      foreach ($forums as $fid=>$finfo) { 
       // sets the last read post to the last entry in $posts 
       $s[$fid] = array_search(end($posts), $posts); 
      } 
     } 
    } 

    // mark post as read 
    if (isset($_GET['post'])) { 
     $pid = (int)$_GET['post']; 
     if (array_key_exists($pid, $posts)) { 
      // update activity if $pid is newer 
      $hist = &$s[$posts[$pid]['fID']]; 
      if ($pid > $hist) { 
       $hist = $pid; 
      } 
     } 
    } 

    // link to mark all as read 
    echo '<p>[<a href="?mark=all">Read All</a>]</p>' . PHP_EOL; 

    // display forum/post info 
    foreach ($forums as $fid=>$finfo) { 
     echo '<p>Forum: ' . $finfo; 
     echo ' [<a href="?mark=' . $fid . '">Mark as Read</a>]<br>' . PHP_EOL; 
     foreach ($posts as $pid=>$pinfo) { 
      if ($pinfo['fID'] == $fid) { 
       echo '- Post: <a href="?post=' . $pid . '">' . $pid . '</a>'; 
       echo ' - ' . ($s[$fid] < $pid ? 'NEW' : 'old'); 
       echo ' - "' . $pinfo['title'] . '"<br>' . PHP_EOL; 
      } 
     } 
     echo '</p>' . PHP_EOL; 
    } 

    // debug: display session value and reset link 
    echo '<hr><pre>$_SESSION = '; print_r($_SESSION); echo '</pre>' . PHP_EOL; 
    echo '<hr>[<a href="?reset">Reset Session</a>]' . PHP_EOL; 
?> 

注:顯然這個例子是僅用於演示目的。處理實際數據庫時,可能需要更改某些結構和邏輯。

1

Phpbb2已經實現了這個相當簡單。它只顯示你自從你上次登錄以來的所有帖子。這樣您就不需要存儲關於用戶實際看到或閱讀的內容的任何信息。

0

有人沒有建議使用大數據來存儲這種信息,即NoSQL。它是專門用於處理這類數據的。

我使用MongoDB,但你可以找到一個NoSQL應用程序來滿足你的需求。 http://nosql.findthebest.com/

這將允許您將規模擴大到其他適用的用途,而不僅僅是你現在正在研究。 EG,論壇,帖子,門票,筆記,消息等

另一個建議是,你可以選擇將數據存儲爲「元數據」,類似於csv的建議,但給它一個更靈活和可存儲的結構,使用序列化來壓縮對象的數據以在運行時加載和反序列化。因此,像一個不會過期的與user_id相關聯的會話而不是session_id可以按需加載並分離出來,無論您喜歡。例如,當爲特定用戶加載論壇頁面時。

如:

(幹編碼的例子 - 調整到適合自己的模式)

<?php 
/** 
array(
    "form_id1" => array("post_id1", "post_id2",), 
    "form_id2" => array("post_id1", "post_id2",) 
); 
*/ 

$this->user->metadata = unserialize(file_get_contents('/metadata/forums/' . $this->user->id)); 

if(!isset($this->user->metadata[$this->forum->id])){ 
    $this->user->metadata[$this->forum-id] = array(); 
} 
if(!in_array($this->post->id, $this->user->metadata[$this->forum->id])){ 
    $this->user->metadata[$this->forum-id][] = $this->post->id; 
} 
file_put_contents('/metadata/forums/' . $this->user->id, serialize($this->metadata);); 

你可以換出file_x_contents你的RDBMS - 如:

<?php 
$getMetadata = "SELECT forums FROM user_metadata WHERE user_id = $this->user->id"; 
$dbrs = mysqli_query($getMetadata); 
$this->user->metadata = unserialize($dbrs['forums']); 
$dbrs->close(); 

$metadata = serialize($this->user->metadata); 
$saveMetadata = "UPDATE user_metadata SET forums = '$metadata' WHERE user_id = '$this->user->id'"; 
mysqli_query($saveMetadata); 

你也可以做其他事情,如通過正則表達式搜索,進一步隔離(主題,類別等),或將方法更改爲基於閱讀帖子的用戶在論壇中(forum-> post-> viewedby)而不是用戶閱讀的論壇帖子(user-> metadata-> forums)。特別是如果你已經有了一個「Total Views」的工作,但是這對於檢索已經被特定用戶讀取的帖子將更加困難,而對於其他方法則是相反的,或者甚至同時使用這兩種方法。