Przeczytaj
Współczesne techniki współpracy witryny z bazą danych
W tym e‑materiale poznamy cztery współczesne, istotne usprawnienia algorytmu komunikacji skryptu PHP z bazą danych.
Umieszczenie danych konfiguracyjnych połączenia w zewnętrznym pliku, możliwym do wywołania w pozostałych skryptach komunikujących się z bazą danych.
Obsługa wyjątków
try
..catch
zamiast „twardego” zatrzymania wykonania skryptu funkcjąor die()
.Wariant obiektowy współpracy z bazą danych (zamiast klasycznej wersji proceduralnej), w tym nowa metoda fetchowania obiektowego o nazwie
fetch_object()
.Wykorzystanie pętli typu
foreach
do przetwarzania rezultatów wyszukiwania w bazie.
Znajomość tych metod jest ważna z punktu widzenia optymalizacji tworzonych kodów źródłowych. Omówimy zatem nie tylko sposób wdrożenia tych metod, lecz także logiczne przyczyny wprowadzenia usprawnień i wynikające z nich konsekwencje – zarówno dla programisty webowego, jak i użytkownika tworzonej witryny.
Umieszczenie danych dostępowych do bazy w zewnętrznym pliku
Pierwszym etapem realizowania skryptu współpracującego z bazą danych MySQL jest zawsze podanie informacji dostępowych potrzebnych do nawiązania połączenia z bazą. Początkowo w skryptach zazwyczaj dokonujemy tego, używając danych dostępowych jako argumentów zapisanych wprost wewnątrz funkcji mysqli_connect()
:
Jednak w przypadku tworzenia całych witryn, a więc wielu skryptów realizujących w aplikacji przeróżne zadania, wielokrotne powtarzanie tych danych dostępowych wydaje się nieoptymalne. W przypadku konieczności zmiany konfiguracji serwisu (na przykład wskutek przeniesienia na inny serwer) jesteśmy zmuszeni poprawić dane dostępowe w każdym skrypcie osobno.
Jeżeli podczas przenoszenia witryny na inny serwer dojdzie do przeoczenia konieczności wprowadzenia zmian w którymkolwiek z wielu plików, powstanie serwis internetowy, w którym poprawnie działa tylko wybrana część skryptów (a my w ogóle nie będziemy o tym wiedzieć). Konieczność testowania każdej funkcjonalności osobno okaże się zadaniem wyjątkowo czasochłonnym.
Współcześnie postępujemy więc inaczej: opis aktu nawiązania połączenia oraz wszystkie dane dostępowe umieszczamy w specjalnie wydzielonym, zewnętrznym pliku. W razie konieczności dokonania zmian w danych dostępowych poprawki aplikujemy tylko w jednym skrypcie, zamiast w wielu rozproszonych zasobach. Specjalnie utworzony, dodatkowy plik możemy nazwać np. connect.php
. Oto jego przykładowa zawartość:
Następnie w każdym skrypcie PHP, w którym łączymy się z bazą danych, użyjemy instrukcji jednokrotnego żądania specjalnego pliku:
W ten sposób dane konfiguracyjne zostaną „podpięte” we wszystkich potrzebnych dokumentach. Jest to idea znana z technologii CSS – style elementów witryny również przenosimy do zewnętrznego arkusza, aby w razie chęci dokonania zmian uniknąć wprowadzania dużej liczby poprawek w wielu miejscach jednocześnie.
Testowanie współpracy z bazą danych również staje się dzięki opisanej technice łatwe: wystarczy sprawdzenie jednego ze skryptów korzystających z bazy. Wiele współczesnych frameworków przestrzega tzw. podejścia DRYpodejścia DRY, umieszczając kluczowe informacje konfiguracyjne w zewnętrznym pliku. W przypadku popularnego systemu Wordpress jest to zasób o nazwie wp‑config.php
.
Przyczyny oraz sposób implementacji zewnętrznego pliku zostały także przedstawione w filmie zawartym w sekcji „Film samouczek”.
Obsługa wyjątków: try .. catch
Instrukcję takiej postaci stosujemy współcześnie zamiast klasycznej obsługi błędów komunikacji z bazą danych z użyciem funkcji or die()
. Tak zwane wyłapywanie wyjątków pozwala dużo elastyczniej reagować na poszczególne problemy w aplikacji webowej, zamiast po prostu „zabijać” wykonanie skryptu.
Używając instrukcji try
, podejmujemy próbę nawiązania połączenia z bazą; w razie wystąpienia problemów (gdy kod błędu jest inny niż zero) dokonujemy tzw. rzucenia wyjątku. Korzystamy przy tym z zapisu throw new Exception
. Taki wyjątek, mający unikalny identyfikator, można później „złapać” w specjalnej sekcji catch
:
W profesjonalnych aplikacjach webowych przetworzeniem obiektu o nazwie $error
można się zająć za pomocą specjalnie napisanych własnych funkcji (a w przypadku zastosowania technik obiektowych używając metod, czyli funkcji zdefiniowanych wewnątrz klas). Kod błędu pobiera metoda o nazwie mysqli_connect_errno()
, wywołana już w momencie rzucania wyjątku. Wewnątrz sekcji catch
dostęp do tego kodu uzyskujemy dzięki metodzie getMessage()
wywołanej na rzecz obiektu $error
Wykrycie błędu, połączone z unikatową reakcją na jego rodzaj (osiąganą za sprawą identyfikatora numerycznego), daje możliwość zaprogramowania inteligentnej reakcji skryptu na problemy z komunikacją z serwerem. Nie musimy już zwyczajnie „zabijać” dalszego przetwarzania dokumentu – możemy użytkownika ostrzec, przekierować albo wyświetlić zawartość zastępczą. Współczesne zaawansowane aplikacje internetowe zapewniają internaucie właśnie taką „miękką” reakcję na kłopoty z bazą danych, zamiast brutalnie uniemożliwiać przeglądanie witryny.
Użycie konstrukcji try
.. catch
zazwyczaj łączymy z pierwszą opisaną techniką, czyli z przeniesieniem aktu nawiązania połączenia z bazą do zewnętrznego pliku. Więcej szczegółów na temat zrealizowania obsługi połączenia w taki sposób podajemy w filmie samouczku.
Wariant obiektowy współpracy witryny z bazą danych
We współczesnym programowaniu wyróżniamy dwa podstawowe paradygmaty, czyli przyjęte konwencje budowania aplikacji. W podejściu proceduralnympodejściu proceduralnym stosujemy funkcje, czyli wydzielone fragmenty kodu, które realizują konkretne zadania. Konwencja obiektowaKonwencja obiektowa jest bardziej abstrakcyjna i złożona, gdyż używamy obiektów skonstruowanych na bazie szablonów zwanych klasami. Każdy obiekt ma przypisane pewne atrybuty (zmienne) oraz metody (funkcje zdefiniowane wewnątrz klas, a służące do przetwarzania zmiennych).
Programowanie zorientowane obiektowo jest trudniejsze do zrozumienia, jednak to właśnie za sprawą rozbudowanej struktury logicznej zapewnia utrzymanie porządku w kodzie, chroni dane przed niepożądanym nadpisaniem (dzięki tzw. hermetyzacji), a ponadto ułatwia rozbudowę aplikacji (tzw. skalowanie). Współpraca witryny z bazą danych za pośrednictwem biblioteki mysqli
także może zostać zrealizowana obiektowo.
Nawiązanie połączenia z bazą danych
W wersji obiektowej pojawia się dodatkowa klauzula new
, która tworzy nowy obiekt o nazwie $c
. Metoda mysqli()
to tzw. konstruktor obiektu. Kolejność argumentów wewnątrz nawiasów okrągłych pozostaje w obu wariantach identyczna.
wersja proceduralna | wersja obiektowa |
---|---|
|
|
Obsługa polskich znaków
Podobnie jak w wersji proceduralnej, odpowiednią linię kodu umieszczamy już po nawiązaniu aktywnego połączenia. Tym razem jednak zapis ma następującą postać: obiekt->metoda("argument");
wersja proceduralna | wersja obiektowa |
---|---|
|
|
Wykonanie zapytania SQL
Zakładamy, że treść kwerendy do wykonania została uprzednio umieszczona w pomocniczej zmiennej $q
.
wersja proceduralna | wersja obiektowa |
---|---|
|
|
Pobranie liczby zwróconych rekordów
W tym przypadku mamy do czynienia nie z metodą obiektu, tylko z jego atrybutem o nazwie num_rows
(zwróć uwagę na brak nawiasów okrągłych, właściwych zapisowi metody):
wersja proceduralna | wersja obiektowa |
---|---|
|
|
Fetchowanie pojedynczych rekordów
W poprzednich e‑materiałach poznaliśmy trzy sposoby fetchowania rekordów do tablicy jednowymiarowej:
mysqli_fetch_row()
– przetwarzanie liczbowe – ponumeruj (od zera) szufladki tablicy jednowymiarowej;mysqli_fetch_assoc()
– wyjmowanie asocjacyjne (skojarzeniowe) – ponazywaj słownie szufladki tablicy jednowymiarowej, nadając im zawsze taki sam identyfikator, jaki mają kolumny w bazie danych;mysqli_fetch_array()
– przetwarzanie równocześnie numeryczne i asocjacyjne (dostępne są oba rodzaje indeksów).
Zapis obiektowy tych metod (tutaj na przykładzie przetwarzania liczbowego) wygląda następująco:
Dla każdej z trzech klasycznych metod pozbywamy się przedrostka mysqli_
występującego w nazwie instrukcji, a ponadto $result
nie jest już argumentem funkcji, tylko obiektem umieszczonym po lewej stronie operatora: ->
.
Na rzecz obiektu $result
wywołujemy metodę fetch_row()
, która kopiuje do tablicy jednowymiarowej pojedynczy rekord. W bieżącym e‑materiale poznamy jeszcze możliwość przetwarzania rekordów nową, czwartą metodą o nazwie fetch_object()
.
Fetchowanie obiektowe fetch_object
Podejście obiektowe umożliwia także przetwarzanie rekordów nie w postaci tablic jednowymiarowych, lecz obiektów mających atrybuty:
Obiekt o nazwie $obj
nada atrybuty noszące nazwy takie same, jak kolumny w tabeli bazy danych. Zamiast podawania indeksów tablicy z użyciem nawiasów kwadratowych, stosujemy w tym przypadku zapis obiekt->atrybut
.
Idea przechowywania atrybutów wydobytych z bazy danych wewnątrz obiektów, a nie tablic, stała się bardzo rozpowszechniona w wielu współczesnych frameworkach webowych. Warto więc jak najszybciej przyzwyczaić się do takiego modelu fetchowania rekordów – czas na to poświęcony zwróci się później z nawiązką.
Zwolnienie pamięci zajmowanej przez rekordy
Kolejnym logicznym usprawnieniem działania skryptu współpracującego z bazą danych jest zwolnienie pamięci zajmowanej przez wyjęte rekordy. Oczywiście może to nastąpić dopiero w momencie, gdy dane znajdujące się w $result
nie są już w skrypcie dłużej potrzebne. Takie wyczyszczenie pamięci ma największy sens w skryptach wykonujących wiele zapytań, a generujących duże liczby zwracanych rekordów.
wersja proceduralna | wersja obiektowa |
---|---|
|
|
Zamknięcie połączenia z bazą danych
Na rzecz obiektu $c
reprezentującego nawiązane połączenie wywołujemy metodę o nazwie close()
. W tym przypadku argument staje się zbędny, gdyż po lewej stronie operatora ->
znajduje się już informacja, które z istniejących połączeń zamierzamy zakończyć:
wersja proceduralna | wersja obiektowa |
---|---|
|
|
Wykorzystanie pętli foreach
Tego typu instrukcja iteracyjna upraszcza wizualnie klasyczny zapis fetchowania rekordów. Nagłówek pętli jest dużo prostszy w porównaniu do analogicznej instrukcji while
. Po lewej stronie operatora as
znajduje się obiekt $result
klasy mysqli_result
, przechowujący wszystkie wyjęte z bazy rekordy. Natomiast po prawej stronie tego operatora znajduje się pomocnicza, jednowymiarowa tablica $row
, w której komórkach umieszczamy atrybuty pojedynczego rekordu.
Zapis as
(ang. „jako”) możemy logicznie potraktować tak samo, jak znany z języka SQL operator o identycznej nazwie, stosowany w kwerendach – jest to przecież logicznie nowy alias właśnie fetchowanego rekordu.
Słownik
konwencja projektowania oprogramowania zalecająca traktowanie programu komputerowego jako współpracujących ze sobą abstrakcyjnych obiektów, mających pewne atrybuty (określające stan) oraz metody (funkcje definiujące zachowania); obiekty skonstruowane na podstawie szablonów (klas) współpracują ze sobą w celu wykonywania określonych zadań
konwencja projektowania oprogramowania zalecająca dzielenie instrukcji na realizujące konkretne zadania funkcje lub procedury (funkcje zwracają wartość, zaś procedury nie); program komputerowy traktujemy jako zbiór współpracujących ze sobą funkcji, wymieniających między sobą wartości nieglobalne
(od ang. Don't Repeat Yourself – „nie powtarzaj się”) reguła projektowania oprogramowania, która zaleca unikanie stosowania tych samych fragmentów kodów źródłowych (lub bardzo zbliżonych) w wielu miejscach; lepszą praktyką jest wyodrębnienie powtarzającego się fragmentu z ewentualnym zastąpieniem wartości parametrami i jedynie odwoływanie się do niego w licznych wystąpieniach