Przejdź do głównej treści
INF.03-12-25.01-SGStyczeń 2025 · 34 kroki · ok. 83 min · 40 kryteriów z klucza

Rozwiązanie krok po kroku: Witryna internetowa piekarni (Nowoczesny kod (PDO, zapytania przygotowane))

To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza piekarnia z czterema zapytaniami, grafika wypieków na tylnym planie i strona piekarnia.php z listą rodzajów wypieków i tabelą wyrobów wybranego rodzaju - i tak samo spełnia każde kryterium klucza. Inna jest droga:

  • CSS: kolory z arkusza w zmiennych (:root), margin-inline dla „jedynie lewego i prawego” marginesu; bloki stoją jeden pod drugim, więc nie potrzebują ani float, ani siatki,
  • HTML: lista rozwijana powiązana z nagłówkiem nad nią (czytnik ekranu powie, czego dotyczy), tabela z <thead> i <tbody>, data w znaczniku <time>,
  • PHP na PDO: oba zapytania na górze pliku, połączenie zamknięte, zanim zacznie się HTML, lista i tabela wypisywane pętlą foreach w szablonie,
  • bezpieczeństwo od pierwszej linii: wartość z formularza odczytana przez filter_input, sprawdzona z białą listą rodzajów z bazy i wysłana zapytaniem przygotowanym - bez etapu „dziura i naprawa” z wersji podstawowej; escape() przy każdej wartości z bazy, try/catch przy awarii,
  • wygoda: lista po wysłaniu pokazuje wybrany rodzaj.

Na końcu krótkie porównanie z wersją podstawową (zakładka obok). Baza i grafiki pochodzą z prawdziwego archiwum pliki1, zapytania zostały wykonane na tej bazie w MariaDB z XAMPP-a, a podgląd strony od etapu PHP to wynik prawdziwego uruchomienia kodu z danego kroku w PHP 8.2 - od Skryptu 2 po wysłaniu formularza z rodzajem „PIECZYWO MIESZANE”, jak na ilustracji 2.

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

  1. Etap 1: Baza danych

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

    Krok 1: Baza danych piekarnia

    ok. 2 min
    Polecenie z arkusza

    Utwórz bazę danych o nazwie piekarnia, z zestawem polskich znaków (np. utf8_unicode_ci).

    Archiwum pliki1.7z rozpakuj hasłem ULeptnny! do folderu z numerem zdającego: baza.sql, wypieki.jpg, rogal.jpg i teksty.txt. W phpMyAdmin Nowa → piekarnia, porównywanie utf8_unicode_ci, albo zapytaniem:

    CREATE DATABASE piekarnia CHARACTER SET utf8 COLLATE utf8_unicode_ci;
    
    Typowe błędy

    Nazwa bazy musi być dokładnie piekarnia - skrypty łączą się z bazą po nazwie (R.5.2).

  2. Krok 2: Import tabeli z pliku baza.sql

    ok. 2 min
    Polecenie z arkusza

    Z rozpakowanego archiwum zaimportuj tabelę z pliku baza.sql do utworzonej bazy.

    Import → baza.sql. Tabela wyroby z ilustracji 1: 93 wyroby z rodzajem, nazwą, gramaturą (w kilogramach) i ceną. Nazwy kolumn zaczynają się wielką literą (Rodzaj, Nazwa, Cena) - tak trzeba je pisać w zapytaniach i w PHP.

  3. Krok 3: Zrzut ekranu import.png

    Kryteria:

    R.1.1 Wykonany import tabeli do bazy danych piekarnia co jest udokumentowane plikiem o nazwie import w formacie PNG

    ok. 2 min
    Polecenie z arkusza

    Wykonaj zrzut ekranu po imporcie. Zapisz zrzut w formacie PNG pod nazwą import. Nie kadruj zrzutu. Powinien on obejmować cały ekran monitora, z widocznym paskiem zadań. Na zrzucie powinny być widoczne elementy wskazujące na poprawnie wykonany import tabel.

    Cały ekran z paskiem zadań, a na nim komunikat phpMyAdmin o udanym imporcie albo tabela wyroby w drzewku bazy. Zapis jako import.png.

    Typowe błędy

    Wykadrowany zrzut albo zrzut bez paska zadań nie jest oceniany.

  4. Krok 4: Zapytanie 1: wyroby z rodzaju INNE

    Kryteria: ,

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

    R.1.3 Wykonane zapytanie 1 wybierające jedynie pola z tabeli wyroby : Rodzaj, Nazwa, Gramatura i Cena dla wyrobów z rodzaju „INNE”. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT Rodzaj, Nazwa, Gramatura, Cena FROM wyroby WHERE Rodzaj = "INNE"; oraz wynik działania zapytania udokumentowany zrzutem, na którym widoczne są dokładnie 3 rekordy zawierające jedynie pola: Rodzaj, Nazwa, Gramatura, Cena

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 1: wybierające jedynie pola: Rodzaj, Nazwa, Gramatura i Cena dla wyrobów z rodzaju „INNE”.

    Każda klauzula w osobnej linii, napis 'INNE' w apostrofach - tak zapisuje napisy standard SQL (cudzysłów z przykładu w kluczu MariaDB też przyjmie, ale w innych bazach oznacza nazwę kolumny). Wynik: 3 wiersze. Zapytanie zapisz w kwerendy.txt, zrzut jako kw1.png. To zapytanie - z rodzajem z formularza - wyśle Skrypt 2.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT Rodzaj, Nazwa, Gramatura, Cena
    FROM wyroby
    WHERE Rodzaj = 'INNE';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: rodzaje bez powtórzeń, malejąco

    Kryteria:

    R.1.4 Wykonane zapytanie 2 wybierające jedynie posortowane malejąco rodzaje wyrobów. Rodzaje wyrobów nie mogą się powtarzać. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT DISTINCT Rodzaj FROM wyroby ORDER BY Rodzaj DESC; (lub GROUP BY wtedy opcjonalnie DISTINCT ) oraz wynik działania zapytania udokumentowany zrzutem, na którym widocznych jest jedynie 11 rekordów z kolumną Rodzaj

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 2: wybierające jedynie posortowane malejąco rodzaje wyrobów. Rodzaje wyrobów nie mogą się powtarzać.

    DISTINCT zostawia każdy rodzaj raz: zamiast 93 wierszy - 11. ORDER BY Rodzaj DESC - alfabetycznie od końca: pierwszy „WYROBY CUKIERNICZE I CIASTKARSKIE”, ostatni „INNE”. Zrzut: kw2.png. To zapytanie wypełni listę rozwijaną (Skrypt 1).

    kwerendy.txt Tekst zmienione: 5 linii
    -- Zapytanie 1
    SELECT Rodzaj, Nazwa, Gramatura, Cena
    FROM wyroby
    WHERE Rodzaj = 'INNE';
    
    -- Zapytanie 2
    SELECT DISTINCT Rodzaj
    FROM wyroby
    ORDER BY Rodzaj DESC;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: wyroby ze słowem „Chałka”

    Kryteria:

    R.1.5 Wykonane zapytanie 3 wybierające jedynie pola: ID oraz Nazwa dla wyrobów, których nazwa zawiera słowo „Chałka” . W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT ID, Nazwa FROM wyroby WHERE Nazwa LIKE '%Chałka%'; oraz wynik działania zapytania udokumentowany zrzutem, na którym widoczne są dokładnie 4 rekordy, jedynie pola: ID, Nazwa o numerach id: 57, 58, 61, 62

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie pola: ID oraz Nazwa dla wyrobów, których nazwa zawiera słowo „Chałka”.

    „Zawiera” to LIKE '%Chałka%': % zastępuje dowolny ciąg znaków (także pusty) przed słowem i po nim. Wynik: 4 wiersze o ID 57, 58, 61 i 62, jak w kluczu. Zrzut: kw3.png.

    Typowe błędy

    LIKE 'Chałka%' pominie nazwy, w których słowo nie stoi na początku.

    kwerendy.txt Tekst zmienione: 5 linii
    -- Zapytanie 1
    SELECT Rodzaj, Nazwa, Gramatura, Cena
    FROM wyroby
    WHERE Rodzaj = 'INNE';
    
    -- Zapytanie 2
    SELECT DISTINCT Rodzaj
    FROM wyroby
    ORDER BY Rodzaj DESC;
    
    -- Zapytanie 3
    SELECT ID, Nazwa
    FROM wyroby
    WHERE Nazwa LIKE '%Chałka%';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: średnia cena w każdym rodzaju

    Kryteria:

    R.1.6 Wykonane zapytanie 4 wybierające pole Rodzaj oraz obliczające średnią cenę dla każdej grupy wyrobów z kategorii Rodzaj, zaokrągloną do dwóch miejsc po przecinku. Nazwa (alias) kolumny ze średnią: "Średnia_cena". W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT Rodzaj, ROUND(AVG(Cena), 2) AS "Średnia_cena" FROM wyroby GROUP BY Rodzaj; (AS opcjonalnie ) oraz wynik działania zapytania udokumentowany zrzutem, na którym widocznych jest dokładnie 11 rekordów zawierających jedynie kolumny Rodzaj i alias Średnia_cena oraz pierwszy rekord ma wartości: INNE, 13.88

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 4: wybierające pole Rodzaj oraz obliczające średnią cenę dla każdej grupy wyrobów z kategorii Rodzaj, zaokrągloną do dwóch miejsc po przecinku. Obliczona średnia cena powinna mieć nadaną nazwę kolumny (alias) „Średnia cena”.

    GROUP BY Rodzaj - jeden wiersz wyniku na rodzaj, ROUND(AVG(Cena), 2) - średnia zaokrąglona do dwóch miejsc. Alias ze spacją w odwróconych apostrofach `Średnia cena` - w MySQL i MariaDB to znak dla nazw (kolumn, tabel, aliasów), a apostrof jest dla napisów. Cudzysłów z przykładu w kluczu też zadziała.

    ⚠️ Rozbieżność arkusz-klucz: arkusz podaje alias „Średnia cena” (ze spacją), klucz R.1.6 - Średnia_cena (z podkreśleniem). Tu jest wersja z arkusza; zapis bez spacji (Średnia_cena) nie wymaga cudzysłowu i spełnia brzmienie klucza.

    Wynik: 11 wierszy, pierwszy INNE 13.88 - jak w kluczu. Zrzut: kw4.png.

    Typowe błędy

    Alias ze spacją bez cudzysłowu (AS Średnia cena) to błąd składni.

    kwerendy.txt Tekst zmienione: 5 linii
    -- Zapytanie 1
    SELECT Rodzaj, Nazwa, Gramatura, Cena
    FROM wyroby
    WHERE Rodzaj = 'INNE';
    
    -- Zapytanie 2
    SELECT DISTINCT Rodzaj
    FROM wyroby
    ORDER BY Rodzaj DESC;
    
    -- Zapytanie 3
    SELECT ID, Nazwa
    FROM wyroby
    WHERE Nazwa LIKE '%Chałka%';
    
    -- Zapytanie 4
    SELECT Rodzaj, ROUND(AVG(Cena), 2) AS `Średnia cena`
    FROM wyroby
    GROUP BY Rodzaj;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Grafika i strona HTML

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

    Krok 8: Grafika wypieki.png: rogal pod preclem, cień, 1700 × 240 px

    Kryteria: ,

    R.2.1 Obraz wypieki zawiera różne wypieki w tym rogala po prawej stronie. Rogal zachodzi pod precel oraz dodany cień przesunięty w prawo i w dół dla wszystkich wypieków

    R.2.2 Tło obrazu jest przezroczyste oraz szerokość wynosi 1700 px, a wysokość 240 px

    ok. 10 min
    Polecenie z arkusza

    Do grafiki z pliku wypieki.jpg należy dołączyć grafikę z pliku rogal.jpg po prawej stronie. Lewy koniec rogala zachodzi pod precel. Cechy połączonej grafiki: Tło przezroczyste. Cień przesunięty w prawo i w dół. Wymiar: szerokość 1700 px i wysokość 240 px. Nazwa pliku wypieki, format obsługujący przezroczystość.

    Grafika jest taka sama jak w wersji podstawowej. W GIMP-ie:

    1. wypieki.jpg (1400 × 240), Obraz → Rozmiar płótna: szerokość 1700, przesunięcie 0 - płótno rośnie w prawo; Warstwa → Warstwa do rozmiaru obrazu,
    2. kanał alfa i Różdżka na białym tle; zamknięte otwory precla dodaj do zaznaczenia z Shift, Delete,
    3. Plik → Otwórz jako warstwy rogal.jpg, białe tło tak samo,
    4. w oknie Warstwy warstwa rogala pod warstwą wypieków - lewy koniec chowa się pod preclem; Przesunięcie ustawia rogal przy prawej krawędzi,
    5. Warstwa → Scal w dół, potem Filtry → Światło i cień → Cień z dodatnimi X i Y - cień dla wszystkich wypieków naraz (R.2.1),
    6. Eksportuj jako wypieki.png - PNG ma przezroczystość, JPG nie (R.2.2).
    Typowe błędy

    Obraz → Spłaszcz obraz wypełnia przezroczystość kolorem tła. Cień dodany przed wklejeniem rogala obejmie tylko część wypieków.

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

    Kryteria:

    R.2.3 Witryna internetowa zapisana w pliku piekarnia.php oraz zapisany jawnie standard kodowania polskich znaków, deklaracja HTML5 <!DOCTYPE HTML>, zadeklarowany język witryny: polski np. <html lang="pl"> oraz nadany tytuł strony "PIEKARNIA”. Strona zawiera sekcje head i body

    ok. 2 min
    Polecenie z arkusza

    Cechy witryny: Składa się ze strony o nazwie piekarnia.php. Zapisana w języku HTML5. Zadeklarowany polski język zawartości witryny. Jawnie zastosowany właściwy standard kodowania polskich znaków.

    Plik piekarnia.php: <!DOCTYPE html>, <html lang="pl"> i <meta charset="UTF-8"> jako pierwszy element <head>. Rozszerzenie .php, bo strona pobierze dane z bazy - otwierasz ją przez http://localhost/....

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

    Kryteria: ,

    R.2.3 Witryna internetowa zapisana w pliku piekarnia.php oraz zapisany jawnie standard kodowania polskich znaków, deklaracja HTML5 <!DOCTYPE HTML>, zadeklarowany język witryny: polski np. <html lang="pl"> oraz nadany tytuł strony "PIEKARNIA”. Strona zawiera sekcje head i body

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

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony „PIEKARNIA”, widoczny na karcie przeglądarki. Arkusz stylów w pliku o nazwie styles.css prawidłowo połączony z kodem strony.

    <title>, <link> do styles.css - z „s” na końcu (R.3.2) - i viewport, żeby telefon nie pomniejszał strony.

    piekarnia.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>PIEKARNIA</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (piekarnia.php)
  11. Krok 11: Obraz wypieków

    Kryteria:

    R.2.6 Umieszczona grafika z wypiekami z tekstem alternatywnym "Produkty naszej piekarni"

    ok. 1 min
    Polecenie z arkusza

    Obraz wypieki.png z tekstem alternatywnym o treści: „Produkty naszej piekarni” umieszczony jest na tylnym planie strony. Bloki strony zakrywają obraz.

    Obraz bezpośrednio w <body>, poza blokami - jest tłem całej strony (R.2.6). Na tylny plan przeniesie go CSS; na razie stoi w zwykłym miejscu.

    piekarnia.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>PIEKARNIA</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <img src="wypieki.png" alt="Produkty naszej piekarni">
    </body>
    </html>
    Podgląd strony po tym kroku (piekarnia.php)
  12. Krok 12: Bloki semantyczne

    Kryteria: ,

    R.2.4 Zdefiniowany układ strony dzielący ją na bloki: nawigacyjny, nagłówkowy, główny i stopki przy pomocy semantycznych znaczników sekcji języka HTML5 zgodnie z ich znaczeniem, w tym do części nagłówkowej - header, do stopki - footer. Dopuszcza się dodatkowe użycie bloków div lub section

    R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z ilustracją 2 w arkuszu egzaminacyjnym. Nie należy brać pod uwagę położenia obrazu

    ok. 1 min
    Polecenie z arkusza

    Podział strony na bloki: nawigacyjny, nagłówkowy, główny, stopki. Podział zrealizowany za pomocą semantycznych znaczników sekcji języka HTML5 tak, aby po uruchomieniu w przeglądarce wygląd układu bloków był zgodny z ilustracją 2.

    <nav> (na ilustracji nad nagłówkiem), <header>, <main> i <footer> - w tej kolejności w kodzie (R.2.4). Wszystkie bloki stoją jeden pod drugim na całą szerokość, a tak układa je sama przeglądarka - siatka czy flex nic by tu nie dodały (R.3.1).

    piekarnia.php PHP zmienione: 8 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>PIEKARNIA</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <img src="wypieki.png" alt="Produkty naszej piekarni">
        <nav>
        </nav>
        <header>
        </header>
        <main>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (piekarnia.php)
  13. Krok 13: Nawigacja: odnośniki do zrzutów

    Kryteria: ,

    R.2.7 W bloku nawigacyjnym umieszczone 4 odnośniki o treści KWERENDA1, KWERENDA2, KWERENDA3, KWERENDA4

    R.3.3 Po wybraniu przynajmniej jednego odnośnika otwiera/pobiera się plik graficzny

    ok. 2 min
    Polecenie z arkusza

    Zawartość bloku nawigacyjnego: Cztery odnośniki o treści: KWERENDA1, KWERENDA2, KWERENDA3, KWERENDA4. Odnośniki prowadzą do kolejnych zrzutów ekranu z kwerendami.

    Cztery <a> do zrzutów kw1.png - kw4.png (R.2.7, R.3.3). Zrzuty leżą w tym samym folderze co strona, więc w href wystarczy nazwa pliku. W podglądzie kliknięcia są zablokowane.

    piekarnia.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>PIEKARNIA</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <img src="wypieki.png" alt="Produkty naszej piekarni">
        <nav>
            <a href="kw1.png">KWERENDA1</a>
            <a href="kw2.png">KWERENDA2</a>
            <a href="kw3.png">KWERENDA3</a>
            <a href="kw4.png">KWERENDA4</a>
        </nav>
        <header>
        </header>
        <main>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (piekarnia.php)
  14. Krok 14: Blok nagłówkowy: nagłówki i akapit

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku nagłówkowego: Nagłówek pierwszego stopnia o treści: „WITAMY”. Nagłówek czwartego stopnia o treści: „NA STRONIE PIEKARNI”. Paragraf (akapit) zawierający tekst. Tekst można skopiować z pliku teksty.txt z wypakowanego archiwum.

    <h1>, <h4> i <p> z treścią teksty.txt.

    piekarnia.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>PIEKARNIA</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <img src="wypieki.png" alt="Produkty naszej piekarni">
        <nav>
            <a href="kw1.png">KWERENDA1</a>
            <a href="kw2.png">KWERENDA2</a>
            <a href="kw3.png">KWERENDA3</a>
            <a href="kw4.png">KWERENDA4</a>
        </nav>
        <header>
            <h1>WITAMY</h1>
            <h4>NA STRONIE PIEKARNI</h4>
            <p>Od 31 lat oferujemy najwyższej jakości pieczywo. Naturalnie świeże, naturalnie smaczne. Pieczemy wyłącznie wypieki na naturalnym zakwasie bez polepszaczy i zagęstników. Korzystamy wyłącznie z najlepszych ziaren pochodzących z ekologicznych upraw położonych w rejonach zgierskim i ozorkowskim.</p>
        </header>
        <main>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (piekarnia.php)
  15. Krok 15: Blok główny: nagłówek i formularz

    Kryteria: , , ,

    R.2.8 W bloku głównym umieszczony formularz z listą rozwijaną oraz przyciskiem „Wybierz”, zastosowane znaczniki <form>, <select>, <option> i przycisku (w skrypcie lub HTML)

    R.2.9 Zapisany nagłówek <h1>: „WITAMY”, <h4>: "NA STRONIE PIEKARNI" oraz <h4>: „Wybierz rodzaj wypieków:"

    R.3.4 Formularz wysyła dane metodą POST do tego samego pliku (poprawne również bez atrybutu action )

    R.3.5 Przycisk realizuje funkcję wysłania danych z formularza (input lub button z typem submit lub button bez typu )

    ok. 3 min
    Polecenie z arkusza

    Zawartość bloku głównego: Nagłówek czwartego stopnia o treści: „Wybierz rodzaj wypieków:”. Formularz wysyłający dane metodą bezpieczną do tego samego pliku. Pola formularza: Lista rozwijana wypełniona przez skrypt 1. Przycisk „Wybierz” wysyłający dane do skryptu 2 z formularza.

    • <h4> z identyfikatorem choose-type, a lista ma aria-labelledby="choose-type" - czytnik ekranu przeczyta przy niej „Wybierz rodzaj wypieków”. Arkusz każe dać ten tekst w nagłówku, nie w <label>, a aria-labelledby łączy jedno z drugim,
    • method="post" - „metoda bezpieczna” w rozumieniu arkusza; klucz R.3.4 przyjmuje tylko POST. Formularz, który niczego nie zapisuje, a tylko filtruje, w prawdziwych projektach wysyła się raczej GET-em (adres z wyborem da się zapisać w zakładkach), ale tu decyduje klucz. Bez action formularz wraca do piekarnia.php,
    • <select name="type"> na razie pusty - opcje dopisze Skrypt 1; przycisk type="submit" z napisem „Wybierz” (R.2.8, R.3.5).

    Nagłówki h1, dwa h4 i akapit to komplet R.2.9.

    Typowe błędy

    method="get" - klucz R.3.4 odbiera punkt, a skrypt czytający dane z POST nic nie dostanie.

    piekarnia.php PHP zmienione: 6 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>PIEKARNIA</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <img src="wypieki.png" alt="Produkty naszej piekarni">
        <nav>
            <a href="kw1.png">KWERENDA1</a>
            <a href="kw2.png">KWERENDA2</a>
            <a href="kw3.png">KWERENDA3</a>
            <a href="kw4.png">KWERENDA4</a>
        </nav>
        <header>
            <h1>WITAMY</h1>
            <h4>NA STRONIE PIEKARNI</h4>
            <p>Od 31 lat oferujemy najwyższej jakości pieczywo. Naturalnie świeże, naturalnie smaczne. Pieczemy wyłącznie wypieki na naturalnym zakwasie bez polepszaczy i zagęstników. Korzystamy wyłącznie z najlepszych ziaren pochodzących z ekologicznych upraw położonych w rejonach zgierskim i ozorkowskim.</p>
        </header>
        <main>
            <h4 id="choose-type">Wybierz rodzaj wypieków:</h4>
            <form method="post">
                <select name="type" aria-labelledby="choose-type">
                </select>
                <button type="submit">Wybierz</button>
            </form>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (piekarnia.php)
  16. Krok 16: Tabela: wiersz nagłówkowy

    Kryteria:

    R.2.5 W bloku głównym zapisana tabela o 4 kolumnach. Pierwszy wiersz tabeli jest nagłówkowy. Zastosowane znaczniki <table>, <tr>, <th>, <td> (w skrypcie lub HTML)

    ok. 2 min
    Polecenie z arkusza

    Tabela o czterech kolumnach zawierająca: Wiersz z komórkami nagłówkowymi: Rodzaj, Nazwa, Gramatura, Cena. Wiersze z danymi wypełnione skryptem 2.

    <thead> z czterema <th scope="col"> w kolejności kolumn zapytania 1 i pusty <tbody> - wiersze z wyrobami dopisze Skrypt 2 (R.2.5). Podział pomaga czytnikom ekranu i wydrukowi długiej tabeli.

    piekarnia.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>PIEKARNIA</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <img src="wypieki.png" alt="Produkty naszej piekarni">
        <nav>
            <a href="kw1.png">KWERENDA1</a>
            <a href="kw2.png">KWERENDA2</a>
            <a href="kw3.png">KWERENDA3</a>
            <a href="kw4.png">KWERENDA4</a>
        </nav>
        <header>
            <h1>WITAMY</h1>
            <h4>NA STRONIE PIEKARNI</h4>
            <p>Od 31 lat oferujemy najwyższej jakości pieczywo. Naturalnie świeże, naturalnie smaczne. Pieczemy wyłącznie wypieki na naturalnym zakwasie bez polepszaczy i zagęstników. Korzystamy wyłącznie z najlepszych ziaren pochodzących z ekologicznych upraw położonych w rejonach zgierskim i ozorkowskim.</p>
        </header>
        <main>
            <h4 id="choose-type">Wybierz rodzaj wypieków:</h4>
            <form method="post">
                <select name="type" aria-labelledby="choose-type">
                </select>
                <button type="submit">Wybierz</button>
            </form>
            <table>
                <thead>
                    <tr>
                        <th scope="col">Rodzaj</th>
                        <th scope="col">Nazwa</th>
                        <th scope="col">Gramatura</th>
                        <th scope="col">Cena</th>
                    </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (piekarnia.php)
  17. Krok 17: Stopka: autor i data

    Kryteria:

    R.2.10 W bloku stopki umieszczony paragraf z numerem zdającego oraz paragraf z datą egzaminu

    ok. 1 min
    Polecenie z arkusza

    Zawartość stopki: Paragraf „AUTOR ”, dalej wstawiony numer zdającego. Paragraf „Data: ”, dalej wpisana data egzaminu.

    Dwa akapity (R.2.10). Na egzaminie po „AUTOR” wpisujesz swój numer zdającego (PESEL), a po „Data:” dzień egzaminu; tu - autor rozwiązania i data jego przygotowania. Data w znaczniku <time datetime="2026-10-02">: na ekranie zapis polski, a w atrybucie zapis maszynowy RRRR-MM-DD, który rozumieją wyszukiwarki i programy.

    piekarnia.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>PIEKARNIA</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <img src="wypieki.png" alt="Produkty naszej piekarni">
        <nav>
            <a href="kw1.png">KWERENDA1</a>
            <a href="kw2.png">KWERENDA2</a>
            <a href="kw3.png">KWERENDA3</a>
            <a href="kw4.png">KWERENDA4</a>
        </nav>
        <header>
            <h1>WITAMY</h1>
            <h4>NA STRONIE PIEKARNI</h4>
            <p>Od 31 lat oferujemy najwyższej jakości pieczywo. Naturalnie świeże, naturalnie smaczne. Pieczemy wyłącznie wypieki na naturalnym zakwasie bez polepszaczy i zagęstników. Korzystamy wyłącznie z najlepszych ziaren pochodzących z ekologicznych upraw położonych w rejonach zgierskim i ozorkowskim.</p>
        </header>
        <main>
            <h4 id="choose-type">Wybierz rodzaj wypieków:</h4>
            <form method="post">
                <select name="type" aria-labelledby="choose-type">
                </select>
                <button type="submit">Wybierz</button>
            </form>
            <table>
                <thead>
                    <tr>
                        <th scope="col">Rodzaj</th>
                        <th scope="col">Nazwa</th>
                        <th scope="col">Gramatura</th>
                        <th scope="col">Cena</th>
                    </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
        </main>
        <footer>
            <p>AUTOR: Bartosz Bryniarski</p>
            <p>Data: <time datetime="2026-10-02">02.10.2026</time></p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (piekarnia.php)
  18. Etap 3: Styl CSS

    Kroki 18-24 · po tym etapie masz 31 z 40 kryteriów (pkt)

    Krok 18: Plik styles.css: zmienne, krój, ciało strony

    Kryteria:

    R.4.1 Ustawione domyślne formatowanie wszystkich selektorów (*) krój czcionki: Verdana oraz dla body lub kontenera całej strony tło np. linear-gradient(Teal, AliceBlue), biały kolor czcionki, wyrównanie tekstu do środka, marginesy zewnętrzne (margin) lewy i prawy 20px

    ok. 3 min
    Polecenie z arkusza

    Domyślne formatowanie wszystkich selektorów: krój czcionki Verdana. Dla ciała strony: tło jako gradient liniowy pionowy od koloru Teal do AliceBlue, biały kolor czcionki, wyrównanie tekstu do środka, jedynie marginesy zewnętrzne lewy i prawy 20 px.

    • zmienne CSS w :root: kolory z arkusza zapisane raz; Sienna występuje w stopce i w tabeli, SandyBrown w bloku głównym i w ramkach,
    • * z Verdaną i zapasowym krojem bezszeryfowym,
    • linear-gradient(...) bez kierunku biegnie z góry na dół, czyli pionowo; gradient to obraz, więc stoi w background,
    • margin-inline: 20px - właściwość logiczna, skrót od margin-left i margin-right naraz. Ustawia jedynie lewy i prawy margines, dokładnie jak w arkuszu; górny i dolny zostają domyślne. Zapis margin: 0 20px z wersji podstawowej przy okazji zeruje górę i dół.

    Komplet cech ciała strony i krój to R.4.1.

    styles.css CSS nowy plik
    :root {
        --teal: Teal;
        --alice: AliceBlue;
        --sandy: SandyBrown;
        --sienna: Sienna;
        --header-bg: rgba(160, 82, 45, 0.6);
    }
    
    * {
        font-family: Verdana, sans-serif;
    }
    
    body {
        background: linear-gradient(var(--teal), var(--alice));
        color: white;
        text-align: center;
        margin-inline: 20px;
    }
    Podgląd strony po tym kroku (piekarnia.php)
  19. Krok 19: Obraz na tylnym planie

    Kryteria: ,

    R.4.4 Ustawione cechy obrazu: blokowy sposób wyświetlania, stałe położenie na stronie z odstępem od góry 70px, np.: display: block; position: fixed; (lub absolute; ) top: 70px; dopuszcza się też margin-top: 70px;

    R.4.5 Obraz jest pod spodem innych elementów strony np. z-index = -1

    ok. 2 min
    Polecenie z arkusza

    Dla obrazu: szerokość 100%, blokowy sposób wyświetlania, stałe położenie na stronie z odstępem od góry 70 px. Obraz jest zawsze pod spodem treści strony.

    position: fixed z top: 70px przypina obraz do okna - nie przewija się ze stroną (R.4.4). z-index: -1 kładzie go pod blokami (R.4.5), a width: 100% liczy się od szerokości okna (R.4.6). Element fixed wypada z przepływu, więc bloki zaczynają się od góry, jakby obrazu nie było. Selektor img wystarcza - to jedyny obraz na stronie.

    Typowe błędy

    Bez z-index: -1 obraz zasłania nagłówek.

    styles.css CSS zmienione: 8 linii
    :root {
        --teal: Teal;
        --alice: AliceBlue;
        --sandy: SandyBrown;
        --sienna: Sienna;
        --header-bg: rgba(160, 82, 45, 0.6);
    }
    
    * {
        font-family: Verdana, sans-serif;
    }
    
    body {
        background: linear-gradient(var(--teal), var(--alice));
        color: white;
        text-align: center;
        margin-inline: 20px;
    }
    
    img {
        display: block;
        width: 100%;
        position: fixed;
        top: 70px;
        z-index: -1;
    }
    Podgląd strony po tym kroku (piekarnia.php)
  20. Krok 20: Odnośniki

    Kryteria:

    R.4.7 Ustawiony rozmiar czcionki 130% dla odnośnika

    ok. 1 min
    Polecenie z arkusza

    Dla odnośnika: rozmiar czcionki 130%, marginesy wewnętrzne 30 px, biały kolor czcionki.

    Odnośnik jest elementem w linii, więc padding rozsuwa odnośniki na boki - stąd odstępy między nimi na ilustracji 2 (R.4.7).

    styles.css CSS zmienione: 6 linii
    :root {
        --teal: Teal;
        --alice: AliceBlue;
        --sandy: SandyBrown;
        --sienna: Sienna;
        --header-bg: rgba(160, 82, 45, 0.6);
    }
    
    * {
        font-family: Verdana, sans-serif;
    }
    
    body {
        background: linear-gradient(var(--teal), var(--alice));
        color: white;
        text-align: center;
        margin-inline: 20px;
    }
    
    img {
        display: block;
        width: 100%;
        position: fixed;
        top: 70px;
        z-index: -1;
    }
    
    a {
        font-size: 130%;
        padding: 30px;
        color: white;
    }
    Podgląd strony po tym kroku (piekarnia.php)
  21. Krok 21: Blok nagłówkowy: półprzezroczyste tło, zaokrąglone górne rogi

    Kryteria:

    R.4.9 Ustawione zaokrąglenie górnych rogów bloku nagłówkowego np. border-radius: 15px 15px 0 0; oraz dolnych rogów bloku stopki, np. border-radius: 0 0 15px 15px;

    ok. 2 min
    Polecenie z arkusza

    Dla bloku nagłówkowego: kolor tła RGB 160, 82, 45 z przezroczystością 0.6, marginesy wewnętrzne 10 px, jedynie margines zewnętrzny górny 100 px, jedynie górne rogi zaokrąglone o 15 px.

    • rgba(160, 82, 45, 0.6) - krycie 0.6, przez tło prześwituje obraz wypieków; tekst zostaje w pełni biały (opacity zrobiłby półprzezroczystym cały blok). Nowa składnia CSS pozwala pisać rgb(160 82 45 / 0.6), ale klucz R.4.2 podaje zapis rgba(...) i ten zostaje,
    • margin-top: 100px - tylko górny margines,
    • border-radius: 15px 15px 0 0 - rogi zgodnie z ruchem wskazówek zegara od lewego górnego: górne zaokrąglone, dolne proste (R.4.9).
    styles.css CSS zmienione: 7 linii
    :root {
        --teal: Teal;
        --alice: AliceBlue;
        --sandy: SandyBrown;
        --sienna: Sienna;
        --header-bg: rgba(160, 82, 45, 0.6);
    }
    
    * {
        font-family: Verdana, sans-serif;
    }
    
    body {
        background: linear-gradient(var(--teal), var(--alice));
        color: white;
        text-align: center;
        margin-inline: 20px;
    }
    
    img {
        display: block;
        width: 100%;
        position: fixed;
        top: 70px;
        z-index: -1;
    }
    
    a {
        font-size: 130%;
        padding: 30px;
        color: white;
    }
    
    header {
        background-color: var(--header-bg);
        padding: 10px;
        margin-top: 100px;
        border-radius: 15px 15px 0 0;
    }
    Podgląd strony po tym kroku (piekarnia.php)
  22. Krok 22: Blok główny i stopka

    ok. 2 min
    Polecenie z arkusza

    Dla bloku głównego: kolor tła SandyBrown, marginesy wewnętrzne 30 px. Dla bloku stopki: kolor tła Sienna, marginesy wewnętrzne 30 px, jedynie dolne rogi zaokrąglone o 15 px.

    Stopka zaokrągla dolne rogi (0 0 15px 15px) - nagłówek, blok główny i stopka tworzą razem jedną kartę z zaokrąglonymi rogami.

    styles.css CSS zmienione: 11 linii
    :root {
        --teal: Teal;
        --alice: AliceBlue;
        --sandy: SandyBrown;
        --sienna: Sienna;
        --header-bg: rgba(160, 82, 45, 0.6);
    }
    
    * {
        font-family: Verdana, sans-serif;
    }
    
    body {
        background: linear-gradient(var(--teal), var(--alice));
        color: white;
        text-align: center;
        margin-inline: 20px;
    }
    
    img {
        display: block;
        width: 100%;
        position: fixed;
        top: 70px;
        z-index: -1;
    }
    
    a {
        font-size: 130%;
        padding: 30px;
        color: white;
    }
    
    header {
        background-color: var(--header-bg);
        padding: 10px;
        margin-top: 100px;
        border-radius: 15px 15px 0 0;
    }
    
    main {
        background-color: var(--sandy);
        padding: 30px;
    }
    
    footer {
        background-color: var(--sienna);
        padding: 30px;
        border-radius: 0 0 15px 15px;
    }
    Podgląd strony po tym kroku (piekarnia.php)
  23. Krok 23: Cień nagłówka pierwszego stopnia

    Kryteria:

    R.4.3 Ustawiony biały kolor czcionki dla odnośnika oraz cień zewnętrzny dla selektora h1: np. text-shadow: 2px 2px 5px black

    ok. 1 min
    Polecenie z arkusza

    Dla selektora nagłówka pierwszego stopnia: zewnętrzny cień tekstu przesunięty w poziomie i pionie o 2 px, promień rozmycia 5 px, kolor cienia czarny.

    Selektor znacznika h1, jak wymaga uwaga arkusza; z białym kolorem odnośników to R.4.3.

    styles.css CSS zmienione: 4 linie
    :root {
        --teal: Teal;
        --alice: AliceBlue;
        --sandy: SandyBrown;
        --sienna: Sienna;
        --header-bg: rgba(160, 82, 45, 0.6);
    }
    
    * {
        font-family: Verdana, sans-serif;
    }
    
    body {
        background: linear-gradient(var(--teal), var(--alice));
        color: white;
        text-align: center;
        margin-inline: 20px;
    }
    
    img {
        display: block;
        width: 100%;
        position: fixed;
        top: 70px;
        z-index: -1;
    }
    
    a {
        font-size: 130%;
        padding: 30px;
        color: white;
    }
    
    header {
        background-color: var(--header-bg);
        padding: 10px;
        margin-top: 100px;
        border-radius: 15px 15px 0 0;
    }
    
    main {
        background-color: var(--sandy);
        padding: 30px;
    }
    
    footer {
        background-color: var(--sienna);
        padding: 30px;
        border-radius: 0 0 15px 15px;
    }
    
    h1 {
        text-shadow: 2px 2px 5px black;
    }
    Podgląd strony po tym kroku (piekarnia.php)
  24. Krok 24: Tabela: wyśrodkowanie, tło, ramki

    Kryteria: , , ,

    R.4.2 Ustawiony kolor tła RGBA(160, 82, 45, 0.6) dla bloku nagłówkowego (poprawnie również gdy zdefiniowano opacity: 0.6 ), SandyBrown dla bloku głównego, Sienna dla stopki i dla selektora table

    R.4.6 Ustawiona szerokość 100% dla obrazu i 70% dla tabeli

    R.4.8 Ustawione marginesy wewnętrzne (padding) 30px dla odnośnika, bloku głównego i stopki, 10px dla bloku nagłówkowego oraz margines zewnętrzny górny 100px dla bloku nagłówkowego i auto dla tabeli

    R.4.10 Ustawione dla tabeli i komórek tabeli obramowanie linią ciągłą o grubości 1px i kolorze SandyBrown, dla tabeli obramowanie połączone (border-collapse: collapse)

    ok. 2 min
    Polecenie z arkusza

    Dla selektora tabeli: marginesy zewnętrzne automatycznie wyliczane przez przeglądarkę, szerokość 70%, kolor tła Sienna. Wspólne dla selektora tabeli i komórek tabeli: obramowanie linią ciągłą o grubości 1 px i kolorze SandyBrown, obramowanie połączone.

    border-collapse dotyczy całej tabeli, więc stoi w regule table; ramka - wspólnie dla tabeli i komórek (R.4.10). Ramka w kolorze tła bloku głównego wygląda jak jasne odstępy między komórkami. Tła czterech bloków i tabeli to R.4.2, szerokości obrazu i tabeli R.4.6, a odstępy odnośników, bloków i tabeli R.4.8.

    styles.css CSS zmienione: 13 linii
    :root {
        --teal: Teal;
        --alice: AliceBlue;
        --sandy: SandyBrown;
        --sienna: Sienna;
        --header-bg: rgba(160, 82, 45, 0.6);
    }
    
    * {
        font-family: Verdana, sans-serif;
    }
    
    body {
        background: linear-gradient(var(--teal), var(--alice));
        color: white;
        text-align: center;
        margin-inline: 20px;
    }
    
    img {
        display: block;
        width: 100%;
        position: fixed;
        top: 70px;
        z-index: -1;
    }
    
    a {
        font-size: 130%;
        padding: 30px;
        color: white;
    }
    
    header {
        background-color: var(--header-bg);
        padding: 10px;
        margin-top: 100px;
        border-radius: 15px 15px 0 0;
    }
    
    main {
        background-color: var(--sandy);
        padding: 30px;
    }
    
    footer {
        background-color: var(--sienna);
        padding: 30px;
        border-radius: 0 0 15px 15px;
    }
    
    h1 {
        text-shadow: 2px 2px 5px black;
    }
    
    table {
        margin: auto;
        width: 70%;
        background-color: var(--sienna);
        border-collapse: collapse;
    }
    
    table,
    th,
    td {
        border: 1px solid var(--sandy);
    }
    Podgląd strony po tym kroku (piekarnia.php)
  25. Etap 4: Skrypty PHP

    Kroki 25-32 · po tym etapie masz 40 z 40 kryteriów (pkt)

    Krok 25: Połączenie PDO na górze pliku

    Kryteria: , ,

    R.5.1 Skrypt jest napisany w języku PHP i zawiera przynajmniej jedną instrukcję zgodną z treścią zadania

    R.5.2 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy piekarnia

    R.5.3 Ostatnią operacją na bazie jest jej zamknięcie

    ok. 4 min
    Polecenie z arkusza

    Napisane w języku PHP. Należy stosować znaczące nazewnictwo zmiennych i funkcji w języku polskim lub angielskim. Łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie piekarnia. Na końcu jest zamykane połączenie z serwerem.

    Praca z bazą stoi przed <!DOCTYPE html>: najpierw dane, potem HTML, który je tylko wypisuje. Połączenie przez PDO (klucz R.5 przyjmuje MySQLi i PDO):

    • DSN 'mysql:host=localhost;dbname=piekarnia;charset=utf8mb4', użytkownik root, puste hasło (R.5.1, R.5.2),
    • ERRMODE_EXCEPTION - błąd bazy rzuca wyjątek, FETCH_ASSOC - wiersze z nazwami kolumn,
    • $pdo = null zamyka połączenie; oba skrypty trafią przed tę linię, więc zamknięcie zostanie ostatnią operacją na bazie (R.5.3),
    • try / catch - przy awarii bazy szczegóły idą do dziennika serwera, a strona dostaje ogólny komunikat,
    • escape() - htmlspecialchars(..., ENT_QUOTES) pod nazwą, która mówi, co robi. Nazwy wyrobów mają cudzysłowy (Chleb wieloziarnisty "Słońce"), więc ENT_QUOTES jest tu potrzebne.

    Od tego kroku podgląd to wynik prawdziwego uruchomienia.

    piekarnia.php PHP zmienione: 21 linii
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    $types = [];
    $products = [];
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=piekarnia;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać oferty. 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>PIEKARNIA</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <img src="wypieki.png" alt="Produkty naszej piekarni">
        <nav>
            <a href="kw1.png">KWERENDA1</a>
            <a href="kw2.png">KWERENDA2</a>
            <a href="kw3.png">KWERENDA3</a>
            <a href="kw4.png">KWERENDA4</a>
        </nav>
        <header>
            <h1>WITAMY</h1>
            <h4>NA STRONIE PIEKARNI</h4>
            <p>Od 31 lat oferujemy najwyższej jakości pieczywo. Naturalnie świeże, naturalnie smaczne. Pieczemy wyłącznie wypieki na naturalnym zakwasie bez polepszaczy i zagęstników. Korzystamy wyłącznie z najlepszych ziaren pochodzących z ekologicznych upraw położonych w rejonach zgierskim i ozorkowskim.</p>
        </header>
        <main>
            <h4 id="choose-type">Wybierz rodzaj wypieków:</h4>
            <form method="post">
                <select name="type" aria-labelledby="choose-type">
                </select>
                <button type="submit">Wybierz</button>
            </form>
            <table>
                <thead>
                    <tr>
                        <th scope="col">Rodzaj</th>
                        <th scope="col">Nazwa</th>
                        <th scope="col">Gramatura</th>
                        <th scope="col">Cena</th>
                    </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
        </main>
        <footer>
            <p>AUTOR: Bartosz Bryniarski</p>
            <p>Data: <time datetime="2026-10-02">02.10.2026</time></p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (piekarnia.php)
  26. Krok 26: Skrypt 1: zapytanie 2

    Kryteria:

    R.5.4 Skrypt 1 zawiera instrukcję wysyłającą do bazy danych zapytanie 2 lub skrypt 2 zawiera instrukcję wysyłającą do bazy danych zmodyfikowane zapytanie 1 w ten sposób, że wartość pola Rodzaj w warunku jest pobrana z formularza

    ok. 2 min
    Polecenie z arkusza

    Skrypt 1: Wysyła do bazy danych zapytanie 2.

    $pdo->query(...) wysyła zapytanie 2 (R.5.4). fetchAll(PDO::FETCH_COLUMN) oddaje zwykłą listę napisów - 11 rodzajów - zamiast listy wierszy z jedną kolumną: $types[0] to od razu „WYROBY CUKIERNICZE I CIASTKARSKIE”. Lista przyda się dwa razy: do opcji i do sprawdzenia, czy wysłany rodzaj w ogóle istnieje.

    piekarnia.php PHP zmienione: 6 linii
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    $types = [];
    $products = [];
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=piekarnia;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // Skrypt 1: rodzaje wypieków do listy
        $types = $pdo->query(
            'SELECT DISTINCT Rodzaj FROM wyroby ORDER BY Rodzaj DESC'
        )->fetchAll(PDO::FETCH_COLUMN);
    
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać oferty. 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>PIEKARNIA</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <img src="wypieki.png" alt="Produkty naszej piekarni">
        <nav>
            <a href="kw1.png">KWERENDA1</a>
            <a href="kw2.png">KWERENDA2</a>
            <a href="kw3.png">KWERENDA3</a>
            <a href="kw4.png">KWERENDA4</a>
        </nav>
        <header>
            <h1>WITAMY</h1>
            <h4>NA STRONIE PIEKARNI</h4>
            <p>Od 31 lat oferujemy najwyższej jakości pieczywo. Naturalnie świeże, naturalnie smaczne. Pieczemy wyłącznie wypieki na naturalnym zakwasie bez polepszaczy i zagęstników. Korzystamy wyłącznie z najlepszych ziaren pochodzących z ekologicznych upraw położonych w rejonach zgierskim i ozorkowskim.</p>
        </header>
        <main>
            <h4 id="choose-type">Wybierz rodzaj wypieków:</h4>
            <form method="post">
                <select name="type" aria-labelledby="choose-type">
                </select>
                <button type="submit">Wybierz</button>
            </form>
            <table>
                <thead>
                    <tr>
                        <th scope="col">Rodzaj</th>
                        <th scope="col">Nazwa</th>
                        <th scope="col">Gramatura</th>
                        <th scope="col">Cena</th>
                    </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
        </main>
        <footer>
            <p>AUTOR: Bartosz Bryniarski</p>
            <p>Data: <time datetime="2026-10-02">02.10.2026</time></p>
        </footer>
    </body>
    </html>

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

  27. Krok 27: Skrypt 1: rodzaje w elementach listy

    Kryteria: ,

    R.5.5 Skrypt 1 wyświetla zwrócone zapytaniem wiersze w kolejnych elementach listy rozwijanej

    R.5.6 Działanie skryptu 1 oraz skryptu 2 jest uniwersalne dla dowolnej liczby zwróconych rekordów i jest zrealizowane z wykorzystaniem pętli

    ok. 3 min
    Polecenie z arkusza

    Dane zwrócone zapytaniem wyświetla w kolejnych elementach listy rozwijanej.

    Pętla foreach ($types as $type): w składni alternatywnej - dla każdego rodzaju jeden <option> (R.5.5); działa dla dowolnej liczby rodzajów (R.5.6). Wartość stoi jawnie w value - to ją wyśle formularz - i przechodzi przez escape(): w atrybucie cudzysłów bez zamiany na encję uciąłby wartość. Podgląd: lista z 11 rodzajami.

    piekarnia.php PHP zmienione: 3 linie
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    $types = [];
    $products = [];
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=piekarnia;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // Skrypt 1: rodzaje wypieków do listy
        $types = $pdo->query(
            'SELECT DISTINCT Rodzaj FROM wyroby ORDER BY Rodzaj DESC'
        )->fetchAll(PDO::FETCH_COLUMN);
    
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać oferty. 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>PIEKARNIA</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <img src="wypieki.png" alt="Produkty naszej piekarni">
        <nav>
            <a href="kw1.png">KWERENDA1</a>
            <a href="kw2.png">KWERENDA2</a>
            <a href="kw3.png">KWERENDA3</a>
            <a href="kw4.png">KWERENDA4</a>
        </nav>
        <header>
            <h1>WITAMY</h1>
            <h4>NA STRONIE PIEKARNI</h4>
            <p>Od 31 lat oferujemy najwyższej jakości pieczywo. Naturalnie świeże, naturalnie smaczne. Pieczemy wyłącznie wypieki na naturalnym zakwasie bez polepszaczy i zagęstników. Korzystamy wyłącznie z najlepszych ziaren pochodzących z ekologicznych upraw położonych w rejonach zgierskim i ozorkowskim.</p>
        </header>
        <main>
            <h4 id="choose-type">Wybierz rodzaj wypieków:</h4>
            <form method="post">
                <select name="type" aria-labelledby="choose-type">
                    <?php foreach ($types as $type): ?>
                        <option value="<?= escape($type) ?>"><?= escape($type) ?></option>
                    <?php endforeach; ?>
                </select>
                <button type="submit">Wybierz</button>
            </form>
            <table>
                <thead>
                    <tr>
                        <th scope="col">Rodzaj</th>
                        <th scope="col">Nazwa</th>
                        <th scope="col">Gramatura</th>
                        <th scope="col">Cena</th>
                    </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
        </main>
        <footer>
            <p>AUTOR: Bartosz Bryniarski</p>
            <p>Data: <time datetime="2026-10-02">02.10.2026</time></p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (piekarnia.php)
  28. Krok 28: Skrypt 2: wartość z formularza i biała lista

    Kryteria:

    R.5.8 Skrypt 2 jest zabezpieczony na wypadek braku danych wysyłanych z formularza

    ok. 3 min
    Polecenie z arkusza

    W stanie początkowym, gdy nie wybrano opcji listy rozwijanej, w tabeli nie są wyświetlane wiersze z danymi.

    • filter_input(INPUT_POST, 'type') czyta pole z wysłanego formularza; gdy formularza nie wysłano, zwraca null, a ?? '' zamienia go na pusty napis. trim obcina spacje z brzegów,
    • warunek ma dwie części: $selectedType !== '' - formularz wysłano (przy pierwszym wejściu tabela zostaje z samym nagłówkiem, R.5.8), oraz in_array($selectedType, $types, true) - wartość jest jednym z 11 rodzajów z bazy. To biała lista: lista rozwijana podpowiada wybór, ale nie pilnuje go - formularz da się wysłać z dowolnym tekstem (narzędzia programisty, curl). Trzeci argument true porównuje dokładnie, bez zamiany typów.

    Blok if jest jeszcze pusty; podgląd: stan po wysłaniu „PIECZYWO MIESZANE”.

    piekarnia.php PHP zmienione: 5 linii
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    $types = [];
    $products = [];
    $dbError = '';
    $selectedType = trim(filter_input(INPUT_POST, 'type') ?? '');
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=piekarnia;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // Skrypt 1: rodzaje wypieków do listy
        $types = $pdo->query(
            'SELECT DISTINCT Rodzaj FROM wyroby ORDER BY Rodzaj DESC'
        )->fetchAll(PDO::FETCH_COLUMN);
    
        // Skrypt 2: wyroby wybranego rodzaju
        if ($selectedType !== '' && in_array($selectedType, $types, true)) {
        }
    
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać oferty. 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>PIEKARNIA</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <img src="wypieki.png" alt="Produkty naszej piekarni">
        <nav>
            <a href="kw1.png">KWERENDA1</a>
            <a href="kw2.png">KWERENDA2</a>
            <a href="kw3.png">KWERENDA3</a>
            <a href="kw4.png">KWERENDA4</a>
        </nav>
        <header>
            <h1>WITAMY</h1>
            <h4>NA STRONIE PIEKARNI</h4>
            <p>Od 31 lat oferujemy najwyższej jakości pieczywo. Naturalnie świeże, naturalnie smaczne. Pieczemy wyłącznie wypieki na naturalnym zakwasie bez polepszaczy i zagęstników. Korzystamy wyłącznie z najlepszych ziaren pochodzących z ekologicznych upraw położonych w rejonach zgierskim i ozorkowskim.</p>
        </header>
        <main>
            <h4 id="choose-type">Wybierz rodzaj wypieków:</h4>
            <form method="post">
                <select name="type" aria-labelledby="choose-type">
                    <?php foreach ($types as $type): ?>
                        <option value="<?= escape($type) ?>"><?= escape($type) ?></option>
                    <?php endforeach; ?>
                </select>
                <button type="submit">Wybierz</button>
            </form>
            <table>
                <thead>
                    <tr>
                        <th scope="col">Rodzaj</th>
                        <th scope="col">Nazwa</th>
                        <th scope="col">Gramatura</th>
                        <th scope="col">Cena</th>
                    </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
        </main>
        <footer>
            <p>AUTOR: Bartosz Bryniarski</p>
            <p>Data: <time datetime="2026-10-02">02.10.2026</time></p>
        </footer>
    </body>
    </html>

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

  29. Krok 29: Skrypt 2: zapytanie przygotowane z rodzajem z formularza

    ok. 4 min
    Polecenie z arkusza

    Skrypt 2: Wysyła do bazy danych zapytanie 1 zmodyfikowane w ten sposób, że wartość pola Rodzaj w warunku jest pobrana z formularza.

    Zapytanie 1 z parametrem nazwanym :type w miejscu „INNE”:

    • prepare(...) wysyła do bazy samo zapytanie, z dziurą na wartość,
    • execute(['type' => $selectedType]) wysyła wartość osobno - baza nigdy nie potraktuje jej jak kodu SQL, więc nawet x' OR '1'='1 jest tylko napisem, którego nie ma w kolumnie Rodzaj,
    • fetchAll() - wyroby wybranego rodzaju trafiają do $products, a $statement = null zwalnia zapytanie przed zamknięciem połączenia.

    Biała lista z poprzedniego kroku i zapytanie przygotowane chronią na dwa sposoby: pierwsza odrzuca wartości spoza listy, drugie gwarantuje, że żadna wartość nie zmieni zapytania. Podgląd: zapytanie poszło, wyniku jeszcze nie wypisano.

    piekarnia.php PHP zmienione: 6 linii
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    $types = [];
    $products = [];
    $dbError = '';
    $selectedType = trim(filter_input(INPUT_POST, 'type') ?? '');
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=piekarnia;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // Skrypt 1: rodzaje wypieków do listy
        $types = $pdo->query(
            'SELECT DISTINCT Rodzaj FROM wyroby ORDER BY Rodzaj DESC'
        )->fetchAll(PDO::FETCH_COLUMN);
    
        // Skrypt 2: wyroby wybranego rodzaju
        if ($selectedType !== '' && in_array($selectedType, $types, true)) {
            $statement = $pdo->prepare(
                'SELECT Rodzaj, Nazwa, Gramatura, Cena FROM wyroby WHERE Rodzaj = :type'
            );
            $statement->execute(['type' => $selectedType]);
            $products = $statement->fetchAll();
            $statement = null;
        }
    
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać oferty. 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>PIEKARNIA</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <img src="wypieki.png" alt="Produkty naszej piekarni">
        <nav>
            <a href="kw1.png">KWERENDA1</a>
            <a href="kw2.png">KWERENDA2</a>
            <a href="kw3.png">KWERENDA3</a>
            <a href="kw4.png">KWERENDA4</a>
        </nav>
        <header>
            <h1>WITAMY</h1>
            <h4>NA STRONIE PIEKARNI</h4>
            <p>Od 31 lat oferujemy najwyższej jakości pieczywo. Naturalnie świeże, naturalnie smaczne. Pieczemy wyłącznie wypieki na naturalnym zakwasie bez polepszaczy i zagęstników. Korzystamy wyłącznie z najlepszych ziaren pochodzących z ekologicznych upraw położonych w rejonach zgierskim i ozorkowskim.</p>
        </header>
        <main>
            <h4 id="choose-type">Wybierz rodzaj wypieków:</h4>
            <form method="post">
                <select name="type" aria-labelledby="choose-type">
                    <?php foreach ($types as $type): ?>
                        <option value="<?= escape($type) ?>"><?= escape($type) ?></option>
                    <?php endforeach; ?>
                </select>
                <button type="submit">Wybierz</button>
            </form>
            <table>
                <thead>
                    <tr>
                        <th scope="col">Rodzaj</th>
                        <th scope="col">Nazwa</th>
                        <th scope="col">Gramatura</th>
                        <th scope="col">Cena</th>
                    </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
        </main>
        <footer>
            <p>AUTOR: Bartosz Bryniarski</p>
            <p>Data: <time datetime="2026-10-02">02.10.2026</time></p>
        </footer>
    </body>
    </html>

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

  30. Krok 30: Skrypt 2: wyroby w wierszach tabeli

    Kryteria: ,

    R.5.7 Skrypt 2 wyświetla zwrócone zapytaniem rekordy w kolejnych wierszach tabeli, w przeznaczonych do tego komórkach

    R.5.9 W skryptach zastosowana przynajmniej jedna zmienna lub funkcja oraz nazwy wszystkich funkcji i zmiennych są w języku polskim lub angielskim i odzwierciedlają cel zastosowania

    ok. 3 min
    Polecenie z arkusza

    Zwrócone zapytaniem wiersze wyświetla w kolejnych wierszach tabeli, w przeznaczonych do tego komórkach.

    W <tbody> pętla foreach ($products as $product): - wiersz <tr> z czterema <td> po nazwach kolumn ($product['Nazwa']), każda przez escape() (R.5.7). Przy pierwszym wejściu $products jest pustą tablicą i pętla nie wypisze nic. Nazwy zmiennych i funkcji po angielsku mówią, co przechowują (R.5.9). Podgląd: 7 wyrobów z rodzaju PIECZYWO MIESZANE - jak na ilustracji 2.

    piekarnia.php PHP zmienione: 8 linii
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    $types = [];
    $products = [];
    $dbError = '';
    $selectedType = trim(filter_input(INPUT_POST, 'type') ?? '');
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=piekarnia;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // Skrypt 1: rodzaje wypieków do listy
        $types = $pdo->query(
            'SELECT DISTINCT Rodzaj FROM wyroby ORDER BY Rodzaj DESC'
        )->fetchAll(PDO::FETCH_COLUMN);
    
        // Skrypt 2: wyroby wybranego rodzaju
        if ($selectedType !== '' && in_array($selectedType, $types, true)) {
            $statement = $pdo->prepare(
                'SELECT Rodzaj, Nazwa, Gramatura, Cena FROM wyroby WHERE Rodzaj = :type'
            );
            $statement->execute(['type' => $selectedType]);
            $products = $statement->fetchAll();
            $statement = null;
        }
    
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać oferty. 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>PIEKARNIA</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <img src="wypieki.png" alt="Produkty naszej piekarni">
        <nav>
            <a href="kw1.png">KWERENDA1</a>
            <a href="kw2.png">KWERENDA2</a>
            <a href="kw3.png">KWERENDA3</a>
            <a href="kw4.png">KWERENDA4</a>
        </nav>
        <header>
            <h1>WITAMY</h1>
            <h4>NA STRONIE PIEKARNI</h4>
            <p>Od 31 lat oferujemy najwyższej jakości pieczywo. Naturalnie świeże, naturalnie smaczne. Pieczemy wyłącznie wypieki na naturalnym zakwasie bez polepszaczy i zagęstników. Korzystamy wyłącznie z najlepszych ziaren pochodzących z ekologicznych upraw położonych w rejonach zgierskim i ozorkowskim.</p>
        </header>
        <main>
            <h4 id="choose-type">Wybierz rodzaj wypieków:</h4>
            <form method="post">
                <select name="type" aria-labelledby="choose-type">
                    <?php foreach ($types as $type): ?>
                        <option value="<?= escape($type) ?>"><?= escape($type) ?></option>
                    <?php endforeach; ?>
                </select>
                <button type="submit">Wybierz</button>
            </form>
            <table>
                <thead>
                    <tr>
                        <th scope="col">Rodzaj</th>
                        <th scope="col">Nazwa</th>
                        <th scope="col">Gramatura</th>
                        <th scope="col">Cena</th>
                    </tr>
                </thead>
                <tbody>
                    <?php foreach ($products as $product): ?>
                        <tr>
                            <td><?= escape($product['Rodzaj']) ?></td>
                            <td><?= escape($product['Nazwa']) ?></td>
                            <td><?= escape($product['Gramatura']) ?></td>
                            <td><?= escape($product['Cena']) ?></td>
                        </tr>
                    <?php endforeach; ?>
                </tbody>
            </table>
        </main>
        <footer>
            <p>AUTOR: Bartosz Bryniarski</p>
            <p>Data: <time datetime="2026-10-02">02.10.2026</time></p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (piekarnia.php)
  31. Krok 31: Zapamiętany wybór na liście

    ok. 2 min

    Dodatek spoza arkusza. Po wysłaniu formularza strona ładuje się od nowa i lista wracała do pierwszej opcji - nie było widać, którego rodzaju dotyczy tabela. Opcja równa $selectedType dostaje atrybut selected. Podgląd: po wysłaniu „PIECZYWO MIESZANE” lista pokazuje ten rodzaj.

    piekarnia.php PHP zmienione: 1 linia
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    $types = [];
    $products = [];
    $dbError = '';
    $selectedType = trim(filter_input(INPUT_POST, 'type') ?? '');
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=piekarnia;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // Skrypt 1: rodzaje wypieków do listy
        $types = $pdo->query(
            'SELECT DISTINCT Rodzaj FROM wyroby ORDER BY Rodzaj DESC'
        )->fetchAll(PDO::FETCH_COLUMN);
    
        // Skrypt 2: wyroby wybranego rodzaju
        if ($selectedType !== '' && in_array($selectedType, $types, true)) {
            $statement = $pdo->prepare(
                'SELECT Rodzaj, Nazwa, Gramatura, Cena FROM wyroby WHERE Rodzaj = :type'
            );
            $statement->execute(['type' => $selectedType]);
            $products = $statement->fetchAll();
            $statement = null;
        }
    
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać oferty. 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>PIEKARNIA</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <img src="wypieki.png" alt="Produkty naszej piekarni">
        <nav>
            <a href="kw1.png">KWERENDA1</a>
            <a href="kw2.png">KWERENDA2</a>
            <a href="kw3.png">KWERENDA3</a>
            <a href="kw4.png">KWERENDA4</a>
        </nav>
        <header>
            <h1>WITAMY</h1>
            <h4>NA STRONIE PIEKARNI</h4>
            <p>Od 31 lat oferujemy najwyższej jakości pieczywo. Naturalnie świeże, naturalnie smaczne. Pieczemy wyłącznie wypieki na naturalnym zakwasie bez polepszaczy i zagęstników. Korzystamy wyłącznie z najlepszych ziaren pochodzących z ekologicznych upraw położonych w rejonach zgierskim i ozorkowskim.</p>
        </header>
        <main>
            <h4 id="choose-type">Wybierz rodzaj wypieków:</h4>
            <form method="post">
                <select name="type" aria-labelledby="choose-type">
                    <?php foreach ($types as $type): ?>
                        <option value="<?= escape($type) ?>"<?= $type === $selectedType ? ' selected' : '' ?>><?= escape($type) ?></option>
                    <?php endforeach; ?>
                </select>
                <button type="submit">Wybierz</button>
            </form>
            <table>
                <thead>
                    <tr>
                        <th scope="col">Rodzaj</th>
                        <th scope="col">Nazwa</th>
                        <th scope="col">Gramatura</th>
                        <th scope="col">Cena</th>
                    </tr>
                </thead>
                <tbody>
                    <?php foreach ($products as $product): ?>
                        <tr>
                            <td><?= escape($product['Rodzaj']) ?></td>
                            <td><?= escape($product['Nazwa']) ?></td>
                            <td><?= escape($product['Gramatura']) ?></td>
                            <td><?= escape($product['Cena']) ?></td>
                        </tr>
                    <?php endforeach; ?>
                </tbody>
            </table>
        </main>
        <footer>
            <p>AUTOR: Bartosz Bryniarski</p>
            <p>Data: <time datetime="2026-10-02">02.10.2026</time></p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (piekarnia.php)
  32. Krok 32: Komunikaty: rodzaj spoza listy i awaria bazy

    ok. 3 min
    Polecenie z arkusza

    Na końcu jest zamykane połączenie z serwerem.

    Nad tabelą dwa komunikaty, każdy tylko wtedy, gdy jest co powiedzieć:

    • $formError - formularz przyszedł z wartością spoza białej listy. Sprawdzone uruchomieniem: wysłanie x' OR '1'='1 (w wersji podstawowej ta sama próba bez zabezpieczeń pokazała wszystkie 93 wyroby) daje komunikat „Nie ma takiego rodzaju wypieków.” i pustą tabelę, a <b>X</b> nie zamienia się w pogrubienie,
    • $dbError - awaria bazy; uruchomienie z błędną nazwą bazy pokazuje komunikat, pustą listę i tabelę z samym nagłówkiem, bez nazwy bazy i kodu błędu SQL na stronie.

    W normalnym działaniu połączenie jest zamykane ($pdo = null) zaraz po obu skryptach, przed pierwszą linią HTML. Podgląd: stan po wysłaniu „PIECZYWO MIESZANE”.

    piekarnia.php PHP zmienione: 9 linii
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    $types = [];
    $products = [];
    $dbError = '';
    $formError = '';
    $selectedType = trim(filter_input(INPUT_POST, 'type') ?? '');
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=piekarnia;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // Skrypt 1: rodzaje wypieków do listy
        $types = $pdo->query(
            'SELECT DISTINCT Rodzaj FROM wyroby ORDER BY Rodzaj DESC'
        )->fetchAll(PDO::FETCH_COLUMN);
    
        // Skrypt 2: wyroby wybranego rodzaju
        if ($selectedType !== '' && in_array($selectedType, $types, true)) {
            $statement = $pdo->prepare(
                'SELECT Rodzaj, Nazwa, Gramatura, Cena FROM wyroby WHERE Rodzaj = :type'
            );
            $statement->execute(['type' => $selectedType]);
            $products = $statement->fetchAll();
            $statement = null;
        } elseif ($selectedType !== '') {
            $formError = 'Nie ma takiego rodzaju wypieków.';
        }
    
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać oferty. 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>PIEKARNIA</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <img src="wypieki.png" alt="Produkty naszej piekarni">
        <nav>
            <a href="kw1.png">KWERENDA1</a>
            <a href="kw2.png">KWERENDA2</a>
            <a href="kw3.png">KWERENDA3</a>
            <a href="kw4.png">KWERENDA4</a>
        </nav>
        <header>
            <h1>WITAMY</h1>
            <h4>NA STRONIE PIEKARNI</h4>
            <p>Od 31 lat oferujemy najwyższej jakości pieczywo. Naturalnie świeże, naturalnie smaczne. Pieczemy wyłącznie wypieki na naturalnym zakwasie bez polepszaczy i zagęstników. Korzystamy wyłącznie z najlepszych ziaren pochodzących z ekologicznych upraw położonych w rejonach zgierskim i ozorkowskim.</p>
        </header>
        <main>
            <h4 id="choose-type">Wybierz rodzaj wypieków:</h4>
            <form method="post">
                <select name="type" aria-labelledby="choose-type">
                    <?php foreach ($types as $type): ?>
                        <option value="<?= escape($type) ?>"<?= $type === $selectedType ? ' selected' : '' ?>><?= escape($type) ?></option>
                    <?php endforeach; ?>
                </select>
                <button type="submit">Wybierz</button>
            </form>
            <?php if ($dbError !== ''): ?>
                <p><?= escape($dbError) ?></p>
            <?php endif; ?>
            <?php if ($formError !== ''): ?>
                <p><?= escape($formError) ?></p>
            <?php endif; ?>
            <table>
                <thead>
                    <tr>
                        <th scope="col">Rodzaj</th>
                        <th scope="col">Nazwa</th>
                        <th scope="col">Gramatura</th>
                        <th scope="col">Cena</th>
                    </tr>
                </thead>
                <tbody>
                    <?php foreach ($products as $product): ?>
                        <tr>
                            <td><?= escape($product['Rodzaj']) ?></td>
                            <td><?= escape($product['Nazwa']) ?></td>
                            <td><?= escape($product['Gramatura']) ?></td>
                            <td><?= escape($product['Cena']) ?></td>
                        </tr>
                    <?php endforeach; ?>
                </tbody>
            </table>
        </main>
        <footer>
            <p>AUTOR: Bartosz Bryniarski</p>
            <p>Data: <time datetime="2026-10-02">02.10.2026</time></p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (piekarnia.php)
  33. Etap 5: Porównanie z wersją podstawową

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

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

    ok. 2 min

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

    wersja podstawowawersja nowoczesna
    kolorywpisane w każdej regulezmienne CSS w :root
    marginesy ciała stronymargin: 0 20px (zeruje też górę i dół)margin-inline: 20px (jedynie lewy i prawy)
    lista rozwijanabez powiązania z nagłówkiemaria-labelledby na nagłówek, value w opcjach
    bazamysqli_connect, mysqli_fetch_row, $row[0]PDO z wyjątkami, FETCH_COLUMN, $product['Nazwa']
    wartość z formularza$_POST["type"] wklejone w zapytanie (luka pokazana i naprawiona w dobrych praktykach)filter_input + biała lista + prepare z :type od początku
    wybór po wysłaniulista wraca do pierwszej opcji (w dobrych praktykach selected)selected od razu
    zła wartość / awaria bazypusta tabela / biała strona z błędemkomunikat nad tabelą

    Na egzaminie wersja podstawowa trzyma się funkcji z tabeli 1 arkusza i jest szybsza do napisania, a klucz R.5.4 ocenia tylko to, że wartość z formularza trafia do zapytania - nie ocenia bezpieczeństwa. Nowoczesna nie ma luki, którą trzeba potem łatać. W prawdziwym projekcie doszłyby jeszcze dane logowania do bazy w osobnym pliku config.php (tu klucz szuka połączenia w kodzie strony).

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

  34. Etap 6: Oddanie pracy

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

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

    ok. 5 min
    Polecenie z arkusza

    Po zakończeniu pracy utwórz plik tekstowy o nazwie przeglądarka.txt. Zapisz w nim nazwę przeglądarki internetowej, w której weryfikowana była poprawność działania witryny. Umieść go w folderze z numerem zdającego.

    W przeglądarka.txt nazwa przeglądarki, w której strona była sprawdzana przez http://localhost/.... W folderze: piekarnia.php, styles.css, kwerendy.txt, import.png, kw1.png - kw4.png, wypieki.png i przeglądarka.txt. Nagraj płytę, sprawdź jej odczyt i opisz ją numerem zdającego.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

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

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.