Przejdź do głównej treści
E.14-04-21.06-SGCzerwiec 2021 · 39 kroków · ok. 82 min · 34 kryteria z klucza

Rozwiązanie krok po kroku: Biblioteka miejska - lista czytelników (Nowoczesny kod (flex, PDO))

To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza biblioteka z czterema zapytaniami i strona biblioteka.php z listą czytelników i formularzem nowego czytelnika - i tak samo spełnia każde kryterium klucza. Inna jest droga:

  • flex zamiast float: trzy panele stoją we wspólnym <main>, który układa je w rzędzie, a stopka nie potrzebuje clear,
  • zmienne CSS dla dwóch kolorów z arkusza, etykiety <label for> i wiersze formularza w blokach zamiast <br>,
  • PHP na PDO na górze pliku, przed HTML-em: najpierw zapis nowego czytelnika, potem lista czytelników - nowa osoba jest na liście od razu, bez odświeżania,
  • zapytanie przygotowane do INSERT, sprawdzenie danych (puste pola, rok urodzenia z sensownego zakresu, długość kolumn), kod czytelnika liczony funkcjami mb_, które rozumieją polskie litery,
  • każdy tekst z bazy i z formularza przechodzi przez htmlspecialchars - lista pokazuje dane wpisane przez użytkowników, więc to tu najważniejsze,
  • dane wracają do pól po błędzie, a po dodaniu czytelnika przekierowanie - odświeżenie strony nie doda go drugi raz.

To arkusz E.14 z czerwca 2021 - kwalifikacji formuły 2012, którą zastąpiła INF.03. Baza i grafika pochodzą z archiwum odtworzonego z plików zgodnych z arkuszem (załącznik pliki4.zip na stronie arkusza, hasło jak w arkuszu), zapytania i zrzuty są policzone na MariaDB 10.4, a podgląd od kroków z PHP to wynik prawdziwego uruchomienia kodu w PHP 8.2 (klucz każe sprawdzać w XAMPP 7.4.8 - użyte konstrukcje działają w obu wersjach).

Jak liczą się punkty. Klucz ma 34 kryteria (R.1.1 - R.5.7), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 26 punktów. Przy każdym kroku jest napisane, które kryteria spełnia; klucz przyjmuje zarówno mysqli, jak i PDO.

  1. Etap 1: Baza danych

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

    Krok 1: Folder zdającego i archiwum pliki4.zip

    ok. 2 min
    Polecenie z arkusza

    Aby wykonać zadanie, zaloguj się na konto Egzamin bez hasła. Na pulpicie znajdziesz archiwum ZIP o nazwie pliki4.zip zabezpieczone hasłem: Z@d@niE_14. Archiwum należy rozpakować. Na pulpicie konta Egzamin utwórz folder. Jako nazwy folderu użyj swojego numeru PESEL. Rozpakowane pliki umieść w tym folderze. Wyniki swojej pracy również zapisz w tym folderze.

    Folder na pulpicie nazwany numerem PESEL (w tym rozwiązaniu w jego miejscu stoi Bartosz Bryniarski), do niego archiwum pliki4.zip rozpakowane z hasłem Z@d@niE_14. W środku: biblioteka.sql i biblioteka.png. Do tego folderu trafiają wszystkie rezultaty pracy.

  2. Krok 2: XAMPP i pusta baza biblioteka

    ok. 2 min
    Polecenie z arkusza

    Uruchom usługi MySQL i Apache z XAMPP Control Panel i przejdź do narzędzia phpMyAdmin. Następnie wykonaj operacje na bazie danych: Utwórz nową bazę danych o nazwie biblioteka

    Start przy Apache i MySQL, potem http://localhost/phpmyadmin, Nowa, nazwa biblioteka, porównywanie np. utf8mb4_polish_ci, Utwórz.

  3. Krok 3: Import pliku biblioteka.sql i zrzut dane.jpg

    Kryteria:

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

    ok. 3 min
    Polecenie z arkusza

    Do bazy zaimportuj tabele z pliku biblioteka.sql, z wcześniej rozpakowanego archiwum. Wykonaj zrzut ekranu po imporcie. Zrzut zapisz w folderze z Twoim numerem PESEL, 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

    Baza biblioteka, karta Import, plik biblioteka.sql, Wykonaj. Plik sam tworzy sześć tabel z obrazu 1 i wstawia dane: autorzy (10), czytelnicy (10), kategorie (3), ksiazki (21), wydawnictwa (4), wypozyczenia (10). Zrzut całego ekranu z komunikatem o imporcie i listą tabel: dane.jpg (R.1.1).

    Podgląd strony po tym kroku
    Podgląd strony po kroku 3
  4. Krok 4: Zapytanie 1: nowa czytelniczka Anna Michalak

    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 dane do tabeli czytelnicy: Anna, Michalak, an05mi. W pliku z kwerendami lub na zrzucie istnieje zapis np.: INSERT INTO czytelnicy VALUES (null, 'Anna', 'Michalak','an05mi'); oraz na zrzucie widoczny jest komunikat o poprawnym wykonaniu kwerendy lub wstawiony wiersze

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 1: wstawiające do tabeli czytelnicy nowy rekord z danymi: Anna, Michalak, an05mi

    Lista kolumn bez id - numer nada AUTO_INCREMENT (Anna dostaje 11). Zapytanie trafia do kwerendy.txt (R.1.2), zrzut do kw1.jpg (R.1.3). Każda klauzula w osobnej linii - przy dłuższych zapytaniach łatwiej wtedy znaleźć błąd. Kod an05mi to wzór dla skryptu: dwie litery imienia, dwie ostatnie cyfry roku urodzenia, dwie litery nazwiska - małymi literami.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    INSERT INTO czytelnicy (imie, nazwisko, kod)
    VALUES ('Anna', 'Michalak', 'an05mi');
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: imiona i nazwiska czytelników

    Kryteria:

    R.1.4 Zapisano zapytanie 2 wybierające jedynie pola imie i nazwisko dla wszystkich rekordów w tabeli czytelnicy. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT imie, nazwisko FROM czytelnicy; oraz na zrzucie widoczna są jedynie imiona i nazwiska wszystkich czytelników, minimum 10 rekordów

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 2: wybierające jedynie pola imie i nazwisko dla wszystkich rekordów w tabeli czytelnicy

    Dwie kolumny, bez warunku - 11 czytelników, z Anną Michalak na końcu. Zrzut: kw2.jpg (R.1.4). To zapytanie wypełni listę na stronie.

    kwerendy.txt Tekst zmienione: 4 linie
    -- Zapytanie 1
    INSERT INTO czytelnicy (imie, nazwisko, kod)
    VALUES ('Anna', 'Michalak', 'an05mi');
    
    -- Zapytanie 2
    SELECT imie, nazwisko
    FROM czytelnicy;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: tytuły Fredry

    Kryteria:

    R.1.5 Zapisano zapytanie 3 wybierające jedynie pole tytul dla wszystkich utworów, których autorem jest Fredro. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT tytul FROM ksiazki, autorzy WHERE ksiazki.id_autor=autorzy.id AND autorzy.nazwisko='Fredro'; (dozwolona również wersja zapytania bez relacji np.: SELECT tytul FROM ksiazki WHERE id_autor = 7, relację można zapisać poprzez INNER JOIN) oraz na zrzucie widoczne są jedynie dwa rekordy z tytułami: Zemsta oraz Pan Jowialski

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie pole tytul dla wszystkich utworów, których autorem jest Fredro

    JOIN autorzy ON ksiazki.id_autor = autorzy.id dołącza do książki jej autora, a WHERE autorzy.nazwisko = 'Fredro' zostawia jego utwory - zapytanie nie zależy od numeru autora w tabeli. Wynik: Zemsta i Pan Jowialski. Zrzut: kw3.jpg (R.1.5).

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    INSERT INTO czytelnicy (imie, nazwisko, kod)
    VALUES ('Anna', 'Michalak', 'an05mi');
    
    -- Zapytanie 2
    SELECT imie, nazwisko
    FROM czytelnicy;
    
    -- Zapytanie 3
    SELECT tytul
    FROM ksiazki
    JOIN autorzy ON ksiazki.id_autor = autorzy.id
    WHERE autorzy.nazwisko = 'Fredro';
    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 ilość tytułów danego autora znajdujących się w bazie biblioteka. Pole zliczające nazwano ilosc. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT autorzy.nazwisko, COUNT(tytul) AS ilosc FROM autorzy, ksiazki WHERE ksiazki.id_autor=autorzy.id GROUP BY (autorzy.nazwisko); oraz na zrzucie widoczne są jedynie nazwiska autorów oraz ilości tytułów: Brzechwa - 2, Chmielewska - 2, Fredro - 2, Gombrowicz - 3, Kasprowicz - 2, Kruczkowski - 2, Sienkiewicz - 3, Tuwim - 3, Witkiewicz - 2 (relację można zapisać również jako autorzy INNER JOIN ksiazki ON ksiazki.id_autor=autorzy.id)

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 4: wybierające jedynie pole nazwisko dla wszystkich rekordów w tabeli autorzy oraz obliczające ilość tytułów danego autora znajdujących się w bazie biblioteka. Pole obliczające ilość tytułów należy nazwać ilosc.

    COUNT(ksiazki.tytul) AS ilosc z GROUP BY autorzy.nazwisko - jeden wiersz na autora z liczbą jego tytułów. Wynik: 9 autorów z liczbami jak w kluczu (Gombrowicz, Sienkiewicz i Tuwim po 3, pozostali po 2). Zrzut: kw4.jpg (R.1.6).

    ⚠️ Niejednoznaczność w arkuszu: „dla wszystkich rekordów w tabeli autorzy” obejmuje też Janusza Korczaka bez żadnej książki - zwykły JOIN go pomija i tak wygląda wynik w kluczu. LEFT JOIN dodałby Korczaka z liczbą 0.

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    INSERT INTO czytelnicy (imie, nazwisko, kod)
    VALUES ('Anna', 'Michalak', 'an05mi');
    
    -- Zapytanie 2
    SELECT imie, nazwisko
    FROM czytelnicy;
    
    -- Zapytanie 3
    SELECT tytul
    FROM ksiazki
    JOIN autorzy ON ksiazki.id_autor = autorzy.id
    WHERE autorzy.nazwisko = 'Fredro';
    
    -- Zapytanie 4
    SELECT autorzy.nazwisko, COUNT(ksiazki.tytul) AS ilosc
    FROM autorzy
    JOIN ksiazki ON ksiazki.id_autor = autorzy.id
    GROUP BY autorzy.nazwisko;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Strona HTML

    Kroki 8-18 · po tym etapie masz 17 z 34 kryteriów (pkt)

    Krok 8: Plik biblioteka.php: kodowanie, tytuł, arkusz stylów

    Kryteria: , ,

    R.2.1 Witrynę internetową zapisano w pliku biblioteka.php oraz z astosowano właściwy standard kodowania polskich znaków

    R.2.2 Nadano tytuł stronie "Biblioteka”, jest on widoczny na karcie przeglądarki

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

    ok. 3 min
    Polecenie z arkusza

    Składa się ze strony o nazwie biblioteka.php. Zastosowany właściwy standard kodowania polskich znaków. Tytuł strony widoczny na karcie przeglądarki: „Biblioteka”. Arkusz stylów w pliku o nazwie styl.css prawidłowo połączony z kodem strony

    Szkielet HTML5 w pliku biblioteka.php: lang="pl", <meta charset="UTF-8"> jako pierwszy element <head> (R.2.1), <meta name="viewport"> - na telefonie strona nie będzie pomniejszana jak widok komputera, tytuł Biblioteka (R.2.2) i odnośnik do styl.css (R.3.5). Plik zapisz w kodowaniu UTF-8.

    Typowe błędy

    R.3.5 przepada, gdy którykolwiek selektor nie trafia w blok (np. .left zamiast #left) albo gdy część formatowania siedzi w atrybutach style.

    biblioteka.php PHP nowy plik
    <!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="styl.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  9. Krok 9: Bloki: baner, trzy panele w main i stopka

    Kryteria:

    R.2.3 Układ 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 po uruchomieniu w przeglądarce wygląd układu bloków był zgodny z obrazem 2

    Baner to <header>, stopka <footer>, a trzy panele - znaczniki <section> z identyfikatorami left, middle i right (R.2.3). Panele stoją we wspólnym <main>: to główna treść strony (czytnik ekranu pozwala do niej przeskoczyć), a jednocześnie pojemnik, który w CSS ułoży panele w rzędzie. Baner i stopka zostają poza nim - zajmują całą szerokość.

    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="styl.css">
    </head>
    <body>
        <header>
        </header>
        <main>
            <section id="left">
            </section>
            <section id="middle">
            </section>
            <section id="right">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  10. Krok 10: Baner: nagłówek trzeciego stopnia

    ok. 1 min
    Polecenie z arkusza

    Zawartość banera: nagłówek trzeciego stopnia o treści „Miejska Biblioteka Publiczna w Książkowicach”

    W tym arkuszu baner ma <h3>, nie <h1> - na egzaminie trzymaj się poziomów z arkusza, bo klucz (R.2.4) sprawdza konkretny znacznik.

    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="styl.css">
    </head>
    <body>
        <header>
            <h3>Miejska Biblioteka Publiczna w Książkowicach</h3>
        </header>
        <main>
            <section id="left">
            </section>
            <section id="middle">
            </section>
            <section id="right">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  11. Krok 11: Blok lewy: nagłówek i lista punktowana

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku lewego: Nagłówek czwartego stopnia o treści „Nasi czytelnicy:”. Lista punktowana (nieuporządkowana) zgodna z obrazem 4, elementy listy generowane są skryptem 1

    <h4> i pusta lista <ul> - elementy wypisze skrypt 1.

    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="styl.css">
    </head>
    <body>
        <header>
            <h3>Miejska Biblioteka Publiczna w Książkowicach</h3>
        </header>
        <main>
            <section id="left">
                <h4>Nasi czytelnicy:</h4>
                <ul>
                </ul>
            </section>
            <section id="middle">
            </section>
            <section id="right">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  12. Krok 12: Blok środkowy: adres z odsyłaczem pocztowym i obraz

    Kryteria: ,

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

    R.3.4 Napis [email protected] jest odsyłaczem pocztowym mającym postać np.: <a href= "mailto:[email protected]" >…

    ok. 2 min
    Polecenie z arkusza

    Zawartość bloku środkowego: Nagłówek piątego stopnia o treści: „ul. Czytelnicza 25 12-120 Książkowice tel.: 777888666 e-mail: [email protected]”. Treść nagłówka podzielona jest na cztery wiersze zgodnie z obrazem 2. Napis [email protected] jest odsyłaczem do tego samego adresu e-mail. Obraz biblioteka.png z tekstem alternatywnym „biblioteka”

    <h5> z trzema <br> - cztery wiersze jak na obrazie 2. Adres e-mail to odnośnik mailto: (R.3.4). Pod adresem obraz z alt="biblioteka" (R.2.6) - tekst alternatywny czyta czytnik ekranu i pokazuje przeglądarka, gdy plik się nie wczyta.

    biblioteka.php PHP zmienione: 5 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="styl.css">
    </head>
    <body>
        <header>
            <h3>Miejska Biblioteka Publiczna w Książkowicach</h3>
        </header>
        <main>
            <section id="left">
                <h4>Nasi czytelnicy:</h4>
                <ul>
                </ul>
            </section>
            <section id="middle">
                <h5>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 777888666<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h5>
                <img src="biblioteka.png" alt="biblioteka">
            </section>
            <section id="right">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  13. Krok 13: Blok prawy: nagłówek i formularz metodą post

    Kryteria: ,

    R.2.4 Zapisano wszystkie nagłówki: w banerze - h3 "Miejska Biblioteka Publiczna w Książkowicach”, w bloku lewym - h4 "Nasi czytelnicy", w bloku środkowym - h5 "ul. Czytelnicza 25 12-120 Książkowice tel.: 777888666 e-mail: [email protected]", w bloku prawym - h4 "Dodaj czytelnika”

    R.3.2 Dane z formularza przesyłane są za pomocą metody POST

    ok. 2 min
    Polecenie z arkusza

    Zawartość bloku prawego: Nagłówek czwartego stopnia o treści „Dodaj czytelnika”. Formularz wysyła dane do serwera za pomocą przycisku DODAJ, metodą post

    Drugi <h4> - wszystkie nagłówki są na miejscu (R.2.4). <form method="post"> bez action - dane wracają do biblioteka.php, gdzie czeka skrypt (R.3.2).

    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="styl.css">
    </head>
    <body>
        <header>
            <h3>Miejska Biblioteka Publiczna w Książkowicach</h3>
        </header>
        <main>
            <section id="left">
                <h4>Nasi czytelnicy:</h4>
                <ul>
                </ul>
            </section>
            <section id="middle">
                <h5>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 777888666<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h5>
                <img src="biblioteka.png" alt="biblioteka">
            </section>
            <section id="right">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  14. Krok 14: Etykieta „imię:” i pole tekstowe

    ok. 2 min
    Polecenie z arkusza

    Pole edycyjne typu tekstowego poprzedzone napisem „imię:”

    Napis staje się etykietą <label for="first-name">, związaną z polem o id="first-name": kliknięcie napisu ustawia kursor w polu, a czytnik ekranu odczyta „imię” przy wejściu w pole. name="imie" to klucz w $_POST. Etykieta z polem stoją we własnym <div>, więc kolejne pole zacznie się w nowym wierszu bez <br>.

    maxlength="30" - kolumna imie ma 30 znaków; required - przeglądarka nie wyśle formularza z pustym polem. To wygoda dla użytkownika: dane i tak sprawdzi serwer, bo formularz da się wysłać z pominięciem przeglądarki.

    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="styl.css">
    </head>
    <body>
        <header>
            <h3>Miejska Biblioteka Publiczna w Książkowicach</h3>
        </header>
        <main>
            <section id="left">
                <h4>Nasi czytelnicy:</h4>
                <ul>
                </ul>
            </section>
            <section id="middle">
                <h5>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 777888666<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h5>
                <img src="biblioteka.png" alt="biblioteka">
            </section>
            <section id="right">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="first-name">imię:</label>
                        <input type="text" id="first-name" name="imie" maxlength="30" required>
                    </div>
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  15. Krok 15: Etykieta „nazwisko:” i pole tekstowe

    ok. 1 min
    Polecenie z arkusza

    Pole edycyjne typu tekstowego poprzedzone napisem „nazwisko:”

    Ten sam wzór; kolumna nazwisko ma 50 znaków, stąd maxlength="50".

    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="styl.css">
    </head>
    <body>
        <header>
            <h3>Miejska Biblioteka Publiczna w Książkowicach</h3>
        </header>
        <main>
            <section id="left">
                <h4>Nasi czytelnicy:</h4>
                <ul>
                </ul>
            </section>
            <section id="middle">
                <h5>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 777888666<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h5>
                <img src="biblioteka.png" alt="biblioteka">
            </section>
            <section id="right">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="first-name">imię:</label>
                        <input type="text" id="first-name" name="imie" maxlength="30" required>
                    </div>
                    <div>
                        <label for="last-name">nazwisko:</label>
                        <input type="text" id="last-name" name="nazwisko" maxlength="50" required>
                    </div>
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  16. Krok 16: Etykieta „rok urodzenia:” i pole numeryczne

    ok. 1 min
    Polecenie z arkusza

    Pole edycyjne typu numerycznego poprzedzone napisem „rok urodzenia:”

    type="number" - pole liczbowe ze strzałkami. min="1900" nie pozwala przewinąć strzałkami do roku 1899; górną granicę (bieżący rok) dopisze PHP.

    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="styl.css">
    </head>
    <body>
        <header>
            <h3>Miejska Biblioteka Publiczna w Książkowicach</h3>
        </header>
        <main>
            <section id="left">
                <h4>Nasi czytelnicy:</h4>
                <ul>
                </ul>
            </section>
            <section id="middle">
                <h5>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 777888666<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h5>
                <img src="biblioteka.png" alt="biblioteka">
            </section>
            <section id="right">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="first-name">imię:</label>
                        <input type="text" id="first-name" name="imie" maxlength="30" required>
                    </div>
                    <div>
                        <label for="last-name">nazwisko:</label>
                        <input type="text" id="last-name" name="nazwisko" maxlength="50" required>
                    </div>
                    <div>
                        <label for="birth-year">rok urodzenia:</label>
                        <input type="number" id="birth-year" name="rok" min="1900" required>
                    </div>
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  17. Krok 17: Przycisk DODAJ

    Kryteria: ,

    R.2.5 W bloku prawym umieszczono: dwa pola edycyjne typu tekstowego, jedno pole edycyjne typu numerycznego oraz przycisk

    R.3.3 Przycisk wysyła dane z formularza (input lub button z typem submit lub button bez typu)

    ok. 1 min
    Polecenie z arkusza

    przycisk DODAJ – realizujący skrypt 2

    <button type="submit"> wysyła formularz (R.3.3). Formularz ma dwa pola tekstowe, pole numeryczne i przycisk (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="styl.css">
    </head>
    <body>
        <header>
            <h3>Miejska Biblioteka Publiczna w Książkowicach</h3>
        </header>
        <main>
            <section id="left">
                <h4>Nasi czytelnicy:</h4>
                <ul>
                </ul>
            </section>
            <section id="middle">
                <h5>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 777888666<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h5>
                <img src="biblioteka.png" alt="biblioteka">
            </section>
            <section id="right">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="first-name">imię:</label>
                        <input type="text" id="first-name" name="imie" maxlength="30" required>
                    </div>
                    <div>
                        <label for="last-name">nazwisko:</label>
                        <input type="text" id="last-name" name="nazwisko" maxlength="50" required>
                    </div>
                    <div>
                        <label for="birth-year">rok urodzenia:</label>
                        <input type="number" id="birth-year" name="rok" min="1900" required>
                    </div>
                    <button type="submit">DODAJ</button>
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  18. Krok 18: Stopka: akapit z autorem

    Kryteria:

    R.2.8 W stopce umieszczono akapit „Projekt witryny:” lub "Projekt strony: " dalej wstawiony jest numer PESEL zdającego

    ok. 1 min
    Polecenie z arkusza

    Zawartość stopki: akapit (paragraf) o treści „Projekt witryny:”, dalej wstawiony Twój numer PESEL

    Na egzaminie po dwukropku wpisujesz swój numer PESEL - w tym rozwiązaniu stoi Bartosz Bryniarski (R.2.8).

    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="styl.css">
    </head>
    <body>
        <header>
            <h3>Miejska Biblioteka Publiczna w Książkowicach</h3>
        </header>
        <main>
            <section id="left">
                <h4>Nasi czytelnicy:</h4>
                <ul>
                </ul>
            </section>
            <section id="middle">
                <h5>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 777888666<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h5>
                <img src="biblioteka.png" alt="biblioteka">
            </section>
            <section id="right">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="first-name">imię:</label>
                        <input type="text" id="first-name" name="imie" maxlength="30" required>
                    </div>
                    <div>
                        <label for="last-name">nazwisko:</label>
                        <input type="text" id="last-name" name="nazwisko" maxlength="50" required>
                    </div>
                    <div>
                        <label for="birth-year">rok urodzenia:</label>
                        <input type="number" id="birth-year" name="rok" min="1900" required>
                    </div>
                    <button type="submit">DODAJ</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Projekt witryny: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  19. Etap 3: Styl CSS

    Kroki 19-29 · po tym etapie masz 26 z 34 kryteriów (pkt)

    Krok 19: Plik styl.css: kolory jako zmienne i baner

    ok. 2 min
    Polecenie z arkusza

    Format banera: kolor tła #FFFDD0, wysokość 40 px

    Na początku pliku zmienne CSS w :root: dwa kolory z arkusza dostają nazwy --cream i --grey, a reguły używają var(--cream). Każdy kolor występuje na stronie trzy razy - zmiana odcienia to wtedy jedna linia. Pierwsza reguła: baner.

    styl.css CSS nowy plik
    :root {
        --cream: #FFFDD0;
        --grey: #7A7D80;
    }
    
    header {
        background-color: var(--cream);
        height: 40px;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  20. Krok 20: Układ: panele w rzędzie

    Kryteria:

    R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z obrazem 2 w arkuszu

    ok. 2 min
    Polecenie z arkusza

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

    display: flex na <main> - trzy sekcje stają obok siebie w jednym rzędzie, a stopka stoi pod <main> bez clear. Szerokości paneli ustawisz na samych panelach - klucz z 2021 roku sprawdza właściwość width przy panelach (R.4.3), dlatego nie siatka z kolumnami.

    Od tego kroku układ bloków odpowiada obrazowi 2 (R.3.1) - panele są na razie wąskie, bo jeszcze nie mają szerokości.

    styl.css CSS zmienione: 4 linie
    :root {
        --cream: #FFFDD0;
        --grey: #7A7D80;
    }
    
    header {
        background-color: var(--cream);
        height: 40px;
    }
    
    main {
        display: flex;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  21. Krok 21: Panel lewy: tło, wysokość, szerokość

    ok. 1 min
    Polecenie z arkusza

    Format panelu lewego: kolor tła #FFFDD0, wysokość 400 px, szerokość 35%

    Trzy właściwości z arkusza. W kontenerze flex procent liczy się od szerokości <main>, czyli całej strony.

    styl.css CSS zmienione: 6 linii
    :root {
        --cream: #FFFDD0;
        --grey: #7A7D80;
    }
    
    header {
        background-color: var(--cream);
        height: 40px;
    }
    
    main {
        display: flex;
    }
    
    #left {
        background-color: var(--cream);
        height: 400px;
        width: 35%;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  22. Krok 22: Panel środkowy: tło, wysokość, szerokość

    ok. 1 min
    Polecenie z arkusza

    Format panelu środkowego: kolor tła #7A7D80, wysokość 400 px, szerokość 35%

    Szary panel - ten sam kolor co stopka, stąd zmienna.

    styl.css CSS zmienione: 6 linii
    :root {
        --cream: #FFFDD0;
        --grey: #7A7D80;
    }
    
    header {
        background-color: var(--cream);
        height: 40px;
    }
    
    main {
        display: flex;
    }
    
    #left {
        background-color: var(--cream);
        height: 400px;
        width: 35%;
    }
    
    #middle {
        background-color: var(--grey);
        height: 400px;
        width: 35%;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  23. Krok 23: Panel prawy: tło, wysokość, szerokość

    Kryteria:

    R.4.3 Ustawiono szerokość panelu lewego i środkowego na 35% oraz panelu prawego na 30%

    ok. 1 min
    Polecenie z arkusza

    Format panelu prawego: kolor tła #FFFDD0, wysokość 400 px, szerokość 30%

    35% + 35% + 30% = 100%. Szerokości wszystkich paneli są ustawione (R.4.3).

    styl.css CSS zmienione: 6 linii
    :root {
        --cream: #FFFDD0;
        --grey: #7A7D80;
    }
    
    header {
        background-color: var(--cream);
        height: 40px;
    }
    
    main {
        display: flex;
    }
    
    #left {
        background-color: var(--cream);
        height: 400px;
        width: 35%;
    }
    
    #middle {
        background-color: var(--grey);
        height: 400px;
        width: 35%;
    }
    
    #right {
        background-color: var(--cream);
        height: 400px;
        width: 30%;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  24. Krok 24: Stopka: tło, wysokość, wyrównanie do lewej

    Kryteria: ,

    R.4.1 Ustawiono kolor tła dla banera i panelu lewego i prawego na #FFFDD0 oraz stopki i panelu środkowego na #7A7D80

    R.4.2 Ustawiono wysokość banera i stopki na 40px oraz paneli: lewego, środkowego i prawego na 400px

    ok. 2 min
    Polecenie z arkusza

    Format stopki: kolor tła #7A7D80, wysokość 40 px, wyrównanie tekstu do lewej

    Kolory tła (R.4.1) i wysokości (R.4.2) wszystkich bloków są ustawione.

    Druga reguła to dodatek spoza arkusza: górny margines akapitu w stopce (16 px) „przecieka” na zewnątrz stopki - to łączenie marginesów - i między panelami a stopką pojawiłby się biały pasek. W wersji podstawowej paska nie ma, bo stopka z clear: both stoi pod pływającymi panelami. margin-top: 0 przy akapicie stopki daje wygląd z obrazu 2.

    styl.css CSS zmienione: 10 linii
    :root {
        --cream: #FFFDD0;
        --grey: #7A7D80;
    }
    
    header {
        background-color: var(--cream);
        height: 40px;
    }
    
    main {
        display: flex;
    }
    
    #left {
        background-color: var(--cream);
        height: 400px;
        width: 35%;
    }
    
    #middle {
        background-color: var(--grey);
        height: 400px;
        width: 35%;
    }
    
    #right {
        background-color: var(--cream);
        height: 400px;
        width: 30%;
    }
    
    footer {
        background-color: var(--grey);
        height: 40px;
        text-align: left;
    }
    
    footer p {
        margin-top: 0;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  25. Krok 25: Lista: lewy margines i kwadratowy punktor

    Kryteria:

    R.4.7 Ustawiono styl punktora dla listy punktowanej na kwadrat (list-style-type: square;)

    ok. 1 min
    Polecenie z arkusza

    Format listy punktowanej: margines zewnętrzny lewy 50 px, punktor elementów listy – kwadrat

    list-style-type: square - punktory jak na obrazie 4 (R.4.7).

    styl.css CSS zmienione: 5 linii
    :root {
        --cream: #FFFDD0;
        --grey: #7A7D80;
    }
    
    header {
        background-color: var(--cream);
        height: 40px;
    }
    
    main {
        display: flex;
    }
    
    #left {
        background-color: var(--cream);
        height: 400px;
        width: 35%;
    }
    
    #middle {
        background-color: var(--grey);
        height: 400px;
        width: 35%;
    }
    
    #right {
        background-color: var(--cream);
        height: 400px;
        width: 30%;
    }
    
    footer {
        background-color: var(--grey);
        height: 40px;
        text-align: left;
    }
    
    footer p {
        margin-top: 0;
    }
    
    ul {
        margin-left: 50px;
        list-style-type: square;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  26. Krok 26: Obraz: lewy margines 150 px

    ok. 1 min
    Polecenie z arkusza

    Formatowanie obrazu: margines zewnętrzny lewy 150 px

    Obraz staje pod adresem, który dostanie ten sam margines.

    styl.css CSS zmienione: 4 linie
    :root {
        --cream: #FFFDD0;
        --grey: #7A7D80;
    }
    
    header {
        background-color: var(--cream);
        height: 40px;
    }
    
    main {
        display: flex;
    }
    
    #left {
        background-color: var(--cream);
        height: 400px;
        width: 35%;
    }
    
    #middle {
        background-color: var(--grey);
        height: 400px;
        width: 35%;
    }
    
    #right {
        background-color: var(--cream);
        height: 400px;
        width: 30%;
    }
    
    footer {
        background-color: var(--grey);
        height: 40px;
        text-align: left;
    }
    
    footer p {
        margin-top: 0;
    }
    
    ul {
        margin-left: 50px;
        list-style-type: square;
    }
    
    img {
        margin-left: 150px;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  27. Krok 27: Formularz: lewy i górny margines

    ok. 1 min
    Polecenie z arkusza

    Formatowanie formularza: margines zewnętrzy lewy 75 px, margines zewnętrzny górny 25 px

    Dwa marginesy zewnętrzne (w arkuszu literówka „zewnętrzy”).

    styl.css CSS zmienione: 5 linii
    :root {
        --cream: #FFFDD0;
        --grey: #7A7D80;
    }
    
    header {
        background-color: var(--cream);
        height: 40px;
    }
    
    main {
        display: flex;
    }
    
    #left {
        background-color: var(--cream);
        height: 400px;
        width: 35%;
    }
    
    #middle {
        background-color: var(--grey);
        height: 400px;
        width: 35%;
    }
    
    #right {
        background-color: var(--cream);
        height: 400px;
        width: 30%;
    }
    
    footer {
        background-color: var(--grey);
        height: 40px;
        text-align: left;
    }
    
    footer p {
        margin-top: 0;
    }
    
    ul {
        margin-left: 50px;
        list-style-type: square;
    }
    
    img {
        margin-left: 150px;
    }
    
    form {
        margin-left: 75px;
        margin-top: 25px;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  28. Krok 28: Nagłówek piątego stopnia: biały tekst i marginesy

    Kryteria: , ,

    R.4.5 Ustawiono margines zewnętrzny górny (margin-top) dla formularza i nagłówka h5 na 25px

    R.4.6 Ustawiono margines zewnętrzny lewy (margin-left) dla obrazu i dla nagłówka h5 na 150px i listy punktowanej na 50px oraz dla formularza na 75px

    R.4.8 Ustawiono dla nagłówka h5 kolor czcionki biały

    ok. 2 min
    Polecenie z arkusza

    Formatowanie nagłówka piątego stopnia: kolor tekstu biały, margines zewnętrzny lewy 150 px, margines zewnętrzny górny 25 px

    Biały tekst na szarym panelu (R.4.8). Górne marginesy formularza i nagłówka (R.4.5) oraz lewe marginesy obrazu, nagłówka, listy i formularza (R.4.6) są ustawione.

    styl.css CSS zmienione: 6 linii
    :root {
        --cream: #FFFDD0;
        --grey: #7A7D80;
    }
    
    header {
        background-color: var(--cream);
        height: 40px;
    }
    
    main {
        display: flex;
    }
    
    #left {
        background-color: var(--cream);
        height: 400px;
        width: 35%;
    }
    
    #middle {
        background-color: var(--grey);
        height: 400px;
        width: 35%;
    }
    
    #right {
        background-color: var(--cream);
        height: 400px;
        width: 30%;
    }
    
    footer {
        background-color: var(--grey);
        height: 40px;
        text-align: left;
    }
    
    footer p {
        margin-top: 0;
    }
    
    ul {
        margin-left: 50px;
        list-style-type: square;
    }
    
    img {
        margin-left: 150px;
    }
    
    form {
        margin-left: 75px;
        margin-top: 25px;
    }
    
    h5 {
        color: white;
        margin-left: 150px;
        margin-top: 25px;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  29. Krok 29: Nagłówki: h3 na środku, h4 do lewej

    Kryteria:

    R.4.4 Ustawiono wyrównanie tekstu dla nagłówka h3 do środka

    ok. 1 min
    Polecenie z arkusza

    Formatowanie nagłówka trzeciego stopnia: wyrównanie tekstu do środka. Formatowanie nagłówka czwartego stopnia: wyrównanie tekstu do lewej

    Napis banera na środku (R.4.4); wyrównanie <h4> do lewej jest domyślne, ale arkusz wymaga tej reguły wprost. Arkusz stylów jest kompletny.

    styl.css CSS zmienione: 8 linii
    :root {
        --cream: #FFFDD0;
        --grey: #7A7D80;
    }
    
    header {
        background-color: var(--cream);
        height: 40px;
    }
    
    main {
        display: flex;
    }
    
    #left {
        background-color: var(--cream);
        height: 400px;
        width: 35%;
    }
    
    #middle {
        background-color: var(--grey);
        height: 400px;
        width: 35%;
    }
    
    #right {
        background-color: var(--cream);
        height: 400px;
        width: 30%;
    }
    
    footer {
        background-color: var(--grey);
        height: 40px;
        text-align: left;
    }
    
    footer p {
        margin-top: 0;
    }
    
    ul {
        margin-left: 50px;
        list-style-type: square;
    }
    
    img {
        margin-left: 150px;
    }
    
    form {
        margin-left: 75px;
        margin-top: 25px;
    }
    
    h5 {
        color: white;
        margin-left: 150px;
        margin-top: 25px;
    }
    
    h3 {
        text-align: center;
    }
    
    h4 {
        text-align: left;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  30. Etap 4: Skrypty PHP

    Kroki 30-37 · po tym etapie masz 34 z 34 kryteriów (pkt)

    Krok 30: Skrypt na górze pliku i połączenie z bazą biblioteka

    Kryteria: ,

    R.5.1 Skrypt napisano w języku PHP i skrypt umieszczono w pliku biblioteka.php

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

    ok. 4 min
    Polecenie z arkusza

    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

    Skrypt stoi na samej górze biblioteka.php, przed <!DOCTYPE html> (R.5.1): najpierw przygotowuje dane, potem HTML je tylko wypisuje. Pod formularzem zostają trzy linie: jeśli jest komunikat, wypisz go w akapicie <p>.

    • e() - krótka funkcja z htmlspecialchars: każdy tekst wypisywany na stronie zamienia <, >, & i cudzysłowy na encje, więc znaczniki wpisane w formularz nie staną się kodem strony,
    • new PDO(...) - serwer localhost, baza biblioteka i kodowanie w jednym napisie, potem użytkownik root i puste hasło (R.5.2). ERRMODE_EXCEPTION - każdy błąd bazy rzuca wyjątek, FETCH_ASSOC - wiersze wyniku przychodzą jako tablice z nazwami kolumn,
    • try / catch - gdy baza nie działa, uczeń dostaje zwykłe zdanie, a nie komunikat PHP z danymi serwera.

    PDO zamiast funkcji 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: 21 linii
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $message = null;
    $readers = [];
    
    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,
        ]);
    } catch (PDOException $exception) {
        $message = 'Nie udało się połączyć z bazą danych, spróbuj ponownie 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="styl.css">
    </head>
    <body>
        <header>
            <h3>Miejska Biblioteka Publiczna w Książkowicach</h3>
        </header>
        <main>
            <section id="left">
                <h4>Nasi czytelnicy:</h4>
                <ul>
                </ul>
            </section>
            <section id="middle">
                <h5>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 777888666<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h5>
                <img src="biblioteka.png" alt="biblioteka">
            </section>
            <section id="right">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="first-name">imię:</label>
                        <input type="text" id="first-name" name="imie" maxlength="30" required>
                    </div>
                    <div>
                        <label for="last-name">nazwisko:</label>
                        <input type="text" id="last-name" name="nazwisko" maxlength="50" required>
                    </div>
                    <div>
                        <label for="birth-year">rok urodzenia:</label>
                        <input type="number" id="birth-year" name="rok" min="1900" required>
                    </div>
                    <button type="submit">DODAJ</button>
                </form>
    <?php if ($message !== null): ?>
                <p><?= e($message) ?></p>
    <?php endif; ?>
            </section>
        </main>
        <footer>
            <p>Projekt witryny: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  31. Krok 31: Skrypt 1: zapytanie 2 i elementy listy

    Kryteria: , ,

    R.2.7 W bloku lewym umieszczono listę punktowaną (nieuporządkowaną) zawierającą imiona i nazwiska czytelników. W kodzie HTML lub PHP użyto znaczników <ul>, <li> oraz ich zamknięcia

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

    R.5.7 Wyświetlane przez skrypt 1 imiona i nazwiska czytelników są umieszczone w elementach listy punktowanej

    ok. 4 min
    Polecenie z arkusza

    Skrypt 1: Wysyła do bazy danych zapytanie 2 (z pliku kwerendy.txt). Imiona i nazwiska wszystkich czytelników pobrane zapytaniem wypisywane są jako elementy listy punktowanej (nieuporządkowanej) zgodnie z obrazem 4

    Dwie części w dwóch miejscach pliku:

    • na górze $pdo->query(...) wysyła zapytanie 2 z kwerendy.txt - bez danych od użytkownika, więc wystarczy zwykłe query - a fetchAll() od razu zwraca wszystkie wiersze jako tablicę, bez pętli while,
    • w liście <ul> pętla foreach w zapisie alternatywnym (foreach (...): … endforeach;), który dobrze czyta się między znacznikami HTML. Każdy czytelnik to jeden <li>, a imię i nazwisko przechodzą przez e() - to dane wpisane kiedyś przez użytkowników formularza.

    Podgląd: lista z kwadratowymi punktorami i 11 czytelnikami. Skrypt 1 wyświetla czytelników z zapytania 2 (R.5.6) w elementach listy (R.5.7), w bloku lewym jest <ul> z <li> (R.2.7).

    biblioteka.php PHP zmienione: 6 linii
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $message = null;
    $readers = [];
    
    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 2, czytelnicy do listy
        $readers = $pdo->query('SELECT imie, nazwisko FROM czytelnicy;')->fetchAll();
    } catch (PDOException $exception) {
        $message = 'Nie udało się połączyć z bazą danych, spróbuj ponownie 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="styl.css">
    </head>
    <body>
        <header>
            <h3>Miejska Biblioteka Publiczna w Książkowicach</h3>
        </header>
        <main>
            <section id="left">
                <h4>Nasi czytelnicy:</h4>
                <ul>
    <?php foreach ($readers as $reader): ?>
                    <li><?= e($reader['imie'] . ' ' . $reader['nazwisko']) ?></li>
    <?php endforeach; ?>
                </ul>
            </section>
            <section id="middle">
                <h5>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 777888666<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h5>
                <img src="biblioteka.png" alt="biblioteka">
            </section>
            <section id="right">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="first-name">imię:</label>
                        <input type="text" id="first-name" name="imie" maxlength="30" required>
                    </div>
                    <div>
                        <label for="last-name">nazwisko:</label>
                        <input type="text" id="last-name" name="nazwisko" maxlength="50" required>
                    </div>
                    <div>
                        <label for="birth-year">rok urodzenia:</label>
                        <input type="number" id="birth-year" name="rok" min="1900" required>
                    </div>
                    <button type="submit">DODAJ</button>
                </form>
    <?php if ($message !== null): ?>
                <p><?= e($message) ?></p>
    <?php endif; ?>
            </section>
        </main>
        <footer>
            <p>Projekt witryny: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  32. Krok 32: Skrypt 2: pobranie i sprawdzenie danych z formularza

    ok. 4 min
    Polecenie z arkusza

    Skrypt 2: Pobiera dane z formularza

    Skrypt 2 stoi przed zapytaniem 2 - najpierw zapis, potem odczyt listy, więc nowy czytelnik będzie na niej od razu. Działa tylko po wysłaniu formularza ($_SERVER['REQUEST_METHOD'] === 'POST'):

    • $_POST['imie'] ?? '' - operator ?? daje pusty napis, gdy pola w ogóle nie wysłano, zamiast ostrzeżenia PHP; trim() zdejmuje spacje z brzegów,
    • filter_var(..., FILTER_VALIDATE_INT, ...) zamienia rok na liczbę całkowitą tylko wtedy, gdy jest liczbą z zakresu 1900 - bieżący rok; w każdym innym przypadku zwraca false,
    • puste imię lub nazwisko albo zły rok - komunikat; za długie imię lub nazwisko (kolumny mają 30 i 50 znaków) - osobny komunikat.

    Pola formularza dostały value z wpisanymi danymi - po błędzie nie trzeba wpisywać wszystkiego od nowa - a pole roku górną granicę max z bieżącego roku. Podgląd: rok urodzenia 206 zamiast 2006.

    biblioteka.php PHP zmienione: 22 linie
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $message = null;
    $readers = [];
    $firstName = '';
    $lastName = '';
    $yearText = '';
    
    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 2 - nowy czytelnik z formularza
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            $firstName = trim($_POST['imie'] ?? '');
            $lastName = trim($_POST['nazwisko'] ?? '');
            $yearText = trim($_POST['rok'] ?? '');
            $year = filter_var($yearText, FILTER_VALIDATE_INT, [
                'options' => ['min_range' => 1900, 'max_range' => (int) date('Y')],
            ]);
    
            if ($firstName === '' || $lastName === '' || $year === false) {
                $message = 'Podaj imię, nazwisko i rok urodzenia (1900 - ' . date('Y') . ').';
            } elseif (mb_strlen($firstName) > 30 || mb_strlen($lastName) > 50) {
                $message = 'Imię może mieć najwyżej 30 znaków, a nazwisko 50.';
            }
        }
    
        // skrypt 1 - zapytanie 2, czytelnicy do listy
        $readers = $pdo->query('SELECT imie, nazwisko FROM czytelnicy;')->fetchAll();
    } catch (PDOException $exception) {
        $message = 'Nie udało się połączyć z bazą danych, spróbuj ponownie 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="styl.css">
    </head>
    <body>
        <header>
            <h3>Miejska Biblioteka Publiczna w Książkowicach</h3>
        </header>
        <main>
            <section id="left">
                <h4>Nasi czytelnicy:</h4>
                <ul>
    <?php foreach ($readers as $reader): ?>
                    <li><?= e($reader['imie'] . ' ' . $reader['nazwisko']) ?></li>
    <?php endforeach; ?>
                </ul>
            </section>
            <section id="middle">
                <h5>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 777888666<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h5>
                <img src="biblioteka.png" alt="biblioteka">
            </section>
            <section id="right">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="first-name">imię:</label>
                        <input type="text" id="first-name" name="imie" maxlength="30" required value="<?= e($firstName) ?>">
                    </div>
                    <div>
                        <label for="last-name">nazwisko:</label>
                        <input type="text" id="last-name" name="nazwisko" maxlength="50" required value="<?= e($lastName) ?>">
                    </div>
                    <div>
                        <label for="birth-year">rok urodzenia:</label>
                        <input type="number" id="birth-year" name="rok" min="1900" max="<?= date('Y') ?>" required value="<?= e($yearText) ?>">
                    </div>
                    <button type="submit">DODAJ</button>
                </form>
    <?php if ($message !== null): ?>
                <p><?= e($message) ?></p>
    <?php endif; ?>
            </section>
        </main>
        <footer>
            <p>Projekt witryny: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  33. Krok 33: Skrypt 2: kod czytelnika

    Kryteria:

    R.5.5 Skrypt 2 generuje kod czytelnika (dwie początkowe litery z imienia, dwie ostatnie cyfry z roku urodzenia oraz dwie początkowe litery z nazwiska, wszystkie litery są małe - sprawdzamy w kodzie źródłowym)

    ok. 3 min
    Polecenie z arkusza

    Tworzy kod czytelnika składający się z dwóch początkowych liter imienia, dwóch ostatnich cyfr z roku urodzenia oraz dwóch początkowych liter nazwiska (wszystkie litery w kodzie powinny być małe)

    • mb_substr($firstName, 0, 2) - dwa pierwsze znaki imienia: z „Tomasz” To,
    • substr((string) $year, -2) - dwie ostatnie cyfry roku jako napis: 06,
    • mb_substr($lastName, 0, 2) - z „Nowak” No,
    • mb_strtolower() - wszystko małymi literami.

    Funkcje mb_ liczą znaki, a nie bajty: zwykłe substr dla imienia „Łucja” wzięłoby tylko Ł (w UTF-8 ta litera zajmuje dwa bajty), a strtolower nie zmienia polskich liter. Z mb_ kod Łucji Żak to łu04ża. Dla danych z podglądu: to06no (R.5.5).

    Typowe błędy

    $year % 100 daje dla roku 2005 liczbę 5, a nie 05 - kod ma wtedy pięć znaków.

    biblioteka.php PHP zmienione: 2 linie
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $message = null;
    $readers = [];
    $firstName = '';
    $lastName = '';
    $yearText = '';
    
    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 2 - nowy czytelnik z formularza
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            $firstName = trim($_POST['imie'] ?? '');
            $lastName = trim($_POST['nazwisko'] ?? '');
            $yearText = trim($_POST['rok'] ?? '');
            $year = filter_var($yearText, FILTER_VALIDATE_INT, [
                'options' => ['min_range' => 1900, 'max_range' => (int) date('Y')],
            ]);
    
            if ($firstName === '' || $lastName === '' || $year === false) {
                $message = 'Podaj imię, nazwisko i rok urodzenia (1900 - ' . date('Y') . ').';
            } elseif (mb_strlen($firstName) > 30 || mb_strlen($lastName) > 50) {
                $message = 'Imię może mieć najwyżej 30 znaków, a nazwisko 50.';
            } else {
                $code = mb_strtolower(mb_substr($firstName, 0, 2) . substr((string) $year, -2) . mb_substr($lastName, 0, 2));
            }
        }
    
        // skrypt 1 - zapytanie 2, czytelnicy do listy
        $readers = $pdo->query('SELECT imie, nazwisko FROM czytelnicy;')->fetchAll();
    } catch (PDOException $exception) {
        $message = 'Nie udało się połączyć z bazą danych, spróbuj ponownie 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="styl.css">
    </head>
    <body>
        <header>
            <h3>Miejska Biblioteka Publiczna w Książkowicach</h3>
        </header>
        <main>
            <section id="left">
                <h4>Nasi czytelnicy:</h4>
                <ul>
    <?php foreach ($readers as $reader): ?>
                    <li><?= e($reader['imie'] . ' ' . $reader['nazwisko']) ?></li>
    <?php endforeach; ?>
                </ul>
            </section>
            <section id="middle">
                <h5>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 777888666<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h5>
                <img src="biblioteka.png" alt="biblioteka">
            </section>
            <section id="right">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="first-name">imię:</label>
                        <input type="text" id="first-name" name="imie" maxlength="30" required value="<?= e($firstName) ?>">
                    </div>
                    <div>
                        <label for="last-name">nazwisko:</label>
                        <input type="text" id="last-name" name="nazwisko" maxlength="50" required value="<?= e($lastName) ?>">
                    </div>
                    <div>
                        <label for="birth-year">rok urodzenia:</label>
                        <input type="number" id="birth-year" name="rok" min="1900" max="<?= date('Y') ?>" required value="<?= e($yearText) ?>">
                    </div>
                    <button type="submit">DODAJ</button>
                </form>
    <?php if ($message !== null): ?>
                <p><?= e($message) ?></p>
    <?php endif; ?>
            </section>
        </main>
        <footer>
            <p>Projekt witryny: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  34. Krok 34: Skrypt 2: zapytanie 1 jako zapytanie przygotowane

    ok. 3 min
    Polecenie z arkusza

    Wysyła do bazy danych zmodyfikowane zapytanie 1 (z pliku kwerendy.txt), wstawiające do tabeli czytelnicy nowy rekord zawierający imię i nazwisko pobrane z formularza oraz kod czytelnika wygenerowany w skrypcie

    Zapytanie 1, w którym wartości zastępują parametry :imie, :nazwisko i :kod: $pdo->prepare(...) wysyła do bazy samą treść zapytania, a $statement->execute([...]) - osobno dane. Dane nigdy nie staną się częścią kodu SQL, więc nazwisko z apostrofem (O'Neal) zapisuje się bez błędu.

    Podgląd: w tabeli jest nowy wiersz 12, Tomasz, Nowak, to06no - i Tomasz jest już na liście w bloku lewym, bo lista jest czytana po zapisie.

    biblioteka.php PHP zmienione: 2 linie
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $message = null;
    $readers = [];
    $firstName = '';
    $lastName = '';
    $yearText = '';
    
    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 2 - nowy czytelnik z formularza
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            $firstName = trim($_POST['imie'] ?? '');
            $lastName = trim($_POST['nazwisko'] ?? '');
            $yearText = trim($_POST['rok'] ?? '');
            $year = filter_var($yearText, FILTER_VALIDATE_INT, [
                'options' => ['min_range' => 1900, 'max_range' => (int) date('Y')],
            ]);
    
            if ($firstName === '' || $lastName === '' || $year === false) {
                $message = 'Podaj imię, nazwisko i rok urodzenia (1900 - ' . date('Y') . ').';
            } elseif (mb_strlen($firstName) > 30 || mb_strlen($lastName) > 50) {
                $message = 'Imię może mieć najwyżej 30 znaków, a nazwisko 50.';
            } else {
                $code = mb_strtolower(mb_substr($firstName, 0, 2) . substr((string) $year, -2) . mb_substr($lastName, 0, 2));
                $statement = $pdo->prepare('INSERT INTO czytelnicy (imie, nazwisko, kod) VALUES (:imie, :nazwisko, :kod);');
                $statement->execute(['imie' => $firstName, 'nazwisko' => $lastName, 'kod' => $code]);
            }
        }
    
        // skrypt 1 - zapytanie 2, czytelnicy do listy
        $readers = $pdo->query('SELECT imie, nazwisko FROM czytelnicy;')->fetchAll();
    } catch (PDOException $exception) {
        $message = 'Nie udało się połączyć z bazą danych, spróbuj ponownie 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="styl.css">
    </head>
    <body>
        <header>
            <h3>Miejska Biblioteka Publiczna w Książkowicach</h3>
        </header>
        <main>
            <section id="left">
                <h4>Nasi czytelnicy:</h4>
                <ul>
    <?php foreach ($readers as $reader): ?>
                    <li><?= e($reader['imie'] . ' ' . $reader['nazwisko']) ?></li>
    <?php endforeach; ?>
                </ul>
            </section>
            <section id="middle">
                <h5>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 777888666<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h5>
                <img src="biblioteka.png" alt="biblioteka">
            </section>
            <section id="right">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="first-name">imię:</label>
                        <input type="text" id="first-name" name="imie" maxlength="30" required value="<?= e($firstName) ?>">
                    </div>
                    <div>
                        <label for="last-name">nazwisko:</label>
                        <input type="text" id="last-name" name="nazwisko" maxlength="50" required value="<?= e($lastName) ?>">
                    </div>
                    <div>
                        <label for="birth-year">rok urodzenia:</label>
                        <input type="number" id="birth-year" name="rok" min="1900" max="<?= date('Y') ?>" required value="<?= e($yearText) ?>">
                    </div>
                    <button type="submit">DODAJ</button>
                </form>
    <?php if ($message !== null): ?>
                <p><?= e($message) ?></p>
    <?php endif; ?>
            </section>
        </main>
        <footer>
            <p>Projekt witryny: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  35. Krok 35: Skrypt 2: napis o dodaniu czytelnika

    Kryteria:

    R.5.4 Skrypt 2 wyświetla napis: "Czytelnik ……. został dodany do bazy danych" (w miejscu kropek powinno znaleźć się nazwisko pobrane z formularza - sprawdzamy w kodzie źródłowym)

    ok. 1 min
    Polecenie z arkusza

    Wyświetla napis „Czytelnik: ……………… został dodany do bazy danych” (w miejscu kropek należy umieścić nazwisko pobrane z formularza)

    W miejscu kropek samo nazwisko. Komunikat ustawiany dopiero po execute - gdyby zapis się nie udał, wyjątek przeskoczy tę linię. Na stronie wypisuje go akapit pod formularzem, przez e() (R.5.4).

    biblioteka.php PHP zmienione: 1 linia
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $message = null;
    $readers = [];
    $firstName = '';
    $lastName = '';
    $yearText = '';
    
    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 2 - nowy czytelnik z formularza
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            $firstName = trim($_POST['imie'] ?? '');
            $lastName = trim($_POST['nazwisko'] ?? '');
            $yearText = trim($_POST['rok'] ?? '');
            $year = filter_var($yearText, FILTER_VALIDATE_INT, [
                'options' => ['min_range' => 1900, 'max_range' => (int) date('Y')],
            ]);
    
            if ($firstName === '' || $lastName === '' || $year === false) {
                $message = 'Podaj imię, nazwisko i rok urodzenia (1900 - ' . date('Y') . ').';
            } elseif (mb_strlen($firstName) > 30 || mb_strlen($lastName) > 50) {
                $message = 'Imię może mieć najwyżej 30 znaków, a nazwisko 50.';
            } else {
                $code = mb_strtolower(mb_substr($firstName, 0, 2) . substr((string) $year, -2) . mb_substr($lastName, 0, 2));
                $statement = $pdo->prepare('INSERT INTO czytelnicy (imie, nazwisko, kod) VALUES (:imie, :nazwisko, :kod);');
                $statement->execute(['imie' => $firstName, 'nazwisko' => $lastName, 'kod' => $code]);
                $message = "Czytelnik: $lastName został dodany do bazy danych";
            }
        }
    
        // skrypt 1 - zapytanie 2, czytelnicy do listy
        $readers = $pdo->query('SELECT imie, nazwisko FROM czytelnicy;')->fetchAll();
    } catch (PDOException $exception) {
        $message = 'Nie udało się połączyć z bazą danych, spróbuj ponownie 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="styl.css">
    </head>
    <body>
        <header>
            <h3>Miejska Biblioteka Publiczna w Książkowicach</h3>
        </header>
        <main>
            <section id="left">
                <h4>Nasi czytelnicy:</h4>
                <ul>
    <?php foreach ($readers as $reader): ?>
                    <li><?= e($reader['imie'] . ' ' . $reader['nazwisko']) ?></li>
    <?php endforeach; ?>
                </ul>
            </section>
            <section id="middle">
                <h5>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 777888666<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h5>
                <img src="biblioteka.png" alt="biblioteka">
            </section>
            <section id="right">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="first-name">imię:</label>
                        <input type="text" id="first-name" name="imie" maxlength="30" required value="<?= e($firstName) ?>">
                    </div>
                    <div>
                        <label for="last-name">nazwisko:</label>
                        <input type="text" id="last-name" name="nazwisko" maxlength="50" required value="<?= e($lastName) ?>">
                    </div>
                    <div>
                        <label for="birth-year">rok urodzenia:</label>
                        <input type="number" id="birth-year" name="rok" min="1900" max="<?= date('Y') ?>" required value="<?= e($yearText) ?>">
                    </div>
                    <button type="submit">DODAJ</button>
                </form>
    <?php if ($message !== null): ?>
                <p><?= e($message) ?></p>
    <?php endif; ?>
            </section>
        </main>
        <footer>
            <p>Projekt witryny: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  36. Krok 36: Zamknięcie połączenia z bazą

    Kryteria:

    R.5.3 Ostatnią operacją na bazie jest jej zamknięcie (zgodne ze składnią)

    ok. 2 min
    Polecenie z arkusza

    Na końcu działania skrypt zamyka połączenie z serwerem

    PDO nie ma funkcji close - połączenie zamyka się, gdy znika ostatnie odwołanie do obiektu. Dlatego $statement = null i $pdo = null w bloku finally: wykona się on zawsze po try, zarówno gdy wszystko się udało, jak i po wyjątku - zamknięcie jest ostatnią operacją na bazie w każdej gałęzi (R.5.3). Lista czytelników jest już wtedy w tablicy $readers, więc HTML nie potrzebuje bazy.

    biblioteka.php PHP zmienione: 3 linie
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $message = null;
    $readers = [];
    $firstName = '';
    $lastName = '';
    $yearText = '';
    
    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 2 - nowy czytelnik z formularza
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            $firstName = trim($_POST['imie'] ?? '');
            $lastName = trim($_POST['nazwisko'] ?? '');
            $yearText = trim($_POST['rok'] ?? '');
            $year = filter_var($yearText, FILTER_VALIDATE_INT, [
                'options' => ['min_range' => 1900, 'max_range' => (int) date('Y')],
            ]);
    
            if ($firstName === '' || $lastName === '' || $year === false) {
                $message = 'Podaj imię, nazwisko i rok urodzenia (1900 - ' . date('Y') . ').';
            } elseif (mb_strlen($firstName) > 30 || mb_strlen($lastName) > 50) {
                $message = 'Imię może mieć najwyżej 30 znaków, a nazwisko 50.';
            } else {
                $code = mb_strtolower(mb_substr($firstName, 0, 2) . substr((string) $year, -2) . mb_substr($lastName, 0, 2));
                $statement = $pdo->prepare('INSERT INTO czytelnicy (imie, nazwisko, kod) VALUES (:imie, :nazwisko, :kod);');
                $statement->execute(['imie' => $firstName, 'nazwisko' => $lastName, 'kod' => $code]);
                $message = "Czytelnik: $lastName został dodany do bazy danych";
            }
        }
    
        // skrypt 1 - zapytanie 2, czytelnicy do listy
        $readers = $pdo->query('SELECT imie, nazwisko FROM czytelnicy;')->fetchAll();
    } catch (PDOException $exception) {
        $message = 'Nie udało się połączyć z bazą danych, spróbuj ponownie za chwilę.';
    } finally {
        $statement = null;
        $pdo = null;    // zamknięcie połączenia - ostatnia operacja na bazie
    }
    ?>
    <!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="styl.css">
    </head>
    <body>
        <header>
            <h3>Miejska Biblioteka Publiczna w Książkowicach</h3>
        </header>
        <main>
            <section id="left">
                <h4>Nasi czytelnicy:</h4>
                <ul>
    <?php foreach ($readers as $reader): ?>
                    <li><?= e($reader['imie'] . ' ' . $reader['nazwisko']) ?></li>
    <?php endforeach; ?>
                </ul>
            </section>
            <section id="middle">
                <h5>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 777888666<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h5>
                <img src="biblioteka.png" alt="biblioteka">
            </section>
            <section id="right">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="first-name">imię:</label>
                        <input type="text" id="first-name" name="imie" maxlength="30" required value="<?= e($firstName) ?>">
                    </div>
                    <div>
                        <label for="last-name">nazwisko:</label>
                        <input type="text" id="last-name" name="nazwisko" maxlength="50" required value="<?= e($lastName) ?>">
                    </div>
                    <div>
                        <label for="birth-year">rok urodzenia:</label>
                        <input type="number" id="birth-year" name="rok" min="1900" max="<?= date('Y') ?>" required value="<?= e($yearText) ?>">
                    </div>
                    <button type="submit">DODAJ</button>
                </form>
    <?php if ($message !== null): ?>
                <p><?= e($message) ?></p>
    <?php endif; ?>
            </section>
        </main>
        <footer>
            <p>Projekt witryny: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  37. Krok 37: Odświeżenie po dodaniu czytelnika nie wysyła formularza drugi raz

    ok. 4 min

    Dodatek spoza arkusza - klucz go nie ocenia. Strona po wysłaniu formularza jest wynikiem żądania POST, więc odświeżenie (F5) pyta, czy wysłać dane ponownie - i drugi raz dodałoby tego samego czytelnika. Wzorzec PRG (Post/Redirect/Get):

    • session_start() na samym początku pliku - sesja przechowa komunikat między dwoma żądaniami,
    • po udanym zapisie $readerAdded = true, a po bloku finally (połączenie jest już zamknięte) komunikat trafia do $_SESSION, a header('Location: biblioteka.php') każe przeglądarce wczytać stronę jeszcze raz zwykłym żądaniem GET. exit jest konieczne - header tylko ustawia nagłówek i nie zatrzymuje skryptu,
    • nowe wejście odczytuje komunikat z sesji i od razu go usuwa (unset), więc pokaże się raz.

    Sprawdzone na prawdziwym PHP: wysłanie formularza daje odpowiedź 302, potem „Czytelnik: Nowak został dodany do bazy danych” i Tomasz Nowak na liście, a kolejne odświeżenie - stronę bez komunikatu i bez drugiego Tomasza w tabeli. Podgląd: stan po przekierowaniu.

    biblioteka.php PHP zmienione: 13 linii
    <?php
    session_start();
    
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    // komunikat zapisany przed przekierowaniem (PRG) - pokazywany raz
    $message = $_SESSION['message'] ?? null;
    unset($_SESSION['message']);
    $readerAdded = false;
    $readers = [];
    $firstName = '';
    $lastName = '';
    $yearText = '';
    
    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 2 - nowy czytelnik z formularza
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            $firstName = trim($_POST['imie'] ?? '');
            $lastName = trim($_POST['nazwisko'] ?? '');
            $yearText = trim($_POST['rok'] ?? '');
            $year = filter_var($yearText, FILTER_VALIDATE_INT, [
                'options' => ['min_range' => 1900, 'max_range' => (int) date('Y')],
            ]);
    
            if ($firstName === '' || $lastName === '' || $year === false) {
                $message = 'Podaj imię, nazwisko i rok urodzenia (1900 - ' . date('Y') . ').';
            } elseif (mb_strlen($firstName) > 30 || mb_strlen($lastName) > 50) {
                $message = 'Imię może mieć najwyżej 30 znaków, a nazwisko 50.';
            } else {
                $code = mb_strtolower(mb_substr($firstName, 0, 2) . substr((string) $year, -2) . mb_substr($lastName, 0, 2));
                $statement = $pdo->prepare('INSERT INTO czytelnicy (imie, nazwisko, kod) VALUES (:imie, :nazwisko, :kod);');
                $statement->execute(['imie' => $firstName, 'nazwisko' => $lastName, 'kod' => $code]);
                $readerAdded = true;
            }
        }
    
        // skrypt 1 - zapytanie 2, czytelnicy do listy
        $readers = $pdo->query('SELECT imie, nazwisko FROM czytelnicy;')->fetchAll();
    } catch (PDOException $exception) {
        $message = 'Nie udało się połączyć z bazą danych, spróbuj ponownie za chwilę.';
    } finally {
        $statement = null;
        $pdo = null;    // zamknięcie połączenia - ostatnia operacja na bazie
    }
    
    if ($readerAdded) {
        $_SESSION['message'] = "Czytelnik: $lastName został dodany do bazy danych";
        header('Location: biblioteka.php');
        exit;
    }
    ?>
    <!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="styl.css">
    </head>
    <body>
        <header>
            <h3>Miejska Biblioteka Publiczna w Książkowicach</h3>
        </header>
        <main>
            <section id="left">
                <h4>Nasi czytelnicy:</h4>
                <ul>
    <?php foreach ($readers as $reader): ?>
                    <li><?= e($reader['imie'] . ' ' . $reader['nazwisko']) ?></li>
    <?php endforeach; ?>
                </ul>
            </section>
            <section id="middle">
                <h5>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 777888666<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h5>
                <img src="biblioteka.png" alt="biblioteka">
            </section>
            <section id="right">
                <h4>Dodaj czytelnika</h4>
                <form method="post">
                    <div>
                        <label for="first-name">imię:</label>
                        <input type="text" id="first-name" name="imie" maxlength="30" required value="<?= e($firstName) ?>">
                    </div>
                    <div>
                        <label for="last-name">nazwisko:</label>
                        <input type="text" id="last-name" name="nazwisko" maxlength="50" required value="<?= e($lastName) ?>">
                    </div>
                    <div>
                        <label for="birth-year">rok urodzenia:</label>
                        <input type="number" id="birth-year" name="rok" min="1900" max="<?= date('Y') ?>" required value="<?= e($yearText) ?>">
                    </div>
                    <button type="submit">DODAJ</button>
                </form>
    <?php if ($message !== null): ?>
                <p><?= e($message) ?></p>
    <?php endif; ?>
            </section>
        </main>
        <footer>
            <p>Projekt witryny: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  38. Etap 5: Porównanie z wersją podstawową

    Krok 38 · po tym etapie masz 34 z 34 kryteriów (pkt)

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

    ok. 3 min

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

    wersja podstawowawersja nowoczesna
    układfloat: left trzech sekcji, clear stopki<main> z display: flex
    kolorywpisane w każdej regulezmienne --cream, --grey
    formularznapis, pole, <br>label for, wiersze w <div>, maxlength, required, zakres roku
    bibliotekamysqli (tabela 1 arkusza)PDO z wyjątkami
    gdzie są skryptyw panelach, połączenie w liście bloku lewegona górze pliku, w panelach tylko wypisywanie
    nowy czytelnik na liściedopiero po odświeżeniu (lista powstaje przed zapisem)od razu (zapis przed odczytem listy)
    dane z formularza$_POST wprost?? '', trim, filter_var z zakresem, długość kolumn
    kod czytelnikasubstr, strtolower - gubią polskie literymb_substr, mb_strtolower
    INSERTdane wklejone w treść zapytaniaparametry :imie, :nazwisko, :kod
    lista i komunikatwprost, znaczniki z bazy działają jak HTMLprzez e() (htmlspecialchars)
    zamknięcie bazymysqli_close na końcu skryptu 2$pdo = null w finally
    po wysłaniupuste pola, F5 dodaje czytelnika ponowniedane wracają do pól po błędzie, po dodaniu przekierowanie

    Jednej rzeczy żadna wersja nie naprawi z poziomu PHP: tabela czytelnicy z pliku biblioteka.sql ma kodowanie latin1, w którym nie ma liter Ł, Ż, ś, ę. Sprawdzone: Łucja Żak zapisuje się jako ?ucja ?ak z kodem ?u04?a i tak pokazuje ją lista. Poza egzaminem tabelę przepisuje się na UTF-8 zapytaniem ALTER TABLE czytelnicy CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_polish_ci; - na egzaminie struktury bazy z arkusza się nie zmienia.

    Na egzaminie wersja podstawowa trzyma się funkcji z tabeli 1 i łatwiej ją napisać bez dokumentacji. Nowoczesna nie wywraca się na apostrofie, nie dodaje pustych czytelników, nie wpuszcza kodu HTML na listę i nie dodaje nikogo drugi raz po odświeżeniu - a klucz przyjmuje obie.

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

  39. Etap 6: Oddanie pracy

    Krok 39 · po tym etapie masz 34 z 34 kryteriów (pkt)

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

    ok. 5 min
    Polecenie z arkusza

    Po zakończeniu pracy utwórz plik tekstowy. Zapisz w nim nazwę przeglądarki internetowej, w której weryfikowałeś poprawność działania witryny. Nazwij plik przeglądarka.txt i zapisz go w folderze z numerem PESEL. 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: przeglądarka.txt, dane.jpg, kw1.jpg - kw4.jpg, kwerendy.txt, biblioteka.php, styl.css i biblioteka.png. Nagraj płytę, sprawdź jej odczyt i opisz ją numerem PESEL.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

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

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.

Potwierdzenie

Informacja