Przejdź do głównej treści
E.14-03-21.06-SGCzerwiec 2021 · 40 kroków · ok. 81 min · 33 kryteria z klucza

Rozwiązanie krok po kroku: Biblioteka miejska - dodawanie 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ą autoró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 autorów - w panelach zostaje samo wypisywanie,
  • 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,
  • 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 pliki3.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 33 kryteria (R.1.1 - R.5.8), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 25 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 33 kryteriów (pkt)

    Krok 1: Folder zdającego i archiwum pliki3.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 pliki3.zip zabezpieczone hasłem: Arch!wumE_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 pliki3.zip rozpakowane z hasłem Arch!wumE_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 import.jpg

    Kryteria:

    R.1.1 Wykonano import tabel z pliku biblioteka.sql do bazy danych o nazwie biblioteka. Czynność udokumentowano plikiem o nazwie import 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 import. 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: import.jpg (R.1.1).

    Podgląd strony po tym kroku
    Podgląd strony po kroku 3
  4. Krok 4: Zapytanie 1: imiona i nazwiska autorów

    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 wybierające jedynie pola imie i nazwisko dla wszystkich rekordów w tabeli autorzy . W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT imie, nazwisko FROM autorzy; oraz na zrzucie widoczne są jedynie imiona i nazwiska wszystkich autorów (10 rekordów)

    ok. 2 min
    Polecenie z arkusza

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

    Dwie kolumny, bez warunku - 10 autorów. 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.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT imie, nazwisko
    FROM autorzy;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: nowy czytelnik Anna Michalak

    Kryteria:

    R.1.4 Zapisano zapytanie 2 wstawiające dane do tabeli czytelnicy: Anna, Michalak, ANMI05. W pliku z kwerendami lub na zrzucie istnieje zapis np.: INSERT INTO czytelnicy VALUES (null, 'Anna', 'Michalak', 'ANMI05'); oraz na zrzucie widoczne jest potwierdzenie dodania rekordu lub wykonanie zapytania bez błędów

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 2: wstawiające do tabeli czytelnicy nowy rekord z danymi: Anna, Michalak, ANMI05

    Lista kolumn bez id - numer nada AUTO_INCREMENT (Anna dostaje 11). Zrzut: kw2.jpg (R.1.4). Kod ANMI05 to wzór dla skryptu: dwie litery imienia, dwie litery nazwiska, dwie ostatnie cyfry roku urodzenia.

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

    Kryteria:

    R.1.5 Zapisano zapytanie 3 wybierające jedynie pole tytul dla wszystkich utworów z kategorii epika. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT tytul FROM ksiazki, kategorie WHERE ksiazki.id_kategoria=kategorie.id AND nazwa='epika'; (dozwolona również wersja zapytania bez relacji: SELECT tytul FROM ksiazki WHERE id_kategoria = 1, relację można zapisać poprzez INNER JOIN) oraz na zrzucie widoczne są jedynie tytuły 10 utworów: Kosmos, Ferdydurke, Transatlantyk, Pech, Lesio, Quo vadis, Potop, Ogniem i mieczem, Panny roztropne, Zamek z piasku

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie pole tytul dla wszystkich utworów w tabeli ksiazki, należących do kategorii epika

    JOIN kategorie ON ksiazki.id_kategoria = kategorie.id dołącza do książki jej kategorię, a WHERE kategorie.nazwa = 'epika' zostawia epikę - zapytanie nie zależy od numeru kategorii w tabeli. Wynik: 10 tytułów, od Kosmosu do Zamku z piasku. Zrzut: kw3.jpg (R.1.5).

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    SELECT imie, nazwisko
    FROM autorzy;
    
    -- Zapytanie 2
    INSERT INTO czytelnicy (imie, nazwisko, kod)
    VALUES ('Anna', 'Michalak', 'ANMI05');
    
    -- Zapytanie 3
    SELECT tytul
    FROM ksiazki
    JOIN kategorie ON ksiazki.id_kategoria = kategorie.id
    WHERE kategorie.nazwa = 'epika';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: książki wypożyczone przez czytelnika 3

    Kryteria:

    R.1.6 Zapisano zapytanie 4 wybierające jedynie pole tytul wszystkich utworów w tabeli ksiazki wypożyczonych przez czytelnika o identyfikatorze 3. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT tytul FROM ksiazki, wypozyczenia WHERE ksiazki.id=wypozyczenia.id_ksiazka AND id_czytelnik=3; lub SELECT tytul FROM ksiazki INNER JOIN wypozyczenia ON ksiazki.id=wypozyczenia.id_ksiazka AND id_czytelnik=3); oraz na zrzucie widoczne są jedynie tytuły: Rzepka oraz Kosmos

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 4: wybierające jedynie pole tytul wszystkich utworów w tabeli książki, wypożyczonych przez czytelnika o identyfikatorze 3

    Złączenie książek z wypożyczeniami i warunek id_czytelnik = 3. Wynik: Rzepka i Kosmos. Trzeci wpis czytelnika 3 w tabeli wypozyczenia wskazuje książkę numer 15, której nie ma w tabeli ksiazki - JOIN pomija wiersz bez pary. Zrzut: kw4.jpg (R.1.6).

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    SELECT imie, nazwisko
    FROM autorzy;
    
    -- Zapytanie 2
    INSERT INTO czytelnicy (imie, nazwisko, kod)
    VALUES ('Anna', 'Michalak', 'ANMI05');
    
    -- Zapytanie 3
    SELECT tytul
    FROM ksiazki
    JOIN kategorie ON ksiazki.id_kategoria = kategorie.id
    WHERE kategorie.nazwa = 'epika';
    
    -- Zapytanie 4
    SELECT tytul
    FROM ksiazki
    JOIN wypozyczenia ON ksiazki.id = wypozyczenia.id_ksiazka
    WHERE wypozyczenia.id_czytelnik = 3;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Strona HTML

    Kroki 8-19 · po tym etapie masz 17 z 33 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 zastosowano 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 pierwszego stopnia

    ok. 1 min
    Polecenie z arkusza

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

    <h1> w <header> - jedyny nagłówek pierwszego stopnia na stronie.

    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>
            <h1>Miejska Biblioteka Publiczna w Książkowicach</h1>
        </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: obraz z tekstem alternatywnym

    Kryteria:

    R.2.7 W bloku lewym umieszczono obraz z pliku biblioteka.png z tekstem alternatywnym "książki"

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku lewego: Obraz biblioteka.png z tekstem alternatywnym „książki”

    alt="książki" opisuje grafikę dla czytnika ekranu i pojawia się, gdy plik się nie wczyta (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="styl.css">
    </head>
    <body>
        <header>
            <h1>Miejska Biblioteka Publiczna w Książkowicach</h1>
        </header>
        <main>
            <section id="left">
                <img src="biblioteka.png" alt="książki">
            </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 lewy: adres w czterech wierszach z odsyłaczem pocztowym

    Kryteria:

    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

    Nagłówek trzeciego stopnia o treści: „ul. Czytelnicza 25 12-120 Książkowice tel.: 555666777 e-mail: [email protected]”. Treść nagłówka podzielona jest na cztery wiersze zgodnie z obrazem 2. Napis [email protected] jest odsyłaczem pocztowym łączącym z tym samym adresem e-mail

    <h3> z trzema <br> - cztery wiersze jak na obrazie 2. Adres e-mail to odnośnik mailto: (R.3.4): kliknięcie otwiera program pocztowy z wpisanym odbiorcą.

    ⚠️ Rozbieżność arkusz-klucz: treść arkusza podaje telefon 555666777, a obraz 2 i klucz (R.2.4) - 123123123. Arkusz sam sobie przeczy, więc wpisany jest numer z obrazu 2 i z klucza.

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

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

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

    <h2> i <form method="post"> bez action - dane wracają do biblioteka.php, gdzie czeka skrypt (R.3.2). Metoda POST przesyła je w treści żądania, nie w adresie strony.

    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>
            <h1>Miejska Biblioteka Publiczna w Książkowicach</h1>
        </header>
        <main>
            <section id="left">
                <img src="biblioteka.png" alt="książki">
                <h3>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 123123123<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h3>
            </section>
            <section id="middle">
                <h2>Dodaj nowego czytelnika</h2>
                <form method="post">
                </form>
            </section>
            <section id="right">
            </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 - bez polskiej litery, jak kolumna w bazie. 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 tylko 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>
            <h1>Miejska Biblioteka Publiczna w Książkowicach</h1>
        </header>
        <main>
            <section id="left">
                <img src="biblioteka.png" alt="książki">
                <h3>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 123123123<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h3>
            </section>
            <section id="middle">
                <h2>Dodaj nowego czytelnika</h2>
                <form method="post">
                    <div>
                        <label for="first-name">imię:</label>
                        <input type="text" id="first-name" name="imie" maxlength="30" required>
                    </div>
                </form>
            </section>
            <section id="right">
            </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>
            <h1>Miejska Biblioteka Publiczna w Książkowicach</h1>
        </header>
        <main>
            <section id="left">
                <img src="biblioteka.png" alt="książki">
                <h3>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 123123123<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h3>
            </section>
            <section id="middle">
                <h2>Dodaj nowego czytelnika</h2>
                <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>
            <section id="right">
            </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>
            <h1>Miejska Biblioteka Publiczna w Książkowicach</h1>
        </header>
        <main>
            <section id="left">
                <img src="biblioteka.png" alt="książki">
                <h3>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 123123123<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h3>
            </section>
            <section id="middle">
                <h2>Dodaj nowego czytelnika</h2>
                <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>
            <section id="right">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  17. Krok 17: Przycisk DODAJ

    Kryteria: ,

    R.2.6 W bloku środkowym 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"> - w odróżnieniu od <input> może zawierać dowolną treść, a typ submit wysyła formularz (R.3.3). Formularz ma dwa pola tekstowe, pole numeryczne i przycisk (R.2.6).

    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>
            <h1>Miejska Biblioteka Publiczna w Książkowicach</h1>
        </header>
        <main>
            <section id="left">
                <img src="biblioteka.png" alt="książki">
                <h3>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 123123123<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h3>
            </section>
            <section id="middle">
                <h2>Dodaj nowego czytelnika</h2>
                <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>
            <section id="right">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  18. Krok 18: Blok prawy: nagłówek i lista numerowana

    Kryteria:

    R.2.4 Zapisano wszystkie nagłówki: w banerze - h1 "Miejska Biblioteka Publiczna w Książkowicach”, w bloku lewym - h3 "ul. Czytelnicza 25 12-120 Książkowice tel.: 123123123 e-mail: [email protected]", w bloku środkowym - h2 "Dodaj nowego czytelnika”, w bloku prawym - h2 "W naszych zbiorach znajdziesz dzieła następujących autorów:”

    ok. 2 min
    Polecenie z arkusza

    Zawartość bloku prawego: Nagłówek drugiego stopnia o treści „W naszych zbiorach znajdziesz dzieła następujących autorów:”. Lista numerowana (uporządkowana) zgodna z obrazem 3, elementy listy generowane są skryptem 1

    Drugi <h2> - wszystkie cztery nagłówki są na miejscu (R.2.4). Pod nim pusta lista numerowana <ol>; elementy wypisze skrypt 1.

    ⚠️ Sprzeczność w arkuszu: obraz 3 i ta część arkusza mówią o liście numerowanej, a opis skryptu 1 - o punktowanej. Klucz (R.2.5) przyjmuje <ol> albo <ul>; wybrana jest lista z obrazów 2 i 3.

    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>
            <h1>Miejska Biblioteka Publiczna w Książkowicach</h1>
        </header>
        <main>
            <section id="left">
                <img src="biblioteka.png" alt="książki">
                <h3>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 123123123<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h3>
            </section>
            <section id="middle">
                <h2>Dodaj nowego czytelnika</h2>
                <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>
            <section id="right">
                <h2>W naszych zbiorach znajdziesz dzieła następujących autorów:</h2>
                <ol>
                </ol>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  19. Krok 19: Stopka: akapit z autorem

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

    Zawartość stopki: paragraf (akapit) o treści „Projekt strony:”, 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>
            <h1>Miejska Biblioteka Publiczna w Książkowicach</h1>
        </header>
        <main>
            <section id="left">
                <img src="biblioteka.png" alt="książki">
                <h3>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 123123123<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h3>
            </section>
            <section id="middle">
                <h2>Dodaj nowego czytelnika</h2>
                <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>
            <section id="right">
                <h2>W naszych zbiorach znajdziesz dzieła następujących autorów:</h2>
                <ol>
                </ol>
            </section>
        </main>
        <footer>
            <p>Projekt strony: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  20. Etap 3: Styl CSS

    Kroki 20-30 · po tym etapie masz 24 z 33 kryteriów (pkt)

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

    ok. 2 min
    Polecenie z arkusza

    format banera: kolor tła #00CCFF, wysokość 70 px

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

    styl.css CSS nowy plik
    :root {
        --blue: #00CCFF;
        --orange: #E3A329;
    }
    
    header {
        background-color: var(--blue);
        height: 70px;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  21. Krok 21: 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> - jego dzieci, trzy sekcje, stają obok siebie w jednym rzędzie. Stopka stoi pod <main> zwyczajnie, bez clear, bo nic tu nie „pływa”. 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 {
        --blue: #00CCFF;
        --orange: #E3A329;
    }
    
    header {
        background-color: var(--blue);
        height: 70px;
    }
    
    main {
        display: flex;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  22. Krok 22: Panel lewy: tło, wysokość, szerokość

    ok. 1 min
    Polecenie z arkusza

    format panelu lewego: kolor tła #E3A329, wysokość 450 px, szerokość 25%

    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 {
        --blue: #00CCFF;
        --orange: #E3A329;
    }
    
    header {
        background-color: var(--blue);
        height: 70px;
    }
    
    main {
        display: flex;
    }
    
    #left {
        background-color: var(--orange);
        height: 450px;
        width: 25%;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  23. Krok 23: Panel środkowy: tło, wysokość, szerokość

    ok. 1 min
    Polecenie z arkusza

    format panelu środkowego: kolor tła #00CCFF, wysokość 450 px, szerokość 50%

    Ten sam kolor co baner - stąd zmienna.

    styl.css CSS zmienione: 6 linii
    :root {
        --blue: #00CCFF;
        --orange: #E3A329;
    }
    
    header {
        background-color: var(--blue);
        height: 70px;
    }
    
    main {
        display: flex;
    }
    
    #left {
        background-color: var(--orange);
        height: 450px;
        width: 25%;
    }
    
    #middle {
        background-color: var(--blue);
        height: 450px;
        width: 50%;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  24. Krok 24: Panel prawy: tło, wysokość, szerokość

    Kryteria:

    R.4.3 Ustawiono szerokość panelu lewego i prawego na 25% oraz panelu środkowego na 50%

    ok. 1 min
    Polecenie z arkusza

    format panelu prawego: kolor tła #E3A329, wysokość 450 px, szerokość 25%

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

    styl.css CSS zmienione: 6 linii
    :root {
        --blue: #00CCFF;
        --orange: #E3A329;
    }
    
    header {
        background-color: var(--blue);
        height: 70px;
    }
    
    main {
        display: flex;
    }
    
    #left {
        background-color: var(--orange);
        height: 450px;
        width: 25%;
    }
    
    #middle {
        background-color: var(--blue);
        height: 450px;
        width: 50%;
    }
    
    #right {
        background-color: var(--orange);
        height: 450px;
        width: 25%;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  25. Krok 25: Stopka: tło, wysokość, wyrównanie do prawej

    Kryteria: , ,

    R.4.1 Ustawiono kolor tła dla banera, stopki i panelu środkowego na #00CCFF oraz panelu lewego i prawego na #E3A329

    R.4.2 Ustawiono wysokość banera i stopki na 70px oraz paneli: lewego i prawego i środkowego na 450px

    R.4.4 Ustawiono wyrównanie tekstu do prawej dla: stopki

    ok. 1 min
    Polecenie z arkusza

    format stopki: kolor tła #00CCFF, wysokość 70 px, wyrównanie tekstu do prawej

    Kolory tła (R.4.1) i wysokości (R.4.2) wszystkich bloków są ustawione, tekst stopki stoi przy prawej krawędzi (R.4.4).

    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 {
        --blue: #00CCFF;
        --orange: #E3A329;
    }
    
    header {
        background-color: var(--blue);
        height: 70px;
    }
    
    main {
        display: flex;
    }
    
    #left {
        background-color: var(--orange);
        height: 450px;
        width: 25%;
    }
    
    #middle {
        background-color: var(--blue);
        height: 450px;
        width: 50%;
    }
    
    #right {
        background-color: var(--orange);
        height: 450px;
        width: 25%;
    }
    
    footer {
        background-color: var(--blue);
        height: 70px;
        text-align: right;
    }
    
    footer p {
        margin-top: 0;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  26. Krok 26: Lista: lewy margines 50 px

    ok. 1 min
    Polecenie z arkusza

    format listy numerowanej: margines zewnętrzny lewy 50 px

    Wyłącznie margin-left - pozostałe marginesy listy zostają domyślne.

    styl.css CSS zmienione: 4 linie
    :root {
        --blue: #00CCFF;
        --orange: #E3A329;
    }
    
    header {
        background-color: var(--blue);
        height: 70px;
    }
    
    main {
        display: flex;
    }
    
    #left {
        background-color: var(--orange);
        height: 450px;
        width: 25%;
    }
    
    #middle {
        background-color: var(--blue);
        height: 450px;
        width: 50%;
    }
    
    #right {
        background-color: var(--orange);
        height: 450px;
        width: 25%;
    }
    
    footer {
        background-color: var(--blue);
        height: 70px;
        text-align: right;
    }
    
    footer p {
        margin-top: 0;
    }
    
    ol {
        margin-left: 50px;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  27. Krok 27: Obraz: marginesy zewnętrzne 30 px

    Kryteria:

    R.4.5 Ustawiono wszystkie marginesy zewnętrzne (margin) dla obrazu na 30px

    ok. 1 min
    Polecenie z arkusza

    formatowanie obrazu: marginesy zewnętrzne 30 px

    Jedna wartość - cztery marginesy (R.4.5).

    styl.css CSS zmienione: 4 linie
    :root {
        --blue: #00CCFF;
        --orange: #E3A329;
    }
    
    header {
        background-color: var(--blue);
        height: 70px;
    }
    
    main {
        display: flex;
    }
    
    #left {
        background-color: var(--orange);
        height: 450px;
        width: 25%;
    }
    
    #middle {
        background-color: var(--blue);
        height: 450px;
        width: 50%;
    }
    
    #right {
        background-color: var(--orange);
        height: 450px;
        width: 25%;
    }
    
    footer {
        background-color: var(--blue);
        height: 70px;
        text-align: right;
    }
    
    footer p {
        margin-top: 0;
    }
    
    ol {
        margin-left: 50px;
    }
    
    img {
        margin: 30px;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  28. Krok 28: Formularz: lewy margines 125 px

    ok. 1 min
    Polecenie z arkusza

    formatowanie formularza: margines zewnętrzny lewy 125 px

    Formularz przesuwa się pod środek nagłówka bloku, jak na obrazie 2.

    styl.css CSS zmienione: 4 linie
    :root {
        --blue: #00CCFF;
        --orange: #E3A329;
    }
    
    header {
        background-color: var(--blue);
        height: 70px;
    }
    
    main {
        display: flex;
    }
    
    #left {
        background-color: var(--orange);
        height: 450px;
        width: 25%;
    }
    
    #middle {
        background-color: var(--blue);
        height: 450px;
        width: 50%;
    }
    
    #right {
        background-color: var(--orange);
        height: 450px;
        width: 25%;
    }
    
    footer {
        background-color: var(--blue);
        height: 70px;
        text-align: right;
    }
    
    footer p {
        margin-top: 0;
    }
    
    ol {
        margin-left: 50px;
    }
    
    img {
        margin: 30px;
    }
    
    form {
        margin-left: 125px;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  29. Krok 29: Nagłówek trzeciego stopnia: lewy margines 50 px

    Kryteria:

    R.4.6 Ustawiono wyłącznie margines zewnętrzny lewy (margin-left) dla nagłówka h3 i listy na 50px oraz dla formularza na 125px

    ok. 1 min
    Polecenie z arkusza

    formatowanie nagłówka trzeciego stopnia: margines zewnętrzny lewy 50 px

    Lewe marginesy nagłówka, listy i formularza są ustawione (R.4.6).

    styl.css CSS zmienione: 4 linie
    :root {
        --blue: #00CCFF;
        --orange: #E3A329;
    }
    
    header {
        background-color: var(--blue);
        height: 70px;
    }
    
    main {
        display: flex;
    }
    
    #left {
        background-color: var(--orange);
        height: 450px;
        width: 25%;
    }
    
    #middle {
        background-color: var(--blue);
        height: 450px;
        width: 50%;
    }
    
    #right {
        background-color: var(--orange);
        height: 450px;
        width: 25%;
    }
    
    footer {
        background-color: var(--blue);
        height: 70px;
        text-align: right;
    }
    
    footer p {
        margin-top: 0;
    }
    
    ol {
        margin-left: 50px;
    }
    
    img {
        margin: 30px;
    }
    
    form {
        margin-left: 125px;
    }
    
    h3 {
        margin-left: 50px;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  30. Krok 30: Nagłówki pierwszego i drugiego stopnia: wyrównanie do lewej

    ok. 1 min
    Polecenie z arkusza

    formatowanie nagłówka pierwszego i drugiego stopnia: wyrównanie tekstu do lewej

    Wyrównanie do lewej jest domyślne, ale arkusz wymaga tej reguły wprost. Arkusz stylów jest kompletny.

    styl.css CSS zmienione: 5 linii
    :root {
        --blue: #00CCFF;
        --orange: #E3A329;
    }
    
    header {
        background-color: var(--blue);
        height: 70px;
    }
    
    main {
        display: flex;
    }
    
    #left {
        background-color: var(--orange);
        height: 450px;
        width: 25%;
    }
    
    #middle {
        background-color: var(--blue);
        height: 450px;
        width: 50%;
    }
    
    #right {
        background-color: var(--orange);
        height: 450px;
        width: 25%;
    }
    
    footer {
        background-color: var(--blue);
        height: 70px;
        text-align: right;
    }
    
    footer p {
        margin-top: 0;
    }
    
    ol {
        margin-left: 50px;
    }
    
    img {
        margin: 30px;
    }
    
    form {
        margin-left: 125px;
    }
    
    h3 {
        margin-left: 50px;
    }
    
    h1,
    h2 {
        text-align: left;
    }
    Podgląd strony po tym kroku (biblioteka.php)
  31. Etap 4: Skrypty PHP

    Kroki 31-38 · po tym etapie masz 33 z 33 kryteriów (pkt)

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

    Kryteria: ,

    R.5.1 Skrypt napisano w języku PHP i 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). Połączenie z nazwą bazy stoi w pliku strony, bo tam klucz je sprawdza, jeśli strona się nie uruchomi. 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;
    $authors = [];
    
    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>
            <h1>Miejska Biblioteka Publiczna w Książkowicach</h1>
        </header>
        <main>
            <section id="left">
                <img src="biblioteka.png" alt="książki">
                <h3>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 123123123<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h3>
            </section>
            <section id="middle">
                <h2>Dodaj nowego czytelnika</h2>
                <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>
            <section id="right">
                <h2>W naszych zbiorach znajdziesz dzieła następujących autorów:</h2>
                <ol>
                </ol>
            </section>
        </main>
        <footer>
            <p>Projekt strony: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  32. Krok 32: Skrypt 1: zapytanie 1 i elementy listy

    Kryteria: , ,

    R.2.5 W bloku prawym umieszczono dowolną listę, zawierającą imiona i nazwiska autorów. W kodzie HTML lub PHP użyto znaczników <ol> lub <ul> oraz <li> i ich zamknięcia

    R.5.4 Skrypt 1 wyświetla imiona i nazwiska wszystkich autorów zwrócone zapytaniem 1

    R.5.5 Wyświetlane przez skrypt 1 imiona i nazwiska autorów są umieszczone w elementach listy

    ok. 4 min
    Polecenie z arkusza

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

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

    • na górze $pdo->query(...) wysyła zapytanie 1 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 <ol> pętla foreach w zapisie alternatywnym (foreach (...): … endforeach;), który dobrze czyta się między znacznikami HTML. Każdy autor to jeden <li>, a imię i nazwisko przechodzą przez e().

    Podgląd: lista numerowana z 10 autorami - jak na obrazie 3. Skrypt 1 wyświetla autorów z zapytania 1 (R.5.4) w elementach listy (R.5.5), w bloku prawym jest lista z imionami i nazwiskami (R.2.5).

    biblioteka.php PHP zmienione: 6 linii
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $message = null;
    $authors = [];
    
    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, autorzy do listy
        $authors = $pdo->query('SELECT imie, nazwisko FROM autorzy;')->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>
            <h1>Miejska Biblioteka Publiczna w Książkowicach</h1>
        </header>
        <main>
            <section id="left">
                <img src="biblioteka.png" alt="książki">
                <h3>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 123123123<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h3>
            </section>
            <section id="middle">
                <h2>Dodaj nowego czytelnika</h2>
                <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>
            <section id="right">
                <h2>W naszych zbiorach znajdziesz dzieła następujących autorów:</h2>
                <ol>
    <?php foreach ($authors as $author): ?>
                    <li><?= e($author['imie'] . ' ' . $author['nazwisko']) ?></li>
    <?php endforeach; ?>
                </ol>
            </section>
        </main>
        <footer>
            <p>Projekt strony: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  33. Krok 33: Skrypt 2: pobranie i sprawdzenie danych z formularza

    ok. 4 min
    Polecenie z arkusza

    Skrypt 2: Pobiera dane z formularza

    Skrypt 2 stoi w skrypcie przed zapytaniem 1 - najpierw zapis, potem odczyt. 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, a XAMPP dłuższy tekst po cichu ucina) - 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;
    $authors = [];
    $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 1, autorzy do listy
        $authors = $pdo->query('SELECT imie, nazwisko FROM autorzy;')->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>
            <h1>Miejska Biblioteka Publiczna w Książkowicach</h1>
        </header>
        <main>
            <section id="left">
                <img src="biblioteka.png" alt="książki">
                <h3>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 123123123<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h3>
            </section>
            <section id="middle">
                <h2>Dodaj nowego czytelnika</h2>
                <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>
            <section id="right">
                <h2>W naszych zbiorach znajdziesz dzieła następujących autorów:</h2>
                <ol>
    <?php foreach ($authors as $author): ?>
                    <li><?= e($author['imie'] . ' ' . $author['nazwisko']) ?></li>
    <?php endforeach; ?>
                </ol>
            </section>
        </main>
        <footer>
            <p>Projekt strony: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  34. Krok 34: Skrypt 2: kod czytelnika

    Kryteria:

    R.5.7 Skrypt 2 generuje kod czytelnika (dwie początkowe litery z imienia, dwie początkowe litery z nazwiska oraz dwie ostatnie cyfry z roku urodzenia, wszystkie litery są wielkie)

    ok. 3 min
    Polecenie z arkusza

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

    • mb_substr($firstName, 0, 2) - dwa pierwsze znaki imienia: z „Tomasz” To; tak samo z nazwiska - No,
    • mb_strtoupper() - wielkie litery: TONO,
    • substr((string) $year, -2) - dwie ostatnie cyfry roku jako napis: 06.

    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 strtoupper nie zmienia polskich liter. Z mb_ kod Łucji Żak to ŁUŻA04. Dla danych z podglądu: TONO06 (R.5.7).

    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;
    $authors = [];
    $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_strtoupper(mb_substr($firstName, 0, 2) . mb_substr($lastName, 0, 2)) . substr((string) $year, -2);
            }
        }
    
        // skrypt 1 - zapytanie 1, autorzy do listy
        $authors = $pdo->query('SELECT imie, nazwisko FROM autorzy;')->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>
            <h1>Miejska Biblioteka Publiczna w Książkowicach</h1>
        </header>
        <main>
            <section id="left">
                <img src="biblioteka.png" alt="książki">
                <h3>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 123123123<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h3>
            </section>
            <section id="middle">
                <h2>Dodaj nowego czytelnika</h2>
                <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>
            <section id="right">
                <h2>W naszych zbiorach znajdziesz dzieła następujących autorów:</h2>
                <ol>
    <?php foreach ($authors as $author): ?>
                    <li><?= e($author['imie'] . ' ' . $author['nazwisko']) ?></li>
    <?php endforeach; ?>
                </ol>
            </section>
        </main>
        <footer>
            <p>Projekt strony: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  35. Krok 35: Skrypt 2: zapytanie 2 jako zapytanie przygotowane

    Kryteria:

    R.5.8 Skrypt 2 dodaje do tabeli czytelnicy nowy rekord, zawierający przynajmniej dwa elementy z trzech: imię, nazwisko pobrane z formularza, wygenerowany kod (należy sprawdzić w bazie czy rekord został dodany)

    ok. 3 min
    Polecenie z arkusza

    Wysyła do bazy danych zmodyfikowane zapytanie 2 (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 2, 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.

    Sprawdzenie, jak każe klucz (R.5.8): w tabeli czytelnicy jest nowy wiersz - 12, Tomasz, Nowak, TONO06.

    biblioteka.php PHP zmienione: 2 linie
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $message = null;
    $authors = [];
    $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_strtoupper(mb_substr($firstName, 0, 2) . mb_substr($lastName, 0, 2)) . substr((string) $year, -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 1, autorzy do listy
        $authors = $pdo->query('SELECT imie, nazwisko FROM autorzy;')->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>
            <h1>Miejska Biblioteka Publiczna w Książkowicach</h1>
        </header>
        <main>
            <section id="left">
                <img src="biblioteka.png" alt="książki">
                <h3>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 123123123<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h3>
            </section>
            <section id="middle">
                <h2>Dodaj nowego czytelnika</h2>
                <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>
            <section id="right">
                <h2>W naszych zbiorach znajdziesz dzieła następujących autorów:</h2>
                <ol>
    <?php foreach ($authors as $author): ?>
                    <li><?= e($author['imie'] . ' ' . $author['nazwisko']) ?></li>
    <?php endforeach; ?>
                </ol>
            </section>
        </main>
        <footer>
            <p>Projekt strony: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

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

  36. Krok 36: Skrypt 2: napis o dodaniu czytelnika

    Kryteria:

    R.5.6 Skrypt 2 wyświetla napis: "Czytelnik ……. został dodany do bazy danych" (w miejscu kropek powinno znaleźć się imię lub/i nazwisko pobrane z formularza)

    ok. 1 min
    Polecenie z arkusza

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

    Komunikat ustawiany dopiero po execute - gdyby zapis się nie udał, wyjątek przeskoczy tę linię i pojawi się komunikat z catch. Na stronie wypisuje go akapit pod formularzem, przez e() (R.5.6).

    biblioteka.php PHP zmienione: 1 linia
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $message = null;
    $authors = [];
    $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_strtoupper(mb_substr($firstName, 0, 2) . mb_substr($lastName, 0, 2)) . substr((string) $year, -2);
                $statement = $pdo->prepare('INSERT INTO czytelnicy (imie, nazwisko, kod) VALUES (:imie, :nazwisko, :kod);');
                $statement->execute(['imie' => $firstName, 'nazwisko' => $lastName, 'kod' => $code]);
                $message = "Czytelnik: $firstName $lastName został dodany do bazy danych";
            }
        }
    
        // skrypt 1 - zapytanie 1, autorzy do listy
        $authors = $pdo->query('SELECT imie, nazwisko FROM autorzy;')->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>
            <h1>Miejska Biblioteka Publiczna w Książkowicach</h1>
        </header>
        <main>
            <section id="left">
                <img src="biblioteka.png" alt="książki">
                <h3>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 123123123<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h3>
            </section>
            <section id="middle">
                <h2>Dodaj nowego czytelnika</h2>
                <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>
            <section id="right">
                <h2>W naszych zbiorach znajdziesz dzieła następujących autorów:</h2>
                <ol>
    <?php foreach ($authors as $author): ?>
                    <li><?= e($author['imie'] . ' ' . $author['nazwisko']) ?></li>
    <?php endforeach; ?>
                </ol>
            </section>
        </main>
        <footer>
            <p>Projekt strony: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  37. Krok 37: 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 autorów jest już wtedy w tablicy $authors, 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;
    $authors = [];
    $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_strtoupper(mb_substr($firstName, 0, 2) . mb_substr($lastName, 0, 2)) . substr((string) $year, -2);
                $statement = $pdo->prepare('INSERT INTO czytelnicy (imie, nazwisko, kod) VALUES (:imie, :nazwisko, :kod);');
                $statement->execute(['imie' => $firstName, 'nazwisko' => $lastName, 'kod' => $code]);
                $message = "Czytelnik: $firstName $lastName został dodany do bazy danych";
            }
        }
    
        // skrypt 1 - zapytanie 1, autorzy do listy
        $authors = $pdo->query('SELECT imie, nazwisko FROM autorzy;')->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>
            <h1>Miejska Biblioteka Publiczna w Książkowicach</h1>
        </header>
        <main>
            <section id="left">
                <img src="biblioteka.png" alt="książki">
                <h3>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 123123123<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h3>
            </section>
            <section id="middle">
                <h2>Dodaj nowego czytelnika</h2>
                <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>
            <section id="right">
                <h2>W naszych zbiorach znajdziesz dzieła następujących autorów:</h2>
                <ol>
    <?php foreach ($authors as $author): ?>
                    <li><?= e($author['imie'] . ' ' . $author['nazwisko']) ?></li>
    <?php endforeach; ?>
                </ol>
            </section>
        </main>
        <footer>
            <p>Projekt strony: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  38. Krok 38: 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: Tomasz Nowak został dodany do bazy danych”, 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;
    $authors = [];
    $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_strtoupper(mb_substr($firstName, 0, 2) . mb_substr($lastName, 0, 2)) . substr((string) $year, -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 1, autorzy do listy
        $authors = $pdo->query('SELECT imie, nazwisko FROM autorzy;')->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: $firstName $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>
            <h1>Miejska Biblioteka Publiczna w Książkowicach</h1>
        </header>
        <main>
            <section id="left">
                <img src="biblioteka.png" alt="książki">
                <h3>ul. Czytelnicza 25<br>
                    12-120 Książkowice<br>
                    tel.: 123123123<br>
                    e-mail: <a href="mailto:[email protected]">[email protected]</a></h3>
            </section>
            <section id="middle">
                <h2>Dodaj nowego czytelnika</h2>
                <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>
            <section id="right">
                <h2>W naszych zbiorach znajdziesz dzieła następujących autorów:</h2>
                <ol>
    <?php foreach ($authors as $author): ?>
                    <li><?= e($author['imie'] . ' ' . $author['nazwisko']) ?></li>
    <?php endforeach; ?>
                </ol>
            </section>
        </main>
        <footer>
            <p>Projekt strony: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (biblioteka.php)
  39. Etap 5: Porównanie z wersją podstawową

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

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

    ok. 3 min

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

    wersja podstawowawersja nowoczesna
    układfloat: left trzech sekcji, clear stopki<main> z display: flex
    kolorywpisane w każdej regulezmienne --blue, --orange
    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 bloku środkowymna górze pliku, w panelach tylko wypisywanie
    dane z formularza$_POST wprost?? '', trim, filter_var z zakresem, długość kolumn
    kod czytelnikasubstr, strtoupper - gubią polskie literymb_substr, mb_strtoupper
    INSERTdane wklejone w treść zapytaniaparametry :imie, :nazwisko, :kod
    wypisanie imienia i nazwiskawprost, znaczniki działają jak HTMLprzez e() (htmlspecialchars)
    zamknięcie bazymysqli_close na końcu skryptu 1$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 ?U?A04. 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 i nie dodaje nikogo drugi raz po odświeżeniu - a klucz przyjmuje obie.

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

  40. Etap 6: Oddanie pracy

    Krok 40 · po tym etapie masz 33 z 33 kryteriów (pkt)

    Krok 40: 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, import.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 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.

Potwierdzenie

Informacja