Przejdź do głównej treści
INF.03-04-25.01-SGStyczeń 2025 · 41 kroków · ok. 78 min · 40 kryteriów z klucza

Rozwiązanie krok po kroku: Aplikacja internetowa hurtowni obuwia (Nowoczesny kod (PDO))

To drugi sposób rozwiązania tego samego arkusza: baza obuwie z czterema zapytaniami i dwie strony PHP - index.php z formularzem zamówienia i blokami produktów z bazy oraz zamow.php, która pokazuje wybrany but i liczy koszt. Obie wersje spełniają wszystkie 40 kryteriów klucza - różni je sposób pisania kodu, nie wynik.

Czym ta wersja różni się od podstawowej:

  • kolory w zmiennych CSS, bloki produktów jako <article> - każdy blok to samodzielna całość z obrazem, nazwą, modelem i ceną,
  • PHP na PDO z wyjątkami: zapytania na górze pliku, połączenie zamknięte, zanim zacznie się HTML, a w HTML tylko pętle foreach wypisujące gotowe dane,
  • zapytanie przygotowane na stronie zamówienia - model z formularza idzie do bazy jako parametr, a nie wklejony w treść zapytania,
  • filter_var z zakresami dla rozmiaru (40-43) i liczby par (co najmniej 1) - puste pole liczby par nie kończy się błędem krytycznym, tylko komunikatem,
  • htmlspecialchars przy każdym wypisaniu (krótka funkcja e()), kwota w polskim formacie przez number_format, bloki w kolejności z ilustracji 2 dzięki ORDER BY.

Kroki nadal idą zdanie po zdaniu za treścią arkusza. Podgląd w każdym kroku z PHP to wynik prawdziwego uruchomienia kodu z tego kroku w PHP 8.2 na bazie z arkusza; strona zamówienia pokazuje model T-02-44, rozmiar 42 i 4 pary - jak na ilustracji 3. Baza i zdjęcia pochodzą z prawdziwego archiwum pliki9.

Jak liczą się punkty. Klucz ma 40 kryteriów (R.1.1 - R.5.10), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 30 punktów. Przy każdym kroku jest napisane, które kryteria spełnia; egzaminator ocenia spełnienie kryteriów z klucza, nie zgodność z tym kodem.

  1. Etap 1: Baza danych

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

    Krok 1: Baza danych obuwie

    ok. 2 min
    Polecenie z arkusza

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

    Archiwum pliki9.7z ma hasło *HurTowni@Buty$ - z gwiazdką na początku i dolarem na końcu; zawiera obuwie.sql i zdjęcia but1.png - but4.png. W phpMyAdmin: Nowa, nazwa obuwie, porównywanie z polskimi znakami, np. utf8mb4_polish_ci albo utf8_unicode_ci z przykładu w arkuszu. Tabele z obuwie.sql mają porównywanie zapisane we własnej definicji (utf8_polish_ci), więc wybór dla bazy ich nie zmienia.

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

    ok. 2 min
    Polecenie z arkusza

    Do utworzonej bazy zaimportuj tabele z pliku obuwie.sql z rozpakowanego archiwum.

    Zaznaczona baza obuwie, Import → obuwie.sql → Wykonaj. Trzy tabele z ilustracji 1: kategorie, buty i produkt; buty i produkt łączy pole model.

  3. Krok 3: Zrzut ekranu import.jpg

    Kryteria:

    R.1.1 Wykonany import tabel do bazy danych obuwie , co jest udokumentowane w pliku import w formacie JPEG

    ok. 2 min
    Polecenie z arkusza

    Wykonaj zrzut ekranu po imporcie. Zapisz zrzut w formacie JPEG 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.

    Print Screen, wklejenie do edytora grafiki i zapis jako import.jpg - import w JPEG, zrzuty zapytań w PNG.

    Typowe błędy

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

  4. Krok 4: Zapytanie 1: modele z tabeli produkt

    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 pole model z tabeli produkt . W pliku z kwerendami lub na zrzucie istnieje zapis np. SELECT model FROM produkt; oraz wynik działania udokumentowany zrzutem, na którym widoczne są jedynie dane: O-23- 45, P-59-03, T-02-44, T-03-32

    ok. 1 min
    Polecenie z arkusza

    Zapytanie 1: wybierające jedynie pole model z tabeli produkt.

    Jedna kolumna, bez warunku. Wynik: O-23-45, P-59-03, T-02-44, T-03-32. Zapytanie trafia do kwerendy.txt, zrzut do kw1.png. Na stronie wypełni listę modeli (Skrypt 1) - tam z ORDER BY model, żeby kolejność opcji nie zależała od tego, jak baza akurat odczyta tabelę.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT model
    FROM produkt;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: buty ze zdjęciami

    Kryteria:

    R.1.4 Wykonane zapytanie 2 wybierające jedynie pola model, nazwa, cena z tabeli buty oraz odpowiadające im pole nazwa_pliku z tabeli produkt . W pliku z kwerendami lub na zrzucie istnieje zapis np. SELECT model, nazwa, cena, nazwa_pliku FROM buty JOIN produkt USING(model); (możliwe też porównanie kluczy po ON lub WHERE ) oraz wynik działania udokumentowany zrzutem, na którym widoczne są dokładnie 4 rekordy dla wymienionych w kryterium 1.3 modeli

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 2: wybierające jedynie pola model, nazwa, cena z tabeli buty oraz odpowiadające im pole nazwa_pliku z tabeli produkt. Należy posłużyć się relacją.

    Tabele z aliasami buty AS b i produkt AS p, każda kolumna z aliasem tabeli - pole model jest w obu tabelach, więc bez aliasu SELECT model przy złączeniu ON byłby niejednoznaczny.

    • JOIN produkt AS p ON p.model = b.model - warunek relacji zapisany wprost (klucz uznaje też USING(model)).

    Wynik: cztery wiersze, po jednym dla każdego modelu. Zrzut: kw2.png.

    Typowe błędy

    ON model = model bez nazw tabel kończy się błędem „Column 'model' in on clause is ambiguous”.

    kwerendy.txt Tekst zmienione: 5 linii
    -- Zapytanie 1
    SELECT model
    FROM produkt;
    
    -- Zapytanie 2
    SELECT b.model, b.nazwa, b.cena, p.nazwa_pliku
    FROM buty AS b
    JOIN produkt AS p ON p.model = b.model;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: szczegóły modelu P-59-03

    Kryteria:

    R.1.5 Wykonane zapytanie 3 wybierające jedynie pola nazwa, cena z tabeli buty i odpowiadające mu pola kolor, kod_produktu, material, nazwa_pliku z tabeli produkt dla modelu „P-59-03”. W pliku z kwerendami lub na zrzucie istnieje zapis np. SELECT nazwa, cena, kolor, kod_produktu, material, nazwa_pliku FROM buty JOIN produkt USING(model) WHERE model = "P-59-03"; (możliwe też porównanie kluczy po ON lub WHERE ) oraz wynik działania udokumentowany zrzutem, na którym jest widoczny jedynie rekord z danymi o nazwie PÓŁBUTY WIZYTOWE

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie pola nazwa, cena z tabeli buty i odpowiadające mu pola kolor, kod_produktu, material, nazwa_pliku z tabeli produkt dla modelu „P-59-03”. Należy posłużyć się relacją.

    To samo złączenie, sześć pól i WHERE b.model = 'P-59-03'. Wynik: jeden wiersz - PÓŁBUTY WIZYTOWE. Zrzut: kw3.png. Na stronie zamówienia w miejscu 'P-59-03' stanie parametr :model zapytania przygotowanego.

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    SELECT model
    FROM produkt;
    
    -- Zapytanie 2
    SELECT b.model, b.nazwa, b.cena, p.nazwa_pliku
    FROM buty AS b
    JOIN produkt AS p ON p.model = b.model;
    
    -- Zapytanie 3
    SELECT b.nazwa, b.cena, p.kolor, p.kod_produktu, p.material, p.nazwa_pliku
    FROM buty AS b
    JOIN produkt AS p ON p.model = b.model
    WHERE b.model = 'P-59-03';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: nowa kategoria Sandały

    Kryteria:

    R.1.6 Wykonane zapytanie 4 wstawiające rekord z nazwą kategorii „Sandały” do tabeli kategorie . Klucz główny generowany automatycznie - pominięty lub wartość NULL. W pliku z kwerendami lub na zrzucie istnieje zapis np. INSERT INTO kategorie VALUES (NULL, 'Sandały'); (możliwe inne warianty zapytania, klucz musi być dodany automatycznie ) oraz wynikiem działania jest widoczny komunikat o poprawnym wstawieniu rekordu lub tabela ze wstawionym rekordem

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 4: wstawiające rekord z nazwą kategorii „Sandały” do tabeli kategorie. Klucz główny generowany automatycznie.

    INSERT INTO kategorie (nazwa_kat) VALUES ('Sandały') - z listą kolumn: klucz główny pominięty, więc baza nada go sama (AUTO_INCREMENT). Lista kolumn sprawia, że zapytanie nie zależy od liczby i kolejności kolumn tabeli - po dodaniu kolumny wersja VALUES (NULL, 'Sandały') przestałaby działać. Nowy rekord dostaje id_kat = 6, bo obuwie.sql ustawia licznik AUTO_INCREMENT=6. Zrzut: kw4.png.

    Typowe błędy

    Klucz wpisany ręcznie (VALUES (5, 'Sandały')) przeczy wymaganiu „klucz generowany automatycznie”.

    kwerendy.txt Tekst zmienione: 4 linie
    -- Zapytanie 1
    SELECT model
    FROM produkt;
    
    -- Zapytanie 2
    SELECT b.model, b.nazwa, b.cena, p.nazwa_pliku
    FROM buty AS b
    JOIN produkt AS p ON p.model = b.model;
    
    -- Zapytanie 3
    SELECT b.nazwa, b.cena, p.kolor, p.kod_produktu, p.material, p.nazwa_pliku
    FROM buty AS b
    JOIN produkt AS p ON p.model = b.model
    WHERE b.model = 'P-59-03';
    
    -- Zapytanie 4
    INSERT INTO kategorie (nazwa_kat)
    VALUES ('Sandały');
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Strony HTML

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

    Krok 8: Plik index.php w HTML5

    Kryteria:

    R.2.1 Witryna internetowa zapisana w plikach index.php i zamow.php oraz zapisany jawnie standard kodowania polskich znaków, deklaracja HTML5 <!DOCTYPE html>, zadeklarowany język witryny: polski np. <html lang="pl">. Strona zawiera sekcje head i body

    ok. 2 min
    Polecenie z arkusza

    Składa się z dwóch stron o nazwach index.php, zamow.php. Cechy wspólne dla obu stron: Zapisane w języku HTML5.

    Najpierw index.php, a zamow.php powstanie z jej kopii. Rozszerzenie .php, bo dojdą skrypty; na egzaminie pliki leżą w htdocs XAMPP-a i otwiera się je przez http://localhost/....

    index.php PHP nowy plik
    <!DOCTYPE html>
    <html>
    <head>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  9. Krok 9: Język strony

    Kryteria:

    R.2.1 Witryna internetowa zapisana w plikach index.php i zamow.php oraz zapisany jawnie standard kodowania polskich znaków, deklaracja HTML5 <!DOCTYPE html>, zadeklarowany język witryny: polski np. <html lang="pl">. Strona zawiera sekcje head i body

    ok. 1 min
    Polecenie z arkusza

    Zadeklarowany polski język zawartości witryny.

    lang="pl" w <html>.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  10. Krok 10: Kodowanie polskich znaków

    Kryteria:

    R.2.1 Witryna internetowa zapisana w plikach index.php i zamow.php oraz zapisany jawnie standard kodowania polskich znaków, deklaracja HTML5 <!DOCTYPE html>, zadeklarowany język witryny: polski np. <html lang="pl">. Strona zawiera sekcje head i body

    ok. 1 min
    Polecenie z arkusza

    Jawnie zastosowany właściwy standard kodowania polskich znaków.

    <meta charset="UTF-8"> jako pierwszy element <head>.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  11. Krok 11: Tytuł strony i viewport

    Kryteria:

    R.2.2 Nadany stronie tytuł: "Obuwie"

    ok. 1 min
    Polecenie z arkusza

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

    <title> - napis na karcie (R.2.2). <meta name="viewport"> każe telefonowi pokazać stronę w jego szerokości - arkusz tego nie wymaga.

    index.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>Obuwie</title>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  12. Krok 12: Podpięcie arkusza stylów

    Kryteria:

    R.3.6 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie style.css formatowanie 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

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

    Nazwa: style.css. <link> bez ukośnika /> (HTML5).

    index.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>Obuwie</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  13. Krok 13: Bloki: nagłówek, blok główny, stopka

    Kryteria:

    R.2.3 Zdefiniowany układ strony dzielący ją na bloki: 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 uzycie bloków div lub section

    ok. 1 min
    Polecenie z arkusza

    Podział strony na bloki: nagłówkowy, poniżej główny, na dole stopka. 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.

    <header>, <main>, <footer> (R.2.3). Bloki stoją jeden pod drugim - zwykły układ dokumentu, bez siatki i float.

    index.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>Obuwie</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
        </header>
        <main>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  14. Krok 14: Nagłówek strony

    Kryteria:

    R.2.4 W bloku nagłówkowym umieszczony <h1>: "Obuwie męskie", w stopce umieszczony paragraf z numerem zdającego oraz na stronie zamówień umieszczony w bloku głównym <h2>: "Zamówienie"

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku nagłówkowego: nagłówek pierwszego stopnia o treści „Obuwie męskie”.

    <h1> w <header>.

    index.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>Obuwie</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Obuwie męskie</h1>
        </header>
        <main>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  15. Krok 15: Stopka

    Kryteria:

    R.2.4 W bloku nagłówkowym umieszczony <h1>: "Obuwie męskie", w stopce umieszczony paragraf z numerem zdającego oraz na stronie zamówień umieszczony w bloku głównym <h2>: "Zamówienie"

    ok. 1 min
    Polecenie z arkusza

    Zawartość stopki: paragraf o treści: „Autor strony: ”, dalej wstawiony numer zdającego.

    Akapit w <footer>. Na egzaminie po dwukropku wpisuje się swój numer zdającego (PESEL) - tutaj stoi imię i nazwisko autora rozwiązania.

    index.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>Obuwie</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Obuwie męskie</h1>
        </header>
        <main>
        </main>
        <footer>
            <p>Autor strony: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  16. Krok 16: Formularz wysyłany metodą POST do zamow.php

    Kryteria: ,

    R.2.5 Na stronie głównej w bloku głównym umieszczony formularz. Zastosowany znacznik <form>

    R.3.2 Dane z formularza przesyłane są za pomocą metody POST do strony zamow.php

    ok. 2 min
    Polecenie z arkusza

    Zawartość bloku głównego strony index.php: Formularz wysyłający dane metodą bezpieczną do pliku zamow.php.

    method="post" - „metoda bezpieczna” w rozumieniu arkusza: dane jadą w treści żądania, a nie w adresie. W specyfikacji HTTP słowo „bezpieczna” znaczy co innego (metoda, która niczego nie zmienia na serwerze, czyli GET), ale klucz wymaga POST - i przy zamówieniu to dobry wybór: dane zamówienia nie powinny lądować w historii przeglądarki. action="zamow.php" wysyła je do drugiej strony (R.2.5, R.3.2).

    index.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>Obuwie</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Obuwie męskie</h1>
        </header>
        <main>
            <form action="zamow.php" method="post">
            </form>
        </main>
        <footer>
            <p>Autor strony: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  17. Krok 17: Lista modeli (na razie pusta)

    Kryteria: ,

    R.2.6 W formularzu umieszczona przynajmniej jedna lista rozwijalna - znaczniki <select> i <option>, pole edycyjne oraz przycisk "Zamów" oraz przynajmniej z jednym polem powiązana etykieta <label>

    R.3.4 Wszystkie elementy formularza mają przypisaną klasę kontrolki oraz przynajmniej jeden blok produktu ma przypisaną klasę buty

    ok. 2 min
    Polecenie z arkusza

    Listy rozwijanej, poprzedzonej etykietą „Model: ”, wypełnionej przez skrypt 1.

    Etykieta <label for="model"> powiązana z listą. name="model" - pod tą nazwą model trafi do $_POST. Klasa kontrolki od razu, bo arkusz przypisuje ją wszystkim elementom formularza (R.3.4). Opcje wypisze Skrypt 1.

    index.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>Obuwie</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Obuwie męskie</h1>
        </header>
        <main>
            <form action="zamow.php" method="post">
                <label for="model">Model: </label>
                <select id="model" name="model" class="kontrolki">
                </select>
            </form>
        </main>
        <footer>
            <p>Autor strony: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  18. Krok 18: Lista rozmiarów 40 - 43

    Kryteria: ,

    R.2.6 W formularzu umieszczona przynajmniej jedna lista rozwijalna - znaczniki <select> i <option>, pole edycyjne oraz przycisk "Zamów" oraz przynajmniej z jednym polem powiązana etykieta <label>

    R.3.4 Wszystkie elementy formularza mają przypisaną klasę kontrolki oraz przynajmniej jeden blok produktu ma przypisaną klasę buty

    ok. 2 min
    Polecenie z arkusza

    Listy rozwijanej, poprzedzonej etykietą: „Rozmiar: ”, wypełnionej liczbami od 40 do 43.

    Cztery opcje; bez atrybutu value wartością jest tekst opcji (R.2.6).

    index.php PHP zmienione: 7 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Obuwie</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Obuwie męskie</h1>
        </header>
        <main>
            <form action="zamow.php" method="post">
                <label for="model">Model: </label>
                <select id="model" name="model" class="kontrolki">
                </select>
                <label for="size">Rozmiar: </label>
                <select id="size" name="size" class="kontrolki">
                    <option>40</option>
                    <option>41</option>
                    <option>42</option>
                    <option>43</option>
                </select>
            </form>
        </main>
        <footer>
            <p>Autor strony: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  19. Krok 19: Pole liczby par

    Kryteria: , ,

    R.2.6 W formularzu umieszczona przynajmniej jedna lista rozwijalna - znaczniki <select> i <option>, pole edycyjne oraz przycisk "Zamów" oraz przynajmniej z jednym polem powiązana etykieta <label>

    R.3.3 Przycisk realizuje funkcję wysłania danych z formularza (input lub button z typem submit lub button bez typu) oraz przynajmniej jedno pole edycyjne jest typu number

    R.3.4 Wszystkie elementy formularza mają przypisaną klasę kontrolki oraz przynajmniej jeden blok produktu ma przypisaną klasę buty

    ok. 1 min
    Polecenie z arkusza

    Pola edycyjnego przeznaczonego do wpisywania wartości liczbowych, poprzedzonego etykietą: „Liczba par: ”.

    type="number" (R.3.3) z min="1" i required: przeglądarka nie wyśle formularza z pustym polem ani z zerem. To wygoda dla uczciwego użytkownika - żądanie da się wysłać z pominięciem formularza, więc zamow.php i tak sprawdzi liczbę par po swojej stronie.

    index.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>Obuwie</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Obuwie męskie</h1>
        </header>
        <main>
            <form action="zamow.php" method="post">
                <label for="model">Model: </label>
                <select id="model" name="model" class="kontrolki">
                </select>
                <label for="size">Rozmiar: </label>
                <select id="size" name="size" class="kontrolki">
                    <option>40</option>
                    <option>41</option>
                    <option>42</option>
                    <option>43</option>
                </select>
                <label for="pairs">Liczba par: </label>
                <input type="number" id="pairs" name="pairs" class="kontrolki" min="1" required>
            </form>
        </main>
        <footer>
            <p>Autor strony: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  20. Krok 20: Przycisk „Zamów”

    Kryteria: , ,

    R.2.6 W formularzu umieszczona przynajmniej jedna lista rozwijalna - znaczniki <select> i <option>, pole edycyjne oraz przycisk "Zamów" oraz przynajmniej z jednym polem powiązana etykieta <label>

    R.3.3 Przycisk realizuje funkcję wysłania danych z formularza (input lub button z typem submit lub button bez typu) oraz przynajmniej jedno pole edycyjne jest typu number

    R.3.4 Wszystkie elementy formularza mają przypisaną klasę kontrolki oraz przynajmniej jeden blok produktu ma przypisaną klasę buty

    ok. 1 min
    Polecenie z arkusza

    Przycisku o treści „Zamów”, który wysyła dane z formularza.

    <button type="submit"> z klasą kontrolki (R.3.3, R.3.4).

    index.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>Obuwie</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Obuwie męskie</h1>
        </header>
        <main>
            <form action="zamow.php" method="post">
                <label for="model">Model: </label>
                <select id="model" name="model" class="kontrolki">
                </select>
                <label for="size">Rozmiar: </label>
                <select id="size" name="size" class="kontrolki">
                    <option>40</option>
                    <option>41</option>
                    <option>42</option>
                    <option>43</option>
                </select>
                <label for="pairs">Liczba par: </label>
                <input type="number" id="pairs" name="pairs" class="kontrolki" min="1" required>
                <button type="submit" class="kontrolki">Zamów</button>
            </form>
        </main>
        <footer>
            <p>Autor strony: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  21. Krok 21: Strona zamow.php: nagłówek i powrót

    Kryteria: ,

    R.2.4 W bloku nagłówkowym umieszczony <h1>: "Obuwie męskie", w stopce umieszczony paragraf z numerem zdającego oraz na stronie zamówień umieszczony w bloku głównym <h2>: "Zamówienie"

    R.3.5 Na stronie zamówień jest umieszczony odsyłacz "Strona główna", którego wybranie otwiera stronę główną

    ok. 2 min
    Polecenie z arkusza

    Zawartość bloku głównego strony zamow.php: Nagłówek drugiego stopnia o treści „Zamówienie”. Odnośnik „Strona główna” prowadzący do strony index.php.

    Kopia jako zamow.php: w bloku głównym <h2> i odnośnik do strony głównej (R.2.4, R.3.5). Między nimi staną dane zamówienia.

    zamow.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>Obuwie</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Obuwie męskie</h1>
        </header>
        <main>
            <h2>Zamówienie</h2>
            <a href="index.php">Strona główna</a>
        </main>
        <footer>
            <p>Autor strony: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zamow.php)
  22. Etap 3: Styl CSS

    Kroki 22-28 · po tym etapie masz 27 z 40 kryteriów (pkt)

    Krok 22: Zmienne z kolorami i krój czcionki

    Kryteria:

    R.4.1 Ustawiony styl domyślny dla wszystkich selektorów (*): krój czcionki Cambria

    ok. 1 min
    Polecenie z arkusza

    Domyślnie, dla wszystkich selektorów: krój czcionki Cambria.

    Brąz Sienna występuje w arkuszu w czterech miejscach (nagłówek, stopka, ramka bloku, pasek ceny) - w zmiennej jest wpisany raz. Selektor * ustawia krój Cambria (R.4.1).

    style.css CSS nowy plik
    :root {
        --color-brown: Sienna;
        --color-cream: AntiqueWhite;
    }
    * {
        font-family: Cambria;
    }
    Podgląd strony po tym kroku (index.php)
  23. Krok 23: Nagłówek i stopka

    Kryteria: , , ,

    R.4.2 Ustawiony kolor tła Sienna dla bloku nagłówkowego, stopki i selektora h4, AntiqueWhite dla bloku głównego

    R.4.3 Ustawiony biały kolor czcionki dla bloku nagłówkowego, stopki i nagłówka czwartego stopnia

    R.4.6 Ustawione marginesy wewnętrzne (padding) 5px dla bloku nagłówkowego i stopki oraz margines zewnętrzny prawy (margin-right) 30px dla selektora img

    R.4.7 Ustawione wyrównanie tekstu do środka dla bloku nagłówkowego i stopki oraz do prawej dla nagłówka czwartego stopnia

    ok. 1 min
    Polecenie z arkusza

    Wspólne dla bloku nagłówkowego i stopki: kolor tła Sienna, biały kolor czcionki, marginesy wewnętrzne 5 px, wyrównanie tekstu do środka.

    Jedna reguła dla dwóch selektorów.

    style.css CSS zmienione: 6 linii
    :root {
        --color-brown: Sienna;
        --color-cream: AntiqueWhite;
    }
    * {
        font-family: Cambria;
    }
    header, footer {
        background-color: var(--color-brown);
        color: white;
        padding: 5px;
        text-align: center;
    }
    Podgląd strony po tym kroku (index.php)
  24. Krok 24: Blok główny: wysokość i paski przewijania

    Kryteria: , ,

    R.4.2 Ustawiony kolor tła Sienna dla bloku nagłówkowego, stopki i selektora h4, AntiqueWhite dla bloku głównego

    R.4.4 Ustawiony dla bloku głównego rozmiar czcionki 120% oraz paski przewijania na auto

    R.4.5 Ustawiona wysokość 700px dla bloku głównego

    ok. 2 min
    Polecenie z arkusza

    Dla bloku głównego: kolor tła AntiqueWhite, wysokość 700 px, rozmiar czcionki 120%, paski przewijania pojawiające się w przypadku gdy zawartość nie mieści się w bloku.

    overflow: auto - pasek przewijania tylko wtedy, gdy treść jest wyższa niż 700 px (R.4.2, R.4.4, R.4.5).

    style.css CSS zmienione: 6 linii
    :root {
        --color-brown: Sienna;
        --color-cream: AntiqueWhite;
    }
    * {
        font-family: Cambria;
    }
    header, footer {
        background-color: var(--color-brown);
        color: white;
        padding: 5px;
        text-align: center;
    }
    main {
        background-color: var(--color-cream);
        height: 700px;
        font-size: 120%;
        overflow: auto;
    }
    Podgląd strony po tym kroku (index.php)
  25. Krok 25: Klasa kontrolki

    Kryteria:

    R.4.8 Zdefiniowana klasa kontrolki o cechach: szerokość 150px, marginesy zewnętrzne 10px

    ok. 1 min
    Polecenie z arkusza

    Zdefiniowana klasa kontrolki o cechach: szerokość 150 px, marginesy zewnętrzne 10 px. Klasa jest przypisana do wszystkich elementów formularza (listy rozwijane, pole edycyjne, przycisk).

    Selektor klasy z kropką (R.4.8).

    style.css CSS zmienione: 4 linie
    :root {
        --color-brown: Sienna;
        --color-cream: AntiqueWhite;
    }
    * {
        font-family: Cambria;
    }
    header, footer {
        background-color: var(--color-brown);
        color: white;
        padding: 5px;
        text-align: center;
    }
    main {
        background-color: var(--color-cream);
        height: 700px;
        font-size: 120%;
        overflow: auto;
    }
    .kontrolki {
        width: 150px;
        margin: 10px;
    }
    Podgląd strony po tym kroku (index.php)
  26. Krok 26: Klasa buty i kolor pod kursorem

    Kryteria: ,

    R.4.9 Zdefiniowana klasa buty o cechach: szerokość 70%, marginesy zewnętrzne 15px, marginesy wewnętrzne 7px, obramowanie linią ciągłą o szerokości 1px i kolorze Sienna, zaokrąglenie rogów 30px

    R.4.10 Gdy kursor znajdzie się na bloku produktu jego kolor tła zmienia się na Linen np. dla .buty:hover

    ok. 2 min
    Polecenie z arkusza

    Zdefiniowana klasa buty o cechach: szerokość 70%, marginesy zewnętrzne 15 px, marginesy wewnętrzne 7 px, obramowanie linią ciągłą o szerokości 1 px i kolorze Sienna, zaokrąglenie rogów 30 px. Gdy kursor znajdzie się na bloku produktu (klasa buty) jego kolor tła zmienia się na Linen.

    Styl bloków, które wypisze Skrypt 2 (R.4.9, R.4.10). Jedna linia ponad arkusz: display: flow-root. Obraz w bloku będzie pływał (float), a pływający element nie wlicza się do wysokości rodzica - gdyby obraz był wyższy niż tekst obok, wystawałby poza ramkę bloku na następny produkt. flow-root każe blokowi objąć pływające dzieci; dawniej robiło się to sztuczką clearfix z pseudoelementem ::after.

    style.css CSS zmienione: 11 linii
    :root {
        --color-brown: Sienna;
        --color-cream: AntiqueWhite;
    }
    * {
        font-family: Cambria;
    }
    header, footer {
        background-color: var(--color-brown);
        color: white;
        padding: 5px;
        text-align: center;
    }
    main {
        background-color: var(--color-cream);
        height: 700px;
        font-size: 120%;
        overflow: auto;
    }
    .kontrolki {
        width: 150px;
        margin: 10px;
    }
    .buty {
        width: 70%;
        margin: 15px;
        padding: 7px;
        border: 1px solid var(--color-brown);
        border-radius: 30px;
        display: flow-root;
    }
    .buty:hover {
        background-color: Linen;
    }
    Podgląd strony po tym kroku (index.php)
  27. Krok 27: Obraz: po lewej, opływany treścią

    Kryteria:

    R.4.6 Ustawione marginesy wewnętrzne (padding) 5px dla bloku nagłówkowego i stopki oraz margines zewnętrzny prawy (margin-right) 30px dla selektora img

    ok. 1 min
    Polecenie z arkusza

    Dla selektora obrazu: opływany po prawej stronie zawartością (obraz po lewej stronie), margines zewnętrzny prawy 30 px.

    float: left zostaje - arkusz opisuje dokładnie opływanie, a klucz R.3.1 sprawdza, czy obrazy są opływane zawartością. Do takiego zadania float jest właściwym narzędziem; siatka czy flex ustawiłyby obraz i nagłówki w kolumnach, ale tekst nie opływałby obrazu (R.4.6). Selektor znacznika img, jak wymaga uwaga arkusza.

    style.css CSS zmienione: 4 linie
    :root {
        --color-brown: Sienna;
        --color-cream: AntiqueWhite;
    }
    * {
        font-family: Cambria;
    }
    header, footer {
        background-color: var(--color-brown);
        color: white;
        padding: 5px;
        text-align: center;
    }
    main {
        background-color: var(--color-cream);
        height: 700px;
        font-size: 120%;
        overflow: auto;
    }
    .kontrolki {
        width: 150px;
        margin: 10px;
    }
    .buty {
        width: 70%;
        margin: 15px;
        padding: 7px;
        border: 1px solid var(--color-brown);
        border-radius: 30px;
        display: flow-root;
    }
    .buty:hover {
        background-color: Linen;
    }
    img {
        float: left;
        margin-right: 30px;
    }
    Podgląd strony po tym kroku (index.php)
  28. Krok 28: Nagłówek czwartego stopnia: pasek z ceną

    Kryteria: , ,

    R.4.2 Ustawiony kolor tła Sienna dla bloku nagłówkowego, stopki i selektora h4, AntiqueWhite dla bloku głównego

    R.4.3 Ustawiony biały kolor czcionki dla bloku nagłówkowego, stopki i nagłówka czwartego stopnia

    R.4.7 Ustawione wyrównanie tekstu do środka dla bloku nagłówkowego i stopki oraz do prawej dla nagłówka czwartego stopnia

    ok. 1 min
    Polecenie z arkusza

    Dla selektora nagłówka czwartego stopnia: kolor tła Sienna, biały kolor czcionki, wyrównanie tekstu do prawej.

    W <h4> Skrypt 2 wypisze cenę - brązowy pasek przy prawej krawędzi bloku (R.4.2, R.4.3, R.4.7).

    style.css CSS zmienione: 5 linii
    :root {
        --color-brown: Sienna;
        --color-cream: AntiqueWhite;
    }
    * {
        font-family: Cambria;
    }
    header, footer {
        background-color: var(--color-brown);
        color: white;
        padding: 5px;
        text-align: center;
    }
    main {
        background-color: var(--color-cream);
        height: 700px;
        font-size: 120%;
        overflow: auto;
    }
    .kontrolki {
        width: 150px;
        margin: 10px;
    }
    .buty {
        width: 70%;
        margin: 15px;
        padding: 7px;
        border: 1px solid var(--color-brown);
        border-radius: 30px;
        display: flow-root;
    }
    .buty:hover {
        background-color: Linen;
    }
    img {
        float: left;
        margin-right: 30px;
    }
    h4 {
        background-color: var(--color-brown);
        color: white;
        text-align: right;
    }
    Podgląd strony po tym kroku (index.php)
  29. Etap 4: Skrypty PHP

    Kroki 29-38 · po tym etapie masz 40 z 40 kryteriów (pkt)

    Krok 29: index.php: połączenie PDO na górze pliku

    Kryteria: , ,

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

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

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

    ok. 3 min
    Polecenie z arkusza

    Łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie obuwie. Na końcu zamykane jest połączenie z serwerem.

    Praca z bazą trafia przed <!DOCTYPE html>, a HTML tylko wypisze gotowe dane:

    • funkcja e() - skrót od htmlspecialchars(..., ENT_QUOTES, 'UTF-8') do wypisywania danych,
    • new PDO(...) łączy się z serwerem i wybiera bazę obuwie (R.5.1, R.5.2); ERRMODE_EXCEPTION - błąd bazy rzuca wyjątek, FETCH_ASSOC - wiersze jako tablice z nazwami kolumn,
    • $pdo = null zamyka połączenie - ostatnia operacja na bazie, jeszcze przed HTML-em (R.5.3),
    • try/catch łapie awarię bazy: szczegóły do dziennika serwera, a pod formularzem krótki komunikat dla klienta.

    Podgląd od tego kroku to wynik prawdziwego uruchomienia strony w PHP 8.2.

    index.php PHP zmienione: 23 linie
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $models = [];
    $error = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=obuwie;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());
        $error = 'Nie udało się odczytać oferty z bazy - spróbuj za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Obuwie</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Obuwie męskie</h1>
        </header>
        <main>
            <form action="zamow.php" method="post">
                <label for="model">Model: </label>
                <select id="model" name="model" class="kontrolki">
                </select>
                <label for="size">Rozmiar: </label>
                <select id="size" name="size" class="kontrolki">
                    <option>40</option>
                    <option>41</option>
                    <option>42</option>
                    <option>43</option>
                </select>
                <label for="pairs">Liczba par: </label>
                <input type="number" id="pairs" name="pairs" class="kontrolki" min="1" required>
                <button type="submit" class="kontrolki">Zamów</button>
            </form>
            <?php if ($error !== ''): ?>
                <p><?= e($error) ?></p>
            <?php endif; ?>
        </main>
        <footer>
            <p>Autor strony: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  30. Krok 30: Skrypt 1: modele w liście rozwijanej

    Kryteria: ,

    R.5.4 Na stronie głównej skrypt 1 zawiera instrukcję wysyłającą do bazy zapytanie 1 lub skrypt 2 - zapytanie 2

    R.5.6 Skrypt 1 wyświetla w liście rozwijalnej nazwy modeli pobrane z bazy

    ok. 3 min
    Polecenie z arkusza

    Skrypt 1 w pliku index.php wysyła do bazy danych zapytanie 1 i wyświetla pobrane zapytaniem wartości jako elementy listy rozwijanej z modelami obuwia.

    • $pdo->query(...) - zapytanie 1 bez żadnych danych od użytkownika, więc nie trzeba go przygotowywać; ORDER BY model ustala kolejność opcji (R.5.4),
    • fetchAll(PDO::FETCH_COLUMN) - od razu prosta lista modeli zamiast tablicy wierszy,
    • w <select> pętla foreach w składni dla szablonów (foreach (...): ... endforeach;) wypisuje <option> dla każdego modelu, przez e().

    Rozwiń listę „Model” w podglądzie: O-23-45, P-59-03, T-02-44, T-03-32 - jak na ilustracji 2 (R.5.6).

    index.php PHP zmienione: 5 linii
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $models = [];
    $error = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=obuwie;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        // Skrypt 1: modele do listy
        $models = $pdo->query('SELECT model FROM produkt ORDER BY model')->fetchAll(PDO::FETCH_COLUMN);
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $error = 'Nie udało się odczytać oferty z bazy - spróbuj za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Obuwie</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Obuwie męskie</h1>
        </header>
        <main>
            <form action="zamow.php" method="post">
                <label for="model">Model: </label>
                <select id="model" name="model" class="kontrolki">
                    <?php foreach ($models as $model): ?>
                        <option><?= e($model) ?></option>
                    <?php endforeach; ?>
                </select>
                <label for="size">Rozmiar: </label>
                <select id="size" name="size" class="kontrolki">
                    <option>40</option>
                    <option>41</option>
                    <option>42</option>
                    <option>43</option>
                </select>
                <label for="pairs">Liczba par: </label>
                <input type="number" id="pairs" name="pairs" class="kontrolki" min="1" required>
                <button type="submit" class="kontrolki">Zamów</button>
            </form>
            <?php if ($error !== ''): ?>
                <p><?= e($error) ?></p>
            <?php endif; ?>
        </main>
        <footer>
            <p>Autor strony: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  31. Krok 31: Skrypt 2: blok produktu ze zdjęciem

    Kryteria: ,

    R.2.7 W bloku głównym umieszczony obraz z tekstem alternatywnym "but męski" (w HTML lub skrypcie )

    R.3.4 Wszystkie elementy formularza mają przypisaną klasę kontrolki oraz przynajmniej jeden blok produktu ma przypisaną klasę buty

    ok. 3 min
    Polecenie z arkusza

    Skrypt 2 w pliku index.php wysyła do bazy danych zapytanie 2. Dla każdego zwróconego zapytaniem wiersza tworzy blok produktu, zawierający obraz o nazwie pliku pobranej zapytaniem i tekście alternatywnym „but męski”.

    Zapytanie 2 z ORDER BY p.kod_produktu - bloki stają w kolejności z ilustracji 2 (PÓŁBUTY WIZYTOWE pierwsze). Bez ORDER BY baza nie gwarantuje kolejności wierszy złączenia, a w wersji podstawowej pierwsze bywa OBUWIE TRENINGOWE.

    W HTML pętla foreach wypisuje dla każdego buta <article class="buty"> - samodzielny blok treści, jak wpis czy karta produktu - a w nim obraz z src z bazy (R.2.7, R.3.4). Atrybut alt jest stały, z arkusza; nazwa pliku idzie przez e(). Najedź na blok w podglądzie - tło zmienia się na Linen.

    index.php PHP zmienione: 12 linii
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $models = [];
    $shoes = [];
    $error = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=obuwie;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        // Skrypt 1: modele do listy
        $models = $pdo->query('SELECT model FROM produkt ORDER BY model')->fetchAll(PDO::FETCH_COLUMN);
        // Skrypt 2: bloki produktów
        $shoes = $pdo->query(
            'SELECT b.model, b.nazwa, b.cena, p.nazwa_pliku
             FROM buty AS b JOIN produkt AS p ON p.model = b.model
             ORDER BY p.kod_produktu'
        )->fetchAll();
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $error = 'Nie udało się odczytać oferty z bazy - spróbuj za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Obuwie</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Obuwie męskie</h1>
        </header>
        <main>
            <form action="zamow.php" method="post">
                <label for="model">Model: </label>
                <select id="model" name="model" class="kontrolki">
                    <?php foreach ($models as $model): ?>
                        <option><?= e($model) ?></option>
                    <?php endforeach; ?>
                </select>
                <label for="size">Rozmiar: </label>
                <select id="size" name="size" class="kontrolki">
                    <option>40</option>
                    <option>41</option>
                    <option>42</option>
                    <option>43</option>
                </select>
                <label for="pairs">Liczba par: </label>
                <input type="number" id="pairs" name="pairs" class="kontrolki" min="1" required>
                <button type="submit" class="kontrolki">Zamów</button>
            </form>
            <?php if ($error !== ''): ?>
                <p><?= e($error) ?></p>
            <?php endif; ?>
            <?php foreach ($shoes as $shoe): ?>
                <article class="buty">
                    <img src="<?= e($shoe['nazwa_pliku']) ?>" alt="but męski">
                </article>
            <?php endforeach; ?>
        </main>
        <footer>
            <p>Autor strony: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  32. Krok 32: Skrypt 2: nazwa, model i cena

    Kryteria: , ,

    R.2.8 Na stronie głównej w bloku głównym umieszczone <h4> i <h5> (w HTML lub skrypcie )

    R.5.7 Skrypt 2 dla każdego zwróconego zapytaniem rekordu danych wyświetla blok zawierający: obraz, którego źródło pobrane jest z bazy, nazwę produktu, model i cenę

    R.5.10 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

    Nagłówek drugiego stopnia zawierający zwróconą zapytaniem nazwę produktu. Nagłówek piątego stopnia o treści: „Model: <model>”, gdzie wartość w nawiasach oznacza dane pobrane zapytaniem. Nagłówek czwartego stopnia o treści: „Cena: <cena>”, gdzie wartość w nawiasach <> oznacza dane pobrane zapytaniem.

    Trzy nagłówki z danymi po nazwach kolumn - $shoe['nazwa'], $shoe['model'], $shoe['cena'] - zamiast numerów $row[1], $row[0], $row[2]. Kolejność poziomów <h2>, <h5>, <h4> narzuca arkusz, choć w zwykłym dokumencie nagłówki nie przeskakują poziomów (R.2.8, R.5.7, R.5.10).

    Cena wymaga number_format: od PHP 8.1 PDO oddaje kolumnę FLOAT jako liczbę, a nie napis, więc 188.00 z bazy wypisuje się jako „188”. number_format($shoe['cena'], 2, ',', ' ') daje zawsze dwa miejsca po przecinku, po polsku: „188,00”. mysqli z wersji podstawowej oddaje napis „188.00” wprost z bazy - stąd tam kropka.

    index.php PHP zmienione: 3 linie
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $models = [];
    $shoes = [];
    $error = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=obuwie;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        // Skrypt 1: modele do listy
        $models = $pdo->query('SELECT model FROM produkt ORDER BY model')->fetchAll(PDO::FETCH_COLUMN);
        // Skrypt 2: bloki produktów
        $shoes = $pdo->query(
            'SELECT b.model, b.nazwa, b.cena, p.nazwa_pliku
             FROM buty AS b JOIN produkt AS p ON p.model = b.model
             ORDER BY p.kod_produktu'
        )->fetchAll();
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $error = 'Nie udało się odczytać oferty z bazy - spróbuj za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Obuwie</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Obuwie męskie</h1>
        </header>
        <main>
            <form action="zamow.php" method="post">
                <label for="model">Model: </label>
                <select id="model" name="model" class="kontrolki">
                    <?php foreach ($models as $model): ?>
                        <option><?= e($model) ?></option>
                    <?php endforeach; ?>
                </select>
                <label for="size">Rozmiar: </label>
                <select id="size" name="size" class="kontrolki">
                    <option>40</option>
                    <option>41</option>
                    <option>42</option>
                    <option>43</option>
                </select>
                <label for="pairs">Liczba par: </label>
                <input type="number" id="pairs" name="pairs" class="kontrolki" min="1" required>
                <button type="submit" class="kontrolki">Zamów</button>
            </form>
            <?php if ($error !== ''): ?>
                <p><?= e($error) ?></p>
            <?php endif; ?>
            <?php foreach ($shoes as $shoe): ?>
                <article class="buty">
                    <img src="<?= e($shoe['nazwa_pliku']) ?>" alt="but męski">
                    <h2><?= e($shoe['nazwa']) ?></h2>
                    <h5>Model: <?= e($shoe['model']) ?></h5>
                    <h4>Cena: <?= number_format($shoe['cena'], 2, ',', ' ') ?></h4>
                </article>
            <?php endforeach; ?>
        </main>
        <footer>
            <p>Autor strony: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  33. Krok 33: Układ bloków zgodny z ilustracją 2

    Kryteria:

    R.3.1 Układ bloków po uruchomieniu w przeglądarce jest zgodny z ilustracją 2 w arkuszu egzaminacyjnym oraz obrazy są opływane zawartością po prawej stronie

    ok. 1 min
    Polecenie z arkusza

    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.

    Nagłówek, blok główny o wysokości 700 px z paskiem przewijania (cztery bloki się nie mieszczą) i stopka; w blokach zdjęcia po lewej, nazwy opływają je z prawej (R.3.1).

    index.php PHP zmienione: 0 linii
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $models = [];
    $shoes = [];
    $error = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=obuwie;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        // Skrypt 1: modele do listy
        $models = $pdo->query('SELECT model FROM produkt ORDER BY model')->fetchAll(PDO::FETCH_COLUMN);
        // Skrypt 2: bloki produktów
        $shoes = $pdo->query(
            'SELECT b.model, b.nazwa, b.cena, p.nazwa_pliku
             FROM buty AS b JOIN produkt AS p ON p.model = b.model
             ORDER BY p.kod_produktu'
        )->fetchAll();
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $error = 'Nie udało się odczytać oferty z bazy - spróbuj za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Obuwie</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Obuwie męskie</h1>
        </header>
        <main>
            <form action="zamow.php" method="post">
                <label for="model">Model: </label>
                <select id="model" name="model" class="kontrolki">
                    <?php foreach ($models as $model): ?>
                        <option><?= e($model) ?></option>
                    <?php endforeach; ?>
                </select>
                <label for="size">Rozmiar: </label>
                <select id="size" name="size" class="kontrolki">
                    <option>40</option>
                    <option>41</option>
                    <option>42</option>
                    <option>43</option>
                </select>
                <label for="pairs">Liczba par: </label>
                <input type="number" id="pairs" name="pairs" class="kontrolki" min="1" required>
                <button type="submit" class="kontrolki">Zamów</button>
            </form>
            <?php if ($error !== ''): ?>
                <p><?= e($error) ?></p>
            <?php endif; ?>
            <?php foreach ($shoes as $shoe): ?>
                <article class="buty">
                    <img src="<?= e($shoe['nazwa_pliku']) ?>" alt="but męski">
                    <h2><?= e($shoe['nazwa']) ?></h2>
                    <h5>Model: <?= e($shoe['model']) ?></h5>
                    <h4>Cena: <?= number_format($shoe['cena'], 2, ',', ' ') ?></h4>
                </article>
            <?php endforeach; ?>
        </main>
        <footer>
            <p>Autor strony: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

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

  34. Krok 34: zamow.php: połączenie PDO na górze pliku

    Kryteria: ,

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

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

    ok. 2 min
    Polecenie z arkusza

    Skrypt 3 w pliku zamow.php. Na końcu zamykane jest połączenie z serwerem.

    Ten sam wzór co na stronie głównej: e(), połączenie w try, $pdo = null na końcu i komunikat przy awarii bazy (R.5.2, R.5.3). Podgląd tej strony pokazuje stan po wysłaniu formularza z ilustracji 3: T-02-44, rozmiar 42, 4 pary.

    zamow.php PHP zmienione: 20 linii
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $shoe = false;
    $error = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=obuwie;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());
        $error = 'Nie udało się odczytać danych z bazy - spróbuj za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Obuwie</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Obuwie męskie</h1>
        </header>
        <main>
            <h2>Zamówienie</h2>
            <a href="index.php">Strona główna</a>
        </main>
        <footer>
            <p>Autor strony: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zamow.php)
  35. Krok 35: Skrypt 3: dane z formularza sprawdzone przy odczycie

    ok. 3 min
    Polecenie z arkusza

    Wykorzystuje dane wysłane z formularza.

    • $_POST['model'] ?? '' - ?? daje pusty napis, gdy strony nie otworzono z formularza,
    • filter_var(..., FILTER_VALIDATE_INT, ['options' => [...]]) zwraca liczbę całkowitą albo false: rozmiar musi mieścić się w 40-43, liczba par być co najmniej 1. „abc”, puste pole, 0 i -3 dają false.

    Wersja podstawowa mnoży cenę przez to, co przyszło - puste pole liczby par kończy się tam w PHP 8 błędem krytycznym „Unsupported operand types”.

    zamow.php PHP zmienione: 4 linie
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    // Skrypt 3: dane z formularza
    $model = $_POST['model'] ?? '';
    $size = filter_var($_POST['size'] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 40, 'max_range' => 43]]);
    $pairs = filter_var($_POST['pairs'] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
    $shoe = false;
    $error = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=obuwie;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());
        $error = 'Nie udało się odczytać danych z bazy - spróbuj za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Obuwie</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Obuwie męskie</h1>
        </header>
        <main>
            <h2>Zamówienie</h2>
            <a href="index.php">Strona główna</a>
        </main>
        <footer>
            <p>Autor strony: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

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

  36. Krok 36: Skrypt 3: zapytanie 3 przygotowane z modelem z formularza

    Kryteria:

    R.5.5 Na stronie zamówień skrypt 3 zawiera instrukcję wysyłającą do bazy zmodyfikowane zapytanie 3, w ten sposób, że w warunku sprawdzany jest model wybrany w formularzu

    ok. 3 min
    Polecenie z arkusza

    Wysyła zmodyfikowane zapytanie 3, w ten sposób, że w warunku sprawdzany jest model buta wybrany w formularzu.

    • prepare z parametrem :model w warunku - treść zapytania jest stała (R.5.5),
    • execute(['model' => $model]) wysyła model osobno; cokolwiek ktoś wpisze w żądanie, baza potraktuje to jako wartość do porównania, nigdy jako fragment SQL,
    • fetch() - jeden wiersz (tablica z nazwami kolumn) albo false, gdy takiego modelu nie ma,
    • $statement = null przed $pdo = null: połączenie PDO zamyka się dopiero, gdy znikną wszystkie odwołania do niego - także zapytanie przygotowane.
    zamow.php PHP zmienione: 8 linii
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    // Skrypt 3: dane z formularza
    $model = $_POST['model'] ?? '';
    $size = filter_var($_POST['size'] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 40, 'max_range' => 43]]);
    $pairs = filter_var($_POST['pairs'] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
    $shoe = false;
    $error = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=obuwie;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $statement = $pdo->prepare(
            'SELECT b.nazwa, b.cena, p.kolor, p.kod_produktu, p.material, p.nazwa_pliku
             FROM buty AS b JOIN produkt AS p ON p.model = b.model
             WHERE b.model = :model'
        );
        $statement->execute(['model' => $model]);
        $shoe = $statement->fetch();
        $statement = null;
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $error = 'Nie udało się odczytać danych z bazy - spróbuj za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Obuwie</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Obuwie męskie</h1>
        </header>
        <main>
            <h2>Zamówienie</h2>
            <a href="index.php">Strona główna</a>
        </main>
        <footer>
            <p>Autor strony: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

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

  37. Krok 37: Skrypt 3: zdjęcie i nazwa

    Kryteria:

    R.5.8 Skrypt 3 dla modelu buta pobranego z formularza wyświetla obraz, którego źródło pobrane jest z bazy oraz pozostałe informacje pobrane z bazy: nazwę produktu, kolor, materiał

    ok. 2 min
    Polecenie z arkusza

    Dane ze zwróconego zapytaniem wiersza są wyświetlane na stronie: obraz o nazwie pliku pobranej zapytaniem i tekście alternatywnym „but męski”, nagłówek drugiego stopnia z nazwą produktu pobraną z bazy.

    W HTML if ($shoe): ... endif; - obraz i nazwa tylko wtedy, gdy model znalazł się w bazie. Dla T-02-44: TRZEWIKI SZNUROWANE (R.5.8).

    zamow.php PHP zmienione: 4 linie
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    // Skrypt 3: dane z formularza
    $model = $_POST['model'] ?? '';
    $size = filter_var($_POST['size'] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 40, 'max_range' => 43]]);
    $pairs = filter_var($_POST['pairs'] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
    $shoe = false;
    $error = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=obuwie;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $statement = $pdo->prepare(
            'SELECT b.nazwa, b.cena, p.kolor, p.kod_produktu, p.material, p.nazwa_pliku
             FROM buty AS b JOIN produkt AS p ON p.model = b.model
             WHERE b.model = :model'
        );
        $statement->execute(['model' => $model]);
        $shoe = $statement->fetch();
        $statement = null;
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $error = 'Nie udało się odczytać danych z bazy - spróbuj za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Obuwie</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Obuwie męskie</h1>
        </header>
        <main>
            <h2>Zamówienie</h2>
            <?php if ($shoe): ?>
                <img src="<?= e($shoe['nazwa_pliku']) ?>" alt="but męski">
                <h2><?= e($shoe['nazwa']) ?></h2>
            <?php endif; ?>
            <a href="index.php">Strona główna</a>
        </main>
        <footer>
            <p>Autor strony: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zamow.php)
  38. Krok 38: Skrypt 3: koszt, szczegóły i rozmiar

    Kryteria: , ,

    R.5.8 Skrypt 3 dla modelu buta pobranego z formularza wyświetla obraz, którego źródło pobrane jest z bazy oraz pozostałe informacje pobrane z bazy: nazwę produktu, kolor, materiał

    R.5.9 Skrypt 3 wyświetla rozmiar buta oraz obliczony koszt jako wynik mnożenia ceny buta pobranej z bazy i liczby par pobranej z formularza

    R.5.10 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

    Paragraf o treści: „cena za <liczbaPar> par: <wartośćCałkowita> zł", gdzie <liczbaPar> oznacza wartość pobraną z formularza, a <wartośćCałkowita> oznacza obliczony koszt za podaną liczbę par wybranego buta. Paragraf o treści: ”Szczegóły produktu: <kolor>, <materiał>”, gdzie wartości w nawiasach <> oznaczają dane pobrane zapytaniem. Paragraf o treści: „Rozmiar: <rozmiar>”, gdzie wartość w nawiasach <> oznacza dane pobrane z formularza.

    • nad HTML-em: gdy modelu nie ma, a rozmiar albo liczba par są złe, $error dostaje komunikat; szablon pokazuje wtedy jego zamiast zamówienia,
    • koszt to cena z bazy razy liczba par z formularza, sformatowany number_format(..., 2, ',', ' '): „1 596,00 zł” - po polsku, z groszami (R.5.9),
    • kolor i materiał z bazy przez e(), rozmiar i liczba par bez niego: to już liczby całkowite z filter_var, nic poza cyframi w nich nie ma (R.5.8, R.5.10).

    Podgląd: TRZEWIKI SZNUROWANE, cena za 4 par: 1 596,00 zł, brązowy, skóra naturalna - zamsz, rozmiar 42 - jak na ilustracji 3.

    zamow.php PHP zmienione: 10 linii
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    // Skrypt 3: dane z formularza
    $model = $_POST['model'] ?? '';
    $size = filter_var($_POST['size'] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 40, 'max_range' => 43]]);
    $pairs = filter_var($_POST['pairs'] ?? null, FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
    $shoe = false;
    $error = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=obuwie;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $statement = $pdo->prepare(
            'SELECT b.nazwa, b.cena, p.kolor, p.kod_produktu, p.material, p.nazwa_pliku
             FROM buty AS b JOIN produkt AS p ON p.model = b.model
             WHERE b.model = :model'
        );
        $statement->execute(['model' => $model]);
        $shoe = $statement->fetch();
        $statement = null;
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $error = 'Nie udało się odczytać danych z bazy - spróbuj za chwilę.';
    }
    
    if ($error === '' && (!$shoe || $size === false || $pairs === false)) {
        $error = 'Nie udało się złożyć zamówienia - wróć na stronę główną i sprawdź model, rozmiar i liczbę par.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Obuwie</title>
        <link rel="stylesheet" href="style.css">
    </head>
    <body>
        <header>
            <h1>Obuwie męskie</h1>
        </header>
        <main>
            <h2>Zamówienie</h2>
            <?php if ($error === ''): ?>
                <img src="<?= e($shoe['nazwa_pliku']) ?>" alt="but męski">
                <h2><?= e($shoe['nazwa']) ?></h2>
                <p>cena za <?= $pairs ?> par: <?= number_format($shoe['cena'] * $pairs, 2, ',', ' ') ?> zł</p>
                <p>Szczegóły produktu: <?= e($shoe['kolor']) ?>, <?= e($shoe['material']) ?></p>
                <p>Rozmiar: <?= $size ?></p>
            <?php else: ?>
                <p><?= e($error) ?></p>
            <?php endif; ?>
            <a href="index.php">Strona główna</a>
        </main>
        <footer>
            <p>Autor strony: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zamow.php)
  39. Etap 5: Porównanie z wersją podstawową

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

    Krok 39: Co jest inaczej i dlaczego

    ok. 3 min
    Polecenie z arkusza

    Ocenie podlegać będzie 5 rezultatów: operacje na bazie danych, zawartość witryny internetowej, działanie witryny internetowej, styl CSS witryny internetowej oraz skrypt połączenia z bazą.

    Obie wersje dają te same punkty. Różnice:

    wersja podstawowata wersja
    bazamysqli_*, mysqli_fetch_rowPDO, wyjątki, FETCH_ASSOC
    kod PHPwpleciony w HTML w trzech miejscachzapytania na górze, w HTML tylko foreach i if
    zamówieniemodel wklejony w treść zapytaniazapytanie przygotowane z :model
    dane z formularza$_POST wprostfilter_var z zakresami, komunikat przy złych danych
    puste „Liczba par”błąd krytyczny PHP 8komunikat, link do strony głównej
    blok produktu<div class="buty"><article class="buty"> z flow-root
    kolejność blokówzależna od bazyORDER BY p.kod_produktu, jak na ilustracji 2
    kwota1596 zł1 596,00 zł

    Sprawdzone w PHP 8.2 na bazie z arkusza: zamówienie z ilustracji 3, puste pole liczby par, liczba ujemna, rozmiar 45, próba wstrzyknięcia SQL w modelu, znaczniki HTML w modelu, wejście bez formularza i wyłączona baza.

    Czego tu nie ma, a w sklepie by było: zapis zamówienia w bazie (wtedy po zapisie przekierowanie PRG, żeby odświeżenie strony nie dublowało zamówień) i token CSRF w formularzu. Arkusz zamówienia nie zapisuje - tylko je pokazuje.

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

  40. Krok 40: Którą wersję wybrać na egzaminie

    ok. 2 min
    Polecenie z arkusza

    Czas przeznaczony na wykonanie zadania wynosi 150 minut.

    • Wersja podstawowa, gdy liczy się czas: funkcje mysqli_* są w tabeli 1 arkusza, a każdy kawałek PHP stoi tam, gdzie widać jego efekt.
    • Ta wersja, gdy PDO pisze się z pamięci. Kodu jest podobnie dużo, a błędy łatwiej znaleźć: wyjątek wskazuje złe zapytanie.

    Jedno warto wziąć z tej wersji zawsze, także do mysqli: ?? '' przy $_POST i rzutowanie liczb - puste pole w formularzu nie zawali wtedy strony.

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

  41. Etap 6: Oddanie pracy

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

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

    ok. 5 min
    Polecenie z arkusza

    Po zakończeniu pracy utwórz plik tekstowy o nazwie przeglądarka.txt. Zapisz w nim nazwę przeglądarki internetowej, w której weryfikowana była poprawność działania witryny. Nagraj płytę z rezultatami pracy.

    W przeglądarka.txt nazwa przeglądarki, w której strony były sprawdzane przez http://localhost/….

    W folderze z numerem zdającego: index.php, zamow.php, style.css, kwerendy.txt, import.jpg, kw1.png - kw4.png, zdjęcia butów i przeglądarka.txt. Po nagraniu trzeba sprawdzić odczyt płyty i opisać ją numerem zdającego.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

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

Podgląd

To jedno z możliwych rozwiązań. Egzaminator ocenia spełnienie kryteriów z klucza odpowiedzi, a nie zgodność z tym kodem - inna, poprawna droga też daje punkty.

Autor rozwiązania: . Licencja CC BY-NC 4.0 - kopiowanie i przeróbki do celów niekomercyjnych, z podaniem autora i źródła (link do tej strony) (zasady wykorzystania). Treść arkusza i klucza oceniania pochodzi z CKE.