Shoutbox w php, mysql i jQuery
Tutorial opisuje tworzenie od podstaw prostego shoutboxa. Najpierw zbudujemy bazę danych. Nazwiemy ją się chat_db. Następnie stwórzmy w niej tabelę wiadomości. Poniższy rysunek pokazuje strukturę tej tabeli w phpmyadminie. Jak widać tabela ma cztery pola: id, autor, treść i czas (oznacza datę i godzinę dodania wiadomości). Stwórzmy teraz plik index.php (tu będzie się wyświetlał nasz shoutbox).
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>Chat</title> <script type="text/javascript" src="jquery-1.4.2.min.js"></script> <script type="text/javascript"> // (js) tu będą skrypty js </script> <style type="text/css"> /*podstawowe style określające wygląd shoutboxa*/ body {font: 11px Arial, Helvetica, sans-serif; } #chat_area { width: 400px; height: 300px; background: #eee; border: 1px solid #9F9F9F; border-width: 1px 1px 0 1px; } #chat_area ul { margin: 0; padding: 0; } #chat_area ul li{ border-bottom: 1px solid #9F9F9F; list-style-type: none; padding: 4px; font: 11px Arial, Helvetica, sans-serif; } .formularz{ background: #eee; width: 380px; border: 1px solid #9F9F9F; border-width: 0 1px 1px 1px; padding: 10px; } .formularz input, .formularz textarea{ font: 11px Arial, Helvetica, sans-serif; border: 1px solid #9F9F9F; } </style> </head> <body> <div id="chat_area"> <ul> <?php //kod php wyświetlający shoutbox ?> </ul> </div> <form action="" method="post" class="formularz"> <div><div>Imię:</div> <input type="text" name="imie" size="20"/></div> <p><div>Treść:</div> <textarea cols="40" rows="5" name="tresc"></textarea></p> <p><input type="submit" value="Wyślij" /></p> <input type="hidden" name="chat" value="1" /> </form> </body> </html>W sekcji head wpisujemy linię dodającą bibliotekę jQuery oraz kolejny znacznik script, w którym będziemy pisać skrypty javascript. Następnie trochę stylów css, których nie będę tu szerzej omawiać. Nasz shoutbox umieścimy w bloku o id chat_area. Wpisy na shoutboxie będą listą nieuporządkowaną. Na końcu strony wyświetlamy formularz dodawania nowej wiadomości. W miejscu na kod php, które zostawiliśmy powyżej połączmy się z bazą danych i wypiszmy 10 ostatnich wpisów.
$dbhost = "localhost"; $dbuser = "user"; $dbpass = "pass"; $dbname = "chat_db"; $wyswietlane = 10; $dbconn = @mysql_connect($dbhost,$dbuser,$dbpass); if($dbconn) { mysql_select_db($dbname,$dbconn); mysql_query("SET NAMES utf8"); $chat_items_num = mysql_query("SELECT COUNT(*) FROM wiadomosci"); $chat_items_num = mysql_fetch_row($chat_items_num); $start = $chat_items_num[0] - 10; $chat_items = mysql_query("SELECT * FROM wiadomosci ORDER BY id LIMIT $start, $wyswietlane"); while($wiadomosc = mysql_fetch_assoc($chat_items)) { echo '<li>'; echo date("d.m.Y H:i",strtotime($wiadomosc['czas'])).' <strong>'.htmlspecialchars($wiadomosc['autor']).'</strong> '.htmlspecialchars($wiadomosc['tresc']); echo '</li>'; } }Najpierw łączymy się z bazą poleceniem mysql_connect, wybieramy bazę i obliczamy ilość wszystkich wiadomości ($chat_items_num). Ponieważ chcemy wypisać 10 ostatnich wpisów w kolejnym zapytaniu stosujemy klauzule ORDER BY oraz LIMIT. Klauzuli LIMIT przekazujemy jako start liczbę wszystkich wpisów pomniejszoną o 10, a jako liczbę do wyświetlenia oczywiście 10. Po pobraniu interesujących nas wiadomości przechodzimy pętlą przez znalezione wpisy i wyświetlamy je jako elementy listy. Należy zwrócić uwagę na wykorzystanie funkcji htmlspecialchars() na wyświetlanych danych. Będziemy przecież pobierać dane od użytkownika i musimy je filtrować.
Plik przetwarząjący dane ajaxowo nazwijmy process_ajax.php. Będzie on odpowiedzialny za dynamiczne dołączenie nowej wiadomości. Fragment pliku widzimy na listingu poniżej.
header("Cache-Control: no-cache"); date_default_timezone_set('Europe/Warsaw'); $dbhost = "localhost"; $dbuser = "user"; $dbpass = "pass"; $dbname = "chat_db"; $wyswietlane = 10; $dbconn = @mysql_connect($dbhost,$dbuser,$dbpass); if($dbconn) { mysql_select_db($dbname,$dbconn); mysql_query("SET NAMES utf8"); if(isset($_POST['chat']) && $_POST['chat']==1) { // (1) tutaj dodamy przetwarzanie po wysłaniu przez nas nowej wiadomości } else { // (2) tutaj przetwarzanie jeśli nie wysyłamy nowej wiadomości } }Na początku łączymy się z bazą, następnie w zależności czy wysłaliśmy wiadomość wykonuje się odpowiedni kod. W praktyce będzie to wyglądało następująco: kod jQuery (napiszemy go za chwilę) odpalony dla zdarzenia wysłania formularza będzie uruchamiał pierwszą część if-a, natomiast kod uruchamiany co 5 sekund drugą. Będzie sprawdzał czy dodano jakieś nowe wiadomości i jeśli tak automatycznie je wyświetli w shoutboxie. W miejsce (1) wstawiamy następujący kod.
$nick = mysql_real_escape_string($_POST['imie']); $tresc = mysql_real_escape_string($_POST['tresc']); mysql_query("INSERT INTO wiadomosci (autor,tresc,czas) VALUES ('$nick','$tresc',NOW())",$dbconn); $last_id = mysql_insert_id(); $last_item = mysql_query("SELECT * FROM wiadomosci WHERE id = '$last_id'"); $wiadomosc = mysql_fetch_assoc($last_item); echo '<li>'; echo date("d.m.Y H:i",strtotime($wiadomosc['czas'])).' <strong>'.htmlspecialchars($wiadomosc['autor']).'</strong> '.htmlspecialchars($wiadomosc['tresc']); echo '</li>';Pod zmiennymi $nick i $tresc zapisujemy oczyszczone dane przekazane przez użytkownika. Potem wykonujemy zapytanie INSERT INTO, które dodaje nam nowy wiersz do bazy danych. Potrzebujemy identyfikator wstawionej przed chwilą wiadomości i tu z pomocą przychodzi nam funkcja mysql_insert_id(). Na końcu pobieramy wszystkie dane dotyczące wstawionej przed chwilą wiadomości i wyświetlamy odpowiedni element li. Tak przygotowane dane odbierzemy później za pomocą jQuery i zaktualizujemy wyświetlane informacje na shoutboxie.
Teraz dodamy kod jQuery powiązany z powyższym kodem php. W miejscu na skrypty Javascript (js) dodajemy następujący kod.
$(function() { $(document).ajaxError(function() { alert('Nie można wysłać danych lub błąd serwera!'); }); $('.formularz').submit(function() { if($(this).find('input[name=imie]').val() && $(this).find('textarea').val()) { $.post('process_ajax.php',$(this).serialize(),function(dane){ $(this).find('input[name=imie],textarea').val(''); $("#chat_area ul li:first").remove(); $('#chat_area ul').append(dane); }); } return false; }); });Kod ten będzie się wykonywał po załadowaniu drzewa dokumentu. Na początku przechwytujemy błędy ajaxa (ajaxError). Następnie gdy zajdzie zdarzenie submit formularza sprawdzamy czy pola formularza są wypełnione i jeśli tak łączymy się ajaxowo z plikiem process_ajax.php, przekazujemy mu dane z formularza $(this).serialize(). Plik process_ajax.php przetwarza je i zwraca nam odpowiedni rezultat w postaci kodu html do dołączenia do listy. Dołączenie wykonujemy metodą append. Ponadto usuwamy pierwszy element listy (chcemy aby na shoutboxie zawsze widoczne było tylko 10 najnowszych wiadomości).
$('#chat_area ul li:first').remove();Możemy jeszcze wyczyścić pola formularza poleceniem poniżej.
$(this).find('input[name=imie], textarea').val('');Teraz zajmiemy się trudniejszą częścią budowy shoutboxa (obsługa sytuacji, gdy nie dodajemy nowej wiadomości, tylko "siedzimy" na shoutboxie i czekamy co napiszą inni), mianowicie sprawdzanie czy w tabeli zaszły jakieś zmiany i odpowiednie dodawanie nowych wiadomości do wyświetlanej listy. Procedura ta będzie wykonywana co 5 sekund.
Zatem do dzieła :)
Będziemy wstawiać kod php w miejsce (2), które zostawiliśmy na jednym z powyższych listingów. Zaczniemy od pobrania daty ostatniej wiadomości z bazy danych. Po pobraniu zamieniamy datę w postaci tekstowej na unix timestamp.
$data_ostatniej_wiadomosci = mysql_query("SELECT MAX(czas) FROM wiadomosci"); $data_ostatniej_wiadomosci = mysql_fetch_row($data_ostatniej_wiadomosci); $data_ostatniej_wiadomosci = strtotime($data_ostatniej_wiadomosci[0]);Przygotujmy sobie zmienną $html, w której będziemy przechowywać kod html do zwrócenia użytkownikowi oraz zapiszmy pod zmienną $data_od_usera datę ostatniej wiadomości od użytkownika (jest to data ostatniej wiadomości, którą widzi użytkownik).
$html = ''; $data_od_usera = intval($_POST['data_ostatniej_wiadomosci']);Teraz najważniejszy fragment tej części skryptu.
if($data_ostatniej_wiadomosci > $data_od_usera) { $items = mysql_query("SELECT * FROM wiadomosci ORDER BY czas DESC LIMIT 10"); $wyniki = array(); while($wiadomosc = mysql_fetch_assoc($items)) { $wyniki[] = $wiadomosc; } $wyniki = array_reverse($wyniki); foreach($wyniki as $wiadomosc) { $html .= '<li>'; $html .= date("d.m.Y H:i",strtotime($wiadomosc['czas'])).' <strong>'.htmlspecialchars($wiadomosc['autor']).'</strong> '.htmlspecialchars($wiadomosc['tresc']); $html .= '</li>'; } } else { $html .= ''; }Jeśli data ostatniej wiadomości z bazy jest większa od daty otrzymanej od usera należy pobrać wiadomości, których użytkownik nie widzi, a które są już w bazie. W przeciwnym razie zwracamy pusty ciąg znaków. Na koniec zwracamy dane JSON następującej postaci.
header('Content-Type: application/json'); echo json_encode(array('html'=>$html,'czas'=>$data_ostatniej_wiadomosci));Pozostaje tylko dopisać kod jQuery. Napiszemy funkcję updateChat(), która będzie wywoływana co 5 sekund tzn.
setInterval('updateChat()',5000);Najpierw zadeklarujmy zmienną globalną przechowującą datę ostatniej wiadomości. Na początku dajmy jej wartość 0, więc po pierwszym otworzeniu strony użytkownik będzie pobierał wszystkie wpisy.
var data_ostatniej_wiadomosci = 0;Kod funkcji updateChat() przedstawia poniższy listing.
function updateChat() { $.post('process_ajax.php',{data_ostatniej_wiadomosci: data_ostatniej_wiadomosci},function(dane){ if(dane) { data_ostatniej_wiadomosci = dane.czas; if(dane.html!='') { $('#chat_area ul').html(dane.html); } } }); }Wysyłamy metodą POST do pliku process_ajax.php datę ostatniej wiadomości, którą widzi użytkownik. Serwer zwróci nam obiekt JSON, który przetwarzamy tzn. zapisujemy pod zmienną data_ostatniej_wiadomosci datę ostatniej wiadomości zwróconą przez serwer (dane.czas). Jeśli dane html nie są pustym ciągiem znaków wstawiamy je do elementu ul. Kody źródłowe shoutboxa.