Обработка непрочитанных сообщений в PHP/MySQL
Для личного проекта мне нужно создать форум с использованием PHP и MySQL. Я не могу использовать уже построенный пакет форума (например, phpBB).
В настоящее время я работаю над логикой, необходимой для создания такого приложения, но это был долгий день, и я борюсь с концепцией обработки непрочитанных сообщений для пользователей. Одно из моих решений заключалось в том, чтобы иметь отдельную таблицу, в которой по существу хранятся все идентификаторы почтовых ящиков и идентификаторы пользователей, чтобы определить, были ли они прочитаны:
tbl_userReadPosts: user_id, post_id, read_timestamp
Очевидно, что если в этой таблице появляется идентификатор пользователя, мы знаем, что они прочитали сообщение. Это замечательно, если только у нас есть тысячи должностей в день (что более чем возможно в предлагаемой системе) и тысячи пользователей. Эта таблица станет огромной в течение нескольких дней, если не часов.
Другим вариантом будет отслеживание последнего действия пользователя как метки времени, а затем получение всех сообщений, сделанных после их последнего действия. Это работает теоретически, но скажем, что пользователь пишет очень длинный пост, а тем временем несколько участников также запускают новые потоки или отвечают на сообщения в других потоках. Когда пользователь отправляет свой новый пост, его последнее действие будет обновляться и, таким образом, не соответствовать тем, которые были сделаны за это время.
Есть ли у кого-нибудь опыт с этим, и как вы справились с этим?
Я проверил phpBB, и кажется, что система назначает пользовательский сеанс каждому пользователю и работает на этой основе, но документация довольно скудна относительно того, как это касается непрочитанных сообщений.
Мысли и мнения с благодарностью получили, как всегда.
Ответы
Ответ 1
Извините за быстрый ответ, но у меня есть только секунда. Вы определенно не хотите хранить прочитанную информацию в базе данных, как вы уже определили, эта таблица станет гигантской.
Что-то между тем, что вы уже предложили: сохранить последние действия пользователей и в совокупности с хранением информации о том, что они видели в файле cookie, чтобы определить, какие потоки/сообщения, которые они уже прочитали.
Это выгружает хранилище в файл cookie на стороне клиента, что намного эффективнее.
Ответ 2
Таблица, содержащая все user_ids и post_ids, является плохой идеей, поскольку она растет экспоненциально. Представьте, что ваше решение для форума выросло до миллиона должностей и 50 000 пользователей. Теперь у вас есть 50 миллиардов записей. Это будет проблемой.
Хитрость заключается в том, чтобы использовать таблицу, как вы сказали, но она содержит только записи, которые были прочитаны с момента входа в систему, сообщений, которые были размещены между последней регистрацией и этим входом.
Все сообщения, сделанные до последнего входа, считаются прочитанными.
IE, я последний раз заходил в систему 4/3/2011, а затем я вхожу в систему сегодня. Все сообщения, сделанные до 3/3/2011, считаются прочитанными (они не новы для меня). Все сообщения между 4/3/2011 и сейчас не прочитаны, если они не видны в таблице чтения. Таблица чтения очищается каждый раз, когда я вхожу в систему.
Таким образом, ваша таблица прочитанных сообщений никогда не должна содержать более нескольких сотен записей для каждого члена.
Ответ 3
Вместо того, чтобы иметь новую строку для каждого пользователя post *, вы можете иметь поле в таблице пользователя, содержащее строку с разделителями-запятыми с сообщениями post-ID, которые пользователь прочитал.
Очевидно, что пользователю не нужно знать, что есть непрочитанные сообщения от 2 лет назад, поэтому вы показываете "Новое сообщение" для сообщений, сделанных за последние 24 часа, и не находятся в разделителе, разделенном запятой.
Вы также можете решить эту проблему с помощью переменной сеанса или файла cookie.
Ответ 4
Этот метод хранит последний доступный 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 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;
?>
Примечание.. Очевидно, что этот пример предназначен только для демонстрационных целей. Некоторая структура и логика могут потребоваться изменить при работе с реальной базой данных.
Ответ 5
Phpbb2 реализовал это довольно просто. Он просто показывает вам все сообщения после вашего последнего входа. Таким образом вам не нужно хранить какую-либо информацию о том, что пользователь действительно видел или читал.
Ответ 6
Что-то, что не было предложено, было использование Big Data для хранения такой информации, а именно 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); );
Вы можете поменять файловую систему с файлами____контента на свои РСУБД - например:
<?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- > playbackby) вместо сообщений на форуме пользователь читает (user- > metadata- > forums). Особенно, если у вас уже есть рабочий "Total Views", но это будет сложнее получить сообщения, которые/не были прочитаны конкретным пользователем, в то время как обратное истинно с другим методом или даже использует оба метода в сочетании.