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.