INF.03-11-25.06-SGCzerwiec 2025 · 50 kroków · ok. 87 min · 37 kryteriów z klucza
Rozwiązanie krok po kroku: Portal sprzedaży opon (Wersja podstawowa)
Rozwiązanie buduje portal sprzedaży opon: baza opony z trzema tabelami i czterema operacjami (ORDER BY z LIMIT, warunek na klucz, losowy rekord przez ORDER BY RAND() LIMIT 1 ze złączeniem, UPDATE obniżający ceny o 25%), zdjęcie opony z przezroczystym tłem przeskalowane do 250 px, strona opony.php bez formularza, za to odświeżana co 10 sekund przez header('refresh'), style w styl.css z kropkowanym obramowaniem i opływaniem oraz trzy skrypty PHP: dziesięć najtańszych opon w blokach z ikoną sezonu, „opona dnia” i losowe zamówienie z wyliczoną wartością.
Kroki idą zdanie po zdaniu za treścią arkusza - każdy zaczyna się poleceniem z arkusza, a kod pokazuje dokładnie te linie, które je spełniają. Kod PHP jest najpierw pisany funkcjami z tabeli 1 arkusza (proceduralne mysqli), a na końcu zostaje przerobiony na wersję z prawdziwych projektów: wiersze czytane po nazwach kolumn, escapowanie danych, mapa ikon zamiast drabinki if i formatowanie kwot. W tym arkuszu nie ma formularza, więc nie ma też wstrzyknięć SQL do zablokowania - i nie ma JavaScriptu.
To jest wersja podstawowa. W zakładce „Nowoczesny kod (grid, PDO)” jest drugi sposób rozwiązania tego samego arkusza: układ na siatce CSS i PHP na PDO z logiką oddzieloną od HTML-u od pierwszej linii - obie wersje są zgodne z kluczem.
Baza i grafiki pochodzą z prawdziwego archiwum pliki8 (załącznik do pobrania na stronie arkusza, hasło w treści zadania). Zrzuty wyników zapytań są odtworzone z tej bazy - na egzaminie robisz je sam, z całego ekranu.
Jak liczą się punkty. Klucz ma 37 kryteriów (R.1.1 - R.5.10), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 28 punktów - można stracić 9 kryteriów i nadal zdać. 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 37 kryteriów (pkt)
Krok 1: Baza danych opony
Kryteria:
R.1.1 Wykonany import tabel do bazy danych opony , czynność udokumentowana w pliku import.png
ok. 2 min
Polecenie z arkusza
Utwórz bazę danych o nazwie opony, z zestawem polskich znaków (np. utf8_polish_ci).
W przeglądarce otwierasz http://localhost/phpmyadmin, klikasz Nowa w lewym panelu, wpisujesz nazwę opony i z listy metod porównywania wybierasz utf8_polish_ci. Uwaga na nazwy: baza to opony, plik do importu opona.sql, a tabela z oponami w środku też nazywa się opony - trzy podobne słowa, które łatwo pomylić.
Typowe błędy
Nazwa bazy musi być dokładnie opony - skrypty PHP łączą się z bazą po nazwie, więc literówka odetnie punkty także za skrypty (R.5.2).
Krok 2: Import tabel z pliku opona.sql
Kryteria:
R.1.1 Wykonany import tabel do bazy danych opony , czynność udokumentowana w pliku import.png
ok. 2 min
Polecenie z arkusza
Z rozpakowanego archiwum zaimportuj tabele z pliku opona.sql do utworzonej bazy.
Wejdź do bazy opony (musi być zaznaczona w lewym panelu), karta Import, Wybierz plik → opona.sql z rozpakowanego archiwum, Wykonaj.
Po imporcie są trzy tabele z ilustracji 1: klient (id, imie, nazwisko - 9 klientów), opony (nr_kat, producent, model, sezon, cena - 27 opon; sezon to letnia, zimowa albo uniwersalna) i zamowienie (id_zam, id, nr_kat, ilosc - 325 zamówień). Tabela zamowienie ma dwa klucze obce: id wskazuje klienta, nr_kat oponę - to po nr_kat złączysz ją z oponami w zapytaniu 3.
Typowe błędy
Import bez wcześniejszego wejścia do bazy opony tworzy tabele w innej bazie (np. test) - skrypty nie znajdą wtedy tabeli opony.
Krok 3: Zrzut ekranu import.png
Kryteria:
R.1.1 Wykonany import tabel do bazy danych opony , czynność udokumentowana w pliku 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.
Klawisz Print Screen (cały ekran, nie Alt+PrtScr, który łapie tylko aktywne okno), wklejasz do edytora grafiki i zapisujesz jako import.png w folderze z numerem zdającego. W tym arkuszu wszystkie zrzuty (import i cztery kwerendy) są w PNG. Na zrzucie ma być widać komunikat phpMyAdmin o pomyślnym imporcie albo trzy tabele w bazie.
Typowe błędy
Wykadrowany zrzut (samo okno przeglądarki, bez paska zadań) nie jest oceniany - klucz mówi to wprost.
Krok 4: Zapytanie 1: dziesięć najtańszych opon
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 pierwsze 10 rekordów z najtańszymi oponami (wszystkie pola). W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nr_kat, producent, model, sezon, cena FROM opony ORDER BY cena LIMIT 10; (lub SELECT * FROM opony ORDER BY cena LIMIT 10;) oraz wynik działania zapytania udokumentowany zrzutem, na którym widocznych jest dokładnie 10 rekordów z wszystkimi polami
ok. 3 min
Polecenie z arkusza
Zapytanie 1: wybierające pierwsze 10 rekordów z najtańszymi oponami (wszystkie pola).
Zakładka SQL w phpMyAdmin (przy zaznaczonej bazie opony). Klauzula po klauzuli:
SELECT * - „wszystkie pola”, więc tym razem gwiazdka jest właściwa (klucz dopuszcza też wypisanie pięciu kolumn).
FROM opony - tabela opon.
ORDER BY cena - od najtańszej (domyślne ASC).
LIMIT 10 - tylko pierwsze dziesięć wierszy posortowanego wyniku. Kolejność klauzul jest stała: LIMIT zawsze na końcu, po ORDER BY.
Wynik: 10 opon, od uLFA AL135 za 101,50 zł. Zapytanie kopiujesz do pliku kwerendy.txt i robisz zrzut: kw1.png. To samo zapytanie wyśle skrypt 1 do bloku bocznego. Zrzuty zapytań 1-3 robisz przed zapytaniem 4, które zmieni ceny - klucz to przewiduje i cen nie ocenia.
Typowe błędy
LIMIT 10 bez ORDER BY cena daje dziesięć pierwszych opon z tabeli, nie dziesięć najtańszych. ORDER BY cena DESC dałoby najdroższe.
kwerendy.txtTekstnowy plik
-- Zapytanie 1SELECT * FROM opony ORDER BY cena LIMIT 10;
Podgląd strony po tym kroku
Krok 5: Zapytanie 2: opona o numerze katalogowym 9
Kryteria:
R.1.4 Wykonane zapytanie 2 wybierające jedynie producenta, model, sezon i cenę opony o numerze katalogowym 9. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT producent, model, sezon, cena FROM opony WHERE nr_kat = 9; oraz wynik działania zapytania udokumentowany zrzutem, na którym widoczny jest dokładnie jeden rekord z danymi: uLFA, AL135 zimowa; oraz widoczna jest cena
ok. 1 min
Polecenie z arkusza
Zapytanie 2: wybierające jedynie producenta, model, sezon i cenę opony o numerze katalogowym 9.
SELECT producent, model, sezon, cena FROM opony WHERE nr_kat = 9 - cztery kolumny w kolejności z arkusza (bez nr_kat, bo „jedynie”) i warunek na klucz główny, więc wynik to jeden wiersz: uLFA, AL135, zimowa, 101,50 - dokładnie jak w kluczu. Zrzut: kw2.png. Skrypt 2 wyśle to zapytanie do sekcji „Opona dnia”.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT * FROM opony ORDER BY cena LIMIT 10;-- Zapytanie 2SELECT producent, model, sezon, cena FROM opony WHERE nr_kat = 9;
Podgląd strony po tym kroku
Krok 6: Zapytanie 3: jedno losowe zamówienie z modelem i ceną
Kryteria:
R.1.5 Wykonane zapytanie 3 wybierające jedynie identyfikator zamówienia i ilość oraz odpowiadające im model i cenę opony. Wybierany jest tylko jeden losowy rekord. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT id_zam, ilosc, model, cena FROM zamowienie JOIN opony USING (nr_kat) ORDER BY RAND() LIMIT 1; (lub porównanie kluczy po ON lub WHERE) oraz wynik działania zapytania udokumentowany zrzutem, na którym widoczny jest dokładnie jeden rekord z 4 polami
ok. 3 min
Polecenie z arkusza
Zapytanie 3: wybierające jedynie identyfikator zamówienia i ilość oraz odpowiadające im model i cenę opony. Wybierany jest tylko jeden losowy rekord. Należy zastosować relację.
Identyfikator i ilość są w zamowienie, model i cena w opony - „zastosować relację” znaczy złączyć tabele po nr_kat.
SELECT id_zam, ilosc, model, cena - cztery kolumny z dwóch tabel.
FROM zamowienie JOIN opony ON zamowienie.nr_kat = opony.nr_kat - do każdego zamówienia dokładasz jego oponę; kolumna nazywa się tak samo w obu tabelach, więc poprzedzasz ją nazwą tabeli (klucz CKE pokazuje skrót USING (nr_kat)).
ORDER BY RAND() LIMIT 1 - przepis z ramki w arkuszu: RAND() nadaje każdemu wierszowi losową liczbę, sortowanie po niej tasuje wiersze, a LIMIT 1 bierze pierwszy. Każde wykonanie zwraca inne zamówienie.
Wynik: jeden wiersz z czterema polami - na zrzucie kw3.png zamówienie 7: 4 sztuki modelu AL100 po 290,50 zł (ten sam rekord jest na ilustracji 2). Skrypt 3 wyśle to zapytanie do sekcji „Najnowsze zamówienie”.
Typowe błędy
LIMIT 1 bez ORDER BY RAND() zawsze zwraca to samo zamówienie - klucz wymaga losowania.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT * FROM opony ORDER BY cena LIMIT 10;-- Zapytanie 2SELECT producent, model, sezon, cena FROM opony WHERE nr_kat = 9;-- Zapytanie 3SELECT id_zam, ilosc, model, cena FROM zamowienie JOIN opony ON zamowienie.nr_kat = opony.nr_kat ORDER BY RAND() LIMIT 1;
Podgląd strony po tym kroku
Krok 7: Zapytanie 4: obniżka cen opon letnich o 25%
Kryteria:
R.1.6 Wykonane zapytanie 4 aktualizujące cenę opon letnich obniżając ją o 25%. W pliku z kwerendami lub na zrzucie istnieje zapis np.: UPDATE opony SET cena = cena - cena * 0.25 WHERE sezon = "letnia"; (opcjonalnie cena = 0.75 * cena) oraz wynik działania zapytania udokumentowany zrzutem, na którym jest komunikat o pomyślnym wykonaniu zapytania lub widoczna tabela ze zmienionymi wartościami
ok. 3 min
Polecenie z arkusza
Zapytanie 4: aktualizujące cenę opon letnich obniżając ją o 25%.
To nie SELECT, tylko UPDATE - zapytanie zmienia dane:
UPDATE opony - którą tabelę,
SET cena = cena * 0.75 - nowa wartość liczona ze starej: cena po obniżce o 25% to 75% ceny, czyli cena * 0.75 (klucz przyjmuje też cena - cena * 0.25),
WHERE sezon = "letnia" - tylko opony letnie. Bez WHERE przecena objęłaby wszystkie 27 opon i nie da się tego cofnąć.
phpMyAdmin odpowiada komunikatem „Zmodyfikowanych rekordów: 17” - to jest zrzut kw4.png (klucz przyjmuje też zrzut tabeli ze zmienionymi cenami). Plik kwerendy.txt ma komplet czterech zapytań. Od tej chwili strona pokaże niższe ceny opon letnich - ilustracja 2 w arkuszu pokazuje stan sprzed przeceny.
Typowe błędy
SET cena = 0.25 * cena obniża cenę o 75%, nie o 25%. Wykonanie zapytania dwa razy przecenia opony dwukrotnie - UPDATE nie jest idempotentny.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT * FROM opony ORDER BY cena LIMIT 10;-- Zapytanie 2SELECT producent, model, sezon, cena FROM opony WHERE nr_kat = 9;-- Zapytanie 3SELECT id_zam, ilosc, model, cena FROM zamowienie JOIN opony ON zamowienie.nr_kat = opony.nr_kat ORDER BY RAND() LIMIT 1;-- Zapytanie 4UPDATE opony SET cena = cena * 0.75 WHERE sezon = "letnia";
Podgląd strony po tym kroku
Etap 2: Grafika i strona HTML
Kroki 8-20 · po tym etapie masz 15 z 37 kryteriów (pkt)
Krok 8: Grafika opona.png: przezroczyste tło
Kryteria:
R.2.1 Grafika opona.png ma przezroczyste tło i widoczna jest opona
ok. 4 min
Polecenie z arkusza
Cechy grafiki opona.png: wykonana na podstawie grafiki opona.jpg i zapisana w formacie PNG. Tło przezroczyste.
Otwórz opona.jpg (385 × 542 px, opona na białym tle) w GIMP-ie. JPEG nie przechowuje przezroczystości, więc najpierw Warstwa → Przezroczystość → Dodaj kanał alfa. Potem narzędzie Zaznaczenie rozmyte (różdżka) z progiem ok. 20: klik w białe tło zaznacza cały obszar wokół opony, Edycja → Wyczyść (Delete) zamienia go w szachownicę przezroczystości. Białe odblaski wewnątrz felgi nie łączą się z tłem, więc różdżka ich nie zaznaczy. Zapis przez Eksportuj jako z rozszerzeniem .png - tylko PNG zachowa kanał alfa (klucz R.2.1: przezroczyste tło i widoczna opona).
Typowe błędy
Eksport do JPEG zamiast PNG spłaszcza przezroczystość na białe tło - kryterium R.2.1 pada, choć w GIMP-ie wszystko wyglądało dobrze.
Podgląd strony po tym kroku
Krok 9: Grafika opona.png: wysokość 250 px
Kryteria:
R.2.2 Grafika przeskalowana do wysokości 250 px, odpowiadająca temu szerokość wynosi 178 px (± 5 px)
ok. 2 min
Polecenie z arkusza
Przeskalowana z zachowaniem proporcji do wysokości 250 px.
Obraz → Skaluj obraz, wysokość 250 px z zamkniętym łańcuchem proporcji - szerokość wychodzi sama: 178 px (klucz: 178 ±5). Przeskaluj po usunięciu tła, na pełnej rozdzielczości różdżka pracuje dokładniej. Ponowny eksport do opona.png. Na stronie opona wyląduje w sekcji 1, gdzie ma 300 px wysokości - 250 px plus marginesy wewnętrzne obrazu zmieszczą się dokładnie.
Typowe błędy
Skalowanie do szerokości 250 px zamiast wysokości daje obraz 250 × 352 px - za wysoki na sekcję i niezgodny z kluczem.
R.2.3 W pliku opony.php zapisana deklaracja HTML5 <!DOCTYPE HTML>, deklaracja języka zawartości: polski np. <html lang="pl">, w sekcji head znajduje się jawnie podany standard kodowania polskich znaków
ok. 2 min
Polecenie z arkusza
Składa się ze strony o nazwie opony.php. Zapisana w języku HTML5.
W edytorze zaznaczającym składnię tworzysz plik opony.php w folderze zdającego, obok grafik. Rozszerzenie .php, bo dojdą skrypty PHP - na razie to czysty HTML.
Pierwsza linia <!DOCTYPE html> mówi przeglądarce, że dokument jest w HTML5. Dalej szkielet każdej strony: <html>, a w nim <head> (informacje o stronie) i <body> (to, co widać w oknie).
R.2.3 W pliku opony.php zapisana deklaracja HTML5 <!DOCTYPE HTML>, deklaracja języka zawartości: polski np. <html lang="pl">, w sekcji head znajduje się jawnie podany standard kodowania polskich znaków
ok. 1 min
Polecenie z arkusza
Zadeklarowany polski język zawartości witryny.
Atrybut lang="pl" w znaczniku <html>. Czytnik ekranu czyta wtedy tekst polską wymową, a przeglądarka dobiera słownik. Na stronie nic się nie zmienia - to informacja dla programów.
R.2.3 W pliku opony.php zapisana deklaracja HTML5 <!DOCTYPE HTML>, deklaracja języka zawartości: polski np. <html lang="pl">, w sekcji head znajduje się jawnie podany standard kodowania polskich znaków
ok. 1 min
Polecenie z arkusza
Jawnie zastosowany właściwy standard kodowania polskich znaków.
<meta charset="UTF-8"> jako pierwsza rzecz w <head>. „Jawnie” znaczy: wpisane w kodzie, nie zdane na domysł przeglądarki. Bez tej linii „Wartość zamówienia” i „zł” mogłyby wyświetlić się jako krzaki. Plik trzeba też zapisać w UTF-8.
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"> w <head>. W tym arkuszu nazwa to styl.css - sprawdź ją w treści zadania, bo w innych arkuszach bywa style.css albo styles.css. Plik napiszesz po HTML.
Typowe błędy
Zła nazwa pliku w href to brak stylów i cały rezultat R.4 przepada, bo „formatowanie CSS pochodzi jedynie z tego arkusza”.
R.2.5 W sekcji body zdefiniowany jest podział na bloki za pomocą semantycznych znaczników HTML 5: main->aside+section+section, footer
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. Zawartość bloku głównego: blok boczny, sekcja 1 i sekcja 2.
Z ilustracji 3: blok główny, a w nim blok boczny po lewej i dwie sekcje jedna pod drugą po prawej; na dole stopka. Nie ma bloku nagłówkowego - arkusz go nie wymienia i klucz R.2.5 też nie: main → aside + section + section, footer (nazwy w tabeli 2 arkusza).
Sekcje dostają identyfikatory offer (opona dnia) i order (najnowsze zamówienie), bo arkusz nadaje im różne tła - w CSS trzeba je rozróżnić. Nazwy identyfikatorów i zmiennych piszesz w tym rozwiązaniu po angielsku (arkusz dopuszcza polski lub angielski); nazwy narzucone przez arkusz (opony.php, styl.css, klasa opona, kolumny bazy) zostają.
Typowe błędy
Dodanie <header> „dla porządku” nie zaszkodzi, ale <aside> poza <main> nie spełnia R.2.5.
R.3.3 Utworzony odnośnik o treści „więcej ofert” jego kliknięcie otwiera stronę https://opona.pl/
ok. 1 min
Polecenie z arkusza
Zawartość bloku bocznego: efekt działania skryptu 1. Paragraf z odnośnikiem o treści „więcej ofert” prowadzący do strony https://opona.pl/.
Paragraf <p> z odnośnikiem <a href="https://opona.pl/"> - adres zewnętrzny w całości, z https://. Skrypt 1 (dziesięć bloków z oponami) dopiszesz nad paragrafem, bo arkusz wymienia go pierwszy. Klucz R.3.3 sprawdza, czy kliknięcie otwiera stronę; w podglądzie kliknięcia są zablokowane.
Typowe błędy
href="opona.pl" bez https:// to ścieżka względna - przeglądarka szuka pliku opona.pl w folderze strony.
R.2.7 W pierwszej sekcji umieszczona grafika opona z tekstem alternatywnym "Opona"
ok. 1 min
Polecenie z arkusza
Zawartość sekcji 1: grafika opona.png z tekstem alternatywnym o treści: „Opona”.
<img> ze src wskazującym przygotowany plik PNG i tekstem alternatywnym z arkusza (klucz R.2.7). Przezroczyste tło widać dopiero, gdy sekcja dostanie kolor w CSS - wtedy przez tło opony prześwituje fiolet, nie biały prostokąt.
R.2.6 Umieszczone w pierwszej sekcji <h2>: „Opona dnia”, w drugiej sekcji <h2>: „Najnowsze zamówienie", oraz paragraf z numerem zdającego w bloku stopki
ok. 1 min
Polecenie z arkusza
Nagłówek drugiego stopnia o treści: „Opona dnia”. Efekt działania skryptu 2.
<h2> pod obrazem. Skrypt 2 dopisze pod nim trzy kolejne <h2> z danymi opony numer 9. Na ilustracji 2 nagłówki stoją po prawej od opony - to zrobi opływanie float z CSS, nie HTML.
R.2.6 Umieszczone w pierwszej sekcji <h2>: „Opona dnia”, w drugiej sekcji <h2>: „Najnowsze zamówienie", oraz paragraf z numerem zdającego w bloku stopki
ok. 1 min
Polecenie z arkusza
Zawartość sekcji 2: nagłówek drugiego stopnia o treści: „Najnowsze zamówienie”. Efekt działania skryptu 3.
<h2> w drugiej sekcji; skrypt 3 dopisze pod nim dwa <h2> z losowym zamówieniem. Klucz R.2.6 sprawdza oba nagłówki sekcji razem z paragrafem stopki.
R.2.6 Umieszczone w pierwszej sekcji <h2>: „Opona dnia”, w drugiej sekcji <h2>: „Najnowsze zamówienie", oraz paragraf z numerem zdającego w bloku stopki
ok. 1 min
Polecenie z arkusza
Zawartość stopki: paragraf o treści „Stronę wykonał: ”, dalej wstawiony numer zdającego.
Paragraf <p> w <footer>. W tym rozwiązaniu po dwukropku stoi nazwisko autora - na egzaminie wpisujesz swój numer zdającego (PESEL), bo tego sprawdza klucz R.2.6. Struktura HTML jest kompletna - dalej style.
Kroki 21-30 · po tym etapie masz 26 z 37 kryteriów (pkt)
Krok 21: Plik styl.css: krój czcionki
Kryteria:
R.4.1 Ustawiony domyślnie dla wszystkich selektorów (*): krój czcionki "Trebuchet MS", sans- serif (tylko taka kolejność)
ok. 1 min
Polecenie z arkusza
Domyślnie dla wszystkich selektorów: krój czcionki Trebuchet MS, w przypadku braku sans-serif.
Utwórz plik styl.css obok opony.php. Selektor uniwersalny * obejmuje każdy element. font-family to lista: nazwa z dwóch słów w cudzysłowie ("Trebuchet MS"), potem rodzina zapasowa sans-serif - klucz R.4.1 mówi: tylko taka kolejność.
R.4.2 Ustawiony kolor tła dla bloku bocznego #C5CAE9, pierwszej sekcji #9FA8DA, drugiej sekcji i selektora h3 #7986CB, stopki #283593
R.4.4 Ustawiona wysokość bloku bocznego 600px obu sekcji 300px oraz szerokość bloku bocznego 40%, obu sekcji 60% (width lub grid-template-columns lub flex)
R.4.7 Ustawiona dla bloku bocznego cecha overflow: auto;
ok. 1 min
Polecenie z arkusza
Dla bloku bocznego: kolor tła #C5CAE9, szerokość 40%, wysokość 600 px, paski przewijania tylko w przypadku przepełnienia bloku.
Selektor znacznika aside. Wysokość 600 px to dokładnie dwie sekcje po 300 px - blok boczny ma sięgać od góry pierwszej do dołu drugiej. „Paski tylko przy przepełnieniu” to overflow: auto (klucz R.4.7): dziesięć bloków z oponami będzie znacznie wyższe niż 600 px, więc pojawi się pionowy pasek jak na ilustracji 2.
R.4.4 Ustawiona wysokość bloku bocznego 600px obu sekcji 300px oraz szerokość bloku bocznego 40%, obu sekcji 60% (width lub grid-template-columns lub flex)
ok. 1 min
Polecenie z arkusza
Dla obu sekcji: szerokość 60%, wysokość 300 px.
Selektor section obejmuje obie sekcje naraz. 40% bloku bocznego plus 60% sekcji daje 100% szerokości.
R.4.2 Ustawiony kolor tła dla bloku bocznego #C5CAE9, pierwszej sekcji #9FA8DA, drugiej sekcji i selektora h3 #7986CB, stopki #283593
ok. 1 min
Polecenie z arkusza
Dodatkowo kolor tła sekcji 1: #9FA8DA, sekcji 2: #7986CB.
Dwie reguły po identyfikatorach - dokładają tło do wspólnych wymiarów z poprzedniego kroku, nie kasują ich. Teraz widać, po co była przezroczystość opony: przez jej tło prześwituje fiolet sekcji.
R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z ilustracją 3 w arkuszu egzaminacyjnym (prawidłowo zastosowane 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)
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.
Trzy dopiski robią układ z ilustracji 3, wszystkie z float: left:
aside pływa w lewo i zajmuje 40% szerokości,
section - pierwsza sekcja staje obok bloku bocznego (40% + 60% = 100%), a druga nie ma już miejsca w tym wierszu, więc schodzi pod pierwszą - ale tylko na wysokość pierwszej sekcji (300 px), bo blok boczny po lewej ma 600 px i nadal zajmuje swoje 40%. Dwie sekcje układają się więc jedna pod drugą, na prawo od bloku bocznego,
footer dostaje clear: both, żeby wrócił pod wszystkie pływające bloki.
Klucz R.3.1 przyjmuje też flex i grid - przy dwóch sekcjach w kolumnie float wymaga najmniej kodu.
Typowe błędy
Bez clear: both stopka wjeżdża pod pływające bloki. display: flex na main ustawiłoby trzy bloki w jednym wierszu - sekcje obok siebie zamiast jedna pod drugą.
Krok 27: Klasa opona: tło, marginesy, obramowanie, zaokrąglenie
Kryteria:
R.4.10 Zdefiniowana klasa dla opony oraz jej styl: kolor tła #9FA8DA, margin: 40px; border: 2px dotted #283593; zaokrąglenie rogów 30px
ok. 2 min
Polecenie z arkusza
Dla klasy CSS opona, przypisanej do bloków generowanych w skrypcie 1: kolor tła #9FA8DA, marginesy zewnętrzne 40 px, obramowanie linią kropkowaną o szerokości 2 px i kolorze #283593, zaokrąglenie rogów 30 px.
Nazwę klasy narzuca arkusz: .opona (kropka = klasa). Obramowanie zapisujesz skrótem border: 2px dotted #283593 - szerokość, styl, kolor; „linia kropkowana” to dotted (dashed byłaby kreskowana). Klucz R.4.10 podaje dokładnie ten zapis. Bloków jeszcze nie ma - wygeneruje je skrypt 1; wtedy każdy dostanie to tło, margines 40 px z każdej strony i zaokrąglone rogi.
Typowe błędy
dashed to linia kreskowana, dotted kropkowana - arkusz mówi „kropkowana”. border-style bez szerokości i koloru nie spełni całego kryterium.
R.4.6 Ustawione marginesy wewnętrzne (padding) dla stopki oraz selektora img 20px
R.4.8 Ustawione dla obrazu opływanie tekstem po prawej stronie float: left;
ok. 1 min
Polecenie z arkusza
Dla selektora obrazu: opływanie tekstem po prawej stronie (obraz jest po lewej stronie), marginesy wewnętrzne 20 px.
Selektor znacznika img - zgodnie z uwagą arkusza bez klas. „Opływanie tekstem po prawej, obraz po lewej” to float: left (klucz R.4.8 podaje go wprost): obraz odsuwa się do lewej krawędzi, a nagłówki stają po jego prawej stronie. Reguła obejmie wszystkie obrazy: oponę w sekcji 1 i ikony sezonów w blokach skryptu 1 - na ilustracji 2 ikony też są po lewej z odstępem. Margines wewnętrzny 20 px to padding.
Krok 29: Nagłówki drugiego stopnia: wyśrodkowanie i marginesy
Kryteria:
R.4.5 Ustawione dla selektora h2 wyrównanie tekstu do środka oraz margines zewnętrzny (margin) 0 i margines wewnętrzny (padding) 15px
ok. 1 min
Polecenie z arkusza
Dla selektora nagłówka drugiego stopnia: wyrównanie tekstu do środka, marginesy zewnętrzne 0 px, marginesy wewnętrzne 15 px.
Selektor h2. margin: 0 zdejmuje domyślne odstępy nagłówka, a padding: 15px daje odstęp wewnątrz - dzięki temu cztery <h2> w sekcji 1 (jeden z HTML, trzy ze skryptu 2) zmieszczą się w 300 px obok opony. Klucz R.4.5 sprawdza wszystkie trzy właściwości.
Krok 30: Nagłówki trzeciego stopnia: tło, kolor, rozmiar
Kryteria: , ,
R.4.2 Ustawiony kolor tła dla bloku bocznego #C5CAE9, pierwszej sekcji #9FA8DA, drugiej sekcji i selektora h3 #7986CB, stopki #283593
R.4.3 Ustawiony biały kolor czcionki dla stopki i nagłówka trzeciego stopnia
R.4.9 Ustawiony dla nagłówka trzeciego stopnia rozmiar czcionki 140%
ok. 1 min
Polecenie z arkusza
Dla selektora nagłówka trzeciego stopnia: kolor tła #7986CB, biały kolor czcionki, rozmiar czcionki 140%.
Selektor h3. Na stronie nie ma jeszcze żadnego <h3> - wypisze je skrypt 1 (cena w każdym bloku). Reguła czeka: klucz R.4 dopuszcza ocenę po kodzie CSS tylko, gdy elementu nie ma w HTML, ale w tym rozwiązaniu elementy będą - z PHP. Rozmiar 140% liczy się względem czcionki rodzica. Arkusz stylów jest kompletny.
Kroki 31-46 · po tym etapie masz 37 z 37 kryteriów (pkt)
Krok 31: Odświeżanie strony co 10 sekund
Kryteria:
R.5.1 Strona jest odświeżana co 10 sekund
ok. 2 min
Polecenie z arkusza
Napisane w języku PHP. Strona jest odświeżana co 10 sekund.
Kod PHP piszesz między <?php a ?> - serwer wykonuje go, zanim strona trafi do przeglądarki. Odświeżanie robi funkcja z ramki w arkuszu: header('refresh: 10') wysyła przeglądarce nagłówek HTTP „odśwież tę stronę za 10 sekund”; bez url=, bo odświeżasz ten sam adres. Ramka ostrzega: nagłówek musi pójść przed jakimkolwiek wypisaniem - dlatego ten blok PHP stoi na samym początku pliku, nawet przed <!DOCTYPE html>. Spacja czy pusta linia przed <?php też jest „wypisaniem” i zepsułaby nagłówek. Po co odświeżanie: skrypt 3 losuje zamówienie, więc co 10 sekund sekcja 2 pokaże inne (podpis ilustracji 2 o tym mówi).
Od tego kroku podgląd to wynik działania strony po stronie serwera (PHP nie wykonuje się w przeglądarce) - na razie wygląda jak wcześniej, a odświeżania w podglądzie nie widać.
Typowe błędy
header() po <!DOCTYPE html> daje ostrzeżenie „headers already sent” i strona się nie odświeża. Znacznik <meta http-equiv="refresh"> też działa, ale arkusz podpowiada header() w sekcji o PHP.
R.5.2 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy opony oraz ostatnią operacją na bazie jest jej zamknięcie
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. 2 min
Polecenie z arkusza
Skrypty łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie opony.
W tym samym bloku, zaraz po nagłówku, otwierasz połączenie raz i trzymasz je w zmiennej $connection (znacząca, angielska nazwa - R.5.10), z której skorzystają trzy skrypty. mysqli_connect z tabeli 1 arkusza przyjmuje kolejno: serwer, użytkownika, hasło (puste) i nazwę bazy.
Typowe błędy
Kolejność argumentów ma znaczenie: mysqli_connect('localhost', 'root', 'opony') podaje „opony” jako HASŁO. Nazwa bazy jest czwarta. Plik sprawdzaj przez http://localhost/…, nie otwierając go z dysku.
R.5.3 Skrypt 1 zawiera instrukcję wysyłającą do bazy danych zapytanie 1 lub skrypt 2 - zapytanie 2 lub skrypt 3 - zapytanie 3
ok. 2 min
Polecenie z arkusza
Skrypt 1: wysyła do bazy danych zapytanie 1.
Skrypt 1 piszesz w bloku bocznym, nad paragrafem z odnośnikiem - tam mają trafić bloki z oponami. mysqli_query($connection, ...) wysyła zapytanie i zwraca wynik, który zapisujesz w $tyres (klucz R.5.3). Treść to dokładnie zapytanie 1 z kwerendy.txt: dziesięć najtańszych opon, wszystkie pola. Kolejność kolumn w SELECT * jest taka jak w tabeli: nr_kat, producent, model, sezon, cena - od niej zależą indeksy w kolejnych krokach.
Krok 34: Skrypt 1: blok z klasą opona dla każdego rekordu
Kryteria: ,
R.2.8 W bloku bocznym utworzony przynajmniej jeden blok z klasą CSS opona oraz nagłówek <h3>, <h4> (w HTML lub skrypcie)
R.5.4 Skrypt 1 tworzy tyle bloków, ile wybiera zapytanie
ok. 2 min
Polecenie z arkusza
Dla każdego zwróconego zapytaniem rekordu wyświetla blok z przypisaną klasą CSS opona, w którym znajdują się: obraz sezonu, nagłówek czwartego stopnia i nagłówek trzeciego stopnia.
„Dla każdego rekordu” to pętla: mysqli_fetch_row($tyres) (tabela 1) oddaje kolejny wiersz jako tablicę od zera, a gdy wierszy zabraknie - false, co kończy while. Każdy obieg otwiera <div class='opona'> i go zamyka - klasa z CSS, więc blok od razu dostaje fioletowe tło, kropkowaną ramkę i zaokrąglenie. Podgląd: dziesięć pustych bloków, tyle, ile zwraca zapytanie (klucz R.5.4). Zawartość dokładasz w trzech następnych krokach.
R.5.6 Skrypt 1 wyświetla obraz lato.png, gdy opona jest letnia, zima.png , gdy jest zimowa, uniwer.png gdy jest uniwersalna
ok. 3 min
Polecenie z arkusza
W przypadku opony letniej obraz lato.png, w przypadku zimowej obraz zima.png, w przypadku uniwersalnej obraz uniwer.png.
Trzy przypadki to drabinka if / elseif / else na kolumnie sezon ($row[3] - czwarta kolumna tabeli). Porównaj z dokładnymi wartościami z bazy: letnia, zimowa; wszystko inne (czyli uniwersalna) trafia do else. Każda gałąź wypisuje <img> z odpowiednim plikiem z archiwum; alt z nazwą sezonu dopisujesz dla porządku, arkusz go nie wymaga. Dzięki regule img z CSS ikona pływa po lewej z marginesem 20 px, jak na ilustracji 2.
Typowe błędy
Porównanie $row[3] == 'letnie' (inna forma słowa) nigdy nie będzie prawdziwe - wartość musi być dokładnie taka jak w kolumnie sezon.
Krok 36: Skrypt 1: nagłówek czwartego stopnia z producentem i modelem
Kryteria: ,
R.2.8 W bloku bocznym utworzony przynajmniej jeden blok z klasą CSS opona oraz nagłówek <h3>, <h4> (w HTML lub skrypcie)
R.5.5 Skrypt 1 wyświetla przynajmniej dla jednego rekordu teksty „Opona: <producent> <model>” oraz „Cena: <cena>”
ok. 1 min
Polecenie z arkusza
Nagłówek czwartego stopnia o treści „Opona: <producent><model>”.
<h4> z tekstem „Opona: ” i dwiema kolumnami: $row[1] producent, $row[2] model, rozdzielone spacją (klucz R.5.5 sprawdza ten format). Nagłówek staje po prawej od pływającej ikony.
Krok 37: Skrypt 1: nagłówek trzeciego stopnia z ceną
Kryteria:
R.5.5 Skrypt 1 wyświetla przynajmniej dla jednego rekordu teksty „Opona: <producent> <model>” oraz „Cena: <cena>”
ok. 1 min
Polecenie z arkusza
Nagłówek trzeciego stopnia o treści „Cena: <cena>”.
<h3> z ceną z piątej kolumny ($row[4]). Teraz zaczyna działać czekająca reguła h3 z CSS: ciemniejszy pasek, biały tekst, większa czcionka - blok wygląda jak na ilustracji 2. Skrypt 1 jest kompletny; blok boczny dostał pasek przewijania.
Typowe błędy
$row[3] to sezon, $row[4] to cena - przy SELECT * indeksy liczy się od zera po kolumnach tabeli.
R.5.3 Skrypt 1 zawiera instrukcję wysyłającą do bazy danych zapytanie 1 lub skrypt 2 - zapytanie 2 lub skrypt 3 - zapytanie 3
ok. 2 min
Polecenie z arkusza
Skrypt 2: wysyła do bazy zapytanie 2.
Skrypt 2 piszesz w sekcji 1, pod nagłówkiem „Opona dnia”. Zapytanie 2 z kwerendy.txt zwraca dokładnie jeden wiersz, więc nie potrzeba pętli - pojedyncze mysqli_fetch_row wkłada go do $row. Kolejność kolumn jak w SELECT: producent, model, sezon, cena.
Krok 39: Skrypt 2: nagłówek z producentem i modelem
Kryteria:
R.5.7 Skrypt 2 wyświetla zwrócony rekord w postaci: „<producent> model <model>”, „Sezon: <sezon>”, „Tylko <cena> zł!”
ok. 1 min
Polecenie z arkusza
Wyświetla w nagłówku drugiego stopnia treść „<producent> model <model>”.
<h2> z producentem ($row[0]), słowem „model” i modelem ($row[1]). Reguła h2 z CSS wyśrodkowuje go w miejscu obok pływającej opony. Podgląd: „uLFA model AL135”.
R.5.7 Skrypt 2 wyświetla zwrócony rekord w postaci: „<producent> model <model>”, „Sezon: <sezon>”, „Tylko <cena> zł!”
ok. 1 min
Polecenie z arkusza
W kolejnym nagłówku drugiego stopnia treść „Tylko <cena> zł!”.
Trzeci <h2>: „Tylko ”, cena z czwartej kolumny, „ zł!”. Kolumna decimal(10,2) oddaje cenę z dwoma miejscami po kropce: „Tylko 101.50 zł!”, jak na ilustracji 2. Skrypt 2 jest kompletny; cztery nagłówki mieszczą się obok opony dzięki margin: 0 z CSS.
R.5.3 Skrypt 1 zawiera instrukcję wysyłającą do bazy danych zapytanie 1 lub skrypt 2 - zapytanie 2 lub skrypt 3 - zapytanie 3
ok. 2 min
Polecenie z arkusza
Skrypt 3: wysyła do bazy zapytanie 3.
Skrypt 3 piszesz w sekcji 2, pod nagłówkiem. Zapytanie 3 z kwerendy.txt zwraca jeden losowy wiersz (ORDER BY RAND() LIMIT 1), więc znów bez pętli - mysqli_fetch_row do $row. Kolejność kolumn: id_zam, ilosc, model, cena. Przy każdym odświeżeniu strony (co 10 sekund) baza wylosuje inne zamówienie - podgląd pokazuje to z ilustracji 2 (zamówienie 7).
opony.phpPHPzmienione: 4 linie
<?phpheader('refresh: 10');$connection = mysqli_connect('localhost', 'root', '', 'opony');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>OPONY</title> <linkrel="stylesheet"href="styl.css"></head><body> <main> <aside><?php$tyres = mysqli_query($connection, 'SELECT * FROM opony ORDER BY cena LIMIT 10;');while ($row = mysqli_fetch_row($tyres)) {echo"<div class='opona'>";if ($row[3] == 'letnia') {echo"<img src='lato.png' alt='letnia'>"; } elseif ($row[3] == 'zimowa') {echo"<img src='zima.png' alt='zimowa'>"; } else {echo"<img src='uniwer.png' alt='uniwersalna'>"; }echo"<h4>Opona: $row[1]$row[2]</h4>";echo"<h3>Cena: $row[4]</h3>";echo'</div>';}?> <p><ahref="https://opona.pl/">więcej ofert</a></p> </aside> <sectionid="offer"> <imgsrc="opona.png"alt="Opona"> <h2>Opona dnia</h2><?php$offer = mysqli_query($connection, 'SELECT producent, model, sezon, cena FROM opony WHERE nr_kat = 9;');$row = mysqli_fetch_row($offer);echo"<h2>$row[0] model $row[1]</h2>";echo"<h2>Sezon: $row[2]</h2>";echo"<h2>Tylko $row[3] zł!</h2>";?> </section> <sectionid="order"> <h2>Najnowsze zamówienie</h2><?php$order = mysqli_query($connection, 'SELECT id_zam, ilosc, model, cena FROM zamowienie JOIN opony ON zamowienie.nr_kat = opony.nr_kat ORDER BY RAND() LIMIT 1;');$row = mysqli_fetch_row($order);?> </section> </main> <footer> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Krok 43: Skrypt 3: nagłówek z identyfikatorem, ilością i modelem
Kryteria:
R.5.9 Skrypt 3 wyświetla teksty „<id_zam> <ilosc> sztuki modelu <model>” lub „Wartość zamówienia <wartość> zł”, z wyliczoną wartością
ok. 1 min
Polecenie z arkusza
Wyświetla w nagłówku drugiego stopnia treść „<id_zam><ilosc> sztuki modelu <model>”.
<h2> składany z trzech kolumn i słów z arkusza: identyfikator ($row[0]), ilość ($row[1]), „sztuki modelu”, model ($row[2]). Podgląd: „7 4 sztuki modelu AL100” - dokładnie jak na ilustracji 2 (format jest z arkusza, nawet jeśli brzmi nienaturalnie).
opony.phpPHPzmienione: 1 linia
<?phpheader('refresh: 10');$connection = mysqli_connect('localhost', 'root', '', 'opony');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>OPONY</title> <linkrel="stylesheet"href="styl.css"></head><body> <main> <aside><?php$tyres = mysqli_query($connection, 'SELECT * FROM opony ORDER BY cena LIMIT 10;');while ($row = mysqli_fetch_row($tyres)) {echo"<div class='opona'>";if ($row[3] == 'letnia') {echo"<img src='lato.png' alt='letnia'>"; } elseif ($row[3] == 'zimowa') {echo"<img src='zima.png' alt='zimowa'>"; } else {echo"<img src='uniwer.png' alt='uniwersalna'>"; }echo"<h4>Opona: $row[1]$row[2]</h4>";echo"<h3>Cena: $row[4]</h3>";echo'</div>';}?> <p><ahref="https://opona.pl/">więcej ofert</a></p> </aside> <sectionid="offer"> <imgsrc="opona.png"alt="Opona"> <h2>Opona dnia</h2><?php$offer = mysqli_query($connection, 'SELECT producent, model, sezon, cena FROM opony WHERE nr_kat = 9;');$row = mysqli_fetch_row($offer);echo"<h2>$row[0] model $row[1]</h2>";echo"<h2>Sezon: $row[2]</h2>";echo"<h2>Tylko $row[3] zł!</h2>";?> </section> <sectionid="order"> <h2>Najnowsze zamówienie</h2><?php$order = mysqli_query($connection, 'SELECT id_zam, ilosc, model, cena FROM zamowienie JOIN opony ON zamowienie.nr_kat = opony.nr_kat ORDER BY RAND() LIMIT 1;');$row = mysqli_fetch_row($order);echo"<h2>$row[0]$row[1] sztuki modelu $row[2]</h2>";?> </section> </main> <footer> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (opony.php)
Krok 44: Skrypt 3: wartość zamówienia
Kryteria:
R.5.8 Skrypt 3 wylicza wartość zamówienia jako iloczyn ceny i ilości opon
ok. 1 min
Polecenie z arkusza
Wartość oznacza wyliczoną wartość na podstawie zwróconych z bazy ilości i ceny.
Wartość to iloczyn ilości i ceny: $row[1] * $row[3] do zmiennej $value (klucz R.5.8 sprawdza mnożenie). PHP sam zamienia napis „290.50” z bazy na liczbę przy mnożeniu. Dla zamówienia z ilustracji: 4 × 290,50 = 1162.
Typowe błędy
$row[1] * $row[2] mnoży ilość przez model (napis) - PHP 8 zgłosi błąd typu. Cena jest czwartą kolumną zapytania 3.
opony.phpPHPzmienione: 1 linia
<?phpheader('refresh: 10');$connection = mysqli_connect('localhost', 'root', '', 'opony');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>OPONY</title> <linkrel="stylesheet"href="styl.css"></head><body> <main> <aside><?php$tyres = mysqli_query($connection, 'SELECT * FROM opony ORDER BY cena LIMIT 10;');while ($row = mysqli_fetch_row($tyres)) {echo"<div class='opona'>";if ($row[3] == 'letnia') {echo"<img src='lato.png' alt='letnia'>"; } elseif ($row[3] == 'zimowa') {echo"<img src='zima.png' alt='zimowa'>"; } else {echo"<img src='uniwer.png' alt='uniwersalna'>"; }echo"<h4>Opona: $row[1]$row[2]</h4>";echo"<h3>Cena: $row[4]</h3>";echo'</div>';}?> <p><ahref="https://opona.pl/">więcej ofert</a></p> </aside> <sectionid="offer"> <imgsrc="opona.png"alt="Opona"> <h2>Opona dnia</h2><?php$offer = mysqli_query($connection, 'SELECT producent, model, sezon, cena FROM opony WHERE nr_kat = 9;');$row = mysqli_fetch_row($offer);echo"<h2>$row[0] model $row[1]</h2>";echo"<h2>Sezon: $row[2]</h2>";echo"<h2>Tylko $row[3] zł!</h2>";?> </section> <sectionid="order"> <h2>Najnowsze zamówienie</h2><?php$order = mysqli_query($connection, 'SELECT id_zam, ilosc, model, cena FROM zamowienie JOIN opony ON zamowienie.nr_kat = opony.nr_kat ORDER BY RAND() LIMIT 1;');$row = mysqli_fetch_row($order);echo"<h2>$row[0]$row[1] sztuki modelu $row[2]</h2>";$value = $row[1] * $row[3];?> </section> </main> <footer> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Krok 45: Skrypt 3: nagłówek z wartością zamówienia
Kryteria:
R.5.9 Skrypt 3 wyświetla teksty „<id_zam> <ilosc> sztuki modelu <model>” lub „Wartość zamówienia <wartość> zł”, z wyliczoną wartością
ok. 1 min
Polecenie z arkusza
W kolejnym nagłówku drugiego stopnia treść „Wartość zamówienia <wartość> zł”.
Drugi <h2> z wyliczoną wartością. Podgląd: „Wartość zamówienia 1162 zł”, jak na ilustracji 2 - PHP wypisuje liczbę bez zbędnych zer po przecinku. Skrypt 3 jest kompletny.
opony.phpPHPzmienione: 1 linia
<?phpheader('refresh: 10');$connection = mysqli_connect('localhost', 'root', '', 'opony');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>OPONY</title> <linkrel="stylesheet"href="styl.css"></head><body> <main> <aside><?php$tyres = mysqli_query($connection, 'SELECT * FROM opony ORDER BY cena LIMIT 10;');while ($row = mysqli_fetch_row($tyres)) {echo"<div class='opona'>";if ($row[3] == 'letnia') {echo"<img src='lato.png' alt='letnia'>"; } elseif ($row[3] == 'zimowa') {echo"<img src='zima.png' alt='zimowa'>"; } else {echo"<img src='uniwer.png' alt='uniwersalna'>"; }echo"<h4>Opona: $row[1]$row[2]</h4>";echo"<h3>Cena: $row[4]</h3>";echo'</div>';}?> <p><ahref="https://opona.pl/">więcej ofert</a></p> </aside> <sectionid="offer"> <imgsrc="opona.png"alt="Opona"> <h2>Opona dnia</h2><?php$offer = mysqli_query($connection, 'SELECT producent, model, sezon, cena FROM opony WHERE nr_kat = 9;');$row = mysqli_fetch_row($offer);echo"<h2>$row[0] model $row[1]</h2>";echo"<h2>Sezon: $row[2]</h2>";echo"<h2>Tylko $row[3] zł!</h2>";?> </section> <sectionid="order"> <h2>Najnowsze zamówienie</h2><?php$order = mysqli_query($connection, 'SELECT id_zam, ilosc, model, cena FROM zamowienie JOIN opony ON zamowienie.nr_kat = opony.nr_kat ORDER BY RAND() LIMIT 1;');$row = mysqli_fetch_row($order);echo"<h2>$row[0]$row[1] sztuki modelu $row[2]</h2>";$value = $row[1] * $row[3];echo"<h2>Wartość zamówienia $value zł</h2>";?> </section> </main> <footer> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (opony.php)
Krok 46: Zamknięcie połączenia z bazą
Kryteria:
R.5.2 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy opony oraz ostatnią operacją na bazie jest jej zamknięcie
ok. 1 min
Polecenie z arkusza
Na końcu zamykane jest połączenie z bazą.
mysqli_close($connection) na końcu pliku, po wszystkich trzech skryptach. Klucz R.5.2 sprawdza, że zamknięcie jest ostatnią operacją na bazie. Strona spełnia wszystkie kryteria arkusza - dalej trzy kroki „jak w prawdziwym projekcie”.
Typowe błędy
mysqli_close po skrypcie 1 sprawia, że skrypty 2 i 3 idą po zamkniętym połączeniu - sekcje zostają puste.
opony.phpPHPzmienione: 3 linie
<?phpheader('refresh: 10');$connection = mysqli_connect('localhost', 'root', '', 'opony');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>OPONY</title> <linkrel="stylesheet"href="styl.css"></head><body> <main> <aside><?php$tyres = mysqli_query($connection, 'SELECT * FROM opony ORDER BY cena LIMIT 10;');while ($row = mysqli_fetch_row($tyres)) {echo"<div class='opona'>";if ($row[3] == 'letnia') {echo"<img src='lato.png' alt='letnia'>"; } elseif ($row[3] == 'zimowa') {echo"<img src='zima.png' alt='zimowa'>"; } else {echo"<img src='uniwer.png' alt='uniwersalna'>"; }echo"<h4>Opona: $row[1]$row[2]</h4>";echo"<h3>Cena: $row[4]</h3>";echo'</div>';}?> <p><ahref="https://opona.pl/">więcej ofert</a></p> </aside> <sectionid="offer"> <imgsrc="opona.png"alt="Opona"> <h2>Opona dnia</h2><?php$offer = mysqli_query($connection, 'SELECT producent, model, sezon, cena FROM opony WHERE nr_kat = 9;');$row = mysqli_fetch_row($offer);echo"<h2>$row[0] model $row[1]</h2>";echo"<h2>Sezon: $row[2]</h2>";echo"<h2>Tylko $row[3] zł!</h2>";?> </section> <sectionid="order"> <h2>Najnowsze zamówienie</h2><?php$order = mysqli_query($connection, 'SELECT id_zam, ilosc, model, cena FROM zamowienie JOIN opony ON zamowienie.nr_kat = opony.nr_kat ORDER BY RAND() LIMIT 1;');$row = mysqli_fetch_row($order);echo"<h2>$row[0]$row[1] sztuki modelu $row[2]</h2>";$value = $row[1] * $row[3];echo"<h2>Wartość zamówienia $value zł</h2>";?> </section> </main> <footer> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer><?phpmysqli_close($connection);?></body></html>
Kroki 47-49 · po tym etapie masz 37 z 37 kryteriów (pkt)
Krok 47: Refaktor: wiersze po nazwach kolumn
Kryteria: , ,
R.5.5 Skrypt 1 wyświetla przynajmniej dla jednego rekordu teksty „Opona: <producent> <model>” oraz „Cena: <cena>”
R.5.7 Skrypt 2 wyświetla zwrócony rekord w postaci: „<producent> model <model>”, „Sezon: <sezon>”, „Tylko <cena> zł!”
R.5.9 Skrypt 3 wyświetla teksty „<id_zam> <ilosc> sztuki modelu <model>” lub „Wartość zamówienia <wartość> zł”, z wyliczoną wartością
ok. 4 min
Polecenie z arkusza
Skrypt 1: wysyła do bazy danych zapytanie 1.
Wszystkie trzy skrypty czytają wiersze po numerach: $row[4] to cena, $row[3] sezon - kto czyta ten kod za miesiąc, musi zaglądać do zapytania, a przy SELECT * wystarczy dodać kolumnę do tabeli, żeby numery się przesunęły. mysqli_fetch_assoc oddaje wiersz jako tablicę po nazwach kolumn: $row['cena'], $row['sezon']. W napisie z cudzysłowem taki element trzeba objąć klamrami: {$row['model']}. Kod czyta się jak zdanie, a zmiana kolejności kolumn w zapytaniu niczego nie psuje. Podgląd bez zmian.
Typowe błędy
Klucz tablicy musi być dokładnie nazwą kolumny z wyniku zapytania, z wielkością liter włącznie: $row['Cena'] zamiast $row['cena'] daje ostrzeżenie „Undefined array key” i pustą wartość (sprawdzone w PHP 8.2).
opony.phpPHPzmienione: 12 linii
<?phpheader('refresh: 10');$connection = mysqli_connect('localhost', 'root', '', 'opony');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>OPONY</title> <linkrel="stylesheet"href="styl.css"></head><body> <main> <aside><?php$tyres = mysqli_query($connection, 'SELECT * FROM opony ORDER BY cena LIMIT 10;');while ($row = mysqli_fetch_assoc($tyres)) {echo"<div class='opona'>";if ($row['sezon'] == 'letnia') {echo"<img src='lato.png' alt='letnia'>"; } elseif ($row['sezon'] == 'zimowa') {echo"<img src='zima.png' alt='zimowa'>"; } else {echo"<img src='uniwer.png' alt='uniwersalna'>"; }echo"<h4>Opona: {$row['producent']}{$row['model']}</h4>";echo"<h3>Cena: {$row['cena']}</h3>";echo'</div>';}?> <p><ahref="https://opona.pl/">więcej ofert</a></p> </aside> <sectionid="offer"> <imgsrc="opona.png"alt="Opona"> <h2>Opona dnia</h2><?php$offer = mysqli_query($connection, 'SELECT producent, model, sezon, cena FROM opony WHERE nr_kat = 9;');$row = mysqli_fetch_assoc($offer);echo"<h2>{$row['producent']} model {$row['model']}</h2>";echo"<h2>Sezon: {$row['sezon']}</h2>";echo"<h2>Tylko {$row['cena']} zł!</h2>";?> </section> <sectionid="order"> <h2>Najnowsze zamówienie</h2><?php$order = mysqli_query($connection, 'SELECT id_zam, ilosc, model, cena FROM zamowienie JOIN opony ON zamowienie.nr_kat = opony.nr_kat ORDER BY RAND() LIMIT 1;');$row = mysqli_fetch_assoc($order);echo"<h2>{$row['id_zam']}{$row['ilosc']} sztuki modelu {$row['model']}</h2>";$value = $row['ilosc'] * $row['cena'];echo"<h2>Wartość zamówienia $value zł</h2>";?> </section> </main> <footer> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer><?phpmysqli_close($connection);?></body></html>
R.5.5 Skrypt 1 wyświetla przynajmniej dla jednego rekordu teksty „Opona: <producent> <model>” oraz „Cena: <cena>”
R.5.7 Skrypt 2 wyświetla zwrócony rekord w postaci: „<producent> model <model>”, „Sezon: <sezon>”, „Tylko <cena> zł!”
R.5.9 Skrypt 3 wyświetla teksty „<id_zam> <ilosc> sztuki modelu <model>” lub „Wartość zamówienia <wartość> zł”, z wyliczoną wartością
ok. 3 min
Polecenie z arkusza
Nagłówek czwartego stopnia o treści „Opona: <producent><model>”.
Ta strona nie ma formularza, ale nazwy producentów, modeli i sezonów ktoś kiedyś wpisał do bazy - a napis <script>…</script> w nazwie modelu wykonałby się u każdego, kto otworzy stronę (XSS). htmlspecialchars() zamienia <, >, ", & na encje, więc tekst wyświetla się dosłownie i nie staje się kodem. Napisy przechodzą przez funkcję do zmiennych o angielskich nazwach, a echo używa już tylko ich. Liczby (id_zam, ilosc, cena) zostają - nie niosą znaczników. Podgląd bez zmian.
Typowe błędy
Escapowanie przy zapisie do bazy (zamiast przy wypisywaniu) zniekształca dane - encje < trafiłyby do tabeli. Escapujesz na wyjściu.
opony.phpPHPzmienione: 10 linii
<?phpheader('refresh: 10');$connection = mysqli_connect('localhost', 'root', '', 'opony');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>OPONY</title> <linkrel="stylesheet"href="styl.css"></head><body> <main> <aside><?php$tyres = mysqli_query($connection, 'SELECT * FROM opony ORDER BY cena LIMIT 10;');while ($row = mysqli_fetch_assoc($tyres)) {echo"<div class='opona'>";if ($row['sezon'] == 'letnia') {echo"<img src='lato.png' alt='letnia'>"; } elseif ($row['sezon'] == 'zimowa') {echo"<img src='zima.png' alt='zimowa'>"; } else {echo"<img src='uniwer.png' alt='uniwersalna'>"; }$producer = htmlspecialchars($row['producent']);$model = htmlspecialchars($row['model']);echo"<h4>Opona: $producer$model</h4>";echo"<h3>Cena: {$row['cena']}</h3>";echo'</div>';}?> <p><ahref="https://opona.pl/">więcej ofert</a></p> </aside> <sectionid="offer"> <imgsrc="opona.png"alt="Opona"> <h2>Opona dnia</h2><?php$offer = mysqli_query($connection, 'SELECT producent, model, sezon, cena FROM opony WHERE nr_kat = 9;');$row = mysqli_fetch_assoc($offer);$producer = htmlspecialchars($row['producent']);$model = htmlspecialchars($row['model']);echo"<h2>$producer model $model</h2>";$season = htmlspecialchars($row['sezon']);echo"<h2>Sezon: $season</h2>";echo"<h2>Tylko {$row['cena']} zł!</h2>";?> </section> <sectionid="order"> <h2>Najnowsze zamówienie</h2><?php$order = mysqli_query($connection, 'SELECT id_zam, ilosc, model, cena FROM zamowienie JOIN opony ON zamowienie.nr_kat = opony.nr_kat ORDER BY RAND() LIMIT 1;');$row = mysqli_fetch_assoc($order);$model = htmlspecialchars($row['model']);echo"<h2>{$row['id_zam']}{$row['ilosc']} sztuki modelu $model</h2>";$value = $row['ilosc'] * $row['cena'];echo"<h2>Wartość zamówienia $value zł</h2>";?> </section> </main> <footer> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer><?phpmysqli_close($connection);?></body></html>
R.5.6 Skrypt 1 wyświetla obraz lato.png, gdy opona jest letnia, zima.png , gdy jest zimowa, uniwer.png gdy jest uniwersalna
ok. 3 min
Polecenie z arkusza
W przypadku opony letniej obraz lato.png, w przypadku zimowej obraz zima.png, w przypadku uniwersalnej obraz uniwer.png.
Dwie poprawki na czytelność i wygląd:
mapa zamiast drabinki if: tablica $icons wiąże sezon z plikiem, a $icons[$row['sezon']] ?? 'uniwer.png' bierze ikonę jednym wyrażeniem (operator ?? daje wartość domyślną, gdy klucza nie ma). Czwarty sezon to jeden wpis w tablicy, nie kolejny elseif. Stara drabinka zostaje w komentarzu,
number_format($cena, 2, ',', ' ') zapisuje kwoty po polsku: „1 162,00 zł” zamiast „1162 zł”, „101,50” zamiast „101.50”.
Na egzaminie wystarczy wersja z kroków wcześniejszych - klucz tego nie sprawdza. To już kod gotowy do prawdziwego użycia.
Typowe błędy
number_format zwraca napis - formatuj dopiero wynik mnożenia, nie cenę przed rachunkiem.
Krok 50 · po tym etapie masz 37 z 37 kryteriów (pkt)
Krok 50: 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 wpisujesz nazwę przeglądarki, w której sprawdzasz stronę przez http://localhost/….
W folderze z numerem zdającego muszą być: import.png, kw1.png … kw4.png, kwerendy.txt, lato.png, opona.png, opony.php, przeglądarka.txt, styl.css, uniwer.png, zima.png. Nagraj płytę, sprawdź jej odczyt i opisz numerem zdającego. Pusta lub nieczytelna płyta to zero punktów za wszystkie pięć rezultatów.
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.