Przejdź do głównej treści
INF.03-07-23.06-SGCzerwiec 2023 · 42 kroki · ok. 81 min · 37 kryteriów z klucza

Rozwiązanie krok po kroku: Portal biblioteki (Nowoczesny kod (flex, PDO))

To drugi sposób rozwiązania tego samego arkusza (wydanego też pod numerami 09 i 11). Wynik jest ten sam - baza biblioteka z czterema zapytaniami, grafika w odcieniach szarości i strona biblioteka.php z formularzem dodawania czytelnika, adresem i listą czytelników - i tak samo spełnia każde kryterium klucza. Inna jest droga:

  • flexbox zamiast float przy układzie paneli: trzy panele stoją w znaczniku <main> ustawionym w rząd, bez clear przy stopce. Szerokości 40%, 20% i 40% zostają na samych panelach - tak ocenia je klucz. float zostaje tylko przy obrazie, bo tam robi to, do czego powstał - tekst oblewa grafikę,
  • zmienne CSS dla kolorów z arkusza, wiersze formularza w blokach zamiast <br>, pola z required i podpowiedziami autouzupełniania,
  • PHP na PDO: całą pracę z bazą strona wykonuje na górze pliku biblioteka.php, przed HTML-em - najpierw zapis nowego czytelnika (Skrypt 1), potem lista (Skrypt 2), więc nowa osoba od razu jest na liście,
  • sprawdzanie danych z formularza (trim, symbol od 1 do 6 cyfr) i zapytanie przygotowane do INSERT,
  • wzorzec PRG (Post/Redirect/Get): po zapisie strona przekierowuje sama na siebie, a napis „Dodano czytelnika …” przenosi sesja - odświeżenie nie doda tej samej osoby drugi raz,
  • bezpieczne wypisywanie (htmlspecialchars w funkcji e()) i komunikaty dla niepełnych danych i awarii bazy.

Na końcu krótkie porównanie z wersją podstawową (zakładka obok). Baza i grafika pochodzą z prawdziwego archiwum materialy7, a podgląd od kroków z PHP to wynik prawdziwego uruchomienia kodu w PHP 8.2 na bazie z arkusza po zapytaniu 1 - od Skryptu 1 po wysłaniu formularza z danymi z obrazu 2, Moniki Nowak (obraz nie pokazuje jej symbolu, więc w podglądzie stoi przykładowy 56678).

Jak liczą się punkty. Klucz ma 37 kryteriów (R.1.1 - R.5.7), każde za 1 punkt. Próg zdania to 75%, czyli 28 punktów. Przy każdym kroku jest napisane, które kryteria spełnia.

  1. Etap 1: Baza danych

    Kroki 1-7 · po tym etapie masz 6 z 37 kryteriów (pkt)

    Krok 1: Baza danych biblioteka

    ok. 2 min
    Polecenie z arkusza

    Utwórz nową bazę danych o nazwie biblioteka

    Rozpakuj materialy7.7z z pulpitu (hasło @Z@d@nie$s7 - małpy zamiast „a”, dolar zamiast „s”, wielkość liter ma znaczenie) do folderu z numerem zdającego: biblioteka.sql i biblioteka.png. W phpMyAdmin Nowa → biblioteka albo zapytaniem:

    CREATE DATABASE biblioteka CHARACTER SET utf8 COLLATE utf8_polish_ci;
    

    Arkusz nie podaje porównywania, więc wybór należy do Ciebie. Tabele z biblioteka.sql mają zestaw znaków zapisany we własnej definicji (część latin1, część utf8), więc ustawienie bazy ich nie zmienia.

    Typowe błędy

    Skrypt łączy się z bazą po nazwie - literówka w nazwie bazy zabiera też punkty za część PHP (R.5.1).

  2. Krok 2: Import tabel z pliku biblioteka.sql

    ok. 2 min
    Polecenie z arkusza

    Do bazy zaimportuj tabele z pliku biblioteka.sql, z wcześniej rozpakowanego archiwum

    Zaznaczona baza biblioteka, Import → biblioteka.sql → Wykonaj. Zadanie używa trzech tabel z obrazu 1: czytelnicy (10 osób), autorzy (10) i ksiazki (21 tytułów, pole id_autor wskazuje autora).

  3. Krok 3: Zrzut ekranu dane.jpg

    Kryteria:

    R.1.1 Wykonano import tabel do bazy danych o nazwie biblioteka , czynność udokumentowano plikiem o nazwie dane w formacie JPEG

    ok. 2 min
    Polecenie z arkusza

    Wykonaj zrzut ekranu po imporcie. Zrzut zapisz w folderze z numerem zdającego, w formacie JPEG i nazwij dane. Nie kadruj zrzutu. Powinien on obejmować cały ekran monitora, z widocznym paskiem zadań. Na zrzucie powinny być widoczne elementy wskazujące na poprawnie wykonany import tabel

    Cały ekran z paskiem zadań, w JPEG, pod nazwą dane - w tym arkuszu zrzut importu nie nazywa się import. Zrzuty zapytań: kwerenda1 - kwerenda4, z widocznym zapytaniem; zapytania skopiuj też do kwerendy.txt.

    Typowe błędy

    Wykadrowany zrzut albo zrzut bez paska zadań nie jest oceniany (uwaga w kluczu przy R.1).

  4. Krok 4: Zapytanie 1: nowa czytelniczka Ewelina Romanowska

    Kryteria: ,

    R.1.2 Zapisano plik kwerendy.txt zawierający co najmniej jedno zapytanie SQL wynikające z treści zadania

    R.1.3 Zapisano zapytanie 1 wstawiające do tabeli czytelnicy rekord Ewelina, Romanowska, 56677, wartość klucza głównego jest nadawana automatycznie. W pliku z kwerendami lub na zrzucie istnieje zapis np.: INSERT INTO czytelnicy(`imie`, `nazwisko`, `kod`) VALUES ('Ewelina', 'Romanowska', '56677'); (możliwe również bez struktury tabeli lub przez przypisania SET) oraz wynik działania zapytania udokumentowano zrzutem ekranu na którym widoczny jest komunikat o poprawnym wykonaniu zapytania lub tabela z wstawionym wierszem

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 1: wstawiające do tabeli czytelnicy nowy rekord z danymi: Ewelina Romanowska z kodem 56677. Pole klucza głównego nadawane automatycznie

    Lista pól bez id - klucz główny ma AUTO_INCREMENT, baza nada numer 11 sama. kod to tekst (varchar(6)), więc też w apostrofach. Komunikat „1 wiersz wstawiony” na zrzucie kwerenda1.jpg. To zapytanie wyśle Skrypt 1 - z parametrami w miejscu danych.

    Typowe błędy

    Zapytanie wykonane dwa razy doda dwie Eweliny Romanowskie - na liście czytelników będą wtedy dwie.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    INSERT INTO czytelnicy (imie, nazwisko, kod)
    VALUES ('Ewelina', 'Romanowska', '56677');
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: czytelnicy alfabetycznie

    Kryteria:

    R.1.4 Zapisano zapytanie 2 wybierające jedynie pola imie i nazwisko dla wszystkich rekordów w tabeli czytelnicy , posortowane rosnąco nazwiskami. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT imie, nazwisko FROM czytelnicy ORDER BY nazwisko; (opcjonalnie ASC) oraz wynik działania zapytania udokumentowano zrzutem ekranu, na którym widocznych jest minimum 10 rekordów jedynie pola imie i nazwisko

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 2: wybierające jedynie pola imie i nazwisko dla wszystkich rekordów w tabeli czytelnicy, posortowane rosnąco według nazwisk

    ORDER BY nazwisko, imie - rosnąco według nazwisk, a przy takich samych nazwiskach według imion: kolejność osób o tym samym nazwisku nie zależy wtedy od przypadku. Wynik: 11 osób (10 z importu i Ewelina Romanowska), od Olgi Domys do Macieja Wysockiego. Zrzut: kwerenda2.jpg. To zapytanie wyśle Skrypt 2.

    kwerendy.txt Tekst zmienione: 5 linii
    -- Zapytanie 1
    INSERT INTO czytelnicy (imie, nazwisko, kod)
    VALUES ('Ewelina', 'Romanowska', '56677');
    
    -- Zapytanie 2
    SELECT imie, nazwisko
    FROM czytelnicy
    ORDER BY nazwisko, imie;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: książki Sienkiewicza

    Kryteria:

    R.1.5 Zapisano zapytanie 3 wybierające jedynie pole tytul dla wszystkich utworów, których nazwisko autora to Sienkiewicz. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT tytul FROM ksiazki JOIN autorzy ON ksiazki.id_autor = autorzy.id WHERE nazwisko = 'Sienkiewicz'; (lub INNER JOIN, lub porównanie kluczy po WHERE; warunek id_autor=8 jest błędem) oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczne są dokładnie 3 tytuły: Quo vadis, Potop, Ogniem i mieczem

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie pole tytul dla wszystkich utworów, których nazwisko autora to Sienkiewicz

    Aliasy k (ksiazki) i a (autorzy), JOIN autorzy AS a ON a.id = k.id_autor - nazwisko jest w innej tabeli niż tytuł - i warunek na nazwisku. Wynik: Quo vadis, Potop, Ogniem i mieczem. Zrzut: kwerenda3.jpg.

    Typowe błędy

    Klucz wprost uznaje warunek id_autor = 8 za błąd - arkusz mówi o nazwisku, więc trzeba złączyć tabele.

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    INSERT INTO czytelnicy (imie, nazwisko, kod)
    VALUES ('Ewelina', 'Romanowska', '56677');
    
    -- Zapytanie 2
    SELECT imie, nazwisko
    FROM czytelnicy
    ORDER BY nazwisko, imie;
    
    -- Zapytanie 3
    SELECT k.tytul
    FROM ksiazki AS k
    JOIN autorzy AS a ON a.id = k.id_autor
    WHERE a.nazwisko = 'Sienkiewicz';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: liczba tytułów każdego autora

    Kryteria:

    R.1.6 Zapisano zapytanie 4 wybierające jedynie pole nazwisko dla wszystkich rekordów w tabeli autorzy oraz obliczające liczbę tytułów danego autora znajdujących się w bazie. Nazwa kolumny (alias) obliczającej liczbę tytułów to ile_tytulow. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwisko, COUNT(tytul) AS ile_tytulow FROM autorzy JOIN ksiazki ON ksiazki.id_autor = autorzy.id GROUP BY nazwisko; (możliwe również porównanie kluczy po WHERE, poprawne są LEFT JOIN, RIGHT JOIN, INNER JOIN) oraz wynik działania zapytania udokumentowano zrzutem ekranu, na którym widoczne są jedynie nazwiska autorów i pole ile_tytulow: Brzechwa 2, Chmielewska 2, Fredro 2, Gombrowicz 3, Kasprowicz 2, Kruczkowski 2, Sienkiewicz 3, Tuwim 3, Witkiewicz 2 i opcjonalnie Korczak 0

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 4: wybierające jedynie pole nazwisko dla wszystkich rekordów w tabeli autorzy oraz obliczające liczbę tytułów danego autora znajdujących się w bazie biblioteka. Nazwa kolumny (alias) dla zliczania tytułów: ile_tytulow

    • LEFT JOIN - złączenie lewostronne zostawia też autora bez książki („wszystkie rekordy w tabeli autorzy”): Janusz Korczak dostaje 0,
    • COUNT(k.id) - liczy książki, a nie wiersze: COUNT(*) policzyłby Korczakowi 1 (pusty wiersz złączenia),
    • GROUP BY a.id, a.nazwisko - grupa na autora po kluczu głównym; dwóch autorów o tym samym nazwisku nie zleje się w jeden wiersz,
    • ORDER BY a.nazwisko - kolejność alfabetyczna jak w kluczu.

    Wynik: 10 wierszy od Brzechwy (2) do Witkiewicz (2), z Korczakiem 0 (klucz dopuszcza go opcjonalnie). Zrzut: kwerenda4.jpg.

    kwerendy.txt Tekst zmienione: 7 linii
    -- Zapytanie 1
    INSERT INTO czytelnicy (imie, nazwisko, kod)
    VALUES ('Ewelina', 'Romanowska', '56677');
    
    -- Zapytanie 2
    SELECT imie, nazwisko
    FROM czytelnicy
    ORDER BY nazwisko, imie;
    
    -- Zapytanie 3
    SELECT k.tytul
    FROM ksiazki AS k
    JOIN autorzy AS a ON a.id = k.id_autor
    WHERE a.nazwisko = 'Sienkiewicz';
    
    -- Zapytanie 4
    SELECT a.nazwisko, COUNT(k.id) AS ile_tytulow
    FROM autorzy AS a
    LEFT JOIN ksiazki AS k ON k.id_autor = a.id
    GROUP BY a.id, a.nazwisko
    ORDER BY a.nazwisko;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Grafika i strona HTML

    Kroki 8-23 · po tym etapie masz 19 z 37 kryteriów (pkt)

    Krok 8: Grafika: 100 px szerokości, odcienie szarości

    Kryteria:

    R.2.1 Grafikę biblioteka.png zapisano w odcieniach szarości oraz przeskalowano z zachowaniem proporcji do szerokości 100 px, odpowiadająca temu wysokość to 157 px (± 5 px)

    ok. 3 min
    Polecenie z arkusza

    Grafikę biblioteka.png przeskalować z zachowaniem proporcji tak, aby szerokość wynosiła dokładnie 100 px oraz zapisać w odcieniach szarości.

    Grafika jest taka sama jak w wersji podstawowej. W GIMP-ie Obraz → Skaluj obraz, łańcuch proporcji zamknięty, szerokość 100 - wysokość wyjdzie 157 px (235 × 100 / 150). Potem Obraz → Tryb → Odcienie szarości i Nadpisz biblioteka.png.

    Typowe błędy

    Odcienie szarości to zmiana trybu obrazu, a nie filtr odbarwiający - po filtrze plik dalej jest kolorowy (RGB), tylko szary w wyglądzie.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 8
  9. Krok 9: Plik biblioteka.php: HTML5, język, kodowanie

    Kryteria:

    R.2.2 Witrynę internetową zapisano w pliku biblioteka.php, zastosowano deklarację języka HTML5 <!DOCTYPE html>, zadeklarowano język witryny polski np. <html lang = "pl"> oraz zastosowano właściwy 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. Ustawiony język zawartości strony na polski. Jawnie zastosowany właściwy standard kodowania polskich znaków

    <!DOCTYPE html>, <html lang="pl"> i <meta charset="UTF-8"> jako pierwszy element <head> - razem R.2.2. Plik leży w htdocs XAMPP-a i otwierasz go przez http://localhost/....

    biblioteka.php PHP nowy plik
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  10. Krok 10: Tytuł strony

    Kryteria:

    R.2.3 Nadano stronie tytuł "Biblioteka”

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony widoczny na karcie przeglądarki: „Biblioteka”

    <title> (R.2.3) i viewport - telefon nie pomniejsza strony do rozmiaru ekranu komputera.

    biblioteka.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biblioteka</title>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  11. Krok 11: Podpięcie arkusza stylów

    Kryteria:

    R.3.6 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie style.css , formatowanie pochodzi jedynie z tego arkusza Uwaga: kryterium to nie jest spełnione również, gdy zdający źle przypisał co najmniej jeden selektor do bloku (np. kropka zamiast #)

    ok. 1 min
    Polecenie z arkusza

    Arkusz stylów w pliku o nazwie style.css prawidłowo połączony z kodem strony

    Nazwa pliku to style.css. Całe formatowanie pochodzi z tego pliku (R.3.6).

    Typowe błędy

    Klucz odbiera R.3.6 także wtedy, gdy choć jeden selektor nie trafia w blok (np. kropka zamiast #).

    biblioteka.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biblioteka</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  12. Krok 12: Bloki: baner, trzy panele w main, stopka

    Kryteria:

    R.2.4 Wygląd strony zdefiniowano dzieląc ją na baner, panele lewy, środkowy, prawy oraz stopkę. Zastosowano znaczniki sekcji

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki: na górze baner, poniżej bloki: lewy, środkowy i prawy oraz na dole blok stopki. Podział zrealizowany za pomocą znaczników sekcji tak, aby wygląd układu bloków strony po uruchomieniu w przeglądarce, był zgodny z Obrazem 2

    Baner <header>, stopka <footer>, a trzy panele - trzy <section> we wspólnym <main>, który w CSS ustawi je w rząd (R.2.4). Identyfikatory mówią, co jest w panelu:

    • new-reader - formularz dodawania czytelnika (lewy),
    • contact - grafika, adres i kontakt (środkowy),
    • readers - lista czytelników (prawy).
    biblioteka.php PHP zmienione: 12 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biblioteka</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
        </header>
        <main>
            <section id="new-reader">
            </section>
            <section id="contact">
            </section>
            <section id="readers">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  13. Krok 13: Baner: nagłówek pierwszego stopnia

    ok. 1 min
    Polecenie z arkusza

    Zawartość banera: nagłówek pierwszego stopnia o treści „Biblioteka w Książkowicach Małych”

    <h1> w <header>. Panele mają w tym arkuszu nagłówki <h4> i <h6> - takie stopnie podaje arkusz.

    biblioteka.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biblioteka</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Biblioteka w Książkowicach Małych</h1>
        </header>
        <main>
            <section id="new-reader">
            </section>
            <section id="contact">
            </section>
            <section id="readers">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  14. Krok 14: Panel lewy: nagłówek i formularz POST

    Kryteria:

    R.3.2 Dane z formularza przesyłane są za pomocą metody POST do tego samego pliku (poprawne również bez atrybutu action)

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku lewego: Nagłówek czwartego stopnia o treści „Dodaj czytelnika”. Formularz wysyłający dane do tego samego pliku metodą post.

    <h4> i formularz z method="post" - bez action wysyła dane do tego samego pliku (R.3.2).

    Typowe błędy

    Bez method="post" formularz wysyła dane metodą GET i skrypt, który czyta $_POST, niczego nie dostanie.

    biblioteka.php PHP zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biblioteka</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Biblioteka w Książkowicach Małych</h1>
        </header>
        <main>
            <section id="new-reader">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                </form>
            </section>
            <section id="contact">
            </section>
            <section id="readers">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  15. Krok 15: Pole edycyjne „imię”

    ok. 2 min
    Polecenie z arkusza

    Pole edycyjne poprzedzone napisem „imię: ”

    • <label for="name"> - napis powiązany z polem po id: kliknięcie napisu ustawia kursor w polu, a czytnik ekranu odczyta napis jako nazwę pola,
    • name="name" - pod tą nazwą wartość trafi do $_POST,
    • required - przeglądarka nie wyśle formularza z pustym polem, autocomplete="given-name" - podpowie zapisane imię,
    • pole z napisem w <div> - blok sam zaczyna nową linię, bez <br>.
    biblioteka.php PHP zmienione: 4 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biblioteka</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Biblioteka w Książkowicach Małych</h1>
        </header>
        <main>
            <section id="new-reader">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="name">imię: </label>
                        <input type="text" id="name" name="name" required autocomplete="given-name">
                    </div>
                </form>
            </section>
            <section id="contact">
            </section>
            <section id="readers">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  16. Krok 16: Pole edycyjne „nazwisko”

    ok. 1 min
    Polecenie z arkusza

    Pole edycyjne poprzedzone napisem „nazwisko: ”

    To samo dla nazwiska: name="surname", autocomplete="family-name".

    biblioteka.php PHP zmienione: 4 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biblioteka</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Biblioteka w Książkowicach Małych</h1>
        </header>
        <main>
            <section id="new-reader">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="name">imię: </label>
                        <input type="text" id="name" name="name" required autocomplete="given-name">
                    </div>
                    <div>
                        <label for="surname">nazwisko: </label>
                        <input type="text" id="surname" name="surname" required autocomplete="family-name">
                    </div>
                </form>
            </section>
            <section id="contact">
            </section>
            <section id="readers">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  17. Krok 17: Pole numeryczne „symbol”

    Kryteria:

    R.3.4 Pole edycyjne do wpisania symbolu jest typu numerycznego

    ok. 1 min
    Polecenie z arkusza

    Pole edycyjne typu numerycznego poprzedzone napisem „symbol: ”

    type="number" (R.3.4). Symbol trafi do kolumny kod typu varchar(6), więc max="999999" - najwyżej sześć cyfr - a min="0" blokuje liczby ujemne. Skrypt 1 sprawdzi symbol jeszcze raz, bo żądanie da się wysłać z pominięciem formularza.

    biblioteka.php PHP zmienione: 4 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biblioteka</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Biblioteka w Książkowicach Małych</h1>
        </header>
        <main>
            <section id="new-reader">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="name">imię: </label>
                        <input type="text" id="name" name="name" required autocomplete="given-name">
                    </div>
                    <div>
                        <label for="surname">nazwisko: </label>
                        <input type="text" id="surname" name="surname" required autocomplete="family-name">
                    </div>
                    <div>
                        <label for="code">symbol: </label>
                        <input type="number" id="code" name="code" required min="0" max="999999">
                    </div>
                </form>
            </section>
            <section id="contact">
            </section>
            <section id="readers">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  18. Krok 18: Przycisk AKCEPTUJ

    Kryteria: ,

    R.2.7 W bloku lewym umieszczono formularz z trzema polami edycyjnymi i przyciskiem AKCEPTUJ

    R.3.3 Przycisk w formularzu realizuje wysyłanie danych do skryptu (input lub button z typem submit lub button bez typu)

    ok. 1 min
    Polecenie z arkusza

    Przycisk AKCEPTUJ wysyłający dane z formularza

    <button type="submit"> wysyła formularz (R.3.3). Formularz ma trzy pola i przycisk - komplet do R.2.7.

    biblioteka.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biblioteka</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Biblioteka w Książkowicach Małych</h1>
        </header>
        <main>
            <section id="new-reader">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="name">imię: </label>
                        <input type="text" id="name" name="name" required autocomplete="given-name">
                    </div>
                    <div>
                        <label for="surname">nazwisko: </label>
                        <input type="text" id="surname" name="surname" required autocomplete="family-name">
                    </div>
                    <div>
                        <label for="code">symbol: </label>
                        <input type="number" id="code" name="code" required min="0" max="999999">
                    </div>
                    <button type="submit">AKCEPTUJ</button>
                </form>
            </section>
            <section id="contact">
            </section>
            <section id="readers">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  19. Krok 19: Panel lewy: miejsce na Skrypt 1

    ok. 1 min
    Polecenie z arkusza

    Efekt działania skryptu 1

    Napis o dodanym czytelniku stanie w panelu lewym pod formularzem, jak na obrazie 2 - dopisze go etap skryptów. Ten krok nie zmienia pliku.

    Podgląd strony bez zmian - taki jak po kroku 18.

  20. Krok 20: Panel środkowy: obraz i adres ze spacją nierozdzielającą

    Kryteria: ,

    R.2.8 W bloku środkowym umieszczono obraz z pliku biblioteka.png z tekstem alternatywnym "biblioteka"

    R.2.9 Zastosowano jedynie znak specjalny &nbsp; w danych adresowych: Czytelników&nbsp;15 (bez zwykłych spacji)

    ok. 2 min
    Polecenie z arkusza

    Zawartość bloku środkowego: Obraz biblioteka.png z tekstem alternatywnym „biblioteka”. Nagłówek szóstego stopnia o treści: „ul. Czytelników 15; Książkowice Małe”, nazwa ulicy i numer lokalu połączone nierozdzielającą spacją

    Obraz z tekstem alternatywnym (R.2.8) stoi w kodzie przed tekstem - tekst opłynie go dopiero wtedy, gdy obraz dostanie w CSS float. W <h6> encja &nbsp; zamiast spacji między „Czytelników” a „15”: przeglądarka nie złamie tam wiersza, więc numer nie zostanie sam w następnej linii (R.2.9).

    Typowe błędy

    Klucz wymaga encji tylko w „Czytelników 15” i bez zwykłej spacji obok niej.

    biblioteka.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biblioteka</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Biblioteka w Książkowicach Małych</h1>
        </header>
        <main>
            <section id="new-reader">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="name">imię: </label>
                        <input type="text" id="name" name="name" required autocomplete="given-name">
                    </div>
                    <div>
                        <label for="surname">nazwisko: </label>
                        <input type="text" id="surname" name="surname" required autocomplete="family-name">
                    </div>
                    <div>
                        <label for="code">symbol: </label>
                        <input type="number" id="code" name="code" required min="0" max="999999">
                    </div>
                    <button type="submit">AKCEPTUJ</button>
                </form>
            </section>
            <section id="contact">
                <img src="biblioteka.png" alt="biblioteka">
                <h6>ul. Czytelników&nbsp;15; Książkowice Małe</h6>
            </section>
            <section id="readers">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  21. Krok 21: Panel środkowy: odsyłacz pocztowy

    Kryteria: ,

    R.2.5 Zapisano w banerze nagłówek h1 "Biblioteka w Książkowicach Małych", w panelu środkowym h6 z adresem biblioteki, w panelach lewym i prawym przynajmniej jeden h4: "Dodaj czytelnika” lub "Nasi czytelnicy:” oraz zapisano przynajmniej jeden paragraf

    R.3.5 Napis "Czy masz jakieś uwagi?" jest odsyłaczem pocztowym do adresu [email protected], w definicji zastosowano "mailto:"

    ok. 1 min
    Polecenie z arkusza

    Paragraf (akapit) o treści: „Czy masz jakieś uwagi?”, który jest odsyłaczem pocztowym do adresu [email protected]

    Akapit z odnośnikiem mailto: (R.3.5). Z <h1>, <h6> i <h4> to komplet do R.2.5.

    biblioteka.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biblioteka</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Biblioteka w Książkowicach Małych</h1>
        </header>
        <main>
            <section id="new-reader">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="name">imię: </label>
                        <input type="text" id="name" name="name" required autocomplete="given-name">
                    </div>
                    <div>
                        <label for="surname">nazwisko: </label>
                        <input type="text" id="surname" name="surname" required autocomplete="family-name">
                    </div>
                    <div>
                        <label for="code">symbol: </label>
                        <input type="number" id="code" name="code" required min="0" max="999999">
                    </div>
                    <button type="submit">AKCEPTUJ</button>
                </form>
            </section>
            <section id="contact">
                <img src="biblioteka.png" alt="biblioteka">
                <h6>ul. Czytelników&nbsp;15; Książkowice Małe</h6>
                <p><a href="mailto:[email protected]">Czy masz jakieś uwagi?</a></p>
            </section>
            <section id="readers">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  22. Krok 22: Panel prawy: nagłówek i lista numerowana

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku prawego: Nagłówek czwartego stopnia o treści „Nasi czytelnicy:”. Lista numerowana (uporządkowana) generowana za pomocą skryptu 2

    <h4> i pusta <ol> - elementy <li> wypisze Skrypt 2, po jednym na czytelnika.

    biblioteka.php PHP zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biblioteka</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Biblioteka w Książkowicach Małych</h1>
        </header>
        <main>
            <section id="new-reader">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="name">imię: </label>
                        <input type="text" id="name" name="name" required autocomplete="given-name">
                    </div>
                    <div>
                        <label for="surname">nazwisko: </label>
                        <input type="text" id="surname" name="surname" required autocomplete="family-name">
                    </div>
                    <div>
                        <label for="code">symbol: </label>
                        <input type="number" id="code" name="code" required min="0" max="999999">
                    </div>
                    <button type="submit">AKCEPTUJ</button>
                </form>
            </section>
            <section id="contact">
                <img src="biblioteka.png" alt="biblioteka">
                <h6>ul. Czytelników&nbsp;15; Książkowice Małe</h6>
                <p><a href="mailto:[email protected]">Czy masz jakieś uwagi?</a></p>
            </section>
            <section id="readers">
                <h4>Nasi czytelnicy:</h4>
                <ol>
                </ol>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  23. Krok 23: Stopka

    ok. 1 min
    Polecenie z arkusza

    Zawartość stopki: paragraf (akapit) o treści „Projekt witryny: ”, dalej wstawiony numer zdającego

    Akapit w <footer>. Na egzaminie po dwukropku wpisujesz swój numer zdającego (PESEL), tu stoi autor rozwiązania.

    biblioteka.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biblioteka</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Biblioteka w Książkowicach Małych</h1>
        </header>
        <main>
            <section id="new-reader">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="name">imię: </label>
                        <input type="text" id="name" name="name" required autocomplete="given-name">
                    </div>
                    <div>
                        <label for="surname">nazwisko: </label>
                        <input type="text" id="surname" name="surname" required autocomplete="family-name">
                    </div>
                    <div>
                        <label for="code">symbol: </label>
                        <input type="number" id="code" name="code" required min="0" max="999999">
                    </div>
                    <button type="submit">AKCEPTUJ</button>
                </form>
            </section>
            <section id="contact">
                <img src="biblioteka.png" alt="biblioteka">
                <h6>ul. Czytelników&nbsp;15; Książkowice Małe</h6>
                <p><a href="mailto:[email protected]">Czy masz jakieś uwagi?</a></p>
            </section>
            <section id="readers">
                <h4>Nasi czytelnicy:</h4>
                <ol>
                </ol>
            </section>
        </main>
        <footer>
            <p>Projekt witryny: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  24. Etap 3: Styl CSS

    Kroki 24-32 · po tym etapie masz 29 z 37 kryteriów (pkt)

    Krok 24: Plik style.css: zmienne i krój Tahoma

    Kryteria:

    R.4.1 Ustawiono domyślne formatowanie wszystkich selektorów (*) krój czcionki Tahoma

    ok. 2 min
    Polecenie z arkusza

    Domyślne formatowanie wszystkich selektorów : krój czcionki Tahoma

    Cztery kolory z arkusza w zmiennych - nazwy kolorów CSS (SaddleBrown, Tan, Wheat, Sienna) pisze się bez # i bez cudzysłowu. Selektor * z Tahomą i krojem zapasowym sans-serif (R.4.1).

    style.css CSS nowy plik
    :root {
        --banner: SaddleBrown;
        --side: Tan;
        --middle: Wheat;
        --hover: Sienna;
    }
    
    * {
        font-family: Tahoma, sans-serif;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  25. Krok 25: Baner i stopka

    Kryteria:

    R.4.3 Ustawiono biały kolor czcionki dla banera i stopki

    ok. 1 min
    Polecenie z arkusza

    Dla banera i stopki: kolor tła SaddleBrown, biały kolor czcionki, marginesy wewnętrzne 3 px, wyrównanie tekstu do środka

    Jedna reguła dla dwóch selektorów, kolor ze zmiennej. Marginesy wewnętrzne to padding.

    style.css CSS zmienione: 8 linii
    :root {
        --banner: SaddleBrown;
        --side: Tan;
        --middle: Wheat;
        --hover: Sienna;
    }
    
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header,
    footer {
        background-color: var(--banner);
        color: white;
        padding: 3px;
        text-align: center;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  26. Krok 26: Panele lewy i prawy

    ok. 1 min
    Polecenie z arkusza

    Dla panelu lewego i prawego: kolor tła Tan, wysokość 400 px, szerokość 40%

    Dwa identyfikatory, wspólne właściwości. Szerokość zostaje na samych panelach, tak jak ją ocenia klucz.

    Typowe błędy

    .readers (kropka) zamiast #readers nie trafia w panel - klucz R.3.6 wprost wymienia ten błąd.

    style.css CSS zmienione: 7 linii
    :root {
        --banner: SaddleBrown;
        --side: Tan;
        --middle: Wheat;
        --hover: Sienna;
    }
    
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header,
    footer {
        background-color: var(--banner);
        color: white;
        padding: 3px;
        text-align: center;
    }
    
    #new-reader,
    #readers {
        background-color: var(--side);
        height: 400px;
        width: 40%;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  27. Krok 27: Panel środkowy

    Kryteria: , ,

    R.4.2 Ustawiono kolor tła dla banera i stopki na SaddleBrown, panelu lewego i prawego Tan, środkowego Wheat

    R.4.4 Ustawiono wysokość wszystkich paneli 400px

    R.4.5 Ustawiono szerokość panelu lewego i prawego 40%, środkowego 20%

    ok. 1 min
    Polecenie z arkusza

    Dla panelu środkowego: kolor tła Wheat, wysokość 400 px, szerokość 20%

    40% + 20% + 40% = 100%. Wszystkie kolory, wysokości i szerokości paneli są ustawione (R.4.2, R.4.4, R.4.5).

    style.css CSS zmienione: 6 linii
    :root {
        --banner: SaddleBrown;
        --side: Tan;
        --middle: Wheat;
        --hover: Sienna;
    }
    
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header,
    footer {
        background-color: var(--banner);
        color: white;
        padding: 3px;
        text-align: center;
    }
    
    #new-reader,
    #readers {
        background-color: var(--side);
        height: 400px;
        width: 40%;
    }
    
    #contact {
        background-color: var(--middle);
        height: 400px;
        width: 20%;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  28. Krok 28: Układ: panele w jednym rzędzie

    Kryteria:

    R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z obrazem 2 w arkuszu (prawidłowo zastosowano właściwości CSS układające bloki na stronie, np. float i clear albo display z wartością flex lub grid albo position)

    ok. 2 min
    Polecenie z arkusza

    Podział zrealizowany za pomocą znaczników sekcji tak, aby wygląd układu bloków strony po uruchomieniu w przeglądarce, był zgodny z Obrazem 2

    display: flex na <main> ustawia trzy panele w rzędzie, a baner i stopka stoją nad i pod nim same z siebie - bez float paneli i bez clear stopki (R.3.1).

    Dlaczego nie siatka? W siatce procent szerokości elementu liczy się od jego kolumny: panel z width: 40% w kolumnie szerokiej na 40% strony zająłby 16% strony. Szerokości trzeba by przenieść do grid-template-columns, a klucz z tego arkusza ocenia je na panelach. Flex liczy procenty od pojemnika.

    Typowe błędy

    Margines albo ramka dodana panelom przekracza 100% szerokości - flex ściśnie wtedy panele i żaden nie będzie miał dokładnie 40% ani 20%.

    style.css CSS zmienione: 4 linie
    :root {
        --banner: SaddleBrown;
        --side: Tan;
        --middle: Wheat;
        --hover: Sienna;
    }
    
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header,
    footer {
        background-color: var(--banner);
        color: white;
        padding: 3px;
        text-align: center;
    }
    
    #new-reader,
    #readers {
        background-color: var(--side);
        height: 400px;
        width: 40%;
    }
    
    #contact {
        background-color: var(--middle);
        height: 400px;
        width: 20%;
    }
    
    main {
        display: flex;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  29. Krok 29: Obraz: margines prawy i oblewanie z lewej

    Kryteria:

    R.4.8 Ustawiono dla obrazu opływanie tekstem z lewej strony (np. float: right;)

    ok. 1 min
    Polecenie z arkusza

    Dla selektora obrazu: margines zewnętrzny prawy 20 px, oblewanie z lewej strony zgodnie z Obrazem 2

    „Oblewanie z lewej strony” - tekst opływa obraz od lewej, czyli obraz dosuwa się do prawej krawędzi panelu: float: right (R.4.8). Tu float zostaje, bo robi dokładnie to, do czego powstał - oblewanie obrazu tekstem; układ bloków strony robi flex. margin-right: 20px odsuwa obraz od krawędzi.

    Typowe błędy

    float: left daje odwrotny układ (obraz z lewej, tekst z prawej) - klucz wymaga oblewania z lewej, np. float: right.

    style.css CSS zmienione: 5 linii
    :root {
        --banner: SaddleBrown;
        --side: Tan;
        --middle: Wheat;
        --hover: Sienna;
    }
    
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header,
    footer {
        background-color: var(--banner);
        color: white;
        padding: 3px;
        text-align: center;
    }
    
    #new-reader,
    #readers {
        background-color: var(--side);
        height: 400px;
        width: 40%;
    }
    
    #contact {
        background-color: var(--middle);
        height: 400px;
        width: 20%;
    }
    
    main {
        display: flex;
    }
    
    img {
        margin-right: 20px;
        float: right;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  30. Krok 30: Nagłówki czwartego stopnia wyśrodkowane

    Kryteria:

    R.4.6 Ustawiono wyrównanie tekstu do środka dla banera, stopki i selektora h4

    ok. 1 min
    Polecenie z arkusza

    Dla selektora nagłówka czwartego stopnia: wyrównanie tekstu do środka

    Selektor znacznika h4 - uwaga arkusza zabrania tu klasy i identyfikatora (R.4.6 razem z banerem i stopką).

    style.css CSS zmienione: 4 linie
    :root {
        --banner: SaddleBrown;
        --side: Tan;
        --middle: Wheat;
        --hover: Sienna;
    }
    
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header,
    footer {
        background-color: var(--banner);
        color: white;
        padding: 3px;
        text-align: center;
    }
    
    #new-reader,
    #readers {
        background-color: var(--side);
        height: 400px;
        width: 40%;
    }
    
    #contact {
        background-color: var(--middle);
        height: 400px;
        width: 20%;
    }
    
    main {
        display: flex;
    }
    
    img {
        margin-right: 20px;
        float: right;
    }
    
    h4 {
        text-align: center;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  31. Krok 31: Pola edycyjne: marginesy

    Kryteria:

    R.4.7 Ustawiono marginesy wewnętrzne (padding) 3px dla banera i stopki oraz marginesy zewnętrzne (margin) 5px dla selektora input oraz margines zewnętrzny prawy (np. margin- right) 20px dla selektora img

    ok. 1 min
    Polecenie z arkusza

    Dla selektora pola edycyjnego: marginesy zewnętrzne 5 px

    Selektor znacznika input. Z padding banera i stopki i margin-right obrazu to komplet odstępów z arkusza (R.4.7).

    style.css CSS zmienione: 4 linie
    :root {
        --banner: SaddleBrown;
        --side: Tan;
        --middle: Wheat;
        --hover: Sienna;
    }
    
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header,
    footer {
        background-color: var(--banner);
        color: white;
        padding: 3px;
        text-align: center;
    }
    
    #new-reader,
    #readers {
        background-color: var(--side);
        height: 400px;
        width: 40%;
    }
    
    #contact {
        background-color: var(--middle);
        height: 400px;
        width: 20%;
    }
    
    main {
        display: flex;
    }
    
    img {
        margin-right: 20px;
        float: right;
    }
    
    h4 {
        text-align: center;
    }
    
    input {
        margin: 5px;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  32. Krok 32: Element listy pod kursorem

    Kryteria:

    R.4.9 Po najechaniu na element listy jego tło jest zmieniane na Sienna, kolor czcionki na biały

    ok. 1 min
    Polecenie z arkusza

    Po najechaniu na element listy, jego tło jest zmieniane na Sienna a kolor czcionki na biały

    li:hover - zmiana działa tylko nad wskazanym elementem (R.4.9). Efekt będzie widać, gdy Skrypt 2 wypisze czytelników.

    style.css CSS zmienione: 5 linii
    :root {
        --banner: SaddleBrown;
        --side: Tan;
        --middle: Wheat;
        --hover: Sienna;
    }
    
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header,
    footer {
        background-color: var(--banner);
        color: white;
        padding: 3px;
        text-align: center;
    }
    
    #new-reader,
    #readers {
        background-color: var(--side);
        height: 400px;
        width: 40%;
    }
    
    #contact {
        background-color: var(--middle);
        height: 400px;
        width: 20%;
    }
    
    main {
        display: flex;
    }
    
    img {
        margin-right: 20px;
        float: right;
    }
    
    h4 {
        text-align: center;
    }
    
    input {
        margin: 5px;
    }
    
    li:hover {
        background-color: var(--hover);
        color: white;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  33. Etap 4: Skrypty PHP

    Kroki 33-40 · po tym etapie masz 37 z 37 kryteriów (pkt)

    Krok 33: Połączenie z bazą na górze pliku i zamknięcie

    Kryteria: ,

    R.5.1 Zastosowano funkcję realizującą połączenie z serwerem bazy danych na localhost, użytkownik root, bez hasła, baza danych biblioteka

    R.5.2 Ostatnią operacją na bazie danych jest zamknięcie połączenia z serwerem bazy danych

    ok. 4 min
    Polecenie z arkusza

    Wymagania dotyczące skryptu: Napisany w języku PHP, umieszczony w pliku biblioteka.php. Skrypt łączy się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie biblioteka. Na końcu działania skrypt zamyka połączenie z serwerem

    Całą pracę z bazą strona wykona przed HTML-em, w jednym bloku PHP na samej górze biblioteka.php. To porządkuje kolejność: najpierw zapis nowego czytelnika, potem odczyt listy - więc dodana osoba od razu jest na liście, jak na obrazie 2. Ten układ przyda się też do przekierowania po zapisie, które da się wysłać tylko przed wypisaniem czegokolwiek.

    • new PDO(...) - serwer localhost, baza biblioteka, kodowanie utf8mb4, użytkownik root bez hasła (R.5.1). ERRMODE_EXCEPTION - błąd bazy rzuca wyjątek; FETCH_ASSOC - wiersze z nazwami kolumn,
    • $pdo = null - połączenie PDO zamyka się, gdy znika ostatnie odwołanie do obiektu. Stoi na końcu bloku, więc pozostanie ostatnią operacją na bazie (R.5.2),
    • try / catch (PDOException ...) - gdy baza nie działa, uczeń zobaczy zwykłe zdanie, a nie komunikat PHP ze szczegółami serwera.

    PDO zamiast mysqli z tabeli 1 - klucz przyjmuje obie biblioteki (uwaga przy R.5). Od tego kroku podgląd to wynik prawdziwego uruchomienia strony.

    biblioteka.php PHP zmienione: 12 linii
    <?php
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=biblioteka;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        $pdo = null;    // zamknięcie połączenia - ostatnia operacja na bazie
    } catch (PDOException $exception) {
        $databaseError = 'Nie udało się połączyć z bazą. Spróbuj odświeżyć stronę za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biblioteka</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Biblioteka w Książkowicach Małych</h1>
        </header>
        <main>
            <section id="new-reader">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="name">imię: </label>
                        <input type="text" id="name" name="name" required autocomplete="given-name">
                    </div>
                    <div>
                        <label for="surname">nazwisko: </label>
                        <input type="text" id="surname" name="surname" required autocomplete="family-name">
                    </div>
                    <div>
                        <label for="code">symbol: </label>
                        <input type="number" id="code" name="code" required min="0" max="999999">
                    </div>
                    <button type="submit">AKCEPTUJ</button>
                </form>
            </section>
            <section id="contact">
                <img src="biblioteka.png" alt="biblioteka">
                <h6>ul. Czytelników&nbsp;15; Książkowice Małe</h6>
                <p><a href="mailto:[email protected]">Czy masz jakieś uwagi?</a></p>
            </section>
            <section id="readers">
                <h4>Nasi czytelnicy:</h4>
                <ol>
                </ol>
            </section>
        </main>
        <footer>
            <p>Projekt witryny: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  34. Krok 34: Skrypt 1: dane z formularza

    Kryteria:

    R.5.5 Skrypt 1 zawiera instrukcję pobierającą dane z tablicy globalnej odpowiadającej zastosowanej metodzie formularza ($_POST, $_GET, $_REQUEST)

    ok. 4 min
    Polecenie z arkusza

    Skrypt 1: Pobiera dane z formularza

    • $_SERVER['REQUEST_METHOD'] === 'POST' - czy strona przyszła z wysłanego formularza,
    • $_POST['name'] ?? '' - wartość z tablicy globalnej $_POST (R.5.5), a gdy formularza nie wysłano - pusty napis, bez ostrzeżenia „Undefined array key”,
    • trim usuwa spacje z brzegów - sama spacja w polu to też „nic”,
    • $isValid - imię i nazwisko niepuste, a symbol to od 1 do 6 cyfr (preg_match z wyrażeniem ^[0-9]{1,6}$), bo tyle mieści kolumna kod typu varchar(6).

    Od tego kroku podgląd pokazuje stronę po wysłaniu formularza z danymi Moniki Nowak; na razie nic się nie zmienia.

    biblioteka.php PHP zmienione: 7 linii
    <?php
    // Skrypt 1: dane z formularza
    $formSent = $_SERVER['REQUEST_METHOD'] === 'POST';
    $name = trim($_POST['name'] ?? '');
    $surname = trim($_POST['surname'] ?? '');
    $code = trim($_POST['code'] ?? '');
    $isValid = $name !== '' && $surname !== '' && preg_match('/^[0-9]{1,6}$/', $code) === 1;
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=biblioteka;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        $pdo = null;    // zamknięcie połączenia - ostatnia operacja na bazie
    } catch (PDOException $exception) {
        $databaseError = 'Nie udało się połączyć z bazą. Spróbuj odświeżyć stronę za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biblioteka</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Biblioteka w Książkowicach Małych</h1>
        </header>
        <main>
            <section id="new-reader">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="name">imię: </label>
                        <input type="text" id="name" name="name" required autocomplete="given-name">
                    </div>
                    <div>
                        <label for="surname">nazwisko: </label>
                        <input type="text" id="surname" name="surname" required autocomplete="family-name">
                    </div>
                    <div>
                        <label for="code">symbol: </label>
                        <input type="number" id="code" name="code" required min="0" max="999999">
                    </div>
                    <button type="submit">AKCEPTUJ</button>
                </form>
            </section>
            <section id="contact">
                <img src="biblioteka.png" alt="biblioteka">
                <h6>ul. Czytelników&nbsp;15; Książkowice Małe</h6>
                <p><a href="mailto:[email protected]">Czy masz jakieś uwagi?</a></p>
            </section>
            <section id="readers">
                <h4>Nasi czytelnicy:</h4>
                <ol>
                </ol>
            </section>
        </main>
        <footer>
            <p>Projekt witryny: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

    Podgląd strony bez zmian - taki jak po kroku 33.

  35. Krok 35: Skrypt 1: napis o dodanym czytelniku

    Kryteria:

    R.5.3 Skrypt 1 wyświetla napis: "Dodano czytelnika <imie> <nazwisko>”, gdzie pola <> pobrano z formularza

    ok. 2 min
    Polecenie z arkusza

    Wyświetla napis „Dodano czytelnika <imie> <nazwisko>”, gdzie pola <> pobrano z formularza

    Zmienna $addedReader - imię i nazwisko, gdy formularz wysłano z poprawnymi danymi, a w przeciwnym razie null. Pod formularzem akapit „Dodano czytelnika Monika Nowak”, jak na obrazie 2 (R.5.3). Imię i nazwisko przechodzą przez funkcję e() (htmlspecialchars) - tekst od użytkownika nie wstrzyknie do strony kodu HTML.

    biblioteka.php PHP zmienione: 9 linii
    <?php
    // Skrypt 1: dane z formularza
    $formSent = $_SERVER['REQUEST_METHOD'] === 'POST';
    $name = trim($_POST['name'] ?? '');
    $surname = trim($_POST['surname'] ?? '');
    $code = trim($_POST['code'] ?? '');
    $isValid = $name !== '' && $surname !== '' && preg_match('/^[0-9]{1,6}$/', $code) === 1;
    $addedReader = $formSent && $isValid ? "$name $surname" : null;
    
    function e(string|int|float|null $value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES);
    }
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=biblioteka;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        $pdo = null;    // zamknięcie połączenia - ostatnia operacja na bazie
    } catch (PDOException $exception) {
        $databaseError = 'Nie udało się połączyć z bazą. Spróbuj odświeżyć stronę za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biblioteka</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Biblioteka w Książkowicach Małych</h1>
        </header>
        <main>
            <section id="new-reader">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="name">imię: </label>
                        <input type="text" id="name" name="name" required autocomplete="given-name">
                    </div>
                    <div>
                        <label for="surname">nazwisko: </label>
                        <input type="text" id="surname" name="surname" required autocomplete="family-name">
                    </div>
                    <div>
                        <label for="code">symbol: </label>
                        <input type="number" id="code" name="code" required min="0" max="999999">
                    </div>
                    <button type="submit">AKCEPTUJ</button>
                </form>
    <?php if ($addedReader !== null): ?>
                <p>Dodano czytelnika <?= e($addedReader) ?></p>
    <?php endif; ?>
            </section>
            <section id="contact">
                <img src="biblioteka.png" alt="biblioteka">
                <h6>ul. Czytelników&nbsp;15; Książkowice Małe</h6>
                <p><a href="mailto:[email protected]">Czy masz jakieś uwagi?</a></p>
            </section>
            <section id="readers">
                <h4>Nasi czytelnicy:</h4>
                <ol>
                </ol>
            </section>
        </main>
        <footer>
            <p>Projekt witryny: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  36. Krok 36: Skrypt 1: zapytanie 1 z danymi z formularza

    Kryteria:

    R.5.4 Skrypt 1 dodaje do tabeli czytelnicy nowy rekord, zawierający dane pobrane z formularza, należy sprawdzić w phpMyAdmin

    ok. 3 min
    Polecenie z arkusza

    Wysyła do bazy danych zmodyfikowane zapytanie 1 tak, że wstawia do bazy danych dane z formularza

    Zapytanie przygotowane: w miejscu danych stoją parametry :name, :surname, :code, a wartości podaje execute([...]). Baza dostaje treść zapytania i dane osobno, więc tekst z formularza nigdy nie stanie się częścią SQL - nazwisko O'Hara z apostrofem zapisuje się poprawnie (sprawdzone), zamiast przerwać zapytanie (R.5.4).

    • zapytanie wykonuje się tylko dla poprawnych danych (if ($formSent && $isValid)),
    • $addedReader dostaje imię i nazwisko dopiero po zapisie - napis pojawia się tylko wtedy, gdy czytelnik naprawdę jest w bazie,
    • $statement = null zwalnia zapytanie przed zamknięciem połączenia.

    Sprawdzenie, jak każe klucz: w phpMyAdmin tabela czytelnicy ma nowy wiersz Monika Nowak, 56678.

    Typowe błędy

    Tabela czytelnicy z pliku arkusza ma zestaw znaków latin1 - polskie litery z formularza zapisują się jako „?” (sprawdzone: „Łucja Żak” → „?ucja ?ak”), choć napis na stronie jest poprawny. Na egzaminie nie zmieniaj struktury tabel z arkusza; poza egzaminem naprawia to ALTER TABLE czytelnicy CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_polish_ci;.

    biblioteka.php PHP zmienione: 9 linii
    <?php
    // Skrypt 1: dane z formularza
    $formSent = $_SERVER['REQUEST_METHOD'] === 'POST';
    $name = trim($_POST['name'] ?? '');
    $surname = trim($_POST['surname'] ?? '');
    $code = trim($_POST['code'] ?? '');
    $isValid = $name !== '' && $surname !== '' && preg_match('/^[0-9]{1,6}$/', $code) === 1;
    $addedReader = null;
    
    function e(string|int|float|null $value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES);
    }
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=biblioteka;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // Skrypt 1: zapytanie 1 - nowy czytelnik z formularza
        if ($formSent && $isValid) {
            $statement = $pdo->prepare('INSERT INTO czytelnicy (imie, nazwisko, kod) VALUES (:name, :surname, :code)');
            $statement->execute(['name' => $name, 'surname' => $surname, 'code' => $code]);
            $statement = null;
            $addedReader = "$name $surname";
        }
    
        $pdo = null;    // zamknięcie połączenia - ostatnia operacja na bazie
    } catch (PDOException $exception) {
        $databaseError = 'Nie udało się połączyć z bazą. Spróbuj odświeżyć stronę za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biblioteka</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Biblioteka w Książkowicach Małych</h1>
        </header>
        <main>
            <section id="new-reader">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="name">imię: </label>
                        <input type="text" id="name" name="name" required autocomplete="given-name">
                    </div>
                    <div>
                        <label for="surname">nazwisko: </label>
                        <input type="text" id="surname" name="surname" required autocomplete="family-name">
                    </div>
                    <div>
                        <label for="code">symbol: </label>
                        <input type="number" id="code" name="code" required min="0" max="999999">
                    </div>
                    <button type="submit">AKCEPTUJ</button>
                </form>
    <?php if ($addedReader !== null): ?>
                <p>Dodano czytelnika <?= e($addedReader) ?></p>
    <?php endif; ?>
            </section>
            <section id="contact">
                <img src="biblioteka.png" alt="biblioteka">
                <h6>ul. Czytelników&nbsp;15; Książkowice Małe</h6>
                <p><a href="mailto:[email protected]">Czy masz jakieś uwagi?</a></p>
            </section>
            <section id="readers">
                <h4>Nasi czytelnicy:</h4>
                <ol>
                </ol>
            </section>
        </main>
        <footer>
            <p>Projekt witryny: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

    Podgląd strony bez zmian - taki jak po kroku 35.

  37. Krok 37: Skrypt 2: zapytanie 2

    ok. 2 min
    Polecenie z arkusza

    Skrypt 2: Wysyła do bazy danych zapytanie 2

    Zapytanie bez danych od użytkownika, więc wystarczy query; fetchAll() zwraca od razu wszystkie wiersze. Stoi po zapisie ze Skryptu 1, więc lista zawiera już nowego czytelnika. W catch lista dostaje wartość pustą, żeby HTML niżej nie trafił na niezdefiniowaną zmienną. Na stronie jeszcze nic nie widać.

    biblioteka.php PHP zmienione: 4 linie
    <?php
    // Skrypt 1: dane z formularza
    $formSent = $_SERVER['REQUEST_METHOD'] === 'POST';
    $name = trim($_POST['name'] ?? '');
    $surname = trim($_POST['surname'] ?? '');
    $code = trim($_POST['code'] ?? '');
    $isValid = $name !== '' && $surname !== '' && preg_match('/^[0-9]{1,6}$/', $code) === 1;
    $addedReader = null;
    
    function e(string|int|float|null $value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES);
    }
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=biblioteka;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // Skrypt 1: zapytanie 1 - nowy czytelnik z formularza
        if ($formSent && $isValid) {
            $statement = $pdo->prepare('INSERT INTO czytelnicy (imie, nazwisko, kod) VALUES (:name, :surname, :code)');
            $statement->execute(['name' => $name, 'surname' => $surname, 'code' => $code]);
            $statement = null;
            $addedReader = "$name $surname";
        }
    
        // Skrypt 2: zapytanie 2 - czytelnicy po nazwisku
        $readers = $pdo->query('SELECT imie, nazwisko FROM czytelnicy ORDER BY nazwisko, imie')->fetchAll();
    
        $pdo = null;    // zamknięcie połączenia - ostatnia operacja na bazie
    } catch (PDOException $exception) {
        $readers = [];
        $databaseError = 'Nie udało się połączyć z bazą. Spróbuj odświeżyć stronę za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biblioteka</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Biblioteka w Książkowicach Małych</h1>
        </header>
        <main>
            <section id="new-reader">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="name">imię: </label>
                        <input type="text" id="name" name="name" required autocomplete="given-name">
                    </div>
                    <div>
                        <label for="surname">nazwisko: </label>
                        <input type="text" id="surname" name="surname" required autocomplete="family-name">
                    </div>
                    <div>
                        <label for="code">symbol: </label>
                        <input type="number" id="code" name="code" required min="0" max="999999">
                    </div>
                    <button type="submit">AKCEPTUJ</button>
                </form>
    <?php if ($addedReader !== null): ?>
                <p>Dodano czytelnika <?= e($addedReader) ?></p>
    <?php endif; ?>
            </section>
            <section id="contact">
                <img src="biblioteka.png" alt="biblioteka">
                <h6>ul. Czytelników&nbsp;15; Książkowice Małe</h6>
                <p><a href="mailto:[email protected]">Czy masz jakieś uwagi?</a></p>
            </section>
            <section id="readers">
                <h4>Nasi czytelnicy:</h4>
                <ol>
                </ol>
            </section>
        </main>
        <footer>
            <p>Projekt witryny: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

    Podgląd strony bez zmian - taki jak po kroku 35.

  38. Krok 38: Skrypt 2: czytelnicy jako elementy listy

    Kryteria: , ,

    R.2.6 W bloku prawym umieszczono jedną listę numerowaną, w kodzie HTML lub skrypcie, zastosowano znaczniki <ol>, <li>

    R.5.6 Skrypt 2 wyświetla imiona i nazwiska czytelników zwrócone zapytaniem 2

    R.5.7 Skrypt 2 generuje rekordy jako elementy listy z wykorzystaniem pętli

    ok. 3 min
    Polecenie z arkusza

    Pobrane zapytaniem imiona i nazwiska czytelników wypisywane są jako elementy listy numerowanej (uporządkowanej)

    • foreach z dwukropkiem i endforeach - pętla wypisuje jeden <li> na czytelnika (R.5.7); numery dopisuje przeglądarka, bo elementy stoją w <ol>,
    • pola po nazwach kolumn ($reader['imie']), nie po numerach,
    • każda dana z bazy przez e() - imię zapisane kiedyś z formularza (np. ze znacznikiem <b>) nie wstrzyknie kodu przy każdym kolejnym otwarciu strony.

    Podgląd: 12 czytelników (R.5.6, R.2.6), w tym Ewelina Romanowska z zapytania 1 i dodana przed chwilą Monika Nowak - jak na obrazie 2.

    biblioteka.php PHP zmienione: 3 linie
    <?php
    // Skrypt 1: dane z formularza
    $formSent = $_SERVER['REQUEST_METHOD'] === 'POST';
    $name = trim($_POST['name'] ?? '');
    $surname = trim($_POST['surname'] ?? '');
    $code = trim($_POST['code'] ?? '');
    $isValid = $name !== '' && $surname !== '' && preg_match('/^[0-9]{1,6}$/', $code) === 1;
    $addedReader = null;
    
    function e(string|int|float|null $value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES);
    }
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=biblioteka;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // Skrypt 1: zapytanie 1 - nowy czytelnik z formularza
        if ($formSent && $isValid) {
            $statement = $pdo->prepare('INSERT INTO czytelnicy (imie, nazwisko, kod) VALUES (:name, :surname, :code)');
            $statement->execute(['name' => $name, 'surname' => $surname, 'code' => $code]);
            $statement = null;
            $addedReader = "$name $surname";
        }
    
        // Skrypt 2: zapytanie 2 - czytelnicy po nazwisku
        $readers = $pdo->query('SELECT imie, nazwisko FROM czytelnicy ORDER BY nazwisko, imie')->fetchAll();
    
        $pdo = null;    // zamknięcie połączenia - ostatnia operacja na bazie
    } catch (PDOException $exception) {
        $readers = [];
        $databaseError = 'Nie udało się połączyć z bazą. Spróbuj odświeżyć stronę za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biblioteka</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Biblioteka w Książkowicach Małych</h1>
        </header>
        <main>
            <section id="new-reader">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="name">imię: </label>
                        <input type="text" id="name" name="name" required autocomplete="given-name">
                    </div>
                    <div>
                        <label for="surname">nazwisko: </label>
                        <input type="text" id="surname" name="surname" required autocomplete="family-name">
                    </div>
                    <div>
                        <label for="code">symbol: </label>
                        <input type="number" id="code" name="code" required min="0" max="999999">
                    </div>
                    <button type="submit">AKCEPTUJ</button>
                </form>
    <?php if ($addedReader !== null): ?>
                <p>Dodano czytelnika <?= e($addedReader) ?></p>
    <?php endif; ?>
            </section>
            <section id="contact">
                <img src="biblioteka.png" alt="biblioteka">
                <h6>ul. Czytelników&nbsp;15; Książkowice Małe</h6>
                <p><a href="mailto:[email protected]">Czy masz jakieś uwagi?</a></p>
            </section>
            <section id="readers">
                <h4>Nasi czytelnicy:</h4>
                <ol>
    <?php foreach ($readers as $reader): ?>
                    <li><?= e($reader['imie']) ?> <?= e($reader['nazwisko']) ?></li>
    <?php endforeach; ?>
                </ol>
            </section>
        </main>
        <footer>
            <p>Projekt witryny: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  39. Krok 39: Odświeżenie strony nie dodaje czytelnika drugi raz (PRG)

    ok. 4 min

    Dodatek spoza arkusza - klucz go nie ocenia. Po wysłaniu formularza strona jest wynikiem żądania POST: odświeżenie (F5) wysyła formularz jeszcze raz i na liście są dwie Moniki Nowak. Wzorzec PRG (Post/Redirect/Get) to usuwa, a napis „Dodano czytelnika …” z arkusza zostaje:

    • po udanym zapisie strona zapamiętuje imię i nazwisko w sesji ($_SESSION, wymaga session_start() na samym początku pliku),
    • header('Location: biblioteka.php') i exit - przeglądarka wczytuje stronę jeszcze raz, zwykłym żądaniem GET. exit jest konieczne: header tylko ustawia nagłówek odpowiedzi i nie zatrzymuje skryptu,
    • nowe wejście odczytuje napis z sesji, od razu go usuwa (unset) i pokazuje raz - nad listą, w której nowa osoba już jest.

    Połączenie jest zamknięte, zanim padnie przekierowanie, więc $pdo = null dalej jest ostatnią operacją na bazie. Sprawdzone na prawdziwym PHP: wysłanie formularza daje odpowiedź 302, po niej napis i Monika Nowak na liście, a kolejne odświeżenie - lista bez drugiej Moniki i bez napisu.

    biblioteka.php PHP zmienione: 11 linii
    <?php
    session_start();
    
    // Skrypt 1: dane z formularza
    $formSent = $_SERVER['REQUEST_METHOD'] === 'POST';
    $name = trim($_POST['name'] ?? '');
    $surname = trim($_POST['surname'] ?? '');
    $code = trim($_POST['code'] ?? '');
    $isValid = $name !== '' && $surname !== '' && preg_match('/^[0-9]{1,6}$/', $code) === 1;
    $addedReader = null;
    
    function e(string|int|float|null $value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES);
    }
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=biblioteka;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // Skrypt 1: zapytanie 1 - nowy czytelnik z formularza
        if ($formSent && $isValid) {
            $statement = $pdo->prepare('INSERT INTO czytelnicy (imie, nazwisko, kod) VALUES (:name, :surname, :code)');
            $statement->execute(['name' => $name, 'surname' => $surname, 'code' => $code]);
            $statement = null;
            $addedReader = "$name $surname";
        }
    
        // Skrypt 2: zapytanie 2 - czytelnicy po nazwisku
        $readers = $pdo->query('SELECT imie, nazwisko FROM czytelnicy ORDER BY nazwisko, imie')->fetchAll();
    
        $pdo = null;    // zamknięcie połączenia - ostatnia operacja na bazie
    } catch (PDOException $exception) {
        $readers = [];
        $databaseError = 'Nie udało się połączyć z bazą. Spróbuj odświeżyć stronę za chwilę.';
    }
    
    // PRG: po zapisie przekierowanie na tę samą stronę - odświeżenie nie wyśle formularza drugi raz
    if ($addedReader !== null) {
        $_SESSION['addedReader'] = $addedReader;
        header('Location: biblioteka.php');
        exit;
    }
    $addedReader = $_SESSION['addedReader'] ?? null;
    unset($_SESSION['addedReader']);
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biblioteka</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Biblioteka w Książkowicach Małych</h1>
        </header>
        <main>
            <section id="new-reader">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="name">imię: </label>
                        <input type="text" id="name" name="name" required autocomplete="given-name">
                    </div>
                    <div>
                        <label for="surname">nazwisko: </label>
                        <input type="text" id="surname" name="surname" required autocomplete="family-name">
                    </div>
                    <div>
                        <label for="code">symbol: </label>
                        <input type="number" id="code" name="code" required min="0" max="999999">
                    </div>
                    <button type="submit">AKCEPTUJ</button>
                </form>
    <?php if ($addedReader !== null): ?>
                <p>Dodano czytelnika <?= e($addedReader) ?></p>
    <?php endif; ?>
            </section>
            <section id="contact">
                <img src="biblioteka.png" alt="biblioteka">
                <h6>ul. Czytelników&nbsp;15; Książkowice Małe</h6>
                <p><a href="mailto:[email protected]">Czy masz jakieś uwagi?</a></p>
            </section>
            <section id="readers">
                <h4>Nasi czytelnicy:</h4>
                <ol>
    <?php foreach ($readers as $reader): ?>
                    <li><?= e($reader['imie']) ?> <?= e($reader['nazwisko']) ?></li>
    <?php endforeach; ?>
                </ol>
            </section>
        </main>
        <footer>
            <p>Projekt witryny: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

    Podgląd strony bez zmian - taki jak po kroku 38.

  40. Krok 40: Niepełne dane i awaria bazy: komunikaty

    ok. 3 min

    Dodatek spoza arkusza - klucz go nie ocenia. Pod formularzem pojawia się komunikat także w sytuacjach, których obraz 2 nie pokazuje:

    • niepełne albo złe dane (sama spacja w imieniu, puste nazwisko, symbol „1234567” albo „-5”, wysłane z pominięciem formularza) - „Wpisz imię, nazwisko i symbol (od 1 do 6 cyfr).”, a do bazy nic nie trafia,
    • awaria bazy (catch) - komunikat z role="alert" (czytnik ekranu przeczyta go od razu), a reszta strony działa dalej.

    Sprawdzone na prawdziwym PHP 8.2: cztery błędne zestawy danych i złe hasło do bazy - za każdym razem cała strona, bez ostrzeżeń PHP i bez nowych wierszy w tabeli czytelnicy.

    biblioteka.php PHP zmienione: 5 linii
    <?php
    session_start();
    
    // Skrypt 1: dane z formularza
    $formSent = $_SERVER['REQUEST_METHOD'] === 'POST';
    $name = trim($_POST['name'] ?? '');
    $surname = trim($_POST['surname'] ?? '');
    $code = trim($_POST['code'] ?? '');
    $isValid = $name !== '' && $surname !== '' && preg_match('/^[0-9]{1,6}$/', $code) === 1;
    $addedReader = null;
    
    function e(string|int|float|null $value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES);
    }
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=biblioteka;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // Skrypt 1: zapytanie 1 - nowy czytelnik z formularza
        if ($formSent && $isValid) {
            $statement = $pdo->prepare('INSERT INTO czytelnicy (imie, nazwisko, kod) VALUES (:name, :surname, :code)');
            $statement->execute(['name' => $name, 'surname' => $surname, 'code' => $code]);
            $statement = null;
            $addedReader = "$name $surname";
        }
    
        // Skrypt 2: zapytanie 2 - czytelnicy po nazwisku
        $readers = $pdo->query('SELECT imie, nazwisko FROM czytelnicy ORDER BY nazwisko, imie')->fetchAll();
    
        $pdo = null;    // zamknięcie połączenia - ostatnia operacja na bazie
    } catch (PDOException $exception) {
        $readers = [];
        $databaseError = 'Nie udało się połączyć z bazą. Spróbuj odświeżyć stronę za chwilę.';
    }
    
    // PRG: po zapisie przekierowanie na tę samą stronę - odświeżenie nie wyśle formularza drugi raz
    if ($addedReader !== null) {
        $_SESSION['addedReader'] = $addedReader;
        header('Location: biblioteka.php');
        exit;
    }
    $addedReader = $_SESSION['addedReader'] ?? null;
    unset($_SESSION['addedReader']);
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Biblioteka</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Biblioteka w Książkowicach Małych</h1>
        </header>
        <main>
            <section id="new-reader">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="name">imię: </label>
                        <input type="text" id="name" name="name" required autocomplete="given-name">
                    </div>
                    <div>
                        <label for="surname">nazwisko: </label>
                        <input type="text" id="surname" name="surname" required autocomplete="family-name">
                    </div>
                    <div>
                        <label for="code">symbol: </label>
                        <input type="number" id="code" name="code" required min="0" max="999999">
                    </div>
                    <button type="submit">AKCEPTUJ</button>
                </form>
    <?php if (isset($databaseError)): ?>
                <p role="alert"><?= e($databaseError) ?></p>
    <?php elseif ($formSent && !$isValid): ?>
                <p role="alert">Wpisz imię, nazwisko i symbol (od 1 do 6 cyfr).</p>
    <?php endif; ?>
    <?php if ($addedReader !== null): ?>
                <p>Dodano czytelnika <?= e($addedReader) ?></p>
    <?php endif; ?>
            </section>
            <section id="contact">
                <img src="biblioteka.png" alt="biblioteka">
                <h6>ul. Czytelników&nbsp;15; Książkowice Małe</h6>
                <p><a href="mailto:[email protected]">Czy masz jakieś uwagi?</a></p>
            </section>
            <section id="readers">
                <h4>Nasi czytelnicy:</h4>
                <ol>
    <?php foreach ($readers as $reader): ?>
                    <li><?= e($reader['imie']) ?> <?= e($reader['nazwisko']) ?></li>
    <?php endforeach; ?>
                </ol>
            </section>
        </main>
        <footer>
            <p>Projekt witryny: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

    Podgląd strony bez zmian - taki jak po kroku 38.

  41. Etap 5: Porównanie z wersją podstawową

    Krok 41 · po tym etapie masz 37 z 37 kryteriów (pkt)

    Krok 41: Co jest inaczej i którą wersję wybrać na egzaminie

    ok. 3 min

    Obie wersje dają komplet 37 punktów. Różnice:

    wersja podstawowawersja nowoczesna
    układ panelifloat: left i clear stopki<main> z display: flex (obraz dalej z float: right)
    formularz<label> wokół pola, <br>label for, wiersze w <div>, required, autocomplete, max
    zapytaniapełne nazwy tabel, GROUP BY nazwiskoaliasy, ORDER BY nazwisko, imie, GROUP BY a.id, a.nazwisko
    bibliotekamysqli (tabela 1 arkusza)PDO z wyjątkami
    gdzie są zapytaniaskrypt pod formularzem i w liściejeden blok na górze biblioteka.php
    dane z formularza$_POST wprost do zapytania$_POST + trim + sprawdzenie symbolu
    INSERTdane wklejone w treść zapytaniaparametry :name, :surname, :code
    odświeżenie po zapisiedodaje czytelnika drugi razPRG - przekierowanie, napis z sesji
    wypisywaniedane wproste() - htmlspecialchars

    W prawdziwym projekcie przy formularzu zapisującym dane dochodzi jeszcze token CSRF - losowy napis z sesji w ukrytym polu, który potwierdza, że formularz wysłano z tej strony. Klucz tego nie wymaga. Dane logowania do bazy trzyma się zwykle w osobnym pliku (np. config.php), ale arkusz każe umieścić skrypt w pliku biblioteka.php.

    Na egzaminie wersja podstawowa trzyma się funkcji z tabeli 1 i łatwiej ją napisać bez dokumentacji. Nowoczesna nie zapisuje pustych ani podwójnych czytelników i nie wywraca się na apostrofie. Obie biblioteki klucz przyjmuje.

    Podgląd strony bez zmian - taki jak po kroku 38.

  42. Etap 6: Oddanie pracy

    Krok 42 · po tym etapie masz 37 z 37 kryteriów (pkt)

    Krok 42: Plik przeglądarka.txt i nagranie płyty

    ok. 5 min
    Polecenie z arkusza

    Po zakończeniu pracy utwórz w folderze z numerem zdającego 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 nazwa przeglądarki, w której sprawdzana była strona. W folderze z numerem zdającego: biblioteka.php, biblioteka.png, dane.jpg, kwerenda1.jpg - kwerenda4.jpg, kwerendy.txt, przeglądarka.txt i style.css. Nagraj płytę, sprawdź jej odczyt i opisz ją numerem zdającego.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

    Podgląd strony bez zmian - taki jak po kroku 38.

Podgląd

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: . 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.