Automatyczne odświeżanie po zmianach w bazie
Jest nowa i ulepszona wersja. Jeśli jesteś zainteresowany przejdź do:
Część druga ajaxowego odświeżania z przykładem online
Jeśli chcesz poznać podstawowe zagadnienia związane z automatycznym odświeżaniem zobacz post: “Odśwież zawartość strony automatycznie co określony czas – jQuery“.
W jednym z modułów aplikacji w mojej pracy inżynierskiej musiałam zapewnić, aby użytkownik końcowy posiadał zawsze aktualną listę wiadomości. Było to bardzo ważne, ponieważ wiadomości mogły być edytowane, ukrywane, publikowane na nowo, a użytkownik nie ma czasu pamiętać o tym aby odświeżyć stronę. Oczywiście większego problemu nie ma. Ustawiam czas po jakim strona ma się odświeżać i wszystko gra.
Ze względu na specyficzny charakter użytkownika, ten czas nie mógłby być krótszy niż pół minuty. Odświeżanie strony co pół minuty i za każdym razem ta sama droga:
żądanie HTTP -> zapytanie do bazy -> uformowanie wyniku -> wysłanie wyniku do przeglądarki
Obłęd. Jeszcze przy około 10-20 wiadomościach, ukrywaniem treści wiadomości, ledwo pojawi się pięknie uformowana lista, a już kolejny raz strona zostanie przeładowana.
Rozwiązanie może toporne, ale na moje potrzeby i warunki sprawdziło się doskonale.
Założenie
Stworzyć licznik przechowywany w bazie i w kodzie HTML. Przy każdej zmianie w liście wiadomości (bez względu na akcje – edycja, dodanie, ukrycie) zwiększam licznik w bazie. Następnie zamiast pobierać całą listę wiadomości lub odświeżać stronę, co pół minuty sprawdzam go sobie w tle i porównuję z tym zaszytym w kodzie. Odświeżenie listy wiadomości zostanie wywołane tylko wtedy kiedy te wartości są różne.
I wszyscy są szczęśliwi. Oczywiście ścieżka zapytania się nie zmieni. Jednak czas zapytania i formowania wyniku znacznie się zmniejszy. A co najważniejsze, użytkownik nie będzie chciał mnie zastrzelić. ;o)
Narzędzia
PHP, HTML, prototype
Kod źródłowy z przykładem w którym wykorzystywane jest takie automatyczne odświeżanie możesz pobrać tutaj.
W celu uruchomienia przykładu
- wypakuj pliki do docelowego katalogu (np. news)
- utwórz bazę i tabelę za pomocą dołączonego pliku sql (możesz pominąć opcję tworzenia bazy, ważne jest aby po utworzeniu tabeli został dodany wpis z id=1)
- wprowadź dane do połączenia się z bazą w pliku class_db.php
- otwórz stronę w przeglądarce – powinno już wszystko dzialać
Jeśli wszystko poszło dobrze, wpisując adres http://mojadomena/katalog_z_przykladem powinna być informacja, że nie ma wpisów (jeszcze żadnego nie dodaliśmy, a pierwszy zawsze będzie pomijany).
Otwórz w nowym oknie/karcie http://mojadomena/katalog_z_przykladem/add_news.php pojawi się formularz dodawania wpisu. Dodaj jakiś wpis, poczekaj aż pojawi się “Dodane” a następnie przejdź do poprzedniego okna/karty aby zobaczyć że dodany wpis już tam jest (bądź będzie za kilka sekund)
To tyle jeśli chodzi o kod źródłowy. O co w tym chodzi?
Przygotowanie HTML-a
W sekcji <head></head> dołączamy bibliotekę prototype.
Gdzieś w sekcji <body> – najlepiej na początku – potrzebne są dwa niewidoczne dla użytkownika kontenery z przypisanymi id.
Na przykład:
<p id="old_nr" style="display:none"></p>
<p id="new_nr" style="display:none"></p>
old_nr – przechowywać będzie stan licznika w chwili załadowania się strony.
new_nr – przechowywać będzie stan licznika po kolejnym zapytaniu
Nie ma to większego znaczenia czy będzie to <div>, <p>, lub cokolwiek innego. Ważne aby był nadany id.
I oczywiście kontener w którym przechowuję listę wiadomości
<div id="msg_list"></div>
Przygotowanie skryptów PHP
Ponieważ wiadomości przechowywane są w bazie, tworząc tabelę dodaję automatycznie wiersz o id=1 w którym będę przechowywać licznik. Przy każdej modyfikacji wpisów, czyli: edycja, usuwanie, dodanie nowej, archiwizacja, etc – wartość w dedykowanej kolumnie będzie zwiększana o jeden.
W przykładzie można zauważyć że do przechowywania licznika wybrałam add_user_id. Ponieważ pierwszy wpis nie jest tak naprawdę newsem, a w większości przypadków chcemy przechowywać kto stworzył dany news (poprzez jakiś id użytkownika), to żeby nie tworzyć dodatkowej kolumny tylko dla tego jednego wpisu wykorzystałam właśnie id usera do tego celu.
Ponieważ działam na klasach, nie będę wdawać się w szczegóły łączenia się z bazą.
Poniżej przedstawię tylko najważniejsze fragmenty.
– funkcja do zwiększenia licznika
function register_changes(){
$this->db->query('UPDATE '.$this->table_name.' SET add_user_id = add_user_id + 1 WHERE id=1');
}
– sprawdzanie stanu licznika
function check_changes(){
$result = $this->db->query('SELECT add_user_id as counting FROM '.$this->table_name.' WHERE id=1 LIMIT 1');
return $result ? mysqli_fetch_object($result)->counting : FALSE;
}
Kolejnym krokiem jest utworzenie osobnego pliku wywoływanego poprzez AJAX. Nazwę go check.php.
U mnie wygląda on tak:
require('class_news.php’);
$news = new news();
echo $news->check_changes();
Przygotowanie JS
Na początek tworzymy sobie funkcję, która będzie wykonywała nam “ukryte” żądanie. Poprzez AJAX następuje wywołanie skryptu na serwerze chec.php. Jeśli licznik się zwiększył, lista wiadomości i tylko ona jest odświeżana.
Parametrami wejściowymi naszej funkcji jest:
url – czyli adres/nazwa wywoływanego skryptu
id_st – identyfikator kontenera przechowującego stary stan licznika
id_nw – identyfikator kontenera przechowującego nowy stan licznika
function check(url,id_st,id_nw){
req = new Ajax.Request(url, {
contentType: 'text/html; charset=utf-8',
method: 'post',
onSuccess: function(response) {
//aktualizacja kontenera "new_nr" o aktualną wartość licznika
id_nw.update(response.responseText);
//przypisanie aktualnego licznika do zmiennej new
new_nr = id_nw.innerHTML;
//przypisanie stanu licznika z momentu załadowania się strony do zmiennej
older = id_st.innerHTML;
//jeśli wartości będą różne, nastąpi odświeżenie się listy wiadomości z pomocą Ajax.Updater. Wykorzystując tę funkcję odświeży się tylko lista wiadomości, a nie cała strona.
if(older!=new_nr){
//Nastąpiła zmiana, a nie odświeżamy całej strony do kolejnego porównania potrzebna jest nam nowa wartość licznika.
id_st.update(new);
//Aktualizacja listy wiadomości.
new Ajax.Updater('msg_list','msg.php');
return false;
}
}
});
}
Mamy już funkcję, więc teraz wystarczy tylko ustalić co ile licznik ma być sprawdzany i nadać mu początkową wartość. Początkowa wartość jest pobierana wraz z pierwszym zapytaniem pobierającym listę wiadomości i w php jest przypisana do zmiennej $start
//Co 5 sekund wywołujemy funkcję check
setInterval("check('check.php',$('old_nr'),$('new_nr'));",5000);
//Przy załadowaniu się strony ustalamy początkową wartość licznika
window.onload=function(){
$('old_nr').update('<?php echo $start;?>');
new Ajax.Updater('msg_list','msg.php');
}
Właściwie nic więcej nie jest potrzebne. Lista wiadomości (i tylko ona) jeśli tylko coś w jej zawartości się zmieni, sama się odświeży co pięć sekund. Oczywiście tak częste sprawdzanie nie jest potrzebne. Wystarczy co minutę-kilka minut.
Cześć Eliza,
odgrzewam trochę temat, ale mam problem, wszystko działa pięknie, ale po zastosowaniu Twojego pomysłu wraz z biblioteką prototype.js na stronie wycina mi pozostałe skrypty java np. formularze popup, rozwijane div-y.
Bardzo broszę o pomoc! Div odświeża się po zmianie w bazie, ale zależy mi na pozostałych elementach, czy występuje tutaj jakiś konflikt js? Nie dołączam nigdzie wcześniej tej biblioteki :(
Pozdrawiam
Musi byc konflikt. Np pozostałe skrypty działają z jquery i się rozsypuje. Sprawdź czy https://aiocollective.pl/blog/ajax-automatyczne-odswiezanie-czesc-ii/ rozwiąże problem. W sumie zasada podobna, tylko wykonanie trochę inne i oparte o jQuery
hej Elizka, dzięki za odpowiedź, ale powiedz mi czy dobrze myślę: index.php <div id="message-list" data-counter="”> $start – pobrane zapytaniem ostatnie id w bazie checker.php … //get current counter $data[‘current’] = (int)$news; // $news – pobrane zapytaniem ostatnie id //set initial value of update to false $data[‘update’] = false; //check if it’s ajax call with POST containing current (for user) counter; //and check if that counter is diffrent from the one in database if(isset($_POST) && !empty($_POST[‘counter’]) && (int)$_POST[‘counter’]!=$data[‘current’]){ //the counters are diffrent so get new message list $data[‘news’] = ‘OMG! It\’s alive!!! NEW UPDATE !!!’; $data[‘news’] .= $news; $data[‘update’] = true;… Czytaj więcej »
Dzień dobry Eliza,
walczyłem przez dwie noce i na serwerze lokalnym działa super, a jak wyeksportuję na host to kiszka :( (sprawdzałem setki razy ścieżki do klas i JS i ręce mi opadły)
Pozdrawiam i spokojnej niedzieli życzę
Tomek
Dzięki, za szybką reakcję, co do tabel używam JOIN i zapytanie wygląda tak: <?php $query="SELECT spot.id, spot.Date, spot.Spotter, spot.From, spot.Freq, spot.Mode, spot.QRZ, spot.DXCC, spot.WKD, spot.comment, prefix_list.country, fusione6Igv_users.user_id FROM spot LEFT JOIN prefix_list ON spot.DXCC=prefix_list.id LEFT JOIN fusione6Igv_users ON spot.Spotter=fusione6Igv_users.user_aim ORDER BY id DESC limit 40"; $result=mysql_query($query); $num=mysql_numrows($result); $i=0; while ($i < $num) { $dzisiaj=mysql_result($result,$i,"Date"); $spotter=mysql_result($result,$i,"Spotter"); $from=mysql_result($result,$i,"From"); $freq=mysql_result($result,$i,"Freq"); $mode=mysql_result($result,$i,"Mode"); $dxcc=mysql_result($result,$i,"DXCC"); $qrz=mysql_result($result,$i,"QRZ"); $wkd=mysql_result($result,$i,"WKD"); $comment=mysql_result($result,$i,"comment"); $user_id=mysql_result($result,$i,"user_id"); $country=mysql_result($result,$i,"country"); $str=$spotter; $str=preg_replace('/\s/ms', '', $str); $qstr=$qrz; $qstr=preg_replace('/\s/ms', '', $qstr); ?> <tr> <td style="width:110px; height:20px" bgColor="#ffffff" vAlign="" align="center"><font style="font-size:9px" size="9" face="Tahoma"><?php echo $dzisiaj; ?></font></td> <td style="width:70px; height:20px" bgColor="#afeeee" vAlign="" align="center"><font style="font-size:9px" size="9" face="Tahoma"> <a href="javascript:otworz('/profile_member.php?phrase=<?php echo($spotter);?>', 600,455)"> <strong><?php… Czytaj więcej »
Hej Tomek,
W którym konkretnie miejscu nie udaje Ci się tego dopasować? Masz problem z JS, czy już na poziomie samego zapytania MySQL?
Piszesz o dwóch tabelach, licznik możesz wyciągać tylko z jednej i w jednej updatować jeśli oczywiście zawsze jest jakieś powiązanie pomiędzy nimi (jakimś JOINem je wyciągasz razem). Podaj więcej szczegółów :o)
Witam Kokers,
mam prośbę, testowałem kod i działa super, ale proszę o pomoc, jak zastosować funkcję ajax do formularza, który już posiadam i tabeli wyników. Dane wyciągam z dwóch tabel, gdzie i jak dodać funkcje z twojego kodu aby mój div odświeżał się po dodaniu rekordu.
W tej chwili tabelę umieściłem w iframe i odświeżam ją co 30 sek, ale po co jak nowy rekord może pojawić się dopiero za 3 godz. lub jeszcze później. Walczę z tym już od miesiąca za wszelką pomoc bardzo dziękuję.
Pozdrawiam Tomek
Dominik1250, a co konkretnie Ci nie działa?
pomożesz mi złożyć ten skrypt bo coś mi nie idzie :D