В этой статье я расскажу про систему диалогов на php, которую я создал на одном из сервисов компании, где я работаю.
Изначально, хотелось получить систему, которая бы повторяла функционал диалоговой системы контакта. Требования были следующие: скрипт не должен требовать установки на сервер дополнительных средств (поддержка long-pool запросов, phpdemon, поддержки websocket и т.п), позволять создавать диалог неограниченного числа пользователей и работать на обычном ajax-post сообщении с сайтом.
Приступим
Для начала создадим структуру БД
Вот дамп
Когда один пользователь хочет написать сообщение другому, скрипт ищет подходящий диалог. Диалог, является подходящим, если в нем участвую те же лица. Т.е. если мы раньше писали этому человеку, и никого больше не подключали к этому диалогу, то он подходит. Если же подходящего диалога нет, то создаем новый. Диалог и пользователь связаны таблицей user_to_dialog. Когда пользователь посылает сообщение, оно записывается в табличку message. А информация о том, кому оно предназначено лежит в табличке message_to_user. По сути, эта таблица избыточна, так как у нас уже есть информация кому показывать сообщение исходя из данных user_to_dialog. Но мне было так удобно. Можете это изменить в своем форке.
Перейдем к коду.
Структура класса dialog
<?php class dialog{ public $utime = 0; // время по часовому поясу пользователя в UNIXTIME public $userid = 0;// id пользователя public $err = ''; public $hash = ''; // hash диалога public $id = ''; private $user_id_field = 'IDClient'; // название поля с id пользователя, необходимо для подключения //к Вашему скрипту с уже созданной структурой пользователей private function create(){} // создание нового диалога public function find_suit_dialog($userlist = array()){} // поиск подходящего диалога function get_new_messages_cnt(){} // количество новых сообщений для пользователя function get_users_from_dialog(){} // список пользователей принадлежащих диалогу function get_user_dialogs( $start=0,$cnt = 10 ){} // вывод диалогов пользователя function get_messages_from_dialog($new=false,$reset_status = true){} // вывод сообщений из диалога function remove_users_from_dialog( $userlist = array() ){} function add_users_to_dialog( $userlist = array() ){} // добавить пользователя в диалог function send($msg,$intro = false){} // посылка сообщения в диалог function send_many_users( $msg,$userlist,$intro = false ){} // посылка сообщения нескольким пользователям public function delete_message( $messageid ){} // удаление сообщения }
поле user_id_field нужно для того, чтобы пристроить скрипт к Вашей бд, в которой возможно уже есть система пользователей.
Полная реализация всех методов
Как использовать
Создаем экземпляр класса dialog
<?php $dialog = new dialog($db,time(),$userid,isset($_REQUEST['hash'])?$_REQUEST['hash']:'');
где $db инициализированный и подключенный экземпляр класса db, а $userid это id текущего пользователя.
Получить все диалоги пользователя
$dialogs = $xddialog->get_user_dialogs(); $out = ''; foreach($dialogs as $dg) $out.=' <div class="dialog '.(!$dg['msg_status']?'newmsg':'').'"> <div class="float_left"> <span class="nikname"><a href="#" id="user_'.$dg['senderid'].'">'.$dg['sender_name'].'</a></span> <span class="message_time">'.date('H:i:s d/m/Y',$dg['public']).'</span> <div>'.$dg['message'].'</div> </div> <div class="float_right"> <input class="btn gotodialog" id="dialog_'.$dg['hash'].'" value="ПЕРЕЙТИ К ДИАЛОГУ"/> </div> <div class="clearex"></div> </div>';
получить все сообщения из текущего дилога
$cnt = $xddialog->get_new_messages_cnt(); $messages = (!$cnt)?array():$xddialog->get_messages_from_dialog(); $out = ''; foreach($messages as $msg) $out.=' <div> <div class="float_left"> <span class="nikname"><a href="#" id="user_'.$msg['senderid'].'">'.$msg['sender_name'].'</a></span> <span class="message_time">'.date('H:i:s d/m/Y',$msg['public']).'</span> <div>'.$msg['message'].'</div> </div> <div class="clearex"></div> </div>';
получить только новые сообщения
$cnt = $xddialog->get_new_messages_cnt(); $messages = (!$cnt)?array():$xddialog->get_messages_from_dialog(true);
Отправка сообщения в диалог
$xddialog->send($_POST['message']);
Поиск подходящего диалога и добавление туда всех пользователей
$xddialog->find_suit_dialog(array($userid1,$userid2,$userid3,)); $xddialog->add_users_to_dialog(array($userid,$userid1,$userid2,$userid3,));
где $userid это id текущего пользователя, а $userid,$userid1,$userid2,$userid3, id пользователей с которыми будет вестись диалог
Протестировать систему можно скачав пример с гитхаба, либо на моем сайте в demo.
Разумеется, это мой очередной велосипед.
Комментарии
чтобы получить все новые сообщения
Например:
a1: bla bla
a2: bla bla
a3: bla bla
И так далее!
А вообще это же всего лишь пример)
$db->update('user',array('name'=>'user'.$id,'id ='.$id); // заменяем имя на user{номер}
Спасибо заранее!
В итоге при отправке сообщений одному и тому же пользователю в БД каждый раз создаётся новый диалог?!
А всё из-за логики, а вернее всякое отсутствие логики.
Вот, например, запрос на поиск подходящего диалога (функция public function find_suit_dialog($userlist = array()) ...) выглядит следующим образом:
select count(utd.userid) as cnt,utd.dialogid,dg.hash from #_user_to_dialog as utd left join #_dialog as dg on dg.id=utd.dialogid
where utd.dialogid in (select dialogid from #_user_to_dialog where userid='.intval($userlist[0]).' and dialogid in(select dialogid from #_user_to_dialog where userid='.$this->userid.')) group by utd.dialogid;
Что переводится дословно по-русски: выбрать значения из таблицы то кому адресовано сообщение ГДЕ идентификатор диалога равен выборке идентификатора диалога из этой же таблицы. Это что за идиотизм? Выбрать самого себя что-ли. Или выбрать той записи, которой не существует?!
find_suit_dialog - метод который ищет подходящий диалог для юзера, если вы раньше писали чуваку, то берется он, но если в вашем диалоге учавствовал третий юзер, то этот диалог не подходит и создается новый.
говоря по русски: берем количество юзеров в диалоге, номер диалога и его хеш для каждого диалога где был задействован сам юзер и при этом был задействован его собеседник.
Все вроде верно, предложите свое написание подобного запроса, я внесу правки в исходники. Проект таки опенсорс и работает уже в куче проектов. Разумеется доработанный, когда писал эту статью он был сырой.
Там логика такая же, но просто используется не Ajax для послания запросов к серверу, а используется серверный JS, например, Node.js. Если посылать так запросы каждую секунду серверу на то, нет ли там чего-нибудь новенького, то сервер упадёт при большом количестве одновременно подключенных пользователей.
Это я про ваш пример на гитхабе!
Например, первое: строка 154 у Вас
$this->db->getRows('select userid from #_user_to_dialog where dialogid='.$this->id); // под вопросом, нужно ли добавлять отправителю сообщение
ответ: нет, не добавлять! Зачем добавлять информацию для отправителя? Чтобы при подсчёте новых сообщений для него же самого его же сообщение отображалось и считалось, как новое!
2 пример: строка 67 у Вас
if( !$this->id and (!$this->hash or !($this->id = $this->db->exists('dialog',$this->db->escape($this ->hash),'hash','','id'))) )
Для чего результат работы функции сохранять в атрибут класса?
3 пример: самое главное!!!
при поиске диалога 69 строка
$dlgs = $this->db->getRows('select count(utd.userid) as cnt,utd.dialogid,dg.hash from #_user_to_dialog as utd left join #_dialog as dg on dg.id=utd.dialogid where utd.dialogid in (select dialogid from #_user_to_dialog where userid='.intval($userlist[0]).' and dialogid in(select dialogid from #_user_to_dialog where userid='.$this->userid.')) group by utd.dialogid;');
диалог найден если id первого пользователя из массива userlist существует в таблице и при этом в этой же таблице существует id пользователя который отправляет сообщение.
Но!!! Что если, пользователь с id 1 первый раз отправил сообщение пользователям с id 2,3,4, создался диалог с id, скажем 8.В следующий раз тот же пользователь с id номер 1 отправил новое сообщение, только уже пользователям с id 2,5,7,10. Должен создаться новый диалог, так как списки совершенно разные! Но не тут то было!!! Найдя в первой ячейке userlist id пользователя под номером, 2 скрипт посчитает, что диалог есть, и пользователей 2,5,7,10 добавит в диалог 8.
Это неправильно, неккоректно и нарушает конфиденциальность переписки. Посути пользователи с id 5,7,10 смогут читать переписку пользователей с id 2,3,4 в диалоге с id 8.
Кому нужна помощь обращайтесь: http://vk.com/id60635284
Можете сходу назвать технологию которая бы позволила завести этот чат на подавляющем количестве серверов?
А про код, так он писался сугубо в академических целях и применялся всего один раз на практике в развивающемся проекте, в котором был с успехом заменен когда пришел народ и соответственно деньги.
Я его не дорабатываю. Все лежит на гитхабе, почему бы вам не прислать мне пару пул реквестов к скрипту и сделать его лучше. А не голословно кричать что скрипт плох
Самый дешевый, и реально работающий во всех браузерах это конечно longpool
Только вот никак не могу его приделать к блогу на wordpress. Может кто подскажет что и где надо переписать, что бы был возможен обмен сообщениями между зарегистрированными пользователями? Только за ранее предупреждаю в php я ни черта не понимаю так что если возможно, то конкретней)
С наступившим!
строка 31 private $_logid = 0; // если указать 1 то будет лошировать всеоперации
Free URL shortener to create the perfect short URLs for your business.
Reading this post reminds me of my previous room mate!
He always kept chatting about this. I will forward this write-up to him.
Pretty sure he will have a good read. Thanks for sharing!
Only if you really think about casino online or maybe casino online in our
web portal yo can learn more information about casino uk here https://indaxis.com/
We have more detailed information about best casino
and also about casino games.
If you need more questions about indaxis you can find some examples please go
to our internet portal and find more and detailed info.
Please visit our website about honest casino or please click https://indaxis.com/
Our site have tag's: Online casino, best casino, online casino
And some other and guaranteed information.
Thanks for your attention.
Have a good day.
Thanks
The sketch is attractive, your authored subject matter stylish.
nonetheless, you command get got an edginess over that you wish be delivering
the following. unwell unquestionably come further formerly
again since exactly the same nearly a lot often inside case you shield this hike.
and actual effort to make a superb article… but what can I say… I procrastinate a
whole lot and don't seem to get anything done.
RSS лента комментариев этой записи