处理PHP / MySQL中的未读帖子

时间:2011-04-05 15:47:36

标签: php mysql phpbb forums read-unread

对于个人项目,我需要使用PHP和MySQL构建一个论坛。我不可能使用已经构建的论坛包(例如phpBB)。

我目前正在研究构建这样一个应用程序所需的逻辑,但这是漫长的一天,我正在努力处理用户未读帖子的概念。我的一个解决方案是拥有一个单独的表,它基本上包含所有帖子ID和用户ID,以确定它们是否已被阅读:

tbl_userReadPosts: user_id, post_id, read_timestamp

显然,如果用户的ID出现在此表中,我们就知道他们已经阅读了帖子。这很好,除非我们每天都有帖子(这比在提议的系统中更有可能),以及用户的thousdands。如果不是几个小时,这张表就会在几天之内变得很大。

另一个选项是将用户的上一个活动作为时间戳跟踪,然后检索上次活动更新后发布的所有帖子。这在理论上是有效的,但是假设用户正在写一篇非常长的帖子,同时几个成员也开始新的线程或回复其他线程中的帖子。当用户提交他的新帖子时,他的最后一个活动将被更新,因此与那些同时进行的活动不匹配。

有没有人有这方面的经验,你是如何解决它的?

我已经检查了phpBB,似乎系统为每个用户分配了一个自定义会话,并且在此基础上工作,但是关于如何处理未读帖子的文档很少。

一如既往地感激地收到了一些想法和意见。

6 个答案:

答案 0 :(得分:7)

包含所有user_ids和post_ids的表是一个坏主意,因为它呈指数级增长。想象一下,如果您的论坛解决方案增长到一百万个帖子和50,000个用户。现在你有500亿条记录。那将是一个问题。

诀窍是如你所说的那样使用一个表,但它只保存自此登录以来已经读过的帖子,这些帖子是在上次登录和登录之间发布的。

上次登录前发布的所有帖子都被视为已读。

IE,我上次登录于2011年4月3日,然后我今天登录。 2011年4月3日之前发布的所有帖子都被视为已阅读(对我来说不是新手)。 2011年4月3日至今之间的所有帖子都是未读的,除非在阅读表中看到它们。每次登录时都会刷新读取表。

这样,您的阅读帖子表每个成员的记录不应超过几百条。

答案 1 :(得分:6)

很抱歉快速回答,但我只有一秒钟。您肯定不希望将读取的信息存储在数据库中,正如您已经推断的那样,此表将变得巨大。

您已经建议的内容:存储用户上次活动,并结合存储他们在cookie中看到的内容的信息,以确定他们已经阅读过哪些帖子/帖子。

这会将存储卸载到客户端cookie,这样效率会更高。

答案 2 :(得分:3)

不是为每个post *用户创建一个新行,而是可以在user-table中有一个字段,该字段包含一个逗号分隔的字符串,其中包含用户已读取的post-ID。

显然,用户不需要知道2年前有未读帖子,因此您只显示过去24小时内发布的帖子的“新帖子”,而不是逗号分隔的字符串。

您也可以使用会话变量或Cookie解决此问题。

答案 3 :(得分:2)

此方法为每个postID分别存储最近访问过的forumID

它不像单独跟踪每个帖子的解决方案那样精细,但它会缩减每个用户需要存储的数据量,并且仍然提供了一种跟踪用户视图历史记录的正确方法。 / p>

<?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;
?>

注意:显然此示例仅用于演示目的。在处理实际数据库时,可能需要更改某些结构和逻辑。

答案 4 :(得分:1)

Phpbb2已经实现了这个相当简单。它只显示自您上次登录以来的所有帖子。这样,您无需存储有关用户实际看到或读取的内容的任何信息。

答案 5 :(得分: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 );

您还可以执行其他操作,例如通过regexp进行搜索,进一步隔离(主题,类别等),或者将方法更改为基于阅读论坛帖子的用户(forum-&gt; post-&gt;用户阅读(用户 - &gt;元数据 - &gt;论坛)而不是论坛帖子。特别是如果你已经有一个工作的“全部视图”,但是这对于检索特定用户已经/未被读取的帖子会更加困难,而对于其他方法则相反,或者甚至同时使用这两种方法