Обработка непрочитанных сообщений в PHP / MySQL
для личного проекта мне нужно построить форум с использованием PHP и MySQL. Для меня невозможно использовать уже построенный пакет форума (например, phpBB).
в настоящее время я работаю над логикой, необходимой для создания такого приложения, но это был длинный день, и я борюсь с концепцией обработки непрочитанных сообщений для пользователей. Одним из решений было иметь отдельную таблицу, которая по существу содержит все идентификаторы post и идентификаторы пользователей, чтобы определить, были ли они читайте:
tbl_userReadPosts: user_id, post_id, read_timestamp
очевидно, если идентификатор пользователя появляется в этой таблице, мы знаем, что они прочитали сообщение. Это здорово, за исключением того, что у нас есть тысячи сообщений в день (что более чем возможно в предлагаемой системе) и тысячи пользователей. Этот стол станет огромным в течение нескольких дней, если не часов.
другой вариант-отслеживать последнее действие пользователя в качестве метки времени, а затем извлекать все сообщения, сделанные после их последнего активность была обновлена. Это работает теоретически, но предположим, что пользователь пишет очень длинный пост, а тем временем несколько членов также запускают новые потоки или отвечают на сообщения в других потоках. Когда пользователь отправляет свой новый пост, его последняя деятельность будет обновлена и, таким образом, не будет соответствовать тем, которые были сделаны в то же время.
у кого-нибудь есть опыт в этом, и как вы справились с этим?
Я проверил в phpBB, и кажется, что система назначает пользовательский сеанс каждому пользователь, и работает на этой основе, но документация довольно разрежена относительно того, как это касается непрочитанных сообщений.
мысли и мнения с благодарностью, как всегда.
6 ответов
извините за быстрый ответ, но у меня только второй. Вы определенно не хотите хранить информацию о чтении в базе данных, как вы уже сделали вывод, эта таблица станет гигантской.
Что-то между тем, что вы уже предложили: сохраните последнюю активность пользователей и в сочетании с хранением информации о том, что они видели в файле cookie, чтобы определить, какие темы/сообщения они уже прочитали.
Это выгружает хранилище на клиентскую сторону cookie, что намного эффективнее.
таблица, содержащая все user_ids и post_ids, - плохая идея, поскольку она растет экспоненциально. Представьте, что ваше решение форума выросло до миллиона сообщений и 50 000 пользователей. Теперь у вас 50 миллиардов записей. Это будет проблемой.
трюк состоит в том, чтобы использовать таблицу, как вы сказали, но она содержит только сообщения, которые были прочитаны с момента этого входа, сообщений, которые были размещены между последним входом и этим входом.
все сообщения, сделанные до последнего входа в систему, считаются читать.
IE, я последний раз вошел в систему на 4/3/2011, а затем я войти сегодня. Все сообщения, сделанные до 4/3/2011, считаются прочитанными (они не новы для меня). Все сообщения между 4/3/2011 и сейчас, не читаются, если они не видны в таблице чтения. Таблица чтения сбрасывается при каждом входе в систему.
таким образом, ваша таблица чтения сообщений никогда не должна иметь более пары сотен записей для каждого члена.
вместо того, чтобы иметь новую строку для каждого пользователя post*, вы можете иметь поле в пользовательской таблице, содержащее строку, разделенную запятыми, с идентификаторами post, которые пользователь прочитал.
очевидно, пользователю не нужно знать, что есть непрочитанные сообщения от 2 лет назад, поэтому вы показываете только "новый пост" для сообщений, сделанных за последние 24 часа, и не находится в строке, разделенной запятыми.
вы также можете решить эту проблему с помощью переменной сеанса или файла cookie.
этот метод хранит самый последний доступ postID
отдельно для каждого forumID
.
Это не так мелкозернистой решение, которое отслеживает каждый пост отдельно, но это уменьшает объем данных, которые нужно хранить для каждого пользователя и обеспечивает достойный способ отследить пользователя, посмотреть историю.
<?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;
?>
Примечание: очевидно, что этот пример предназначен только для демонстрационных целей. Некоторые из структуры и логики, возможно, потребуется изменить при работе с фактической базой данных.
Phpbb2 реализовал это довольно просто. Он просто показывает вам все сообщения с момента вашего последнего входа в систему. Таким образом, вам не нужно хранить информацию о том, что пользователь действительно видел или читал.
что-то, что не было предложено, использовало большие данные для хранения такой информации, а именно 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 );
вы также можете делать другие вещи, такие как поиск через regexp, отделить его дальше (тема, категория и т. д.) или изменить метод, основанный на пользователях, которые читают сообщения на форуме (Форум->post->viewedby), а не сообщения форума, которые читает пользователь (пользователь->метаданные->форумы). Особенно, если у вас уже есть рабочий "общий вид", но это будет сложнее получить сообщения, которые были/не были прочитаны конкретным пользователем, в то время как обратное верно для другого метода или даже использовать оба метода в сочетании.