Friday, November 27, 2009

Obsługa formularzy

Komunikacja w sieci odbywa się w stałych cyklach żądanie - odpowiedź. Użytkownik klikając na odnośnik żąda strony WWW, którą serwer dostarcza (albo i nie - wtedy przewidując programista pokazuje stronę z błędem). Szukając informacji zgłasza pytanie wyszukiwarce, a ona (po bardzo skomplikowanych obliczeniach) podaje mu wyniki. Nawet tworzenie tego wpisu było procesem żądań - nierzadko automatycznych. Przeglądarka co pewien czas wysyłała moją dotychczasową pracę z żądaniem zapisu - co serwer czynił. Dzięki temu nie skasowałem sobie tekstu po przypadkowym odświeżeniu strony.

Bardzo ważne jest zatem, w jaki sposób przetwarzamy żądania i udzielamy odpowiedzi. Pomijając kwestie trywialne - w stylu kliknięcia w odnośnik - można to zrobić na co najmniej kilka sposobów, tych dobrych i tych złych.

Prześledźmy proces pojedynczego żądania. Użytkownik wypełnia formularz - powiedzmy dodaje nowy komentarz na blogu - i przesyła go na serwer. Dla ułatwienia załóżmy, że prawidłowo wypełnił wszystkie pola. Proces wygląda wtedy tak:

Użytkownik wysyła formularz, serwer go sprawdza i dodaje do bazy, po czym wyświetla stronę z podsumowaniem (albo z odświeżoną listą komentarzy). Z punktu widzenia użytkownika zatem całość wygląda tak:

Użytkownik nie widzi - a nawet nie powinien - procesu przetwarzania, a jedynie rezultat.

Tyle abstrakcji. W praktyce sieć WWW składa się z konkretnych adresów URL. Zatem w naszym przykładzie użytkownik wchodzi na stronę http://www.example.com/komentarze.php z listą komentarzy i polem do dodania nowego, wpisuje swoje uwagi, a po zapisaniu trafia z powrotem na stronę http://www.example.com/komentarze.php Programista może zastosować kilka podejść:
  1. Umieścić kod weryfikujący i dodający komentarz razem z treścią strony (HTML) w jednym pliku
  2. Umieścić kod dodający formularz w oddzielnym pliku (na przykład dodaj.php) i wywołać go po żądaniu użytkownika.

O tym, że właściwe jest rozwiązanie drugie nie powinno być nawet potrzeby pisać. Ale dla dociekliwych i początkujących już mówię dlaczego. Daje nam to co najmniej dwie podstawowe zalety. Po pierwsze - z naszego punktu widzenia strona jest bardziej czytelna, to znaczy, że jeśli będziemy musieli ją później zmodyfikować (na przykład dodać weryfikację formularza) spędzimy nad tym dużo mniej czasu. Po drugie oddzielamy logikę (czyli dodawanie komentarza) od prezentacji (czyli wyświetlenia listy wszystkich komentarzy). To pozwoli na niezależną modyfikację obu plików. Przydać się to nam może, jeśli ten sam (prawie) kod będziemy chcieli wykorzystać również do dodania na przykład formularza kontaktowego (nie różniącego się specjalnie od komentarza).

Na temat dobrych praktyk programowania napisano wiele artykułów, zatem poprzestanę na powyższym wyjaśnieniu. Chciałbym się zająć kwestią nieco bardziej skomplikowaną - podziałem logicznym procesu przesyłania informacji pomiędzy adresy URL. Od strony programisty mamy podział na trzy logiczne bloki. Od strony użytkownika możliwe są trzy przypadki:

W pierwszym przypadku użytkownik odwiedza trzy strony (mogą mieć ten sam adres - chodzi o żądania HTTP wysłane przez przeglądarkę). Pierwszą - z listą komentarzy, drugą dodającą komentarz i trzecią z nową listą komentarzy. Druga strona nie zwraca żadnej treści, tylko nagłówek przekierowania na stronę trzecią.

W przypadku drugim strony pierwsza i druga mają ten sam adres (formularz PHP kieruje do obecnej strony), a po dodaniu komentarza wysyłane jest żądanie przekierowania (być może znowu do tego samego adresu, chodzi o sam fakt jego wystąpienia) do nowej listy.

W przypadku trzecim komentarz wysyłany jest do strony na której występuje jego dodanie, a następnie ta sama strona wyświetla nową listę formularzy. Na pierwszy rzut oka przypadek trzeci jest najprostszy i najwygodniejszy. Po co komplikować użytkownikowi i sobie życie niepotrzebnymi przekierowaniami? Niestety, to podejście jest błędne.

Wyobraźmy sobie dość powszechną sytuację: użytkownik dodaje komentarz, przegląda stronę, po czym... postanawia ją odświeżyć (bo a nuż ktoś napisał odpowiedź). Jeśli komentarz został dodany w sposób trzeci, w momencie odświeżenia zostanie... dodany ponownie! Wiele przeglądarek spyta się, czy ponownie przesłać dane, ale większość ludzi kliknie na "Ok". I o ile w przypadku dodania komentarza nic szczególnie złego się nie dzieje (choć może nieźle "zaśmiecić" dyskusję), o tyle na przykład w przypadku, gdy wcześniej żądaliśmy usunięcia jakiegoś pliku... Wiele stron w sieci cierpi ciągle na tę przypadłość.

Jeszcze gorsza sytuacja może się zdarzyć, jeśli żądaniem jest odnośnik WWW. Na przykład mamy listę przedmiotów, a na górze odnośnik kierujący nas do jej wyczyszczenia. Na przykład http://www.example.com/list.php?clear=true Z pozoru nic się nie dzieje złego - po kliknięciu użytkownik zostaje przekierowany na stronę z wyczyszczoną listą. Wyobraźmy sobie jednak przypadek, w którym tenże użytkownik spróbuje później dodać tą stronę do ulubionych... Przy każdym na nią wejściu lista będzie czyszczona (co niekoniecznie jest pożądanym działaniem)...

Który zatem sposób wybrać? Nie trzeci, to już wiemy. Pozostałe są generalnie poprawne, chociaż osobiście polecam drugi. Dlaczego? to ilustruje ostatni już rysunek.

W tym scenariuszu użytkownik wprowadził błędne dane i chcemy wyświetlić formularz z zaznaczonymi problemami. Przy liście komentarzy, gdzie żądanie i odpowiedź są zazwyczaj tą samą akcją, nie ma to znaczenia. Jednak już na przykład w formularzu kontaktowym, gdzie strona odpowiedzi jest znacząco inna, jest to istotne. W tym przypadku "zamykamy" wyświetlenie formularza w obrębie jednej akcji, co znacząco upraszcza kod i przyspiesza nasze (późniejsze) w nim zmiany. A przecież o to chodzi.