Система личных сообщений С потоковой передачей / Ответами

StackOverflow https://stackoverflow.com/questions/1173157

  •  19-09-2019
  •  | 
  •  

Вопрос

В настоящее время я работаю над созданием системы личных сообщений (PHP / MySQL), в которой пользователи могут отправлять сообщения нескольким получателям одновременно, а затем эти пользователи могут решить ответить.

Вот с чем я сейчас работаю:

tbl_pm tbl:
id
date_sent
title
content
status ENUM ('unread', 'read') DEFAULT 'unread'

tblpm_info tbl:
id
message_id
sender_id
receiver_id

Однако мне нужна некоторая помощь в определении логики по двум вещам:

1) При создании нового сообщения должен ли "идентификатор" автоматически увеличиваться?Если для столбца 'id' установлено значение автоматического увеличения в обеих таблицах, как бы мне установить столбец "message_id" в 'таблице отношений'?

Например, когда создается новое сообщение, мой оператор MySQL выглядит следующим образом:

<?php
mysql_query("INSERT INTO `tblpm` (title, content, sender_id, date_sent) VALUES ('$subject', '$message', '$sender', NOW())" );

В том же заявлении, как бы мне ввести 'автоматически увеличиваемое' значение tblpm в поле tblpm_info "message_id"?

2) Как должен выглядеть мой оператор MySQL, когда пользователи отвечают на сообщения?

Возможно, я делаю это более сложным, чем мне нужно.Любая помощь очень ценится!

Это было полезно?

Решение

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

   mysql_query("INSERT INTO table1 ...")
   $foreign_key=mysql_insert_id(); //this gives you the last auto-increment for YOUR connection

или, но только если вы абсолютно уверены, что никто другой тем временем не записывает данные в таблицу и не контролирует транзакцию, после того как insert выполнит:

$foreign_key=mysql_query("SELECT LAST_INSERT_ID()")
INSERT INTO table2 message_id=$foreign_key

или, не перенося FK на php, все в одной транзакции (я также советую обернуть SQL как транзакцию) чем-то вроде:

"INSERT INTO table1...; INSERT INTO table2 (message_id,...) VALUES(LAST_INSERT_ID(),...)"

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

2) У этого может быть так много подходов, в зависимости от того, нужно ли вам отвечать и всем получателям (напримерконференция), ответьте в виде темы / форума, может ли клиентская сторона сохранить последнее полученное сообщение / идентификатор (напримерв файле cookie;также влияет на то, действительно ли вам нужно поле "прочитать").

Подход "приватного чата" является самым простым, тогда вам, вероятно, лучше либо сохранить сообщение в одной таблице, а отношения "от-до" - в другой (и использовать для них объединения), либо просто повторно заполнить сообщение в одной таблице (поскольку хранилище в наши дни дешево).Таким образом, упрощенная модель представляла бы собой одну таблицу:

table: message_body,from,to
$recepients=array(1,2,3..);
foreach($recepients as $recepient)
 mysql_query("INSERT INTO table (...,message_body,from,to) VALUES(...,$from,$recepient)");

(дублируйте сообщение и т.д., меняется только получатель)

или

message_table: id,when,message_body
to-from-table: id,msg_id,from,to

$recepients=array(1,2,3,...);
mysql_insert("INSERT INTO message_table (when,message_body) VALUES(NOW(),$body)");
$msg_id=mysql_insert_id();
foreach($recepients as $recepient)
 mysql_query("INSERT INTO to-from-table (msg_id,from,to) VALUES($msg_id,$from,$recepient)");

(сообщение вставлено один раз, сохраните отношения и FK для всех получателей)

Затем каждый клиент сохраняет последнее полученное им message_id (по умолчанию 0) и предполагает, что все предыдущие сообщения уже прочитаны):

"SELECT * FROM message WHERE from=$user_id OR to=$user_id WHERE $msg_id>$last_msg_id"

или мы просто принимаем к сведению время последнего ввода от пользователя и запрашиваем любые новые сообщения с этого момента:

"SELECT * FROM message WHERE from=$user_id OR to=$user_id WHERE when>='".date('Y-m-d H:i:s',$last_input_time)."' "

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

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

id autoincrement (again, no need for a separate message id)
your usual: sent_at, title (if you need one), content
sender (int)
recepients (I'd go with varchar or shorter versions of TEXT; whereas TEXT or BLOB gives you unlimited number of users but may have impact on performance)
readers (same as above)

Секрет для поля recepients / readers заключается в том, чтобы заполнить их список идентификаторов, разделенных запятыми, и снова заключить его в запятые (я расскажу о том, почему позже).

Таким образом, вам пришлось бы снова собирать идентификаторы получателей в массив, например$recepients=массив (2,3,5) и измените свою вставку:

"INSERT INTO table (sent_at,title,content,sender,recepients) VALUES(NOW(),'$title','$content',$sender_id,',".implode(',', $recepients).",')"

вы получаете строки таблицы, подобные
...отправитель | получатели
...1 | ,2, //однопользовательское сообщение
...1 | ,3,5, //многопользовательское сообщение

чтобы выбрать все сообщения для пользователя с идентификатором $user_id=2, вы идете с

SELECT * FROM table WHERE sender=$user_id OR INSTR(recepients, ',$user_id,')

Ранее мы упаковали сжатый список получателей, например'5,2,3' становится ',5, 2,3', и INSTR здесь сообщает, содержится ли ',2,' где-то в качестве подстроки - поскольку поиск только '2',',2' или '2,' может привести к ложным срабатываниям, например'234,56','1**,234','9,452,**89' соответственно - вот почему нам пришлось завершить список в первую очередь.

Когда пользователь читает / получает свое сообщение, вы добавляете его идентификатор в список читателей следующим образом:

UPDATE table SET readers=CONCAT(',',TRIM(TRAILING ',' FROM readers),',$user_id,') WHERE id=${initial message_id here}

что приводит к:

...отправитель | получатели | читатели
...          1 | ,2,              | ,2,
...          1 | ,3,5,           | ,3,5,2,

Или теперь мы можем изменить первоначальный запрос, добавив столбец "is_read", чтобы указать, читал ли пользователь ранее сообщение или нет:

SELECT * FROM table WHERE INSTR(recepients, ',$user_id,'),INSTR(readers, ',$user_id,') AS is_read

соберите идентификаторы сообщений из результата и обновите поля "получатели" одним движением

"UPDATE table SET readers=CONCAT(',',TRIM(TRAILING ',' FROM readers),',$user_id,') WHERE id IN (".implode(',' ,$received_msg_ids).")"

Другие советы

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

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

CREATE TABLE `pm_data` (
  `id` smallint(5) unsigned NOT NULL auto_increment,
  `date_sent` timestamp NOT NULL,
  `title` varchar(255)
  `sender_id` smallint(5) unsigned,
  `parent_message_id` smallint(5) unsigned,
  `content` text,
  PRIMARY_KEY (`id`)
);
CREATE TABLE `pm_info` (
  `id` smallint(5) unsigned NOT NULL auto_increment,
  `pm_id` smallint(5) unsigned NOT NULL,
  `recipient_id` smallint(5) unsigned,
  `read` tinyint(1) unsigned default 0,
  `read_date` timestamp,
  PRIMARY_KEY (`id`)
);

Создайте эти две таблицы и обратите внимание, что для обеих из них установлено значение 'id' с автоматическим увеличением, но таблица 'info' также имеет pm_id поле, которое будет содержать идентификационный номер строки "данные", на которую оно ссылается, чтобы вы были уверены, что каждая строка имеет первичный ключ в таблице "информация", который вы можете использовать для выбора.

Если вы хотите настроить настоящую реляционную базу данных с использованием MySQL, убедитесь, что ваш движок настроен на InnoDB, который позволяет устанавливать связи между таблицами, поэтому (например), если вы попытаетесь вставить что-либо в таблицу "info", которая ссылается на pm_id если этого не существует в таблице "данные", вставка завершится ошибкой.

Как только вы выберете структуру базы данных, ваш PHP-код будет выглядеть примерно так:

 <?php
 // Store these in variables such that if they change, you don't need to edit all your queries
 $data_table = 'data_table';
 $info_table = 'info_table';
 mysql_query("INSERT INTO `$data_table` (title, content, sender_id, date_sent) VALUES ('$subject', '$message', '$sender', NOW())" );
 $pmid = mysql_insert_id(); // Get the inserted ID
 foreach ($recipent_list as $recipient) {
      mysql_query("INSERT INTO `$info_table` (pm_id, recipient_id) VALUES ('$pmid', '$recipient')" );
 }

ДА.Вы бы определенно установили auto_increment для обоих идентификаторов.

Чтобы установить message_id, вы должны программно вставить его туда.

Ваш запрос будет выглядеть следующим образом:

mysql_query("INSERT INTO `tblpm` (title, content, sender_id, date_sent) VALUES ('$subject', '$message', '$sender', NOW())" );

Обратите внимание, это одно и то же!Если для идентификатора установлено значение auto_increment, это сделает все волшебство за вас.

В простых вызовах PHP / Mysql, mysql_insert_id() возвращает автоматически увеличенное значение из предыдущей операции ВСТАВКИ

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

Лично в вашем случае (при условии, что пример не был упрощен, и я не вижу большего) Я бы сохранил данные из обеих этих таблиц в одной таблице, поскольку они, по-видимому, напрямую связаны:

tbl_pm tbl:

message_id - идентификатор сообщения

дата отправки

Название

Содержание

перечисление статуса ('непрочитанный', 'прочитанный') ПО УМОЛЧАНИЮ 'непрочитанный'

идентификатор отправителя

идентификатор получателя

Таким образом, вы получаете что-то вроде описанного выше, на самом деле нет никакой необходимости в объединении, поскольку отношение всегда будет 1 к 1?У вас есть прочитанное / непрочитанное в таблице tbl_pm, которое, несомненно, будет изменено для каждого получателя, что означает, что вам все равно придется хранить копию сообщения для каждого получателя.возможно, предполагается, что staus должен быть в информационной таблице tbl_pm.

Если вы хотите вставить в обе таблицы, попробуйте использовать last_insert_id() в запросе или mysql_insert_id(), как объяснено выше, из php.

Я бы, вероятно, сделал что-то похожее на то, что рекомендовал Гэвин, но если вы хотите потоковые сообщения, вам придется добавить еще один ключ, например, этот:

private_messages
- title (text)
- date (timestamp)
- content (text)
- status (enum)
- sender_id (int)
- receiver_id (int)
- parent_message_id (int)

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

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top