INF.03-06-25.06-SGCzerwiec 2025 · 50 kroków · ok. 102 min · 42 kryteria z klucza
Rozwiązanie krok po kroku: Portal biblioteki internetowej (Wersja podstawowa)
Rozwiązanie buduje portal biblioteki: baza biblioteka z pięcioma operacjami (SELECT, JOIN z sortowaniem i limitem, ALTER TABLE, UPDATE, SELECT), grafika z przezroczystym tłem, strona biblioteka.php z czterema sekcjami i trzema formularzami, style w styl.css z zapytaniem @media oraz cztery skrypty PHP: pętla z grafikami w nagłówku, listy rozwijane z książkami trzech gatunków, rezerwacja wybranej książki i lista zaległych wypożyczeń.
Kroki idą zdanie po zdaniu za treścią arkusza - każdy zaczyna się poleceniem z arkusza, a kod pokazuje dokładnie te linie, które je spełniają. Kod PHP jest najpierw pisany funkcjami z tabeli 1 arkusza (proceduralne mysqli), ze skryptami powielonymi w trzech sekcjach dokładnie tak, jak każe arkusz; na końcu całość zostaje przerobiona na wersję z prawdziwych projektów: jedna funkcja zamiast trzech kopii, zapytania przygotowane z bindowaniem i escapowanie danych. W tym arkuszu nie ma JavaScriptu.
To jest wersja podstawowa. W zakładce „Nowoczesny kod (grid, PDO)” jest drugi sposób rozwiązania tego samego arkusza: układ na siatce CSS i PHP na PDO z zapytaniami przygotowanymi od pierwszej linii - obie wersje są zgodne z kluczem.
Grafika i baza pochodzą z prawdziwego archiwum pliki4 (załącznik do pobrania na stronie arkusza, hasło w treści zadania). Zrzuty wyników zapytań są odtworzone z tej bazy - na egzaminie robisz je sam, z całego ekranu.
Jak liczą się punkty. Klucz ma 42 kryteria (R.1.1 - R.5.10), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 32 punkty - można stracić 10 kryteriów i nadal zdać. Przy każdym kroku jest napisane, które kryteria spełnia; egzaminator ocenia spełnienie kryteriów z klucza, nie zgodność z tym kodem.
Suwak i strzałki obok niego - albo ←→ na klawiaturze - przechodzą krok po kroku; wyjaśnienie, pliki i podgląd zmieniają się razem z krokiem.
Lista „Etap” przenosi od razu do wybranego etapu.
„Rozwiązuję samodzielnie” chowa rozwiązanie kroku - najpierw własna próba, potem odsłonięcie.
„Tryb prezentacji” pokazuje same kroki, bez menu strony - na rzutnik.
Etap 1: Baza danych
Kroki 1-8 · po tym etapie masz 7 z 42 kryteriów (pkt)
Krok 1: Baza danych biblioteka
Kryteria:
R.1.1 Wykonany import tabeli do bazy danych biblioteka , czynność udokumentowana w pliku import.png
ok. 2 min
Polecenie z arkusza
Utwórz bazę danych o nazwie biblioteka, z zestawem polskich znaków (np. utf8_unicode_ci).
W przeglądarce otwierasz http://localhost/phpmyadmin, klikasz Nowa w lewym panelu, wpisujesz nazwę biblioteka i z listy metod porównywania wybierasz utf8_unicode_ci (arkusz podaje ją jako przykład; utf8_polish_ci też jest poprawne). Tabele z pliku SQL mają jednak zestaw utf8 i porównywanie utf8_polish_ci zapisane we własnej definicji, więc tytuły („Pieśń nad pieśniami”, „Śluby panieńskie”) zachowają ogonki przy każdym wyborze - ustawienie bazy dziedziczą tylko tabele zakładane później bez własnego ustawienia.
Typowe błędy
Nazwa bazy musi być dokładnie biblioteka - skrypty PHP łączą się z bazą po nazwie, więc literówka odetnie punkty także za skrypty (R.5.1).
Krok 2: Import tabel z pliku biblio.sql
Kryteria:
R.1.1 Wykonany import tabeli do bazy danych biblioteka , czynność udokumentowana w pliku import.png
ok. 2 min
Polecenie z arkusza
Z rozpakowanego archiwum zaimportuj tabele z pliku biblio.sql do utworzonej bazy.
Wejdź do bazy biblioteka (musi być zaznaczona w lewym panelu), karta Import, Wybierz plik → biblio.sql z rozpakowanego archiwum, Wykonaj.
Po imporcie są dwie tabele z ilustracji 1: ksiazka (id, tytul, gatunek - 20 wierszy; gatunek to „liryka”, „epika” albo „dramat”) i wypozyczenia (id_wyp, id_ks, id_cz, data_wyp, data_odd - 58 wierszy). Kolumna id_ks to klucz obcy do ksiazka.id; id_cz to numer czytelnika (bez osobnej tabeli - model jest uproszczony, jak mówi arkusz).
Typowe błędy
Import bez wcześniejszego wejścia do bazy biblioteka tworzy tabele w innej bazie (np. test) - skrypty nie znajdą wtedy tabeli ksiazka.
Krok 3: Zrzut ekranu import.png
Kryteria:
R.1.1 Wykonany import tabeli do bazy danych biblioteka , czynność udokumentowana w pliku import.png
ok. 2 min
Polecenie z arkusza
Wykonaj zrzut ekranu po imporcie. Zapisz zrzut w formacie PNG pod nazwą import. Nie kadruj zrzutu. Powinien on obejmować cały ekran monitora, z widocznym paskiem zadań.
Klawisz Print Screen (cały ekran, nie Alt+PrtScr, który łapie tylko aktywne okno), wklejasz do edytora grafiki i zapisujesz jako import.png w folderze z numerem zdającego. Na zrzucie ma być widać komunikat phpMyAdmin o pomyślnym imporcie albo obie tabele w bazie.
Uwaga na formaty w tym arkuszu: zrzut importu w PNG, pięć zrzutów kwerend w JPEG.
Typowe błędy
Wykadrowany zrzut (samo okno przeglądarki, bez paska zadań) nie jest oceniany - klucz mówi to wprost.
Krok 4: Zapytanie 1: książki z gatunku liryka
Kryteria: ,
R.1.2 Zapisany plik kwerendy.txt zawierający co najmniej jedno zapytanie SQL, wynikające z treści zadania
R.1.3 Wykonane zapytanie 1 wybierające jedynie id i tytuły książek z gatunku liryka. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT id, tytul FROM ksiazka WHERE gatunek = "liryka"; oraz wynik działania zapytania udokumentowany zrzutem, na którym widocznych jest dokładnie 5 rekordów z id: 1, 5, 6, 19, 20
ok. 2 min
Polecenie z arkusza
Zapytanie 1: wybierające jedynie id i tytuły książek z gatunku liryka.
Zakładka SQL w phpMyAdmin (przy zaznaczonej bazie biblioteka). Klauzula po klauzuli:
SELECT id, tytul - dokładnie dwie kolumny z arkusza; SELECT * dołożyłby gatunek.
FROM ksiazka - tabela książek.
WHERE gatunek = "liryka" - napis w cudzysłowie, dokładnie taki, jak w danych (małe litery).
Wynik: dokładnie 5 wierszy, id 1, 5, 6, 19, 20. Zapytanie kopiujesz do pliku kwerendy.txt i robisz zrzut: kw1.jpg. To samo zapytanie wyśle skrypt 2 do listy rozwijanej - a z gatunkiem „epika” i „dramat” jego kopie.
kwerendy.txtTekstnowy plik
-- Zapytanie 1SELECT id, tytul FROM ksiazka WHERE gatunek = "liryka";
R.1.4 Wykonane zapytanie 2 wybierające pierwsze 15 rekordów, jedynie tytuł książki oraz odpowiadające mu id czytelnika i data oddania posortowane rosnąco według daty oddania. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT tytul, id_cz, data_odd FROM ksiazka JOIN wypozyczenia ON id = id_ks ORDER BY data_odd LIMIT 15; (możliwe również INNER JOIN lub porównanie kluczy po WHERE) oraz wynik działania zapytania udokumentowany zrzutem, na którym widoczne jest dokładnie 15 rekordów, pierwsze 5 rekordów o id_cz: 1, 2, 1, 4, 4
ok. 4 min
Polecenie z arkusza
Zapytanie 2: wybierające pierwsze 15 rekordów, jedynie tytuł książki oraz odpowiadające mu id czytelnika i data oddania posortowane rosnąco według daty oddania. Należy posłużyć się relacją.
Tytuł jest w ksiazka, czytelnik i data w wypozyczenia - „posłużyć się relacją” znaczy złączyć tabele po kluczu.
SELECT tytul, id_cz, data_odd - jedna kolumna z pierwszej tabeli, dwie z drugiej.
FROM ksiazka JOIN wypozyczenia ON ksiazka.id = wypozyczenia.id_ks - do każdego wypożyczenia dokładasz książkę, której id równa się id_ks. Nazwy poprzedzasz tabelą dla czytelności.
ORDER BY data_odd - rosnąco (domyślne ASC), od najwcześniejszej daty oddania.
LIMIT 15 - „pierwsze 15 rekordów” po posortowaniu.
Wynik: dokładnie 15 wierszy, pierwsze pięć z czytelnikami 1, 2, 1, 4, 4. Na piętnastym miejscu jest remis: dwa wypożyczenia mają datę 2018-10-19 („Makbet” i „Śluby panieńskie”), a LIMIT zostawia tylko jedno z nich - które, zależy od bazy. MariaDB z XAMPP zwraca „Śluby panieńskie”, jak na ilustracji 2; klucz sprawdza tylko pierwsze pięć wierszy. Zrzut: kw2.jpg. To zapytanie wyśle skrypt 4 do listy zaległych książek.
Typowe błędy
LIMIT 15 bez ORDER BY zwraca piętnaście dowolnych wierszy, a ORDER BYpoLIMIT to błąd składni - kolejność klauzul: WHERE, ORDER BY, LIMIT.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT id, tytul FROM ksiazka WHERE gatunek = "liryka";-- Zapytanie 2SELECT tytul, id_cz, data_odd FROM ksiazka JOIN wypozyczenia ON ksiazka.id = wypozyczenia.id_ks ORDER BY data_odd LIMIT 15;
Podgląd strony po tym kroku
Krok 6: Zapytanie 3: nowa kolumna rezerwacja
Kryteria:
R.1.5 Wykonane zapytanie 3 modyfikujące strukturę tabeli ksiazka . Dodana jest nowa kolumna o nazwie rezerwacja typu jednocyfrowego TINYINT, przyjmująca wartość domyślną 0. W pliku z kwerendami lub na zrzucie istnieje zapis np.: ALTER TABLE ksiazka ADD COLUMN rezerwacja TINYINT(1) DEFAULT 0; (COLUMN opcjonalne) oraz wynik działania zapytania udokumentowany zrzutem, na którym widoczny jest komunikat o poprawnym wykonaniu zapytania lub zmieniona struktura tabeli
ok. 3 min
Polecenie z arkusza
Zapytanie 3: modyfikujące strukturę tabeli ksiazka, poprzez dodanie nowej kolumny o nazwie rezerwacja typu TINYINT, o długości 1 Bajta, która przyjmuje wartość domyślną 0 (oznacza, że książka nie jest zarezerwowana).
Zmiana struktury tabeli to ALTER TABLE (nie UPDATE, który zmienia dane). ADD COLUMN dokłada kolumnę na końcu: nazwa rezerwacja, typ TINYINT(1) - najmniejszy typ całkowity, jeden bajt, w praktyce 0 albo 1 - i DEFAULT 0, dzięki czemu wszystkie istniejące książki od razu dostają zero, czyli „nie zarezerwowana”.
Zrzut kw3.jpg ma pokazać komunikat o powodzeniu albo kartę Struktura z nową kolumną. Klucz R.5 ostrzega: bez tej kolumny skrypt 3 nie zadziała, bo ją aktualizuje.
Typowe błędy
BOOLEAN w MySQL to w istocie ten sam TINYINT(1), ale klucz R.1.5 wymienia TINYINT wprost - pisz tak, jak w arkuszu. Bez DEFAULT 0 stare wiersze dostaną NULL, nie zero.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT id, tytul FROM ksiazka WHERE gatunek = "liryka";-- Zapytanie 2SELECT tytul, id_cz, data_odd FROM ksiazka JOIN wypozyczenia ON ksiazka.id = wypozyczenia.id_ks ORDER BY data_odd LIMIT 15;-- Zapytanie 3ALTER TABLE ksiazka ADD COLUMN rezerwacja TINYINT(1) DEFAULT 0;
Podgląd strony po tym kroku
Krok 7: Zapytanie 4: rezerwacja książki o id 1
Kryteria:
R.1.6 Wykonane zapytanie 4 aktualizujące w tabeli ksiazka pole rezerwacja na wartość 1 dla id = 1. W pliku z kwerendami lub na zrzucie istnieje zapis np.: UPDATE ksiazka SET rezerwacja = 1 WHERE id = 1;
ok. 2 min
Polecenie z arkusza
Zapytanie 4: aktualizujące pole rezerwacja na wartość 1 dla książki, której id=1.
Zmiana danych to UPDATE: UPDATE ksiazka SET rezerwacja = 1 WHERE id = 1. SET mówi, co zmienić, WHERE - w którym wierszu. Bez WHERE zarezerwowałyby się wszystkie książki. Zrzut kw4.jpg ma pokazać komunikat o zmodyfikowanym wierszu.
Ten sam UPDATE (z id z formularza) wyśle skrypt 3 po kliknięciu „Rezerwuj”.
Typowe błędy
UPDATE ksiazka SET rezerwacja = 1 bez WHERE zmienia wszystkie 20 wierszy. Przy UPDATE zawsze najpierw SELECT z tym samym WHERE.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT id, tytul FROM ksiazka WHERE gatunek = "liryka";-- Zapytanie 2SELECT tytul, id_cz, data_odd FROM ksiazka JOIN wypozyczenia ON ksiazka.id = wypozyczenia.id_ks ORDER BY data_odd LIMIT 15;-- Zapytanie 3ALTER TABLE ksiazka ADD COLUMN rezerwacja TINYINT(1) DEFAULT 0;-- Zapytanie 4UPDATE ksiazka SET rezerwacja = 1 WHERE id = 1;
Podgląd strony po tym kroku
Krok 8: Zapytanie 5: tytuł książki o id 4
Kryteria:
R.1.7 Wykonane zapytanie 5 wybierające jedynie tytuł książki dla id = 4. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT tytul FROM ksiazka WHERE id = 4; oraz wynik działania zapytania udokumentowany zrzutem, na którym widoczny jest tytuł Antygona
ok. 1 min
Polecenie z arkusza
Zapytanie 5: wybierające jedynie tytuł książki, której id=4.
SELECT tytul FROM ksiazka WHERE id = 4 - jedna kolumna, jeden wiersz po kluczu głównym. Wynik: Antygona. Zrzut: kw5.jpg. Plik kwerendy.txt ma komplet pięciu zapytań. Ten SELECT (z id z formularza) wyśle skrypt 3, żeby wypisać tytuł zarezerwowanej książki.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT id, tytul FROM ksiazka WHERE gatunek = "liryka";-- Zapytanie 2SELECT tytul, id_cz, data_odd FROM ksiazka JOIN wypozyczenia ON ksiazka.id = wypozyczenia.id_ks ORDER BY data_odd LIMIT 15;-- Zapytanie 3ALTER TABLE ksiazka ADD COLUMN rezerwacja TINYINT(1) DEFAULT 0;-- Zapytanie 4UPDATE ksiazka SET rezerwacja = 1 WHERE id = 1;-- Zapytanie 5SELECT tytul FROM ksiazka WHERE id = 4;
Podgląd strony po tym kroku
Etap 2: Grafika i strona HTML
Kroki 9-23 · po tym etapie masz 20 z 42 kryteriów (pkt)
Krok 9: Grafika obraz.png: wysokość 200 px, przezroczyste tło
Kryteria:
R.2.1 Grafika obraz.png ma tło przezroczyste oraz została przeskalowana z zachowaniem proporcji do wysokości 200 px, odpowiadająca temu szerokość wynosi 188 px (± 5 px)
ok. 5 min
Polecenie z arkusza
Cechy grafiki obraz.jpg: przeskalowana z zachowaniem proporcji do wysokości 200 px. Przezroczystość zamiast białego tła. Zapisana pod tą samą nazwą, w formacie PNG.
Plik obraz.jpg z archiwum (rząd książek na białym tle) ma 450 × 480 px. W GIMP-ie:
Warstwa → Przezroczystość → Dodaj kanał alfa - JPEG nie ma przezroczystości, trzeba ją najpierw włączyć,
Obraz → Skaluj obraz, wysokość 200 px z zamkniętym łańcuchem proporcji - szerokość wychodzi 188 px (450 · 200 / 480 = 187,5; klucz: 188 ±5),
Plik → Wyeksportuj jakoobraz.png - „pod tą samą nazwą” znaczy obraz, ale w formacie PNG, bo tylko PNG zachowa przezroczystość.
Typowe błędy
Eksport do .jpg zabija przezroczystość (tło wraca białe) - klucz R.2.1 sprawdza przezroczyste tło. Skalowanie przed usunięciem tła też działa, ale różdżka na większym obrazie zaznacza dokładniej.
Podgląd strony po tym kroku
Krok 10: Plik biblioteka.php w HTML5
Kryteria:
R.2.2 W pliku biblioteka.php zapisana deklaracja HTML5 <!DOCTYPE HTML>, deklaracja języka zawartości: polski np. <html lang="pl">, w sekcji head znajduje się jawnie podany standard kodowania polskich znaków
ok. 2 min
Polecenie z arkusza
Składa się ze strony o nazwie biblioteka.php. Zapisana w języku HTML5.
W edytorze zaznaczającym składnię tworzysz plik biblioteka.php w folderze zdającego, obok obraz.png. Rozszerzenie .php, bo dojdą cztery skrypty PHP - na razie to czysty HTML.
Pierwsza linia <!DOCTYPE html> mówi przeglądarce, że dokument jest w HTML5. Dalej szkielet każdej strony: <html>, a w nim <head> (informacje o stronie) i <body> (to, co widać w oknie).
R.2.2 W pliku biblioteka.php zapisana deklaracja HTML5 <!DOCTYPE HTML>, deklaracja języka zawartości: polski np. <html lang="pl">, w sekcji head znajduje się jawnie podany standard kodowania polskich znaków
ok. 1 min
Polecenie z arkusza
Zadeklarowany polski język zawartości witryny.
Atrybut lang="pl" w znaczniku <html>. Czytnik ekranu czyta wtedy tekst polską wymową, a przeglądarka dobiera słownik. Na stronie nic się nie zmienia - to informacja dla programów.
R.2.2 W pliku biblioteka.php zapisana deklaracja HTML5 <!DOCTYPE HTML>, deklaracja języka zawartości: polski np. <html lang="pl">, w sekcji head znajduje się jawnie podany standard kodowania polskich znaków
ok. 1 min
Polecenie z arkusza
Jawnie zastosowany właściwy standard kodowania polskich znaków.
<meta charset="UTF-8"> jako pierwsza rzecz w <head>. „Jawnie” znaczy: wpisane w kodzie, nie zdane na domysł przeglądarki. Bez tej linii tytuły z bazy („Pieśń nad pieśniami”) mogłyby wyświetlić się jako krzaki. Plik trzeba też zapisać w UTF-8.
R.3.2 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl.css formatowanie CSS pochodzi jedynie z tego arkusza
ok. 1 min
Polecenie z arkusza
Arkusz stylów w pliku o nazwie styl.css prawidłowo połączony z kodem strony.
<link rel="stylesheet" href="styl.css"> w <head>. W tym arkuszu nazwa to styl.css - sprawdź ją w treści zadania, bo w innych arkuszach bywa style.css albo styles.css. Plik napiszesz po HTML.
Typowe błędy
Zła nazwa pliku w href to brak stylów i cały rezultat R.4 przepada, bo „formatowanie CSS pochodzi jedynie z tego arkusza”.
R.2.4 W sekcji body zdefiniowany jest podział na bloki za pomocą semantycznych znaczników HTML 5: header, section+section+section+section, footer
ok. 2 min
Polecenie z arkusza
Podział strony na bloki zrealizowany za pomocą semantycznych znaczników bloków języka HTML5 tak, aby po uruchomieniu w przeglądarce układ bloków na stronie był zgodny z ilustracją 3.
Z ilustracji 3: blok nagłówkowy, pod nim cztery bloki sekcji obok siebie, na dole stopka. Klucz R.2.4 wymienia znaczniki wprost: <header>, cztery razy <section>, <footer>. Tu nie ma <main> ani <nav> - arkusz ich nie wymaga, a klucz ich nie wymienia.
Sekcje nie potrzebują identyfikatorów: wszystkie cztery będą stylowane tą samą regułą section, a treść rozróżnia je sama. Nazwy identyfikatorów, klas i zmiennych piszesz w tym rozwiązaniu po angielsku (arkusz dopuszcza polski lub angielski, wymaga konsekwencji); wyjątkiem są nazwy narzucone przez arkusz (biblioteka.php, styl.css, kolumny bazy).
Typowe błędy
Cztery <div> wyglądają identycznie, ale kryterium R.2.4 wymaga znaczników semantycznych - sprawdzane w kodzie źródłowym.
R.2.6 W bloku nagłówkowym umieszczona przynajmniej jedna grafika obraz.png (w HTML lub skrypcie)
ok. 1 min
Polecenie z arkusza
Zawartość bloku nagłówkowego: efekt działania skryptu 1.
Nagłówek zostaje w HTML pusty - wypełni go skrypt 1 (dwadzieścia grafik z pętli). Nic tu nie dopisujesz; to zdanie arkusza realizujesz w etapie PHP. Przejdź do sekcji.
R.2.7 W przynajmniej jednym bloku umieszczony formularz
R.3.3 Przynajmniej jeden formularz wysyła dane metodą POST do tego samego pliku (poprawne również bez atrybutu action)
ok. 1 min
Polecenie z arkusza
Formularz wysyłający dane metodą bezpieczną do tego samego pliku, z elementami: lista rozwijana i przycisk „Rezerwuj”.
„Metoda bezpieczna” w rozumieniu arkusza to POST - dane nie pojawiają się w adresie strony. W specyfikacji HTTP „bezpieczne” są co prawda metody, które niczego nie zmieniają na serwerze (GET i HEAD), ale arkusz używa tego słowa w innym sensie, a klucz wymaga POST. „Do tego samego pliku” to brak atrybutu action (przeglądarka wysyła formularz na adres bieżącej strony) albo action="biblioteka.php" - klucz R.3.3 przyjmuje oba warianty.
Typowe błędy
method="get" (albo brak method) wyśle id książki w adresie - arkusz i klucz wymagają tu POST, a skrypt czytający $_POST nic nie dostanie.
R.2.8 W przynajmniej jednym bloku umieszczona lista rozwijana i przycisk "Rezerwuj", do listy zastosowane znaczniki <select> i <option> (w HTML lub skrypcie)
ok. 1 min
Polecenie z arkusza
Lista rozwijana wypełniona skryptem 2.
<select> z atrybutem name="poetry" - pod tą nazwą wybrane id trafi do PHP jako $_POST['poetry']. Każda z trzech sekcji dostanie inną nazwę listy (poetry, epic, drama) - dzięki temu skrypt 3 pozna, z której sekcji przyszedł formularz, i wypisze komunikat tylko tam (arkusz podkreśla to na stronie 5). Opcje wstawi skrypt 2, więc lista jest na razie pusta.
Typowe błędy
Ta sama nazwa listy w trzech formularzach sprawia, że po rezerwacji w „Epika” komunikat pojawi się także w „Liryka” i „Dramat” - R.5.8 przepada.
R.2.8 W przynajmniej jednym bloku umieszczona lista rozwijana i przycisk "Rezerwuj", do listy zastosowane znaczniki <select> i <option> (w HTML lub skrypcie)
R.3.4 Przynajmniej jeden przycisk realizuje funkcję wysłania danych z formularza (input lub button z typem submit lub button bez typu)
ok. 1 min
Polecenie z arkusza
Przycisk „Rezerwuj” wysyłający dane z formularza do skryptu 3.
<button type="submit">Rezerwuj</button> wewnątrz formularza wysyła go po kliknięciu. Klucz R.3.4 przyjmuje też <input type="submit" value="Rezerwuj">. „Efekt działania skryptu 3” (paragraf z komunikatem) pojawi się pod formularzem - dopisze go PHP.
R.2.5 Umieszczone w blokach sekcji nagłówki <h2>: „Liryka”, „Epika”, „Dramat”, „Zaległe książki” oraz paragraf z numerem zdającego w stopce
R.2.7 W przynajmniej jednym bloku umieszczony formularz
R.2.8 W przynajmniej jednym bloku umieszczona lista rozwijana i przycisk "Rezerwuj", do listy zastosowane znaczniki <select> i <option> (w HTML lub skrypcie)
ok. 2 min
Polecenie z arkusza
Zawartość drugiego bloku sekcji: nagłówek drugiego stopnia o treści „Epika”, formularz wysyłający dane metodą bezpieczną do tego samego pliku, z elementami: lista rozwijana wypełniona zmodyfikowanym skryptem 2 (gatunek to „epika”), przycisk „Rezerwuj” wysyłający dane z formularza do skryptu 3. Zawartość trzeciego bloku sekcji: nagłówek drugiego stopnia o treści „Dramat”, formularz wysyłający dane metodą bezpieczną do tego samego pliku, z elementami: lista rozwijana wypełniona zmodyfikowanym skryptem 2 (gatunek to „dramat”), przycisk „Rezerwuj” wysyłający dane z formularza do skryptu 3.
Dwie kolejne sekcje to kopia pierwszej z innym nagłówkiem i inną nazwą listy: epic dla Epiki, drama dla Dramatu. Arkusz sam mówi „powielić i dostosować” - kopiujesz, zmieniasz tylko to, co się różni. Trzy formularze w jednym pliku to nic złego: PHP rozpozna po nazwie listy, który został wysłany.
R.2.5 Umieszczone w blokach sekcji nagłówki <h2>: „Liryka”, „Epika”, „Dramat”, „Zaległe książki” oraz paragraf z numerem zdającego w stopce
R.2.10 Zastosowany znacznik <strong> do zapisu treści stopki
ok. 1 min
Polecenie z arkusza
Zawartość stopki: paragraf o treści „Autor: ”, dalej wstawiony numer zdającego. Tekst w paragrafie jest zapisany za pomocą znacznika semantycznego oznaczającego tekst ważny, formatowany domyślnie jako pogrubiony.
Paragraf <p> w <footer>. „Znacznik semantyczny oznaczający tekst ważny, domyślnie pogrubiony” to <strong> - nie <b>, które pogrubia bez znaczenia. Arkusz mówi „tekst w paragrafie”, więc w <strong> jest cała treść paragrafu (klucz R.2.10). W tym rozwiązaniu po dwukropku stoi nazwisko autora - na egzaminie wpisujesz swój numer zdającego. Struktura HTML jest kompletna - dalej style.
Typowe błędy
<b> wygląda tak samo (pogrubienie), ale to znacznik prezentacyjny - klucz R.2.10 wymaga <strong>.
Kroki 24-31 · po tym etapie masz 32 z 42 kryteriów (pkt)
Krok 24: Plik styl.css: krój czcionki
Kryteria:
R.4.1 Ustawione domyślnie dla wszystkich selektorów (*): krój czcionki Cambria
ok. 1 min
Polecenie z arkusza
Domyślnie dla wszystkich selektorów: krój czcionki Cambria.
Utwórz plik styl.css obok biblioteka.php. Selektor uniwersalny * obejmuje każdy element strony, także przyciski i listy. W podglądzie zmienia się czcionka - znak, że <link> zadziałał.
styl.cssCSSnowy plik
* {font-family: Cambria;}
Podgląd strony po tym kroku (biblioteka.php)
Krok 25: Ciało strony: kolor tła
Kryteria:
R.4.3 Ustawiony dla selektora body kolor tła MistyRose
ok. 1 min
Polecenie z arkusza
Dla selektora ciała strony: kolor tła MistyRose.
Selektor znacznika body - arkusz zastrzega, że ciało strony stylujesz wyłącznie selektorem znacznika. Tło całego okna robi się bladoróżowe.
Krok 26: Nagłówek i stopka: wysokość, wyrównanie, ukrywanie nadmiaru
Kryteria: , ,
R.4.4 Ustawiona wysokość bloku nagłówkowego i stopki 200px oraz wszystkich bloków sekcji 400px
R.4.5 Ustawione wyrównanie tekstu do środka dla bloku nagłówkowego i stopki oraz selektora h2
R.4.7 Ustawiona dla bloków nagłówkowego, stopki, wszystkich sekcji cecha overflow: hidden;
ok. 2 min
Polecenie z arkusza
Dla bloku nagłówkowego i stopki: wysokość 200 px, wyrównanie tekstu do środka, w przypadku przepełnienia bloku zawartość poza blokiem jest ukrywana.
Wspólne cechy dwóch bloków piszesz raz - header, footer. „Zawartość poza blokiem jest ukrywana” to overflow: hidden - ani pasków przewijania, ani wystawania; co nie mieści się w 200 px, jest obcięte. To ważne dla nagłówka: dwadzieścia grafik po 188 px na pewno nie zmieści się w jednym wierszu i reszta ma zniknąć (klucz R.5.3 wprost dopuszcza ukryte grafiki).
Typowe błędy
overflow: auto dałoby paski przewijania w nagłówku, scroll - zawsze; arkusz mówi „ukrywana”, czyli hidden (R.4.7).
R.4.2 Ustawiony dla wszystkich bloków sekcji kolor tła Plum
R.4.4 Ustawiona wysokość bloku nagłówkowego i stopki 200px oraz wszystkich bloków sekcji 400px
R.4.6 Ustawione marginesy zewnętrzne (margin) dla wszystkich bloków sekcji 5px
R.4.7 Ustawiona dla bloków nagłówkowego, stopki, wszystkich sekcji cecha overflow: hidden;
R.4.8 Ustawiona dla wszystkich bloków sekcji cecha box-shadow: 4px 4px 10px DimGray;
ok. 2 min
Polecenie z arkusza
Dla bloków sekcji: kolor tła Plum, wysokość 400 px, marginesy zewnętrzne 5 px, cień bloku o przesunięciu 4 px w obu osiach, rozmyciu 10 px i kolorze DimGray, w przypadku przepełnienia bloku zawartość poza blokiem jest ukrywana.
Jedna reguła section obejmuje wszystkie cztery sekcje. Cień bloku to box-shadow z tabeli 2 arkusza: przesunięcie w poziomie, w pionie, rozmycie, kolor - 4px 4px 10px DimGray (klucz R.4.8 podaje ten zapis). overflow: hidden jak w nagłówku - lista zaległych książek może być dłuższa niż 400 px i reszta ma zniknąć.
Typowe błędy
text-shadow zamiast box-shadow da cień literom - tabela 2 pokazuje oba, żeby sprawdzić, czy czytasz uważnie. Arkusz mówi „cień bloku”.
Krok 28: Szerokie ekrany: cztery sekcje obok siebie
Kryteria: , ,
R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z ilustracją 3 w arkuszu egzaminacyjnym dla ekranów o szerokości powyżej 800 px (prawidłowo zastosowane właściwości CSS decydujące o układzie bloków na stronie, np. float i clear albo display flex albo grid albo position)
R.3.5 Sekcje są umiejscowione obok siebie dla ekranu o szerokości powyżej 800 px; dla ekranu o szerokości mniejszej niż 800 px, sekcje są umiejscowione jedna pod drugą
R.4.9 W przypadku ekranów o szerokości do 800 px szerokość bloków sekcji 100%, powyżej 800 px szerokość bloków sekcji 22%, np. @media only screen and (max-width: 800px) { section { width:100%; } } (wystarczy jedna reguła @media, wtedy szerokość w sekcji wymagana, obie szerokości 100% i 22% muszą być zdefiniowane)
ok. 3 min
Polecenie z arkusza
W przypadku ekranów o szerokości do 800 px szerokość bloków sekcji 100%, powyżej 800 px szerokość bloków sekcji 22%.
„Powyżej 800 px” to zapytanie medialne @media (min-width: 801px) (tabela 3 arkusza pokazuje składnię @media). W środku: width: 22% i float: left, żeby sekcje pływały obok siebie - cztery razy 22% plus marginesy po 5 px mieszczą się w jednym wierszu. Stopka dostaje clear: both, żeby wróciła pod pływające sekcje. To układ z ilustracji 3 (klucz R.3.1 przyjmuje też flex i grid).
R.3.5 Sekcje są umiejscowione obok siebie dla ekranu o szerokości powyżej 800 px; dla ekranu o szerokości mniejszej niż 800 px, sekcje są umiejscowione jedna pod drugą
R.4.9 W przypadku ekranów o szerokości do 800 px szerokość bloków sekcji 100%, powyżej 800 px szerokość bloków sekcji 22%, np. @media only screen and (max-width: 800px) { section { width:100%; } } (wystarczy jedna reguła @media, wtedy szerokość w sekcji wymagana, obie szerokości 100% i 22% muszą być zdefiniowane)
ok. 2 min
Polecenie z arkusza
W przypadku ekranów o szerokości do 800 px szerokość bloków sekcji 100%.
Drugie zapytanie medialne, @media (max-width: 800px) - „do 800 px” włącznie. Sekcje dostają width: 100% i bez float stoją jedna pod drugą, jak na prawej części ilustracji 2. Klucz R.4.9 wymaga obu szerokości (100% i 22%) i podaje ten zapis jako przykład; R.3.5 sprawdza, zwężając okno przeglądarki.
Typowe błędy
Jedna reguła section { width: 22% } bez @media daje wąskie sekcje także na telefonie - R.3.5 i R.4.9 wymagają dwóch szerokości zależnych od okna.
R.4.10 Gdy kursor znajdzie się na dowolnym przycisku jego kolor tła zmienia się na #505050 a kolor czcionki na biały
ok. 1 min
Polecenie z arkusza
Gdy kursor znajdzie się na dowolnym przycisku jego kolor tła zmienia się na #505050 a kolor czcionki na biały.
Pseudoklasa :hover na button - „dowolny przycisk”, więc selektor znacznika obejmuje wszystkie trzy „Rezerwuj”. Kolor szesnastkowy #505050 to ciemna szarość. Najedź w podglądzie na przycisk. Arkusz stylów jest kompletny.
Kroki 32-46 · po tym etapie masz 42 z 42 kryteriów (pkt)
Krok 32: Połączenie z bazą danych
Kryteria: ,
R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy biblioteka
R.5.10 W skryptach zastosowana przynajmniej jedna zmienna oraz nazwy wszystkich zmiennych są w języku polskim lub angielskim i odzwierciedlają cel zastosowania
ok. 3 min
Polecenie z arkusza
Napisane w języku PHP. Łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie biblioteka.
Kod PHP piszesz między <?php a ?> - serwer wykonuje go, zanim strona trafi do przeglądarki. Połączenie otwierasz raz, na samym początku pliku, i trzymasz w zmiennej $connection (znacząca, angielska nazwa - R.5.10), z której skorzystają wszystkie skrypty. mysqli_connect z tabeli 1 arkusza przyjmuje kolejno: serwer, użytkownika, hasło (puste) i nazwę bazy.
Od tego kroku podgląd to wynik działania strony po stronie serwera (PHP nie wykonuje się w przeglądarce) - na razie wygląda jak wcześniej.
Typowe błędy
Kolejność argumentów ma znaczenie: mysqli_connect('localhost', 'root', 'biblioteka') podaje „biblioteka” jako HASŁO. Nazwa bazy jest czwarta. Plik sprawdzaj przez http://localhost/…, nie otwierając go z dysku.
R.5.3 Skrypt 1 wyświetla 20 razy grafikę za pomocą pętli (w zależności od szerokości ekranu grafiki mogą być ukryte)
ok. 2 min
Polecenie z arkusza
Skrypt 1: wyświetla 20 razy grafikę obraz.png. Należy zastosować instrukcję iteracyjną (pętlę).
Skrypt 1 piszesz wewnątrz<header>. „Instrukcja iteracyjna” to pętla - najprostsza na znaną z góry liczbę powtórzeń to for: for ($i = 0; $i < 20; $i++) wykona ciało dokładnie 20 razy (licznik od 0 do 19). Trzy części w nawiasie: start, warunek, krok. Ciało jest jeszcze puste. Klucz R.5.3 sprawdza, że grafiki pochodzą z pętli - dwadzieścia ręcznie wklejonych <img> nie spełnia wymagania.
Typowe błędy
for ($i = 1; $i < 20; $i++) to 19 obiegów, $i <= 20 od zera - 21. Licz od 0 z <, albo od 1 z <=.
R.2.6 W bloku nagłówkowym umieszczona przynajmniej jedna grafika obraz.png (w HTML lub skrypcie)
R.5.3 Skrypt 1 wyświetla 20 razy grafikę za pomocą pętli (w zależności od szerokości ekranu grafiki mogą być ukryte)
ok. 2 min
Polecenie z arkusza
Wyświetla 20 razy grafikę obraz.png.
echo w pętli wypisuje jeden <img src='obraz.png'> na obieg - razem dwadzieścia. Atrybuty w apostrofach, bo cały napis jest w cudzysłowie; alt nie jest wymagany, ale obraz bez tekstu alternatywnego to zły nawyk. Dzięki overflow: hidden i wysokości 200 px nagłówek pokazuje tyle grafik, ile się mieści w szerokości okna - rząd książek jak na ilustracji 2.
R.5.4 Przynajmniej jeden skrypt zawiera instrukcję wysyłającą do bazy danych zapytanie
ok. 2 min
Polecenie z arkusza
Skrypt 2 w sekcji „Liryka”: wysyła do bazy danych zapytanie 1.
Skrypt 2 piszesz wewnątrz<select> sekcji Liryka, bo tam mają trafić opcje. mysqli_query($connection, ...) wysyła zapytanie i zwraca wynik, który zapisujesz w $poetry_books. Treść to dokładnie zapytanie 1 z kwerendy.txt. Kolejność kolumn zaraz będzie ważna: $row[0] to id, $row[1] tytuł.
R.5.5 Skrypt 2 w przynajmniej jednej sekcji wyświetla tytuły książek zwrócone zapytaniem. Rodzaj utworów zgadza się z nazwą sekcji (liryka, epika, dramat) . Atrybut value każdej opcji to id zwrócone zapytaniem
ok. 2 min
Polecenie z arkusza
Ze zwróconych zapytaniem wierszy generuje kolejne opcje listy rozwijanej w ten sposób, że tytuł jest treścią opcji, a id jest przypisane do atrybutu value.
„Ze zwróconych wierszy” to pętla. mysqli_fetch_row($poetry_books) (tabela 1) oddaje kolejny wiersz wyniku jako tablicę numerowaną od zera, a gdy wierszy zabraknie - false. Zapis while ($row = mysqli_fetch_row(...)) przypisuje wiersz do $row i od razu sprawdza, czy coś przyszło - pętla wykona się pięć razy. Ciało jest jeszcze puste.
Typowe błędy
Pojedyncze = w warunku while jest tu celowe (przypisanie); == porównałoby zmienną z wynikiem i pętla nie zadziałałaby poprawnie.
R.5.5 Skrypt 2 w przynajmniej jednej sekcji wyświetla tytuły książek zwrócone zapytaniem. Rodzaj utworów zgadza się z nazwą sekcji (liryka, epika, dramat) . Atrybut value każdej opcji to id zwrócone zapytaniem
ok. 3 min
Polecenie z arkusza
Ze zwróconych zapytaniem wierszy generuje kolejne opcje listy rozwijanej w ten sposób, że tytuł jest treścią opcji, a id jest przypisane do atrybutu value.
echo w pętli wypisuje jeden <option>: id w atrybucie value ($row[0]), tytuł jako treść między znacznikami ($row[1]). Użytkownik widzi tytuł, ale formularz wyśle id - dokładnie to, czego potrzebuje skrypt 3 do WHERE id = …. Atrybut w apostrofach, bo napis echo jest w cudzysłowie. Podgląd: lista Liryka ma pięć tytułów, jak na ilustracji 4.
Typowe błędy
<option>$row[1]</option> bez value wyśle tytuł zamiast id - skrypt 3 z WHERE id = Treny zwróci błąd. Klucz R.5.5 sprawdza value równe id.
R.5.5 Skrypt 2 w przynajmniej jednej sekcji wyświetla tytuły książek zwrócone zapytaniem. Rodzaj utworów zgadza się z nazwą sekcji (liryka, epika, dramat) . Atrybut value każdej opcji to id zwrócone zapytaniem
R.5.8 Skrypty 2 i 3 działają we wszystkich trzech sekcjach zgadza się gatunek "Liryka", "Epika", "Dramat" z wyświetlanymi w danej liście tytułami oraz tekst pojawia się tylko pod listą w której był dokonany wybór
ok. 3 min
Polecenie z arkusza
Skrypt 2 do sekcji „Epika” należy powielić i dostosować: działa on na zmodyfikowanym zapytaniu 1, tak że wypełnia listę rozwijaną książkami z gatunku „epika”. Skrypt 2 do sekcji „Dramat” należy powielić i dostosować: wypełnia listę rozwijaną książkami z gatunku „dramat”.
Skopiuj skrypt do dwóch pozostałych list i zmień dwie rzeczy: gatunek w WHERE (epika, dramat) i nazwę zmiennej z wynikiem ($epic_books, $drama_books), żeby nie nadpisać poprzedniej. Podgląd: trzy listy, w każdej książki właściwego gatunku (8 epiki, 7 dramatów) - klucz R.5.8 sprawdza zgodność gatunku z nazwą sekcji.
Trzy prawie identyczne kopie to sygnał, że w refaktorze zastąpi je jedna funkcja.
Typowe błędy
Skopiowany skrypt z niezmienionym WHERE gatunek = "liryka" wyświetli we wszystkich trzech listach te same tytuły - R.5.8 przepada.
Krok 39: Skrypt 3: tylko gdy wybrano z listy liryki
Kryteria:
R.5.8 Skrypty 2 i 3 działają we wszystkich trzech sekcjach zgadza się gatunek "Liryka", "Epika", "Dramat" z wyświetlanymi w danej liście tytułami oraz tekst pojawia się tylko pod listą w której był dokonany wybór
ok. 2 min
Polecenie z arkusza
Skrypt 3, jeżeli dokonano wyboru w liście rozwijanej liryki wykonuje czynności: wysyła do bazy danych zapytanie 5 zmodyfikowane tak, że w klauzuli WHERE podana jest wartość id wysłana przez formularz, wyświetla w paragrafie tekst, wysyła do bazy danych zmodyfikowane zapytanie 4.
Skrypt 3 piszesz w sekcji Liryka pod formularzem - tam ma się pojawić komunikat („efekt działania skryptu 3” z opisu sekcji). isset($_POST['poetry']) (tabela 1) jest prawdziwe tylko wtedy, gdy wysłano formularz z listą poetry, czyli ten z sekcji Liryka - formularze Epiki i Dramatu mają inne nazwy list, więc tu nic nie uruchomią. Tak spełniasz uwagę arkusza, że paragraf ma pojawić się tylko w odpowiedniej sekcji.
R.5.4 Przynajmniej jeden skrypt zawiera instrukcję wysyłającą do bazy danych zapytanie
ok. 2 min
Polecenie z arkusza
Wysyła do bazy danych zapytanie 5, zmodyfikowane w ten sposób, że w klauzuli WHERE podana jest wartość id wysłana przez formularz.
Id wybranej książki trafia z $_POST['poetry'] do $book_id (to wartość atrybutu value opcji, czyli id). Zapytanie 5 z kwerendy.txt dostaje je w miejsce 4: SELECT tytul FROM ksiazka WHERE id = $book_id. Napis w cudzysłowie, żeby PHP podstawił zmienną. Wynik trafia do $result.
biblioteka.phpPHPzmienione: 2 linie
<?php$connection = mysqli_connect('localhost', 'root', '', 'biblioteka');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Biblioteka miejska</title> <linkrel="stylesheet"href="styl.css"></head><body> <header><?phpfor ($i = 0; $i < 20; $i++) {echo"<img src='obraz.png' alt='książki'>";}?> </header> <section> <h2>Liryka</h2> <formmethod="post"> <selectname="poetry"><?php$poetry_books = mysqli_query($connection, 'SELECT id, tytul FROM ksiazka WHERE gatunek = "liryka";');while ($row = mysqli_fetch_row($poetry_books)) {echo"<option value='$row[0]'>$row[1]</option>";}?> </select> <buttontype="submit">Rezerwuj</button> </form><?phpif (isset($_POST['poetry'])) {$book_id = $_POST['poetry'];$result = mysqli_query($connection, "SELECT tytul FROM ksiazka WHERE id = $book_id;");}?> </section> <section> <h2>Epika</h2> <formmethod="post"> <selectname="epic"><?php$epic_books = mysqli_query($connection, 'SELECT id, tytul FROM ksiazka WHERE gatunek = "epika";');while ($row = mysqli_fetch_row($epic_books)) {echo"<option value='$row[0]'>$row[1]</option>";}?> </select> <buttontype="submit">Rezerwuj</button> </form> </section> <section> <h2>Dramat</h2> <formmethod="post"> <selectname="drama"><?php$drama_books = mysqli_query($connection, 'SELECT id, tytul FROM ksiazka WHERE gatunek = "dramat";');while ($row = mysqli_fetch_row($drama_books)) {echo"<option value='$row[0]'>$row[1]</option>";}?> </select> <buttontype="submit">Rezerwuj</button> </form> </section> <section> <h2>Zaległe książki</h2> <ul> </ul> </section> <footer> <p><strong>Autor: Bartosz Bryniarski</strong></p> </footer></body></html>
R.5.6 Skrypt 3 wyświetla zwrócony zapytaniem tytuł. Tytuł jest równy wybranemu z listy rozwijanej. Jest wyświetlany w postaci: "Książka <tytuł> została zarezerwowana"
ok. 2 min
Polecenie z arkusza
Wyświetla w paragrafie tekst Książka <tytuł> została zarezerwowana, gdzie pole <> zawiera wartość wybraną z bazy.
Zapytanie po kluczu głównym zwraca jeden wiersz, więc bez pętli: mysqli_fetch_row($result) do $book, tytuł w $book[0]. echo wypisuje paragraf z tekstem dokładnie według wzoru z arkusza. Podgląd pokazuje stan z ilustracji 5: po wybraniu „Na dom w Czarnolesie” i kliknięciu „Rezerwuj” komunikat jest tylko w sekcji Liryka.
biblioteka.phpPHPzmienione: 2 linie
<?php$connection = mysqli_connect('localhost', 'root', '', 'biblioteka');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Biblioteka miejska</title> <linkrel="stylesheet"href="styl.css"></head><body> <header><?phpfor ($i = 0; $i < 20; $i++) {echo"<img src='obraz.png' alt='książki'>";}?> </header> <section> <h2>Liryka</h2> <formmethod="post"> <selectname="poetry"><?php$poetry_books = mysqli_query($connection, 'SELECT id, tytul FROM ksiazka WHERE gatunek = "liryka";');while ($row = mysqli_fetch_row($poetry_books)) {echo"<option value='$row[0]'>$row[1]</option>";}?> </select> <buttontype="submit">Rezerwuj</button> </form><?phpif (isset($_POST['poetry'])) {$book_id = $_POST['poetry'];$result = mysqli_query($connection, "SELECT tytul FROM ksiazka WHERE id = $book_id;");$book = mysqli_fetch_row($result);echo"<p>Książka $book[0] została zarezerwowana</p>";}?> </section> <section> <h2>Epika</h2> <formmethod="post"> <selectname="epic"><?php$epic_books = mysqli_query($connection, 'SELECT id, tytul FROM ksiazka WHERE gatunek = "epika";');while ($row = mysqli_fetch_row($epic_books)) {echo"<option value='$row[0]'>$row[1]</option>";}?> </select> <buttontype="submit">Rezerwuj</button> </form> </section> <section> <h2>Dramat</h2> <formmethod="post"> <selectname="drama"><?php$drama_books = mysqli_query($connection, 'SELECT id, tytul FROM ksiazka WHERE gatunek = "dramat";');while ($row = mysqli_fetch_row($drama_books)) {echo"<option value='$row[0]'>$row[1]</option>";}?> </select> <buttontype="submit">Rezerwuj</button> </form> </section> <section> <h2>Zaległe książki</h2> <ul> </ul> </section> <footer> <p><strong>Autor: Bartosz Bryniarski</strong></p> </footer></body></html>
Podgląd strony po tym kroku (biblioteka.php)
Krok 42: Skrypt 3: zapytanie 4 z id z formularza
Kryteria: ,
R.5.4 Przynajmniej jeden skrypt zawiera instrukcję wysyłającą do bazy danych zapytanie
R.5.7 Skrypt 3 dla wybranej z listy książki uaktualnia jej rekord w bazie danych, wpisując do pola rezerwacja wartość 1. Należy sprawdzić w phpMyAdmin
ok. 2 min
Polecenie z arkusza
Wysyła do bazy danych zmodyfikowane zapytanie 4, w ten sposób że w klauzuli WHERE podana jest wartość id wysłana przez formularz.
Zapytanie 4 z kwerendy.txt z $book_id zamiast 1: UPDATE ksiazka SET rezerwacja = 1 WHERE id = $book_id. mysqli_query wykonuje je - nic nie wypisuje, efekt sprawdza się w phpMyAdmin (klucz R.5.7). Kolumna rezerwacja musi istnieć - to zapytanie 3 z etapu bazy.
Typowe błędy
Wartość z formularza wstawiona prosto do zapytania to podatność SQL injection - na egzaminie wystarcza, w refaktorze to naprawisz.
biblioteka.phpPHPzmienione: 1 linia
<?php$connection = mysqli_connect('localhost', 'root', '', 'biblioteka');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Biblioteka miejska</title> <linkrel="stylesheet"href="styl.css"></head><body> <header><?phpfor ($i = 0; $i < 20; $i++) {echo"<img src='obraz.png' alt='książki'>";}?> </header> <section> <h2>Liryka</h2> <formmethod="post"> <selectname="poetry"><?php$poetry_books = mysqli_query($connection, 'SELECT id, tytul FROM ksiazka WHERE gatunek = "liryka";');while ($row = mysqli_fetch_row($poetry_books)) {echo"<option value='$row[0]'>$row[1]</option>";}?> </select> <buttontype="submit">Rezerwuj</button> </form><?phpif (isset($_POST['poetry'])) {$book_id = $_POST['poetry'];$result = mysqli_query($connection, "SELECT tytul FROM ksiazka WHERE id = $book_id;");$book = mysqli_fetch_row($result);echo"<p>Książka $book[0] została zarezerwowana</p>"; mysqli_query($connection, "UPDATE ksiazka SET rezerwacja = 1 WHERE id = $book_id;");}?> </section> <section> <h2>Epika</h2> <formmethod="post"> <selectname="epic"><?php$epic_books = mysqli_query($connection, 'SELECT id, tytul FROM ksiazka WHERE gatunek = "epika";');while ($row = mysqli_fetch_row($epic_books)) {echo"<option value='$row[0]'>$row[1]</option>";}?> </select> <buttontype="submit">Rezerwuj</button> </form> </section> <section> <h2>Dramat</h2> <formmethod="post"> <selectname="drama"><?php$drama_books = mysqli_query($connection, 'SELECT id, tytul FROM ksiazka WHERE gatunek = "dramat";');while ($row = mysqli_fetch_row($drama_books)) {echo"<option value='$row[0]'>$row[1]</option>";}?> </select> <buttontype="submit">Rezerwuj</button> </form> </section> <section> <h2>Zaległe książki</h2> <ul> </ul> </section> <footer> <p><strong>Autor: Bartosz Bryniarski</strong></p> </footer></body></html>
R.5.6 Skrypt 3 wyświetla zwrócony zapytaniem tytuł. Tytuł jest równy wybranemu z listy rozwijanej. Jest wyświetlany w postaci: "Książka <tytuł> została zarezerwowana"
R.5.7 Skrypt 3 dla wybranej z listy książki uaktualnia jej rekord w bazie danych, wpisując do pola rezerwacja wartość 1. Należy sprawdzić w phpMyAdmin
R.5.8 Skrypty 2 i 3 działają we wszystkich trzech sekcjach zgadza się gatunek "Liryka", "Epika", "Dramat" z wyświetlanymi w danej liście tytułami oraz tekst pojawia się tylko pod listą w której był dokonany wybór
ok. 2 min
Polecenie z arkusza
Skrypt 3 do sekcji „Epika” należy powielić i dostosować: pobiera on dane wysłane formularzem z sekcji „Epika”. Skrypt 3 do sekcji „Dramat” należy powielić i dostosować: pobiera on dane wysłane formularzem z sekcji „Dramat”. Należy zwrócić uwagę na to, że wyświetlany w wyniku wykonania formularza paragraf powinien pojawić się tylko w odpowiedniej sekcji.
Skopiuj skrypt 3 pod formularze Epiki i Dramatu i zmień tylko nazwę listy w $_POST: epic, drama. Zapytania są te same - id książki jest unikalne w całej tabeli, niezależnie od gatunku. Dzięki różnym nazwom list każdy skrypt reaguje tylko na „swój” formularz, więc komunikat pojawia się wyłącznie w sekcji, w której kliknięto „Rezerwuj” (R.5.8).
biblioteka.phpPHPzmienione: 18 linii
<?php$connection = mysqli_connect('localhost', 'root', '', 'biblioteka');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Biblioteka miejska</title> <linkrel="stylesheet"href="styl.css"></head><body> <header><?phpfor ($i = 0; $i < 20; $i++) {echo"<img src='obraz.png' alt='książki'>";}?> </header> <section> <h2>Liryka</h2> <formmethod="post"> <selectname="poetry"><?php$poetry_books = mysqli_query($connection, 'SELECT id, tytul FROM ksiazka WHERE gatunek = "liryka";');while ($row = mysqli_fetch_row($poetry_books)) {echo"<option value='$row[0]'>$row[1]</option>";}?> </select> <buttontype="submit">Rezerwuj</button> </form><?phpif (isset($_POST['poetry'])) {$book_id = $_POST['poetry'];$result = mysqli_query($connection, "SELECT tytul FROM ksiazka WHERE id = $book_id;");$book = mysqli_fetch_row($result);echo"<p>Książka $book[0] została zarezerwowana</p>"; mysqli_query($connection, "UPDATE ksiazka SET rezerwacja = 1 WHERE id = $book_id;");}?> </section> <section> <h2>Epika</h2> <formmethod="post"> <selectname="epic"><?php$epic_books = mysqli_query($connection, 'SELECT id, tytul FROM ksiazka WHERE gatunek = "epika";');while ($row = mysqli_fetch_row($epic_books)) {echo"<option value='$row[0]'>$row[1]</option>";}?> </select> <buttontype="submit">Rezerwuj</button> </form><?phpif (isset($_POST['epic'])) {$book_id = $_POST['epic'];$result = mysqli_query($connection, "SELECT tytul FROM ksiazka WHERE id = $book_id;");$book = mysqli_fetch_row($result);echo"<p>Książka $book[0] została zarezerwowana</p>"; mysqli_query($connection, "UPDATE ksiazka SET rezerwacja = 1 WHERE id = $book_id;");}?> </section> <section> <h2>Dramat</h2> <formmethod="post"> <selectname="drama"><?php$drama_books = mysqli_query($connection, 'SELECT id, tytul FROM ksiazka WHERE gatunek = "dramat";');while ($row = mysqli_fetch_row($drama_books)) {echo"<option value='$row[0]'>$row[1]</option>";}?> </select> <buttontype="submit">Rezerwuj</button> </form><?phpif (isset($_POST['drama'])) {$book_id = $_POST['drama'];$result = mysqli_query($connection, "SELECT tytul FROM ksiazka WHERE id = $book_id;");$book = mysqli_fetch_row($result);echo"<p>Książka $book[0] została zarezerwowana</p>"; mysqli_query($connection, "UPDATE ksiazka SET rezerwacja = 1 WHERE id = $book_id;");}?> </section> <section> <h2>Zaległe książki</h2> <ul> </ul> </section> <footer> <p><strong>Autor: Bartosz Bryniarski</strong></p> </footer></body></html>
Krok 45: Skrypt 4: element listy z trzema wartościami
Kryteria: ,
R.2.9 W czwartym bloku sekcji umieszczona jedna lista punktowana z elementami (w HTML lub skrypcie) , zastosowane znaczniki <ul> i <li>
R.5.9 Skrypt 4 wyświetla w sekcji czwartej zwrócone zapytaniem kolejne rekordy w elementach listy: dane tytuł, id czytelnika i datę (nie wszystkie muszą być widoczne ze względu na właściwość overflow)
ok. 2 min
Polecenie z arkusza
Każdy zwrócony zapytaniem wiersz wyświetla w elemencie listy kolejno: tytuł, id czytelnika i datę oddania oddzielone od siebie spacją.
Pętla while jak w skrypcie 2, a w niej echo z jednym <li>: trzy wartości rozdzielone spacjami w napisie - $row[0] $row[1] $row[2]. Podgląd: piętnaście pozycji w sekcji Zaległe książki; dolne mogą być obcięte przez overflow: hidden i klucz R.5.9 to dopuszcza.
biblioteka.phpPHPzmienione: 3 linie
<?php$connection = mysqli_connect('localhost', 'root', '', 'biblioteka');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Biblioteka miejska</title> <linkrel="stylesheet"href="styl.css"></head><body> <header><?phpfor ($i = 0; $i < 20; $i++) {echo"<img src='obraz.png' alt='książki'>";}?> </header> <section> <h2>Liryka</h2> <formmethod="post"> <selectname="poetry"><?php$poetry_books = mysqli_query($connection, 'SELECT id, tytul FROM ksiazka WHERE gatunek = "liryka";');while ($row = mysqli_fetch_row($poetry_books)) {echo"<option value='$row[0]'>$row[1]</option>";}?> </select> <buttontype="submit">Rezerwuj</button> </form><?phpif (isset($_POST['poetry'])) {$book_id = $_POST['poetry'];$result = mysqli_query($connection, "SELECT tytul FROM ksiazka WHERE id = $book_id;");$book = mysqli_fetch_row($result);echo"<p>Książka $book[0] została zarezerwowana</p>"; mysqli_query($connection, "UPDATE ksiazka SET rezerwacja = 1 WHERE id = $book_id;");}?> </section> <section> <h2>Epika</h2> <formmethod="post"> <selectname="epic"><?php$epic_books = mysqli_query($connection, 'SELECT id, tytul FROM ksiazka WHERE gatunek = "epika";');while ($row = mysqli_fetch_row($epic_books)) {echo"<option value='$row[0]'>$row[1]</option>";}?> </select> <buttontype="submit">Rezerwuj</button> </form><?phpif (isset($_POST['epic'])) {$book_id = $_POST['epic'];$result = mysqli_query($connection, "SELECT tytul FROM ksiazka WHERE id = $book_id;");$book = mysqli_fetch_row($result);echo"<p>Książka $book[0] została zarezerwowana</p>"; mysqli_query($connection, "UPDATE ksiazka SET rezerwacja = 1 WHERE id = $book_id;");}?> </section> <section> <h2>Dramat</h2> <formmethod="post"> <selectname="drama"><?php$drama_books = mysqli_query($connection, 'SELECT id, tytul FROM ksiazka WHERE gatunek = "dramat";');while ($row = mysqli_fetch_row($drama_books)) {echo"<option value='$row[0]'>$row[1]</option>";}?> </select> <buttontype="submit">Rezerwuj</button> </form><?phpif (isset($_POST['drama'])) {$book_id = $_POST['drama'];$result = mysqli_query($connection, "SELECT tytul FROM ksiazka WHERE id = $book_id;");$book = mysqli_fetch_row($result);echo"<p>Książka $book[0] została zarezerwowana</p>"; mysqli_query($connection, "UPDATE ksiazka SET rezerwacja = 1 WHERE id = $book_id;");}?> </section> <section> <h2>Zaległe książki</h2> <ul><?php$overdue = mysqli_query($connection, 'SELECT tytul, id_cz, data_odd FROM ksiazka JOIN wypozyczenia ON ksiazka.id = wypozyczenia.id_ks ORDER BY data_odd LIMIT 15;');while ($row = mysqli_fetch_row($overdue)) {echo"<li>$row[0]$row[1]$row[2]</li>";}?> </ul> </section> <footer> <p><strong>Autor: Bartosz Bryniarski</strong></p> </footer></body></html>
Podgląd strony po tym kroku (biblioteka.php)
Krok 46: Zamknięcie połączenia z bazą
Kryteria:
R.5.2 Ostatnią operacją na bazie jest jej zamknięcie
ok. 1 min
Polecenie z arkusza
Na końcu zamykane jest połączenie z serwerem.
mysqli_close($connection) na końcu pliku, po wszystkich skryptach. Klucz R.5.2 sprawdza, że zamknięcie jest ostatnią operacją na bazie. Strona spełnia wszystkie kryteria arkusza - dalej trzy kroki „jak w prawdziwym projekcie”.
Typowe błędy
mysqli_close po skrypcie 2 sprawia, że skrypty 3 i 4 idą po zamkniętym połączeniu - lista zaległych zostaje pusta.
biblioteka.phpPHPzmienione: 3 linie
<?php$connection = mysqli_connect('localhost', 'root', '', 'biblioteka');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Biblioteka miejska</title> <linkrel="stylesheet"href="styl.css"></head><body> <header><?phpfor ($i = 0; $i < 20; $i++) {echo"<img src='obraz.png' alt='książki'>";}?> </header> <section> <h2>Liryka</h2> <formmethod="post"> <selectname="poetry"><?php$poetry_books = mysqli_query($connection, 'SELECT id, tytul FROM ksiazka WHERE gatunek = "liryka";');while ($row = mysqli_fetch_row($poetry_books)) {echo"<option value='$row[0]'>$row[1]</option>";}?> </select> <buttontype="submit">Rezerwuj</button> </form><?phpif (isset($_POST['poetry'])) {$book_id = $_POST['poetry'];$result = mysqli_query($connection, "SELECT tytul FROM ksiazka WHERE id = $book_id;");$book = mysqli_fetch_row($result);echo"<p>Książka $book[0] została zarezerwowana</p>"; mysqli_query($connection, "UPDATE ksiazka SET rezerwacja = 1 WHERE id = $book_id;");}?> </section> <section> <h2>Epika</h2> <formmethod="post"> <selectname="epic"><?php$epic_books = mysqli_query($connection, 'SELECT id, tytul FROM ksiazka WHERE gatunek = "epika";');while ($row = mysqli_fetch_row($epic_books)) {echo"<option value='$row[0]'>$row[1]</option>";}?> </select> <buttontype="submit">Rezerwuj</button> </form><?phpif (isset($_POST['epic'])) {$book_id = $_POST['epic'];$result = mysqli_query($connection, "SELECT tytul FROM ksiazka WHERE id = $book_id;");$book = mysqli_fetch_row($result);echo"<p>Książka $book[0] została zarezerwowana</p>"; mysqli_query($connection, "UPDATE ksiazka SET rezerwacja = 1 WHERE id = $book_id;");}?> </section> <section> <h2>Dramat</h2> <formmethod="post"> <selectname="drama"><?php$drama_books = mysqli_query($connection, 'SELECT id, tytul FROM ksiazka WHERE gatunek = "dramat";');while ($row = mysqli_fetch_row($drama_books)) {echo"<option value='$row[0]'>$row[1]</option>";}?> </select> <buttontype="submit">Rezerwuj</button> </form><?phpif (isset($_POST['drama'])) {$book_id = $_POST['drama'];$result = mysqli_query($connection, "SELECT tytul FROM ksiazka WHERE id = $book_id;");$book = mysqli_fetch_row($result);echo"<p>Książka $book[0] została zarezerwowana</p>"; mysqli_query($connection, "UPDATE ksiazka SET rezerwacja = 1 WHERE id = $book_id;");}?> </section> <section> <h2>Zaległe książki</h2> <ul><?php$overdue = mysqli_query($connection, 'SELECT tytul, id_cz, data_odd FROM ksiazka JOIN wypozyczenia ON ksiazka.id = wypozyczenia.id_ks ORDER BY data_odd LIMIT 15;');while ($row = mysqli_fetch_row($overdue)) {echo"<li>$row[0]$row[1]$row[2]</li>";}?> </ul> </section> <footer> <p><strong>Autor: Bartosz Bryniarski</strong></p> </footer><?phpmysqli_close($connection);?></body></html>
Kroki 47-49 · po tym etapie masz 42 z 42 kryteriów (pkt)
Krok 47: Refaktor: jedna funkcja zamiast trzech kopii skryptu 2
Kryteria: ,
R.5.5 Skrypt 2 w przynajmniej jednej sekcji wyświetla tytuły książek zwrócone zapytaniem. Rodzaj utworów zgadza się z nazwą sekcji (liryka, epika, dramat) . Atrybut value każdej opcji to id zwrócone zapytaniem
R.5.8 Skrypty 2 i 3 działają we wszystkich trzech sekcjach zgadza się gatunek "Liryka", "Epika", "Dramat" z wyświetlanymi w danej liście tytułami oraz tekst pojawia się tylko pod listą w której był dokonany wybór
ok. 4 min
Polecenie z arkusza
Skrypt 2 do sekcji „Epika” należy powielić i dostosować. Skrypt 2 do sekcji „Dramat” należy powielić i dostosować.
Trzy kopie tego samego kodu różniące się jednym słowem to w prawdziwym projekcie błąd czekający na okazję: poprawka w jednej kopii, zapomniana w dwóch. Rozwiązanie: funkcja z parametrem. printOptions($connection, $genre) dostaje połączenie i gatunek, wysyła zapytanie i wypisuje opcje - raz zdefiniowana na górze pliku, trzy razy wywołana w listach: printOptions($connection, 'liryka') i tak dalej.
W PHP funkcję zdefiniowaną w tym samym pliku można wywołać także przed definicją, ale dla czytelności definicje trzymasz na górze. Podgląd bez zmian - to ta sama logika w jednym miejscu.
Typowe błędy
Funkcja bez parametru $connection nie zobaczy zmiennej z zewnątrz - w PHP zmienne globalne nie są widoczne w funkcjach bez słowa global. Dlatego połączenie przekazujesz jako argument.
biblioteka.phpPHPzmienione: 10 linii
<?php$connection = mysqli_connect('localhost', 'root', '', 'biblioteka');functionprintOptions($connection, $genre) {$books = mysqli_query($connection, "SELECT id, tytul FROM ksiazka WHERE gatunek = '$genre';");while ($row = mysqli_fetch_row($books)) {echo"<option value='$row[0]'>$row[1]</option>"; }}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Biblioteka miejska</title> <linkrel="stylesheet"href="styl.css"></head><body> <header><?phpfor ($i = 0; $i < 20; $i++) {echo"<img src='obraz.png' alt='książki'>";}?> </header> <section> <h2>Liryka</h2> <formmethod="post"> <selectname="poetry"><?php printOptions($connection, 'liryka'); ?> </select> <buttontype="submit">Rezerwuj</button> </form><?phpif (isset($_POST['poetry'])) {$book_id = $_POST['poetry'];$result = mysqli_query($connection, "SELECT tytul FROM ksiazka WHERE id = $book_id;");$book = mysqli_fetch_row($result);echo"<p>Książka $book[0] została zarezerwowana</p>"; mysqli_query($connection, "UPDATE ksiazka SET rezerwacja = 1 WHERE id = $book_id;");}?> </section> <section> <h2>Epika</h2> <formmethod="post"> <selectname="epic"><?php printOptions($connection, 'epika'); ?> </select> <buttontype="submit">Rezerwuj</button> </form><?phpif (isset($_POST['epic'])) {$book_id = $_POST['epic'];$result = mysqli_query($connection, "SELECT tytul FROM ksiazka WHERE id = $book_id;");$book = mysqli_fetch_row($result);echo"<p>Książka $book[0] została zarezerwowana</p>"; mysqli_query($connection, "UPDATE ksiazka SET rezerwacja = 1 WHERE id = $book_id;");}?> </section> <section> <h2>Dramat</h2> <formmethod="post"> <selectname="drama"><?php printOptions($connection, 'dramat'); ?> </select> <buttontype="submit">Rezerwuj</button> </form><?phpif (isset($_POST['drama'])) {$book_id = $_POST['drama'];$result = mysqli_query($connection, "SELECT tytul FROM ksiazka WHERE id = $book_id;");$book = mysqli_fetch_row($result);echo"<p>Książka $book[0] została zarezerwowana</p>"; mysqli_query($connection, "UPDATE ksiazka SET rezerwacja = 1 WHERE id = $book_id;");}?> </section> <section> <h2>Zaległe książki</h2> <ul><?php$overdue = mysqli_query($connection, 'SELECT tytul, id_cz, data_odd FROM ksiazka JOIN wypozyczenia ON ksiazka.id = wypozyczenia.id_ks ORDER BY data_odd LIMIT 15;');while ($row = mysqli_fetch_row($overdue)) {echo"<li>$row[0]$row[1]$row[2]</li>";}?> </ul> </section> <footer> <p><strong>Autor: Bartosz Bryniarski</strong></p> </footer><?phpmysqli_close($connection);?></body></html>
Krok 48: Refaktor: jedna funkcja zamiast trzech kopii skryptu 3
Kryteria: , ,
R.5.6 Skrypt 3 wyświetla zwrócony zapytaniem tytuł. Tytuł jest równy wybranemu z listy rozwijanej. Jest wyświetlany w postaci: "Książka <tytuł> została zarezerwowana"
R.5.7 Skrypt 3 dla wybranej z listy książki uaktualnia jej rekord w bazie danych, wpisując do pola rezerwacja wartość 1. Należy sprawdzić w phpMyAdmin
R.5.8 Skrypty 2 i 3 działają we wszystkich trzech sekcjach zgadza się gatunek "Liryka", "Epika", "Dramat" z wyświetlanymi w danej liście tytułami oraz tekst pojawia się tylko pod listą w której był dokonany wybór
ok. 3 min
Polecenie z arkusza
Skrypt 3 do sekcji „Epika” należy powielić i dostosować. Skrypt 3 do sekcji „Dramat” należy powielić i dostosować.
To samo ze skryptem 3: reserveBook($connection, $field) dostaje nazwę pola formularza (poetry, epic, drama), sprawdza isset($_POST[$field]), pobiera tytuł, wypisuje komunikat i aktualizuje rezerwację. Pod każdym formularzem zostaje jedna linia wywołania. Plik skrócił się o kilkadziesiąt linii, a zmiana treści komunikatu to teraz jedno miejsce. Podgląd bez zmian.
Krok 49: Refaktor: zapytania przygotowane i escapowanie
Kryteria: , , ,
R.5.4 Przynajmniej jeden skrypt zawiera instrukcję wysyłającą do bazy danych zapytanie
R.5.5 Skrypt 2 w przynajmniej jednej sekcji wyświetla tytuły książek zwrócone zapytaniem. Rodzaj utworów zgadza się z nazwą sekcji (liryka, epika, dramat) . Atrybut value każdej opcji to id zwrócone zapytaniem
R.5.6 Skrypt 3 wyświetla zwrócony zapytaniem tytuł. Tytuł jest równy wybranemu z listy rozwijanej. Jest wyświetlany w postaci: "Książka <tytuł> została zarezerwowana"
R.5.7 Skrypt 3 dla wybranej z listy książki uaktualnia jej rekord w bazie danych, wpisując do pola rezerwacja wartość 1. Należy sprawdzić w phpMyAdmin
ok. 5 min
Polecenie z arkusza
Wysyła do bazy danych zapytanie 5, zmodyfikowane w ten sposób, że w klauzuli WHERE podana jest wartość id wysłana przez formularz.
Id z formularza szło prosto do SELECT i UPDATE - to SQL injection: w ukrytym polu formularza można wysłać 1 OR 1=1 i zarezerwować wszystko. Dane trafiają do zapytania przez zapytanie przygotowane: mysqli_prepare z ? w miejscu wartości, mysqli_stmt_bind_param z typem ('i' - liczba całkowita dla id, 's' - napis dla gatunku), mysqli_stmt_execute i mysqli_stmt_get_result, z którego czytasz jak dotąd. Gatunek w printOptions też bindujesz - teraz jest stałą z kodu, ale funkcja ma być bezpieczna niezależnie od tego, skąd przyjdzie argument.
Druga strona: tytuły z bazy przechodzą przez htmlspecialchars() przed wypisaniem, żeby tytuł ze znakiem < nie stał się znacznikiem (XSS). Podgląd bez zmian.
Typowe błędy
mysqli_stmt_get_result zwraca wynik dopiero po mysqli_stmt_execute - wywołane wcześniej daje false, a pętla while nie wypisze żadnej opcji.
Krok 50 · po tym etapie masz 42 z 42 kryteriów (pkt)
Krok 50: Plik przeglądarka.txt i nagranie płyty
ok. 5 min
Polecenie z arkusza
Po zakończeniu pracy utwórz plik tekstowy o nazwie przeglądarka.txt. Zapisz w nim nazwę przeglądarki internetowej, w której weryfikowana była poprawność działania witryny. Nagraj płytę z rezultatami pracy.
W przeglądarka.txt wpisujesz nazwę przeglądarki, w której sprawdzasz stronę przez http://localhost/….
W folderze z numerem zdającego muszą być: biblioteka.php, import.png, kw1.jpg … kw5.jpg, kwerendy.txt, obraz.png, przeglądarka.txt, styl.css. Nagraj płytę, sprawdź jej odczyt i opisz numerem zdającego. Pusta lub nieczytelna płyta to zero punktów za wszystkie pięć rezultatów.
Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.
To jedno z możliwych rozwiązań. Egzaminator ocenia spełnienie kryteriów z klucza odpowiedzi, a nie zgodność z tym kodem - inna, poprawna droga też daje punkty.
Autor rozwiązania: Bartosz Bryniarski. Licencja CC BY-NC 4.0 - kopiowanie i przeróbki do celów niekomercyjnych, z podaniem autora i źródła (link do tej strony) (zasady wykorzystania). Treść arkusza i klucza oceniania pochodzi z CKE.