INF.03-02-26.01-SGStyczeń 2026 · 47 kroków · ok. 90 min · 40 kryteriów z klucza
Rozwiązanie krok po kroku: Portal Zdrowy bazarek (Nowoczesny kod (grid, PDO))
To drugi sposób rozwiązania tego samego arkusza: portal „Zdrowy bazarek”, baza bazar, jedna strona index.php z paskiem owoców, szyldem i formularzem zamówienia oraz trzy skrypty PHP. 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:
siatka CSS (display: grid) w bloku głównym: blok boczny i sekcja dostają po 50% szerokości w jednej linii grid-template-columns,
kolory z arkusza w zmiennych CSS (--color-green: ForestGreen), więc każdy kolor jest wpisany raz,
PHP na PDO z wyjątkami i trybem pobierania tablic asocjacyjnych - $product['nazwa'] zamiast $row[1],
logika PHP na górze pliku, HTML na dole: najpierw wszystkie zapytania i zamknięcie połączenia, a w HTML tylko pętle foreach wypisujące gotowe dane,
bezpieczeństwo od pierwszej linijki: filter_var sprawdza identyfikator i wagę, zanim cokolwiek trafi do bazy, zapytania przygotowane z parametrami nazwanymi, htmlspecialchars przy wyjściu (krótka funkcja escape()).
Kroki nadal idą zdanie po zdaniu za treścią arkusza. Podgląd strony w każdym kroku z PHP to wynik prawdziwego uruchomienia kodu z tego kroku w PHP 8.2 na bazie z arkusza; od Skryptu 3 pokazuje zamówienie z ilustracji 5 (pietruszka, 30 kg). Zdjęcia i baza pochodzą z prawdziwego archiwum pliki3-1.
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 bazar
ok. 2 min
Polecenie z arkusza
Utwórz bazę danych o nazwie bazar, z zestawem polskich znaków (np. utf8_polish_ci)
Archiwum pliki3-1.7z z pulpitu ma hasło baz@rEk100% (małpa zamiast „a”, wielkie E, znak procentu na końcu). W phpMyAdmin: Nowa, nazwa bazar, porównywanie utf8_polish_ci. To samo daje jedno polecenie w zakładce SQL:
CREATE DATABASE bazar CHARACTER SET utf8 COLLATE utf8_polish_ci;
Nazwa bazy to bazar, nie „bazarek” - „Zdrowy bazarek” to tytuł strony.
Typowe błędy
Skrypty łączą się z bazą po nazwie - literówka w bazar zabiera także punkt za połączenie (R.5.1).
Krok 2: Import tabel z pliku baza-nowa.sql
ok. 2 min
Polecenie z arkusza
Z rozpakowanego archiwum zaimportuj tabele z pliku baza-nowa.sql do utworzonej bazy
Zaznacz bazę bazar w lewym panelu, potem Import → baza-nowa.sql → Wykonaj. Powstają trzy tabele z ilustracji 1: towar (rodzaj, nazwa, cena i nazwa pliku ze zdjęciem), sklep i zamowienie - ta ostatnia kluczami obcymi wskazuje towar i sklep i zapisuje liczbę kilogramów.
Typowe błędy
Import bez zaznaczonej bazy kończy się błędem „nie wybrano bazy danych” albo ląduje w innej bazie.
Krok 3: Zrzut ekranu import.png
Kryteria:
R.1.1 Wykonany import tabel do bazy danych bazar - plik import.png
ok. 2 min
Polecenie z arkusza
Wykonaj zrzut ekranu po imporcie. Zapisz zrzut w formacie PNG pod nazwą import. Nie kadruj zrzutu. Powinien on obejmować cały ekran monitora, z widocznym paskiem zadań. Na zrzucie powinny być widoczne elementy wskazujące na poprawnie wykonany import tabel
Print Screen (cały ekran z paskiem zadań), wklej do edytora grafiki i zapisz jako import.png. Na zrzucie ma być widać komunikat o udanym imporcie albo trzy tabele w drzewku bazy. W tym arkuszu wszystkie zrzuty są 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: dziesięć pierwszych towarów
Kryteria: ,
R.1.2 W pliku kwerendy.txt zapisane co najmniej jedno zapytanie SQL, wynikające z treści zadania
R.1.3 Wykonane zapytanie 1 wybierające 10 pierwszych rekordów z tabeli towar , jedynie pola nazwa i plik. W pliku z kwerendami lub na zrzucie istnieje zapis np. SELECT nazwa, plik FROM towar LIMIT 10; oraz na zrzucie widocznych jest dokładnie 10 wierszy z nazwami: gruszka, jabłko, banan, ananas, mango, winogrona, jagody, maliny, pomarańcza, cytryna
ok. 2 min
Polecenie z arkusza
Zapytanie 1: wybierające 10 pierwszych rekordów z tabeli towar, jedynie pola nazwa i plik
SELECT nazwa, plik - dokładnie dwa pola, bez *,
FROM towar,
LIMIT 10 - tylko dziesięć pierwszych wierszy.
Każda klauzula w osobnej linii - dla bazy bez różnicy, a zapytanie czyta się od góry do dołu. Wynik: gruszka, jabłko, banan, ananas, mango, winogrona, jagody, maliny, pomarańcza, cytryna. Zapytania zapisuj w kwerendy.txt z komentarzem -- nad każdym (to też poprawny SQL), zrzut jako kw1.png.
Typowe błędy
„Pierwsze” znaczy tu „w kolejności, w jakiej baza je oddaje” - bez ORDER BY to kolejność wstawiania. Dopisanie ORDER BY nazwa dałoby inne dziesięć towarów niż w kluczu.
Krok 5: Zapytanie 2: identyfikatory i nazwy towarów
Kryteria:
R.1.4 Wykonane zapytanie 2 wybierające jedynie id i nazwę towarów. W pliku z kwerendami lub na zrzucie istnieje zapis np. SELECT id, nazwa FROM towar; oraz na zrzucie widoczne są wyniki działania zapytania (nie wszystkie rekordy będą widoczne, 39 lub 40 rekordów)
ok. 1 min
Polecenie z arkusza
Zapytanie 2: wybierające jedynie identyfikator i nazwę towarów
Dwa pola bez warunku: 39 wierszy (klucz: 39 lub 40). Cały wynik nie zmieści się na ekranie - klucz o tym wie. Z tego zapytania Skrypt 2 zrobi listę rozwijaną: id trafi do value opcji, nazwa będzie jej widoczną treścią. Zrzut: kw2.png.
R.1.5 Wykonane zapytanie 3 wybierające jedynie rodzaj, nazwę i cenę towaru dla id = 1. W pliku z kwerendami lub na zrzucie istnieje zapis np. SELECT rodzaj, nazwa, cena FROM towar WHERE id = 1; oraz na zrzucie widoczny jest dokładnie jeden rekord: owoc gruszka 5.00
ok. 1 min
Polecenie z arkusza
Zapytanie 3: wybierające jedynie rodzaj, nazwę i cenę towaru o identyfikatorze 1
Warunek na klucz główny zwraca najwyżej jeden wiersz: owoc gruszka 5.00. Kolumna cena ma typ DECIMAL, dlatego baza pokazuje dwa miejsca po przecinku. W Skrypcie 3 w miejscu 1 stanie parametr z identyfikatorem z formularza. Zrzut: kw3.png.
R.1.6 Wykonane zapytanie 4 wstawiające do tabeli zamowienie rekord o wartościach: towar o id 9, sklep o id 2, liczba kilogramów 20, klucz główny nadawany automatycznie. W pliku z kwerendami lub na zrzucie istnieje zapis np. INSERT INTO zamowienie(id_towar, id_sklep, liczba_kg) VALUES (9, 2, 20); (klucz główny pominięty lub NULL) oraz na zrzucie jest widoczny komunikat o poprawnie wykonanym zapytaniu lub tabela z dodanym rekordem
ok. 2 min
Polecenie z arkusza
Zapytanie 4: wstawiające do tabeli zamowienie rekord o wartościach: towar o identyfikatorze 9, sklep o identyfikatorze 2, liczba kilogramów 20, klucz główny nadawany automatycznie
Lista kolumn bez id - AUTO_INCREMENT nada następny numer (tu 49). Wymienienie kolumn z nazwy zamiast INSERT INTO zamowienie VALUES (...) chroni przed pomyłką w kolejności i działa nawet wtedy, gdy ktoś później doda kolumnę do tabeli. Zrzut kw4.png: komunikat o wstawieniu wiersza albo tabela z nowym rekordem.
Typowe błędy
Wpisany ręcznie klucz (VALUES (49, 9, 2, 20)) przeczy „kluczowi nadawanemu automatycznie” - pomiń id albo podaj NULL.
Kroki 8-23 · po tym etapie masz 18 z 40 kryteriów (pkt)
Krok 8: Grafika jabłka z przezroczystym tłem
Kryteria:
R.2.1 Grafika jablko.png lub jabłko.png ma przezroczyste tło
ok. 4 min
Polecenie z arkusza
Dla grafiki jablko.png należy nadać tło przezroczyste i zapisać pod tą samą nazwą
W GIMP-ie: Warstwa → Przezroczystość → Dodaj kanał alfa (bez kanału alfa usunięte piksele zamalowałyby się kolorem tła), potem różdżka kliknięta w kremowe tło i Delete. Różdżka zaznacza tylko piksele połączone z miejscem kliknięcia i podobne kolorem, więc jabłko, liść i cień zostają. Eksport do PNG. W podglądzie: oryginał i wynik na szachownicy oznaczającej przezroczystość - przezroczystych jest 14907 z 22 500 pikseli.
Nazwa pliku: arkusz pisze jablko.png, ale w archiwum i w polu plik bazy jest jabłko.png z „ł”. Zapisz pod nazwą z archiwum - tej szuka Skrypt 1; klucz R.2.1 uznaje obie.
Typowe błędy
JPEG nie ma kanału przezroczystości - eksport do .jpg zamieni przezroczyste tło na białe.
Podgląd strony po tym kroku
Krok 9: Grafika market.png: szerokość 300 px
Kryteria:
R.2.2 Grafika market.png przeskalowana z zachowaniem proporcji do szerokości dokładnie 300 px, odpowiadająca temu wysokość to 378 px ( ± 5 px)
ok. 3 min
Polecenie z arkusza
Grafikę market.png należy przeskalować z zachowaniem proporcji do szerokości 300 px
Oryginał ma 508 × 640 px. Obraz → Skaluj obraz, szerokość 300 px przy zamkniętym łańcuchu - wysokość przeliczy się na 378 px (640 · 300 / 508; klucz: 378 ±5). Eksport w PNG, bo szyld ma przezroczyste tło. Skalowanie w samym CSS (width: 300px przy obrazie) nie zmienia pliku i punktu nie daje.
Podgląd strony po tym kroku
Krok 10: Plik index.php w HTML5
ok. 2 min
Polecenie z arkusza
Składa się ze strony o nazwie index.php. Zapisana w języku HTML5
<!DOCTYPE html> to deklaracja HTML5 - bez niej przeglądarka przechodzi w tryb zgodności ze starymi stronami i inaczej liczy wymiary bloków. Plik ma rozszerzenie .php, bo dojdą skrypty; na egzaminie leży w htdocs XAMPP-a i otwiera się go przez http://localhost/..., a nie przez dwuklik.
R.2.3 W pliku index.php znajduje się deklaracja HTML5 <!DOCTYPE HTML> i deklaracja języka zawartości witryny: polski np. <html lang="pl">
ok. 1 min
Polecenie z arkusza
Zadeklarowany polski język zawartości witryny
lang="pl" w <html>: czytnik ekranu czyta stronę polską wymową, a przeglądarka dobiera słownik i dzielenie wyrazów. Z <!DOCTYPE html> to jedno kryterium (R.2.3).
Jawnie zastosowany właściwy standard kodowania polskich znaków
<meta charset="UTF-8"> jako pierwszy element <head> - przeglądarka musi znać kodowanie, zanim przeczyta tytuł z polskimi literami. Sam plik też zapisz w UTF-8 (bez BOM - znak BOM przed <?php to wypisany tekst, który później blokuje nagłówki HTTP).
R.2.4 W sekcji head znajduje się jawnie podany standard kodowania polskich znaków i tytuł strony "Zdrowy bazarek"
ok. 1 min
Polecenie z arkusza
Tytuł strony „Zdrowy bazarek”
<title> to tekst karty przeglądarki (z kodowaniem - R.2.4). Obok <meta name="viewport">: bez niej telefon udaje ekran szeroki na ok. 980 px i pomniejsza całą stronę. Arkusz tego nie wymaga, a nowoczesna strona zawsze ją ma.
R.3.2 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl.css formatowanie CSS pochodzi jedynie z tego arkusza
ok. 1 min
Polecenie z arkusza
Arkusz stylów w pliku o nazwie styl.css prawidłowo połączony z kodem strony
<link rel="stylesheet" href="styl.css"> - znacznik pusty, w HTML5 bez ukośnika />. Całe formatowanie ma pochodzić z tego pliku (R.3.2), więc w HTML nie ma ani jednego atrybutu style.
R.2.5 W sekcji body zdefiniowany jest podział na bloki za pomocą semantycznych znaczników HTML5: header, nav, main->aside+section, footer (dopuszcza się zastosowanie bloków pomocniczych)
ok. 2 min
Polecenie z arkusza
Podział strony na bloki zrealizowany za pomocą semantycznych znaczników bloków języka HTML5 tak, aby po uruchomieniu w przeglądarce układ bloków na stronie był zgodny z ilustracją 3
Klucz R.2.5 wymienia znaczniki: header, nav, main z aside i section w środku, footer. Każdy niesie znaczenie: <nav> to blok nawigacyjny (tu pasek owoców, które w rozbudowanej wersji staną się odnośnikami), <aside> - treść poboczna, <main> - główna treść strony, jedna na stronie.
Blok boczny i sekcja są jedynymi dziećmi <main> - dlatego siatka ustawiona na <main> rozłoży je w dwie kolumny bez żadnych dodatkowych klas.
Typowe błędy
Te same bloki zrobione z <div> wyglądają identycznie, ale R.2.5 sprawdza znaczniki w kodzie.
R.3.3 Formularz wysyła dane metodą POST do tego samego pliku (poprawne również bez atrybutu action)
ok. 1 min
Polecenie z arkusza
Formularz wysyłający dane do tego samego pliku metodą bezpieczną
<form method="post"> bez action - brak action oznacza „ten sam adres”, czyli dokładnie „do tego samego pliku” (R.3.3). „Metoda bezpieczna” w arkuszu to POST: dane jadą w treści żądania, a nie w adresie, więc nie zostają w historii przeglądarki. Jest też drugi powód, ważniejszy w tym zadaniu: formularz zapisuje zamówienie w bazie, a zmiany na serwerze wysyła się POST-em - GET jest od pobierania stron.
<select name="product"> - pod nazwą product wybrany identyfikator trafi do $_POST. Na ilustracji lista nie ma podpisu, więc zamiast widocznej etykiety jest aria-label="Towar": czytnik ekranu przeczyta „Towar, lista rozwijana”, a wygląd się nie zmienia. Opcje wypisze Skrypt 2.
Pole edycyjne przeznaczone do wpisywania jedynie liczb całkowitych
type="number" (R.3.5) z trzema atrybutami, które opisują dane:
step="1" - krok 1 to wartość domyślna, ale zapisany jawnie mówi czytelnikowi kodu „tylko liczby całkowite”; przy próbie wysłania 2,5 przeglądarka zgłosi błąd,
min="1" - zamówienie na 0 albo -5 kg nie ma sensu,
required - formularz bez wagi nie wyjdzie z przeglądarki.
To wygoda dla kupującego, nie zabezpieczenie: żądanie POST da się wysłać z pominięciem formularza, dlatego Skrypt 3 sprawdzi wagę jeszcze raz.
R.3.4 Przycisk realizuje funkcję wysłania danych z formularza (input lub button z typem submit lub button bez typu)
ok. 1 min
Polecenie z arkusza
Przycisk „Zamów” wysyłający dane z formularza do skryptu 3
<button type="submit"> - typ zapisany jawnie, choć submit jest domyślny: przycisk bez typu wewnątrz formularza też wysyła dane, a jawny zapis nie zostawia wątpliwości (R.3.4).
R.2.6 W bloku nagłówkowym umieszczony <h1>: "Zdrowy bazarek", w bloku sekcji <p>, w stopce <p> z numerem zdającego
ok. 1 min
Polecenie z arkusza
Zawartość stopki: paragraf o treści: „Stronę opracował: ”, dalej wstawiony numer zdającego
Akapit w <footer>. Tu stoi imię i nazwisko autora rozwiązania - na egzaminie wpisujesz w to miejsce swój numer zdającego (PESEL). Na stronie są już elementy z R.2.6: <h1>, akapit w sekcji i akapit w stopce.
Kroki 24-31 · po tym etapie masz 29 z 40 kryteriów (pkt)
Krok 24: Zmienne CSS z kolorami arkusza
ok. 2 min
Polecenie z arkusza
Styl CSS zdefiniowany jest w całości w zewnętrznym pliku o nazwie styl.css.
Plik styl.css zaczyna się od własnych właściwości (zmiennych CSS) w :root. ForestGreen pojawi się w trzech miejscach (nagłówek, stopka, ramka szyldu) - wpisany raz w --color-green, dalej jest używany jako var(--color-green). Zmiana koloru strony to wtedy jedna linia.
Nazwy zmiennych opisują kolor, bo w tym arkuszu każdy kolor ma jedną rolę. W inspektorze egzaminator i tak widzi kolor obliczony (ForestGreen = rgb(34, 139, 34)).
R.4.1 domyślne formatowanie wszystkich selektorów (*): krój czcionki Georgia, serif (dozwolona tylko taka kolejność)
ok. 1 min
Polecenie z arkusza
Domyślnie dla wszystkich selektorów: krój czcionki Georgia, w przypadku braku serif
Selektor uniwersalny * (tego wymaga R.4.1) obejmuje też pola formularza i przycisk, które domyślnie mają krój systemowy. Po Georgia stoi rodzina zapasowa serif - klucz dopuszcza tylko taką kolejność.
Dla bloku nawigacyjnego: kolor tła DarkKhaki, wysokość 150 px, w przypadku przepełnienia bloku pozostała jego zawartość jest ukryta (na ilustracji 2 jest 8 owoców – reszta nie zmieściła się w bloku i została ukryta)
Zdjęcia owoców mają po 150 px wysokości - tyle co blok, więc mieści się dokładnie jeden rząd. Obrazy są elementami liniowymi i zawijają się do następnego rzędu, gdy brakuje szerokości; overflow: hidden ten drugi rząd ucina (R.4.8). Dlatego na szerokim ekranie widać więcej owoców, a na wąskim mniej - jak mówi podpis ilustracji 2.
Typowe błędy
overflow: auto dodałby pasek przewijania zamiast ukryć nadmiar - R.4.8 wymaga hidden.
R.4.2 kolor tła: blok nagłówkowy i stopka - ForestGreen, blok nawigacyjny - DarkKhaki, bloki boczny i sekcji - Khaki
R.4.4 wysokość: blok nawigacyjny - 150px, bloki boczny i sekcji - 400px
ok. 1 min
Polecenie z arkusza
Wspólne dla bloku bocznego i sekcji: kolor tła Khaki, szerokość 50%, wysokość 400 px
Tło i wysokość w jednej regule. Szerokości tu nie ma - w tej wersji obie kolumny po 50% wyznacza siatka w następnym kroku. Klucz R.4.5 dopuszcza to wprost: „szerokość 50% (width lub grid-template-columns lub flex)”.
Z tą regułą są komplet kolorów tła (R.4.2) i wysokości (R.4.4).
R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z ilustracją 3 w arkuszu egzaminacyjnym (prawidłowo zastosowano właściwości CSS decydujące o układzie bloków na stronie, np. float i clear albo display flex albo grid albo position)
R.4.5 szerokość: bloki boczny i sekcji - 50% (width lub grid-template-columns lub flex)
ok. 3 min
Polecenie z arkusza
Podział strony na bloki zrealizowany za pomocą semantycznych znaczników bloków języka HTML5 tak, aby po uruchomieniu w przeglądarce układ bloków na stronie był zgodny z ilustracją 3
<main> staje się siatką o dwóch kolumnach po 50% - blok boczny wpada do pierwszej, sekcja do drugiej, w kolejności z HTML. Nagłówek, nawigacja i stopka są zwykłymi blokami poza siatką, więc same stoją jeden pod drugim, a stopka pod blokiem głównym - bez float i bez clear (R.3.1).
Zapis 50% 50% odpowiada słowom arkusza; 1fr 1fr („po równej części wolnego miejsca”) dałby ten sam wynik. Odstępu gap tu nie ma: przy kolumnach w procentach odstęp dołożyłby się do 100% i siatka wyszłaby poza ekran.
Typowe błędy
grid-template-columns działa tylko razem z display: grid na tym samym elemencie - sama lista kolumn bez display: grid nic nie zmienia.
R.4.9 styl jedynie dla obrazu w bloku bocznym: marginesy wewnętrzne - 2px, zewnętrzne - 10px, obramowanie 2px dotted ForestGreen, zaokrąglenie rogów 40px (dowolną metodą: id, klasa, potomek)
ok. 2 min
Polecenie z arkusza
Jedynie dla obrazu w bloku bocznym: marginesy wewnętrzne 2 px, zewnętrzne 10 px, obramowanie linią kropkową o szerokości 2 px i kolorze ForestGreen, zaokrąglenie rogów 40 px
Selektor potomka aside img trafia tylko w obrazy wewnątrz <aside> - zdjęcia owoców w <nav> zostają bez ramki („jedynie dla obrazu w bloku bocznym”). Klucz dopuszcza też klasę albo identyfikator (R.4.9); selektor potomka nie wymaga zmiany w HTML. Skrót border: 2px dotted to grubość, styl (kropki) i kolor ze zmiennej.
Typowe błędy
Sam selektor img objąłby też dziesięć zdjęć owoców - wtedy styl nie jest „jedynie dla obrazu w bloku bocznym”.
R.4.10 styl selektora p: rozmiar czcionki 120%, kursywa
ok. 1 min
Polecenie z arkusza
Dla selektora paragrafu: rozmiar czcionki 120%, kursywa
Selektor znacznika p, jak każe uwaga arkusza (R.4.10). Działa na akapit w sekcji, stopkę i akapit z wartością zamówienia, który wypisze Skrypt 3. 120% liczy się od rozmiaru czcionki rodzica.
Kroki 32-44 · po tym etapie masz 40 z 40 kryteriów (pkt)
Krok 32: Połączenie PDO z obsługą błędu i zamknięcie
Kryteria:
R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy bazar oraz ostatnią operacją na bazie jest jej zamknięcie
ok. 4 min
Polecenie z arkusza
Skrypty łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie bazar. Po wykonaniu wszystkich operacji połączenie z bazą jest zamykane
W tej wersji cały PHP stoi na górze pliku, przed <!DOCTYPE html>: najpierw dane, potem HTML, który je tylko wypisuje. new PDO(...) otwiera połączenie; napis mysql:host=localhost;dbname=bazar;charset=utf8mb4 to sterownik, serwer, baza i od razu kodowanie połączenia, więc polskie nazwy towarów przychodzą poprawnie. Dwie opcje:
PDO::ERRMODE_EXCEPTION - błąd SQL rzuca wyjątek zamiast po cichu zwracać false,
PDO::FETCH_ASSOC - wiersz to tablica z nazwami kolumn: $product['nazwa'] zamiast $row[1].
try/catch (PDOException ...) łapie nieudane połączenie i pokazuje ogólny komunikat z kodem 500. Treść wyjątku ($exception->getMessage()) zawiera nazwę serwera i użytkownika - takich szczegółów nie pokazuje się odwiedzającym.
Na dole bloku od razu $pdo = null; - w PDO tak zamyka się połączenie. Zapytania będą dopisywane nad tą linią, więc zamknięcie zawsze zostanie ostatnią operacją na bazie (R.5.1). Klucz dopuszcza PDO wprost („tylko w przypadku użycia bibliotek MySQLi lub PDO”). Od tego kroku podgląd to wynik prawdziwego uruchomienia strony w PHP 8.2.
Typowe błędy
PDO nie ma w tabeli 1 arkusza - nazwy stałych trzeba znać na pamięć. Literówka w PDO::ATTR_ERRMODE to błąd krytyczny i biała strona; kto nie jest pewny składni, pisze funkcjami mysqli_* z tabeli 1 (wersja podstawowa).
index.phpPHPzmienione: 15 linii
<?php// Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumntry {$pdo = new PDO('mysql:host=localhost;dbname=bazar;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);} catch (PDOException $exception) {http_response_code(500);exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');}// Wszystkie dane pobrane i zapisane - zamknięcie połączenia$pdo = null;?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Zdrowy bazarek</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Zdrowy bazarek</h1> </header> <nav> </nav> <main> <aside> <imgsrc="market.png"alt="bazarek"> </aside> <section> <p>Wybierz owoc lub warzywo i podaj jego wagę:</p> <formmethod="post"> <selectname="product"aria-label="Towar"> </select> <inputtype="number"name="weight"min="1"step="1"requiredaria-label="Liczba kilogramów"> <buttontype="submit">Zamów</button> </form> </section> </main> <footer> <p>Stronę opracował: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (index.php)
Krok 33: Funkcja escape() do bezpiecznego wypisywania
ok. 2 min
Polecenie z arkusza
Należy stosować znaczące nazewnictwo zmiennych i funkcji w języku polskim lub angielskim
Zanim cokolwiek z bazy trafi na stronę, powstaje krótka funkcja escape() - „zabezpiecz znaki”. W frameworkach PHP ta sama funkcja nazywa się często po prostu e(), ale arkusz wymaga nazw, które mówią, do czego służą, więc tu jest pełne słowo. htmlspecialchars zamienia <, >, & i - dzięki ENT_QUOTES - oba cudzysłowy na encje. Nazwa towaru z apostrofem albo cudzysłowem nie rozerwie wtedy atrybutu alt="...", a wpis <script> w bazie wyświetli się jako tekst, zamiast wykonać się w przeglądarce (atak XSS).
Typy w nagłówku (string|int|float|null, wynik : string) dokumentują, co funkcja przyjmuje: z bazy przychodzą napisy, liczby albo NULL.
index.phpPHPzmienione: 5 linii
<?php// Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumntry {$pdo = new PDO('mysql:host=localhost;dbname=bazar;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);} catch (PDOException $exception) {http_response_code(500);exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');}functionescape(string|int|float|null$value): string{returnhtmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');}// Wszystkie dane pobrane i zapisane - zamknięcie połączenia$pdo = null;?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Zdrowy bazarek</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Zdrowy bazarek</h1> </header> <nav> </nav> <main> <aside> <imgsrc="market.png"alt="bazarek"> </aside> <section> <p>Wybierz owoc lub warzywo i podaj jego wagę:</p> <formmethod="post"> <selectname="product"aria-label="Towar"> </select> <inputtype="number"name="weight"min="1"step="1"requiredaria-label="Liczba kilogramów"> <buttontype="submit">Zamów</button> </form> </section> </main> <footer> <p>Stronę opracował: Bartosz Bryniarski</p> </footer></body></html>
R.5.2 Skrypt zawiera instrukcję wysyłającą do bazy danych zapytanie 1 lub zapytanie 2
ok. 2 min
Polecenie z arkusza
Skrypt 1: wysyła do bazy danych zapytanie 1
$pdo->query(...) wysyła zapytanie 1, a fetchAll() od razu odbiera wszystkie dziesięć wierszy do tablicy $fruits (R.5.2). Zwykłe query, bez prepare, jest tu na miejscu: w zapytaniu nie ma żadnej wartości od użytkownika, więc nie ma czego wstrzyknąć.
index.phpPHPzmienione: 3 linie
<?php// Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumntry {$pdo = new PDO('mysql:host=localhost;dbname=bazar;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);} catch (PDOException $exception) {http_response_code(500);exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');}functionescape(string|int|float|null$value): string{returnhtmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');}// Skrypt 1: dziesięć pierwszych towarów do paska zdjęć (zapytanie 1)$fruits = $pdo->query('SELECT nazwa, plik FROM towar LIMIT 10')->fetchAll();// Wszystkie dane pobrane i zapisane - zamknięcie połączenia$pdo = null;?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Zdrowy bazarek</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Zdrowy bazarek</h1> </header> <nav> </nav> <main> <aside> <imgsrc="market.png"alt="bazarek"> </aside> <section> <p>Wybierz owoc lub warzywo i podaj jego wagę:</p> <formmethod="post"> <selectname="product"aria-label="Towar"> </select> <inputtype="number"name="weight"min="1"step="1"requiredaria-label="Liczba kilogramów"> <buttontype="submit">Zamów</button> </form> </section> </main> <footer> <p>Stronę opracował: Bartosz Bryniarski</p> </footer></body></html>
R.5.4 Skrypt 1 wyświetla obrazy dla każdego wiersza pobranego z bazy, źródłem jest pole plik, tekstem alternatywnym jest nazwa (obrazy mogą być ukryte za sprawą właściwości CSS overflow, zamiast obrazu z jabłkiem może się wyświetlać tekst alternatywny)
ok. 3 min
Polecenie z arkusza
Skrypt 1: dla każdego wiersza pobranego z bazy wyświetla obraz o pobranej z bazy nazwie pliku i tekście alternatywnym odpowiadającym nazwie owocu
W HTML zostaje sam szablon: pętla foreach ($fruits as $fruit): ... endforeach; - składnia alternatywna PHP, pomyślana do przeplatania z HTML (dwukropek i endforeach zamiast klamer, które łatwo zgubić między znacznikami). <?= ... ?> to skrót od <?php echo ... ?>.
src dostaje pole plik, alt - pole nazwa (R.5.4). Atrybuty HTML w zwykłych cudzysłowach, bez \", bo PHP nie skleja już napisów z HTML-em. W podglądzie pasek owoców; nadmiar ucina overflow: hidden.
index.phpPHPzmienione: 3 linie
<?php// Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumntry {$pdo = new PDO('mysql:host=localhost;dbname=bazar;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);} catch (PDOException $exception) {http_response_code(500);exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');}functionescape(string|int|float|null$value): string{returnhtmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');}// Skrypt 1: dziesięć pierwszych towarów do paska zdjęć (zapytanie 1)$fruits = $pdo->query('SELECT nazwa, plik FROM towar LIMIT 10')->fetchAll();// Wszystkie dane pobrane i zapisane - zamknięcie połączenia$pdo = null;?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Zdrowy bazarek</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Zdrowy bazarek</h1> </header> <nav><?phpforeach ($fruitsas$fruit): ?> <imgsrc="<?= escape($fruit['plik']) ?>"alt="<?= escape($fruit['nazwa']) ?>"><?phpendforeach; ?> </nav> <main> <aside> <imgsrc="market.png"alt="bazarek"> </aside> <section> <p>Wybierz owoc lub warzywo i podaj jego wagę:</p> <formmethod="post"> <selectname="product"aria-label="Towar"> </select> <inputtype="number"name="weight"min="1"step="1"requiredaria-label="Liczba kilogramów"> <buttontype="submit">Zamów</button> </form> </section> </main> <footer> <p>Stronę opracował: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (index.php)
Krok 36: Skrypt 2: zapytanie 2 do bazy
ok. 1 min
Polecenie z arkusza
Skrypt 2: wysyła do bazy danych zapytanie 2
To samo dla listy towarów: zapytanie 2 i cały wynik w tablicy $products. Każde zapytanie ma własną zmienną o nazwie mówiącej, co w niej jest - zamiast jednego $result nadpisywanego przez kolejne skrypty.
index.phpPHPzmienione: 3 linie
<?php// Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumntry {$pdo = new PDO('mysql:host=localhost;dbname=bazar;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);} catch (PDOException $exception) {http_response_code(500);exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');}functionescape(string|int|float|null$value): string{returnhtmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');}// Skrypt 1: dziesięć pierwszych towarów do paska zdjęć (zapytanie 1)$fruits = $pdo->query('SELECT nazwa, plik FROM towar LIMIT 10')->fetchAll();// Skrypt 2: wszystkie towary do listy rozwijanej (zapytanie 2)$products = $pdo->query('SELECT id, nazwa FROM towar')->fetchAll();// Wszystkie dane pobrane i zapisane - zamknięcie połączenia$pdo = null;?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Zdrowy bazarek</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Zdrowy bazarek</h1> </header> <nav><?phpforeach ($fruitsas$fruit): ?> <imgsrc="<?= escape($fruit['plik']) ?>"alt="<?= escape($fruit['nazwa']) ?>"><?phpendforeach; ?> </nav> <main> <aside> <imgsrc="market.png"alt="bazarek"> </aside> <section> <p>Wybierz owoc lub warzywo i podaj jego wagę:</p> <formmethod="post"> <selectname="product"aria-label="Towar"> </select> <inputtype="number"name="weight"min="1"step="1"requiredaria-label="Liczba kilogramów"> <buttontype="submit">Zamów</button> </form> </section> </main> <footer> <p>Stronę opracował: Bartosz Bryniarski</p> </footer></body></html>
Krok 37: Skrypt 2: opcje z identyfikatorem w value
Kryteria: ,
R.5.5 Skrypt 2 wypełnia listę rozwijaną z danymi pobranymi z bazy, atrybut value to id, wyświetlana wartość opcji to nazwa towaru
R.2.9 Na stronie umieszczona lista rozwijana (w HTML lub skrypcie), zastosowane znaczniki < select> i <option> , pole edycyjne i przycisk "Zamów"
ok. 2 min
Polecenie z arkusza
Skrypt 2: dla każdego wiersza pobranego z bazy generuje opcję listy rozwijanej, gdzie w atrybucie value znajduje się zwrócone pole id, a w treści opcji znajduje się nazwa towaru
Jedna <option> na towar: value = id, treść = nazwa (R.5.5). Kupujący widzi nazwę, a formularz wyśle identyfikator - po nim Skrypt 3 znajdzie cenę. Lista ma 39 pozycji, pierwsza to gruszka, jak na ilustracji 4. Z opcjami jest komplet R.2.9: <select> z <option>, pole edycyjne i przycisk „Zamów”.
index.phpPHPzmienione: 3 linie
<?php// Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumntry {$pdo = new PDO('mysql:host=localhost;dbname=bazar;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);} catch (PDOException $exception) {http_response_code(500);exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');}functionescape(string|int|float|null$value): string{returnhtmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');}// Skrypt 1: dziesięć pierwszych towarów do paska zdjęć (zapytanie 1)$fruits = $pdo->query('SELECT nazwa, plik FROM towar LIMIT 10')->fetchAll();// Skrypt 2: wszystkie towary do listy rozwijanej (zapytanie 2)$products = $pdo->query('SELECT id, nazwa FROM towar')->fetchAll();// Wszystkie dane pobrane i zapisane - zamknięcie połączenia$pdo = null;?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Zdrowy bazarek</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Zdrowy bazarek</h1> </header> <nav><?phpforeach ($fruitsas$fruit): ?> <imgsrc="<?= escape($fruit['plik']) ?>"alt="<?= escape($fruit['nazwa']) ?>"><?phpendforeach; ?> </nav> <main> <aside> <imgsrc="market.png"alt="bazarek"> </aside> <section> <p>Wybierz owoc lub warzywo i podaj jego wagę:</p> <formmethod="post"> <selectname="product"aria-label="Towar"><?phpforeach ($productsas$product): ?> <optionvalue="<?= escape($product['id']) ?>"><?= escape($product['nazwa']) ?></option><?phpendforeach; ?> </select> <inputtype="number"name="weight"min="1"step="1"requiredaria-label="Liczba kilogramów"> <buttontype="submit">Zamów</button> </form> </section> </main> <footer> <p>Stronę opracował: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (index.php)
Krok 38: Skrypt 3: tylko po wysłaniu formularza
ok. 2 min
Polecenie z arkusza
Skrypt 3, wykonywany tylko wtedy, gdy wysłano dane z formularza
$_SERVER['REQUEST_METHOD'] mówi, jak przyszło żądanie: zwykłe wejście na stronę to GET, kliknięcie „Zamów” - POST. Warunek na metodę obejmuje cały formularz naraz, zamiast sprawdzać isset na jednym z pól.
Przed warunkiem dwie zmienne z wartościami „nic się nie stało”: $order (zamówiony towar) i $message (komunikat dla kupującego). Szablon niżej zawsze może z nich skorzystać - także przy zwykłym wejściu, kiedy blok if się nie wykona.
index.phpPHPzmienione: 6 linii
<?php// Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumntry {$pdo = new PDO('mysql:host=localhost;dbname=bazar;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);} catch (PDOException $exception) {http_response_code(500);exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');}functionescape(string|int|float|null$value): string{returnhtmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');}// Skrypt 1: dziesięć pierwszych towarów do paska zdjęć (zapytanie 1)$fruits = $pdo->query('SELECT nazwa, plik FROM towar LIMIT 10')->fetchAll();// Skrypt 2: wszystkie towary do listy rozwijanej (zapytanie 2)$products = $pdo->query('SELECT id, nazwa FROM towar')->fetchAll();// Skrypt 3: zamówienie - tylko po wysłaniu formularza (zapytania 3 i 4)$order = null;$message = '';if ($_SERVER['REQUEST_METHOD'] === 'POST') {}// Wszystkie dane pobrane i zapisane - zamknięcie połączenia$pdo = null;?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Zdrowy bazarek</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Zdrowy bazarek</h1> </header> <nav><?phpforeach ($fruitsas$fruit): ?> <imgsrc="<?= escape($fruit['plik']) ?>"alt="<?= escape($fruit['nazwa']) ?>"><?phpendforeach; ?> </nav> <main> <aside> <imgsrc="market.png"alt="bazarek"> </aside> <section> <p>Wybierz owoc lub warzywo i podaj jego wagę:</p> <formmethod="post"> <selectname="product"aria-label="Towar"><?phpforeach ($productsas$product): ?> <optionvalue="<?= escape($product['id']) ?>"><?= escape($product['nazwa']) ?></option><?phpendforeach; ?> </select> <inputtype="number"name="weight"min="1"step="1"requiredaria-label="Liczba kilogramów"> <buttontype="submit">Zamów</button> </form> </section> </main> <footer> <p>Stronę opracował: Bartosz Bryniarski</p> </footer></body></html>
Krok 39: Skrypt 3: wartości z formularza po sprawdzeniu
Kryteria:
R.5.6 Skrypt 3 stosuje tablicę $_POST dla danych wysłanych z formularza
ok. 3 min
Polecenie z arkusza
Pobiera wartości z formularza: id towaru i liczbę kilogramów
Obie wartości przychodzą w $_POST pod nazwami pól product i weight (R.5.6) - jako tekst, bo HTTP przesyła tylko napisy. Zanim trafią dalej, sprawdza je filter_var(..., FILTER_VALIDATE_INT, ...): zwraca liczbę całkowitą albo false - dla pustego pola, liter, „2.5”, „12 OR 1=1” i liczb spoza zakresu (min_range 1, dla wagi też max_range 10 000 kg). Operator ?? podstawia pusty napis, gdy pola w ogóle nie wysłano.
Przy błędzie $message dostaje komunikat, a szablon wypisuje go w akapicie pod formularzem. Tu widać różnicę z wersją podstawową z egzaminu: tam puste pole wagi kończyło stronę błędem „Unsupported operand types”, a -5 kg trafiało do bazy. Podgląd pokazuje stronę po zamówieniu pietruszki (30 kg) - na razie nic się nie zmienia, wynik dojdzie w kolejnych krokach.
Typowe błędy
filter_input(INPUT_POST, ...) sprawdza to samo, ale w kodzie nie ma wtedy $_POST, a tego dotyczy kryterium R.5.6. Przy filter_var zawsze porównuj wynik ściśle: === false - przy innych filtrach poprawne 0 wzięte za błąd.
index.phpPHPzmienione: 9 linii
<?php// Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumntry {$pdo = new PDO('mysql:host=localhost;dbname=bazar;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);} catch (PDOException $exception) {http_response_code(500);exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');}functionescape(string|int|float|null$value): string{returnhtmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');}// Skrypt 1: dziesięć pierwszych towarów do paska zdjęć (zapytanie 1)$fruits = $pdo->query('SELECT nazwa, plik FROM towar LIMIT 10')->fetchAll();// Skrypt 2: wszystkie towary do listy rozwijanej (zapytanie 2)$products = $pdo->query('SELECT id, nazwa FROM towar')->fetchAll();// Skrypt 3: zamówienie - tylko po wysłaniu formularza (zapytania 3 i 4)$order = null;$message = '';if ($_SERVER['REQUEST_METHOD'] === 'POST') {$productId = filter_var($_POST['product'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);$weight = filter_var($_POST['weight'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 10000]]);if ($productId === false || $weight === false) {$message = 'Wybierz towar i podaj liczbę kilogramów - liczbę całkowitą większą od zera.'; } else { }}// Wszystkie dane pobrane i zapisane - zamknięcie połączenia$pdo = null;?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Zdrowy bazarek</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Zdrowy bazarek</h1> </header> <nav><?phpforeach ($fruitsas$fruit): ?> <imgsrc="<?= escape($fruit['plik']) ?>"alt="<?= escape($fruit['nazwa']) ?>"><?phpendforeach; ?> </nav> <main> <aside> <imgsrc="market.png"alt="bazarek"> </aside> <section> <p>Wybierz owoc lub warzywo i podaj jego wagę:</p> <formmethod="post"> <selectname="product"aria-label="Towar"><?phpforeach ($productsas$product): ?> <optionvalue="<?= escape($product['id']) ?>"><?= escape($product['nazwa']) ?></option><?phpendforeach; ?> </select> <inputtype="number"name="weight"min="1"step="1"requiredaria-label="Liczba kilogramów"> <buttontype="submit">Zamów</button> </form><?phpif ($message !== ''): ?> <p><?= escape($message) ?></p><?phpendif; ?> </section> </main> <footer> <p>Stronę opracował: Bartosz Bryniarski</p> </footer></body></html>
R.5.3 Skrypt zawiera instrukcję wysyłającą do bazy danych zmodyfikowane zapytanie 3: sprawdzane jest id przesłane formularzem lub zmodyfikowane zapytanie 4: wstawiany jest rekord z id towaru i liczbą kg przesłaną zapytaniem
ok. 3 min
Polecenie z arkusza
Wysyła do bazy danych zmodyfikowane zapytanie 3, tak że wybierany jest rekord o id wskazanym w formularzu
Zapytanie przygotowane: w treści stoi parametr nazwany :id, a wartość idzie osobno w execute(['id' => $productId]) (R.5.3). Baza dostaje najpierw strukturę zapytania, a dane dopiero potem - wpisany tekst nigdy nie stanie się częścią SQL. To druga warstwa ochrony obok filter_var: walidacja pilnuje sensu danych, parametr pilnuje składni zapytania.
fetch() zwraca jeden wiersz albo false, gdy towaru o takim id nie ma; ?: null zamienia false na null, żeby „brak zamówienia” miał jedną wartość.
index.phpPHPzmienione: 3 linie
<?php// Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumntry {$pdo = new PDO('mysql:host=localhost;dbname=bazar;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);} catch (PDOException $exception) {http_response_code(500);exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');}functionescape(string|int|float|null$value): string{returnhtmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');}// Skrypt 1: dziesięć pierwszych towarów do paska zdjęć (zapytanie 1)$fruits = $pdo->query('SELECT nazwa, plik FROM towar LIMIT 10')->fetchAll();// Skrypt 2: wszystkie towary do listy rozwijanej (zapytanie 2)$products = $pdo->query('SELECT id, nazwa FROM towar')->fetchAll();// Skrypt 3: zamówienie - tylko po wysłaniu formularza (zapytania 3 i 4)$order = null;$message = '';if ($_SERVER['REQUEST_METHOD'] === 'POST') {$productId = filter_var($_POST['product'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);$weight = filter_var($_POST['weight'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 10000]]);if ($productId === false || $weight === false) {$message = 'Wybierz towar i podaj liczbę kilogramów - liczbę całkowitą większą od zera.'; } else {$statement = $pdo->prepare('SELECT rodzaj, nazwa, cena FROM towar WHERE id = :id');$statement->execute(['id' => $productId]);$order = $statement->fetch() ?: null; }}// Wszystkie dane pobrane i zapisane - zamknięcie połączenia$pdo = null;?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Zdrowy bazarek</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Zdrowy bazarek</h1> </header> <nav><?phpforeach ($fruitsas$fruit): ?> <imgsrc="<?= escape($fruit['plik']) ?>"alt="<?= escape($fruit['nazwa']) ?>"><?phpendforeach; ?> </nav> <main> <aside> <imgsrc="market.png"alt="bazarek"> </aside> <section> <p>Wybierz owoc lub warzywo i podaj jego wagę:</p> <formmethod="post"> <selectname="product"aria-label="Towar"><?phpforeach ($productsas$product): ?> <optionvalue="<?= escape($product['id']) ?>"><?= escape($product['nazwa']) ?></option><?phpendforeach; ?> </select> <inputtype="number"name="weight"min="1"step="1"requiredaria-label="Liczba kilogramów"> <buttontype="submit">Zamów</button> </form><?phpif ($message !== ''): ?> <p><?= escape($message) ?></p><?phpendif; ?> </section> </main> <footer> <p>Stronę opracował: Bartosz Bryniarski</p> </footer></body></html>
R.5.7 Skrypt 3 oblicza wartość iloczynu ceny zwróconej zapytaniem oraz liczby kilogramów wysłanej z formularza
ok. 2 min
Polecenie z arkusza
Oblicza wartość na podstawie zwróconej zapytaniem ceny oraz liczby kilogramów wysłanej z formularza
Cena z bazy razy kilogramy z formularza (R.5.7). PDO oddaje kolumnę DECIMAL jako tekst „14.70” - celowo, żeby nie zgubić groszy w liczbie zmiennoprzecinkowej - a $weight jest już liczbą całkowitą z filter_var, więc mnożenie jest bezpieczne: 14,70 × 30 = 441.
Gdy towaru nie ma (np. identyfikator 999 wysłany z pominięciem formularza), zamiast mnożenia przez nieistniejącą cenę jest komunikat „Nie ma takiego towaru.”.
index.phpPHPzmienione: 5 linii
<?php// Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumntry {$pdo = new PDO('mysql:host=localhost;dbname=bazar;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);} catch (PDOException $exception) {http_response_code(500);exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');}functionescape(string|int|float|null$value): string{returnhtmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');}// Skrypt 1: dziesięć pierwszych towarów do paska zdjęć (zapytanie 1)$fruits = $pdo->query('SELECT nazwa, plik FROM towar LIMIT 10')->fetchAll();// Skrypt 2: wszystkie towary do listy rozwijanej (zapytanie 2)$products = $pdo->query('SELECT id, nazwa FROM towar')->fetchAll();// Skrypt 3: zamówienie - tylko po wysłaniu formularza (zapytania 3 i 4)$order = null;$message = '';if ($_SERVER['REQUEST_METHOD'] === 'POST') {$productId = filter_var($_POST['product'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);$weight = filter_var($_POST['weight'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 10000]]);if ($productId === false || $weight === false) {$message = 'Wybierz towar i podaj liczbę kilogramów - liczbę całkowitą większą od zera.'; } else {$statement = $pdo->prepare('SELECT rodzaj, nazwa, cena FROM towar WHERE id = :id');$statement->execute(['id' => $productId]);$order = $statement->fetch() ?: null;if ($order === null) {$message = 'Nie ma takiego towaru.'; } else {$value = $order['cena'] * $weight; } }}// Wszystkie dane pobrane i zapisane - zamknięcie połączenia$pdo = null;?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Zdrowy bazarek</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Zdrowy bazarek</h1> </header> <nav><?phpforeach ($fruitsas$fruit): ?> <imgsrc="<?= escape($fruit['plik']) ?>"alt="<?= escape($fruit['nazwa']) ?>"><?phpendforeach; ?> </nav> <main> <aside> <imgsrc="market.png"alt="bazarek"> </aside> <section> <p>Wybierz owoc lub warzywo i podaj jego wagę:</p> <formmethod="post"> <selectname="product"aria-label="Towar"><?phpforeach ($productsas$product): ?> <optionvalue="<?= escape($product['id']) ?>"><?= escape($product['nazwa']) ?></option><?phpendforeach; ?> </select> <inputtype="number"name="weight"min="1"step="1"requiredaria-label="Liczba kilogramów"> <buttontype="submit">Zamów</button> </form><?phpif ($message !== ''): ?> <p><?= escape($message) ?></p><?phpendif; ?> </section> </main> <footer> <p>Stronę opracował: Bartosz Bryniarski</p> </footer></body></html>
R.5.8 Skrypt 3 wyświetla w paragrafie tekst: „<rodzaj> <nazwa> wartość: <wartość> zł”, gdzie w znakach <> umieszczono zwrócone zapytaniem pola i wyliczoną wartość
ok. 2 min
Polecenie z arkusza
Wypisuje w dodatkowym paragrafie pod formularzem tekst: „<rodzaj><nazwa> wartość: <wartość> zł”, gdzie w znakach <> umieszczono zwrócone zapytaniem pola i wyliczoną wartość
Pod formularzem if ($order !== null): - akapit według wzoru z arkusza (R.5.8), a w gałęzi elseif komunikat o błędzie. Kwota przez number_format($value, 2, ',', ' '): dwa miejsca po przecinku, polski przecinek dziesiętny i spacja między tysiącami - „441,00 zł”, a dla 3 kg ananasa „24,60 zł” zamiast „24.6 zł”.
Podgląd: „warzywo pietruszka wartość: 441,00 zł” - ilustracja 5 pokazuje „441 zł”, bo tam wartość jest wypisana bez formatowania. Klucz ocenia tekst z polami z bazy i wyliczoną wartością, a format liczby zostawia piszącemu.
index.phpPHPzmienione: 3 linie
<?php// Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumntry {$pdo = new PDO('mysql:host=localhost;dbname=bazar;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);} catch (PDOException $exception) {http_response_code(500);exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');}functionescape(string|int|float|null$value): string{returnhtmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');}// Skrypt 1: dziesięć pierwszych towarów do paska zdjęć (zapytanie 1)$fruits = $pdo->query('SELECT nazwa, plik FROM towar LIMIT 10')->fetchAll();// Skrypt 2: wszystkie towary do listy rozwijanej (zapytanie 2)$products = $pdo->query('SELECT id, nazwa FROM towar')->fetchAll();// Skrypt 3: zamówienie - tylko po wysłaniu formularza (zapytania 3 i 4)$order = null;$message = '';if ($_SERVER['REQUEST_METHOD'] === 'POST') {$productId = filter_var($_POST['product'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);$weight = filter_var($_POST['weight'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 10000]]);if ($productId === false || $weight === false) {$message = 'Wybierz towar i podaj liczbę kilogramów - liczbę całkowitą większą od zera.'; } else {$statement = $pdo->prepare('SELECT rodzaj, nazwa, cena FROM towar WHERE id = :id');$statement->execute(['id' => $productId]);$order = $statement->fetch() ?: null;if ($order === null) {$message = 'Nie ma takiego towaru.'; } else {$value = $order['cena'] * $weight; } }}// Wszystkie dane pobrane i zapisane - zamknięcie połączenia$pdo = null;?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Zdrowy bazarek</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Zdrowy bazarek</h1> </header> <nav><?phpforeach ($fruitsas$fruit): ?> <imgsrc="<?= escape($fruit['plik']) ?>"alt="<?= escape($fruit['nazwa']) ?>"><?phpendforeach; ?> </nav> <main> <aside> <imgsrc="market.png"alt="bazarek"> </aside> <section> <p>Wybierz owoc lub warzywo i podaj jego wagę:</p> <formmethod="post"> <selectname="product"aria-label="Towar"><?phpforeach ($productsas$product): ?> <optionvalue="<?= escape($product['id']) ?>"><?= escape($product['nazwa']) ?></option><?phpendforeach; ?> </select> <inputtype="number"name="weight"min="1"step="1"requiredaria-label="Liczba kilogramów"> <buttontype="submit">Zamów</button> </form><?phpif ($order !== null): ?> <p><?= escape($order['rodzaj']) ?><?= escape($order['nazwa']) ?> wartość: <?= number_format($value, 2, ',', ' ') ?> zł</p><?phpelseif ($message !== ''): ?> <p><?= escape($message) ?></p><?phpendif; ?> </section> </main> <footer> <p>Stronę opracował: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (index.php)
Krok 43: Skrypt 3: zapis zamówienia
Kryteria: ,
R.5.9 Skrypt 3 wstawia do tabeli zamowienie rekord z danymi pobranymi z formularza (należy sprawdzić w phpMyAdmin)
R.5.10 W skryptach zastosowana przynajmniej jedna zmienna oraz nazwy wszystkich zmiennych są w języku polskim lub angielskim i odzwierciedlają cel zastosowania
ok. 3 min
Polecenie z arkusza
Wstawia do bazy rekord za pomocą zmodyfikowanego zapytania 4, w ten sposób, że w miejscu id towaru i liczby kilogramów są wpisywane odpowiednie wartości wysłane z formularza (id sklepu pozostaje bez zmian lub jest pomijane)
Zapytanie 4 jako przygotowane, z parametrami :product i :weight; sklep 2 jest stałą w treści zapytania, bo nie pochodzi od użytkownika. Zapis stoi za sprawdzeniem, że towar istnieje - identyfikator spoza bazy nie dochodzi do INSERT, więc nie ma błędu klucza obcego. Sprawdzone tak, jak każe klucz, w bazie: po zamówieniu pietruszki w tabeli zamowienie przybył rekord 50 (towar 12, sklep 2, 30 kg) - R.5.9.
Przy zamknięciu doszła linia $statement = $insert = null;: obiekt zapytania przygotowanego trzyma odwołanie do połączenia, więc samo $pdo = null zamknęłoby je dopiero na końcu skryptu. Teraz zamknięcie naprawdę jest ostatnią operacją na bazie.
Nazwy zmiennych mówią, co przechowują: $pdo, $fruits, $products, $productId, $weight, $order, $value, $statement, $insert, $message (R.5.10).
Typowe błędy
Bez wzorca PRG (przekierowania po zapisie) odświeżenie strony klawiszem F5 wysyła formularz ponownie i dopisuje drugie zamówienie - przeglądarka pyta wtedy o ponowne przesłanie danych. Tu przekierowania nie ma celowo: arkusz każe pokazać wartość w odpowiedzi na wysłany formularz.
index.phpPHPzmienione: 3 linie
<?php// Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumntry {$pdo = new PDO('mysql:host=localhost;dbname=bazar;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);} catch (PDOException $exception) {http_response_code(500);exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');}functionescape(string|int|float|null$value): string{returnhtmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');}// Skrypt 1: dziesięć pierwszych towarów do paska zdjęć (zapytanie 1)$fruits = $pdo->query('SELECT nazwa, plik FROM towar LIMIT 10')->fetchAll();// Skrypt 2: wszystkie towary do listy rozwijanej (zapytanie 2)$products = $pdo->query('SELECT id, nazwa FROM towar')->fetchAll();// Skrypt 3: zamówienie - tylko po wysłaniu formularza (zapytania 3 i 4)$order = null;$message = '';if ($_SERVER['REQUEST_METHOD'] === 'POST') {$productId = filter_var($_POST['product'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);$weight = filter_var($_POST['weight'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 10000]]);if ($productId === false || $weight === false) {$message = 'Wybierz towar i podaj liczbę kilogramów - liczbę całkowitą większą od zera.'; } else {$statement = $pdo->prepare('SELECT rodzaj, nazwa, cena FROM towar WHERE id = :id');$statement->execute(['id' => $productId]);$order = $statement->fetch() ?: null;if ($order === null) {$message = 'Nie ma takiego towaru.'; } else {$value = $order['cena'] * $weight;$insert = $pdo->prepare('INSERT INTO zamowienie (id_towar, id_sklep, liczba_kg) VALUES (:product, 2, :weight)');$insert->execute(['product' => $productId, 'weight' => $weight]); } }}// Wszystkie dane pobrane i zapisane - zamknięcie połączenia$statement = $insert = null; // obiekty zapytań też trzymają połączenie$pdo = null;?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Zdrowy bazarek</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Zdrowy bazarek</h1> </header> <nav><?phpforeach ($fruitsas$fruit): ?> <imgsrc="<?= escape($fruit['plik']) ?>"alt="<?= escape($fruit['nazwa']) ?>"><?phpendforeach; ?> </nav> <main> <aside> <imgsrc="market.png"alt="bazarek"> </aside> <section> <p>Wybierz owoc lub warzywo i podaj jego wagę:</p> <formmethod="post"> <selectname="product"aria-label="Towar"><?phpforeach ($productsas$product): ?> <optionvalue="<?= escape($product['id']) ?>"><?= escape($product['nazwa']) ?></option><?phpendforeach; ?> </select> <inputtype="number"name="weight"min="1"step="1"requiredaria-label="Liczba kilogramów"> <buttontype="submit">Zamów</button> </form><?phpif ($order !== null): ?> <p><?= escape($order['rodzaj']) ?><?= escape($order['nazwa']) ?> wartość: <?= number_format($value, 2, ',', ' ') ?> zł</p><?phpelseif ($message !== ''): ?> <p><?= escape($message) ?></p><?phpendif; ?> </section> </main> <footer> <p>Stronę opracował: Bartosz Bryniarski</p> </footer></body></html>
Na ilustracji 5 po zamówieniu na liście nadal stoi „pietruszka”. Strona ładuje się od nowa, więc lista sama wróciłaby do gruszki - dlatego opcja, której id jest równe wysłanemu, dostaje atrybut selected. Porównanie ścisłe === działa, bo PDO w PHP 8.2 oddaje kolumny INT jako liczby całkowite, a $productId po filter_var też jest liczbą.
$productId = null; przed warunkiem sprawia, że przy zwykłym wejściu porównanie ma z czym się wykonać i żadna opcja nie jest zaznaczona na siłę. Klucz tego nie ocenia - to zgodność z ilustracją i wygoda kupującego.
index.phpPHPzmienione: 2 linie
<?php// Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumntry {$pdo = new PDO('mysql:host=localhost;dbname=bazar;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);} catch (PDOException $exception) {http_response_code(500);exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');}functionescape(string|int|float|null$value): string{returnhtmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');}// Skrypt 1: dziesięć pierwszych towarów do paska zdjęć (zapytanie 1)$fruits = $pdo->query('SELECT nazwa, plik FROM towar LIMIT 10')->fetchAll();// Skrypt 2: wszystkie towary do listy rozwijanej (zapytanie 2)$products = $pdo->query('SELECT id, nazwa FROM towar')->fetchAll();// Skrypt 3: zamówienie - tylko po wysłaniu formularza (zapytania 3 i 4)$order = null;$message = '';$productId = null;if ($_SERVER['REQUEST_METHOD'] === 'POST') {$productId = filter_var($_POST['product'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);$weight = filter_var($_POST['weight'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'max_range' => 10000]]);if ($productId === false || $weight === false) {$message = 'Wybierz towar i podaj liczbę kilogramów - liczbę całkowitą większą od zera.'; } else {$statement = $pdo->prepare('SELECT rodzaj, nazwa, cena FROM towar WHERE id = :id');$statement->execute(['id' => $productId]);$order = $statement->fetch() ?: null;if ($order === null) {$message = 'Nie ma takiego towaru.'; } else {$value = $order['cena'] * $weight;$insert = $pdo->prepare('INSERT INTO zamowienie (id_towar, id_sklep, liczba_kg) VALUES (:product, 2, :weight)');$insert->execute(['product' => $productId, 'weight' => $weight]); } }}// Wszystkie dane pobrane i zapisane - zamknięcie połączenia$statement = $insert = null; // obiekty zapytań też trzymają połączenie$pdo = null;?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Zdrowy bazarek</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Zdrowy bazarek</h1> </header> <nav><?phpforeach ($fruitsas$fruit): ?> <imgsrc="<?= escape($fruit['plik']) ?>"alt="<?= escape($fruit['nazwa']) ?>"><?phpendforeach; ?> </nav> <main> <aside> <imgsrc="market.png"alt="bazarek"> </aside> <section> <p>Wybierz owoc lub warzywo i podaj jego wagę:</p> <formmethod="post"> <selectname="product"aria-label="Towar"><?phpforeach ($productsas$product): ?> <optionvalue="<?= escape($product['id']) ?>"<?= $product['id'] === $productId ? ' selected' : ''?>><?= escape($product['nazwa']) ?></option><?phpendforeach; ?> </select> <inputtype="number"name="weight"min="1"step="1"requiredaria-label="Liczba kilogramów"> <buttontype="submit">Zamów</button> </form><?phpif ($order !== null): ?> <p><?= escape($order['rodzaj']) ?><?= escape($order['nazwa']) ?> wartość: <?= number_format($value, 2, ',', ' ') ?> zł</p><?phpelseif ($message !== ''): ?> <p><?= escape($message) ?></p><?phpendif; ?> </section> </main> <footer> <p>Stronę opracował: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (index.php)
Etap 5: Porównanie z wersją podstawową
Kroki 45-46 · po tym etapie masz 40 z 40 kryteriów (pkt)
Krok 45: Co jest inaczej i dlaczego
ok. 3 min
Polecenie z arkusza
Ocenie będzie podlegać 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
układ
display: flex na <main>, width: 50% w regule bloków
display: grid i grid-template-columns: 50% 50%
kolory
wpisane w każdej regule
zmienne w :root
baza
mysqli_* z tabeli 1
PDO, wyjątki, FETCH_ASSOC
dane w zapytaniach
sklejane z napisem
parametry nazwane :id, :product, :weight
wejście
$_POST wprost
filter_var z zakresem, komunikat przy błędzie
wypisywanie
echo w pętli while
logika na górze, szablon z foreach i escape()
Czego tu jeszcze nie ma, a w prawdziwym sklepie by było: token CSRF w formularzu (ukryte pole z losową wartością z sesji, sprawdzane przy zapisie - blokuje wysłanie zamówienia z cudzej strony), logowanie kupującego zamiast stałego sklepu 2 i PRG - przekierowanie po zapisie z wynikiem pokazanym na kolejnej stronie, żeby F5 nie dublował zamówień. Klucz tego nie wymaga.
Czas przeznaczony na wykonanie zadania wynosi 150 minut.
Wersja podstawowa, gdy liczy się czas albo PDO nie jest jeszcze opanowane: funkcje mysqli_* są w tabeli 1 arkusza, a każdy kawałek PHP stoi tam, gdzie widać jego efekt.
Ta wersja, gdy PDO i siatka CSS są pisane z pamięci. Kodu jest podobnie dużo, a błąd łatwiej znaleźć: wyjątek PDO wskazuje złe zapytanie, zamiast zostawiać pustą listę.
Mieszanie też daje pełne punkty: siatka z tej wersji i mysqli_* z podstawowej. Najważniejsze, żeby pisać to, co się umie bez zaglądania do dokumentacji - na egzaminie jej nie ma.
Krok 47 · po tym etapie masz 40 z 40 kryteriów (pkt)
Krok 47: Plik przeglądarka.txt i nagranie płyty
ok. 5 min
Polecenie z arkusza
Po zakończeniu pracy utwórz plik tekstowy o nazwie przeglądarka.txt. Zapisz w nim nazwę przeglądarki internetowej, w której weryfikowana była poprawność działania witryny. Umieść go w folderze z numerem zdającego. Nagraj płytę z rezultatami pracy.
W przeglądarka.txt nazwa przeglądarki, w której strona była sprawdzana przez http://localhost/….
W folderze z numerem zdającego: import.png, index.php, jabłko.png, kw1.png - kw4.png, kwerendy.txt, market.png, przeglądarka.txt, styl.css oraz pozostałe zdjęcia owoców z archiwum (Skrypt 1 ich używa). Po nagraniu sprawdź odczyt płyty.
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.