Обработка непрочитанных сообщений в PHP / MySQL - PullRequest
16 голосов
/ 05 апреля 2011

Для личного проекта мне нужно создать форум с использованием PHP и MySQL. Я не могу использовать уже собранный форумный пакет (такой как phpBB).

В настоящее время я работаю над логикой, необходимой для создания такого приложения, но это был долгий день, и я борюсь с концепцией обработки непрочитанных сообщений для пользователей. Одно из решений, которое у меня было, состояло в том, чтобы иметь отдельную таблицу, которая по существу содержит все идентификаторы записей и идентификаторы пользователей, чтобы определить, были ли они прочитаны:

tbl_userReadPosts: user_id, post_id, read_timestamp

Очевидно, что если в этой таблице указан идентификатор пользователя, мы знаем, что он прочитал пост. Это здорово, за исключением случаев, когда у нас есть тысячи постов в день (что более чем возможно в предлагаемой системе) и тысячи пользователей. Эта таблица станет огромной за считанные дни, если не часы.

Другим вариантом может быть отслеживание последней активности пользователя в качестве временной отметки, а затем получение всех сообщений, сделанных после обновления их последней активности. Теоретически это работает, но, скажем, пользователь пишет очень длинный пост, а тем временем несколько участников также создают новые темы или отвечают на сообщения в других темах. Когда пользователь отправляет свой новый пост, его последнее действие будет обновлено и, следовательно, не будет соответствовать тем, которые были сделаны за это время.

У кого-нибудь есть опыт с этим, и как вы справились с этим?

Я зарегистрировался в phpBB и похоже, что система назначает пользовательский сеанс каждому пользователю и работает на этой основе, но документация довольно скудна относительно того, как это работает с непрочитанными публикациями.

Мысли и мнения с благодарностью приняты, как всегда.

Ответы [ 6 ]

7 голосов
/ 05 апреля 2011

Таблица, содержащая все user_ids и post_ids - плохая идея, так как она растет в геометрической прогрессии.Представьте, что ваш форум вырос до миллиона постов и 50000 пользователей.Теперь у вас есть 50 миллиардов записей.Это будет проблемой.

Хитрость заключается в том, чтобы использовать таблицу, как вы сказали, но она содержит только сообщения, которые были прочитаны с момента этого входа, из сообщений, которые были размещены между последним входом и этим входом..

Все сообщения, сделанные до последнего входа в систему, считаются прочитанными.

То есть, я последний раз входил в систему 3 апреля 2011 года, а затем я вхожу сегодня.Все посты, сделанные до 03.04.2011, считаются прочитанными (они не новы для меня).Все посты с 03.04.2011 по настоящее время являются непрочитанными, если они не отображаются в таблице чтения.Таблица чтения сбрасывается каждый раз, когда я вхожу в систему.

Таким образом, в таблице чтения сообщений не должно быть более двухсот записей для каждого участника.

6 голосов
/ 05 апреля 2011

Извините за быстрый ответ, но у меня есть только секунда. Вы определенно не хотите хранить прочитанную информацию в базе данных, как вы уже поняли, эта таблица станет гигантской.

Что-то среднее между тем, что вы уже предложили: сохранить последние действия пользователей и вместе с сохранением информации о том, что они видели в куки, определить, какие темы / сообщения они уже прочитали.

Это выгружает хранилище в файл cookie на стороне клиента, что гораздо эффективнее.

3 голосов
/ 05 апреля 2011

Вместо того, чтобы иметь новую строку для каждого пользователя *, вы можете иметь поле в пользовательской таблице, которое содержит разделенную запятыми строку с идентификаторами записей, которые прочитал пользователь.

Очевидно, что пользователю не нужно знать, что есть непрочитанные сообщения от 2-х лет назад, поэтому вы отображаете «Новое сообщение» только для сообщений, созданных за последние 24 часа и не указанных в строке через запятую.

Вы также можете решить эту проблему с помощью переменной сеанса или файла cookie.

2 голосов
/ 06 апреля 2011

Этот метод хранит последние 100 * *, к которым недавно обращались, отдельно для каждого forumID.

. Он не настолько детализирован, как решение, которое отслеживает каждое сообщение отдельно, но сокращаетданные, которые необходимо хранить для каждого пользователя, и при этом обеспечивают достойный способ отслеживать историю просмотров пользователя.

<code><?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 '
'.PHP_EOL;echo '
[ Сброс сеанса ]'.PHP_EOL;?>

Примечание: Очевидно, этот пример только для демонстрационных целей.Возможно, потребуется изменить некоторые структуру и логику при работе с реальной базой данных.

1 голос
/ 07 апреля 2011

Phpbb2 реализовал это довольно просто.Это просто показывает вам все сообщения с момента вашего последнего входа в систему.Таким образом, вам не нужно хранить информацию о том, что пользователь на самом деле видел или читал.

0 голосов
/ 12 июля 2013

То, что не было предложено, это использование больших данных для хранения такого рода информации, а именно NoSQL.Это эффективно сделано специально для обработки данных такого типа.

Я использую MongoDB, но вы можете найти приложение NoSQL, соответствующее вашим потребностям.http://nosql.findthebest.com/

Это позволит вам масштабироваться для других применений, а не только для того, над чем вы работаете сейчас.Например, форумы, сообщения, билеты, заметки, сообщения и т. Д.

Другое предложение заключается в том, что вы можете альтернативно хранить данные в виде «метаданных», аналогично предложению 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 с помощью своей РСУБД - например:

<?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-> просматриваемые) вместо сообщений на форуме, которые пользователь читает (user-> metadata-> forums).Особенно, если у вас уже есть работающее «Общее количество просмотров», но это будет сложнее для получения сообщений, которые были / не были прочитаны конкретным пользователем, в то время как обратное верно для другого метода, или даже используйте оба метода в сочетании.

...