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.
Suwak i strzałki obok niego - albo ←→ na klawiaturze - przechodzą krok po kroku; wyjaśnienie, pliki i podgląd zmieniają się razem z krokiem.
Lista „Etap” przenosi od razu do wybranego etapu.
„Rozwiązuję samodzielnie” chowa rozwiązanie kroku - najpierw własna próba, potem odsłonięcie.
„Tryb prezentacji” pokazuje same kroki, bez menu strony - na rzutnik.
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.
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.
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).
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.txtTekstnowy plik
-- Zapytanie 1SELECT modelFROM produkt;
Podgląd strony po tym kroku
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.txtTekstzmienione: 5 linii
-- Zapytanie 1SELECT modelFROM produkt;-- Zapytanie 2SELECT b.model, b.nazwa, b.cena, p.nazwa_plikuFROM buty AS bJOIN produkt AS p ON p.model = b.model;
Podgląd strony po tym kroku
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.txtTekstzmienione: 6 linii
-- Zapytanie 1SELECT modelFROM produkt;-- Zapytanie 2SELECT b.model, b.nazwa, b.cena, p.nazwa_plikuFROM buty AS bJOIN produkt AS p ON p.model = b.model;-- Zapytanie 3SELECT b.nazwa, b.cena, p.kolor, p.kod_produktu, p.material, p.nazwa_plikuFROM buty AS bJOIN produkt AS p ON p.model = b.modelWHERE b.model = 'P-59-03';
Podgląd strony po tym kroku
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.
-- Zapytanie 1SELECT modelFROM produkt;-- Zapytanie 2SELECT b.model, b.nazwa, b.cena, p.nazwa_plikuFROM buty AS bJOIN produkt AS p ON p.model = b.model;-- Zapytanie 3SELECT b.nazwa, b.cena, p.kolor, p.kod_produktu, p.material, p.nazwa_plikuFROM buty AS bJOIN produkt AS p ON p.model = b.modelWHERE b.model = 'P-59-03';-- Zapytanie 4INSERT INTO kategorie (nazwa_kat)VALUES ('Sandały');
Podgląd strony po tym kroku
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/....
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
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>.
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.
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.
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”.
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.
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).
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.
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).
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.
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).
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.
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).
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.
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).
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).
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.
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.
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.
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.phpPHPzmienione: 5 linii
<?phpfunctione(string $text): string{returnhtmlspecialchars($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><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Obuwie</title> <linkrel="stylesheet"href="style.css"></head><body> <header> <h1>Obuwie męskie</h1> </header> <main> <formaction="zamow.php"method="post"> <labelfor="model">Model: </label> <selectid="model"name="model"class="kontrolki"><?phpforeach ($modelsas$model): ?> <option><?= e($model) ?></option><?phpendforeach; ?> </select> <labelfor="size">Rozmiar: </label> <selectid="size"name="size"class="kontrolki"> <option>40</option> <option>41</option> <option>42</option> <option>43</option> </select> <labelfor="pairs">Liczba par: </label> <inputtype="number"id="pairs"name="pairs"class="kontrolki"min="1"required> <buttontype="submit"class="kontrolki">Zamów</button> </form><?phpif ($error !== ''): ?> <p><?= e($error) ?></p><?phpendif; ?> </main> <footer> <p>Autor strony: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (index.php)
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.phpPHPzmienione: 12 linii
<?phpfunctione(string $text): string{returnhtmlspecialchars($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><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Obuwie</title> <linkrel="stylesheet"href="style.css"></head><body> <header> <h1>Obuwie męskie</h1> </header> <main> <formaction="zamow.php"method="post"> <labelfor="model">Model: </label> <selectid="model"name="model"class="kontrolki"><?phpforeach ($modelsas$model): ?> <option><?= e($model) ?></option><?phpendforeach; ?> </select> <labelfor="size">Rozmiar: </label> <selectid="size"name="size"class="kontrolki"> <option>40</option> <option>41</option> <option>42</option> <option>43</option> </select> <labelfor="pairs">Liczba par: </label> <inputtype="number"id="pairs"name="pairs"class="kontrolki"min="1"required> <buttontype="submit"class="kontrolki">Zamów</button> </form><?phpif ($error !== ''): ?> <p><?= e($error) ?></p><?phpendif; ?><?phpforeach ($shoesas$shoe): ?> <articleclass="buty"> <imgsrc="<?= e($shoe['nazwa_pliku']) ?>"alt="but męski"> </article><?phpendforeach; ?> </main> <footer> <p>Autor strony: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (index.php)
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.phpPHPzmienione: 3 linie
<?phpfunctione(string $text): string{returnhtmlspecialchars($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><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Obuwie</title> <linkrel="stylesheet"href="style.css"></head><body> <header> <h1>Obuwie męskie</h1> </header> <main> <formaction="zamow.php"method="post"> <labelfor="model">Model: </label> <selectid="model"name="model"class="kontrolki"><?phpforeach ($modelsas$model): ?> <option><?= e($model) ?></option><?phpendforeach; ?> </select> <labelfor="size">Rozmiar: </label> <selectid="size"name="size"class="kontrolki"> <option>40</option> <option>41</option> <option>42</option> <option>43</option> </select> <labelfor="pairs">Liczba par: </label> <inputtype="number"id="pairs"name="pairs"class="kontrolki"min="1"required> <buttontype="submit"class="kontrolki">Zamów</button> </form><?phpif ($error !== ''): ?> <p><?= e($error) ?></p><?phpendif; ?><?phpforeach ($shoesas$shoe): ?> <articleclass="buty"> <imgsrc="<?= 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><?phpendforeach; ?> </main> <footer> <p>Autor strony: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (index.php)
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.phpPHPzmienione: 0 linii
<?phpfunctione(string $text): string{returnhtmlspecialchars($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><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Obuwie</title> <linkrel="stylesheet"href="style.css"></head><body> <header> <h1>Obuwie męskie</h1> </header> <main> <formaction="zamow.php"method="post"> <labelfor="model">Model: </label> <selectid="model"name="model"class="kontrolki"><?phpforeach ($modelsas$model): ?> <option><?= e($model) ?></option><?phpendforeach; ?> </select> <labelfor="size">Rozmiar: </label> <selectid="size"name="size"class="kontrolki"> <option>40</option> <option>41</option> <option>42</option> <option>43</option> </select> <labelfor="pairs">Liczba par: </label> <inputtype="number"id="pairs"name="pairs"class="kontrolki"min="1"required> <buttontype="submit"class="kontrolki">Zamów</button> </form><?phpif ($error !== ''): ?> <p><?= e($error) ?></p><?phpendif; ?><?phpforeach ($shoesas$shoe): ?> <articleclass="buty"> <imgsrc="<?= 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><?phpendforeach; ?> </main> <footer> <p>Autor strony: Bartosz Bryniarski</p> </footer></body></html>
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.phpPHPzmienione: 20 linii
<?phpfunctione(string $text): string{returnhtmlspecialchars($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><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Obuwie</title> <linkrel="stylesheet"href="style.css"></head><body> <header> <h1>Obuwie męskie</h1> </header> <main> <h2>Zamówienie</h2> <ahref="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)
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”.
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.phpPHPzmienione: 8 linii
<?phpfunctione(string $text): string{returnhtmlspecialchars($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><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Obuwie</title> <linkrel="stylesheet"href="style.css"></head><body> <header> <h1>Obuwie męskie</h1> </header> <main> <h2>Zamówienie</h2> <ahref="index.php">Strona główna</a> </main> <footer> <p>Autor strony: Bartosz Bryniarski</p> </footer></body></html>
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.phpPHPzmienione: 4 linie
<?phpfunctione(string $text): string{returnhtmlspecialchars($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><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Obuwie</title> <linkrel="stylesheet"href="style.css"></head><body> <header> <h1>Obuwie męskie</h1> </header> <main> <h2>Zamówienie</h2><?phpif ($shoe): ?> <imgsrc="<?= e($shoe['nazwa_pliku']) ?>"alt="but męski"> <h2><?= e($shoe['nazwa']) ?></h2><?phpendif; ?> <ahref="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)
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.phpPHPzmienione: 10 linii
<?phpfunctione(string $text): string{returnhtmlspecialchars($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><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Obuwie</title> <linkrel="stylesheet"href="style.css"></head><body> <header> <h1>Obuwie męskie</h1> </header> <main> <h2>Zamówienie</h2><?phpif ($error === ''): ?> <imgsrc="<?= 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><?phpelse: ?> <p><?= e($error) ?></p><?phpendif; ?> <ahref="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)
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 podstawowa
ta wersja
baza
mysqli_*, mysqli_fetch_row
PDO, wyjątki, FETCH_ASSOC
kod PHP
wpleciony w HTML w trzech miejscach
zapytania na górze, w HTML tylko foreach i if
zamówienie
model wklejony w treść zapytania
zapytanie przygotowane z :model
dane z formularza
$_POST wprost
filter_var z zakresami, komunikat przy złych danych
puste „Liczba par”
błąd krytyczny PHP 8
komunikat, link do strony głównej
blok produktu
<div class="buty">
<article class="buty"> z flow-root
kolejność bloków
zależna od bazy
ORDER BY p.kod_produktu, jak na ilustracji 2
kwota
1596 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.
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.
Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.
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: Bartosz Bryniarski. 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.