INF.03-07-25.06-SGCzerwiec 2025 · 56 kroków · ok. 100 min · 42 kryteria z klucza
Rozwiązanie krok po kroku: Portal wycieczek turystycznych (Nowoczesny kod (grid, PDO))
To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza wyprawy z pięcioma zapytaniami, odbita grafika, strona index.php z podstronami, formularzem symulacji ceny i galerią ofert - i tak samo spełnia każde kryterium klucza. Inna jest droga:
siatka CSS (grid-template-columns: 50% 50%) zamiast pływających bloków i clear, kolory z arkusza w zmiennych CSS, przezroczystość w nowej składni rgb(0 131 143 / 0.7), formularz ułożony siatką bez <br>,
oferty jako <article class="wycieczka"> - każda oferta to samodzielna treść,
PHP na PDO: wszystkie operacje na bazie na początku pliku, zapytanie przygotowane, filter_input dla liczby osób, sprawdzenie daty, biała lista miejsc z bazy, kwota sformatowana number_format, funkcja e() przy każdym wypisaniu i config.php,
formularz pamięta wpisane dane po symulacji, jak na ilustracji 4.
Tam, gdzie klucz wymaga konkretnej formy - float: right dla obrazu, display: inline-block dla elementów listy - ta forma zostaje, a krok mówi dlaczego. Na końcu krótkie porównanie z wersją podstawową (zakładka obok).
Zdjęcia i baza pochodzą z prawdziwego archiwum pliki10 (załącznik na stronie arkusza), a podgląd każdego kroku z PHP to wynik prawdziwego uruchomienia strony na serwerze z tą bazą.
Jak liczą się punkty. Klucz ma 42 kryteria (R.1.1 - R.5.10), każde za 1 punkt. Próg zdania to 75%, czyli 32 punkty. Przy każdym kroku jest napisane, które kryteria spełnia.
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-8 · po tym etapie masz 7 z 42 kryteriów (pkt)
Krok 1: Baza danych wyprawy
Kryteria:
R.1.1 Wykonany import tabel do bazy danych wyprawy , czynność udokumentowana w pliku import.png
ok. 2 min
Polecenie z arkusza
Utwórz bazę danych o nazwie wyprawy, z zestawem polskich znaków (np. utf8_polish_ci).
phpMyAdmin → Nowa → nazwa wyprawy, porównywanie utf8_polish_ci. W SQL:
CREATE DATABASE wyprawy CHARACTER SET utf8 COLLATE utf8_polish_ci;
Porównywanie bazy odziedziczy tabela klienci z zapytania 5. Tabela miejsca z pliku wyprawy.sql ma utf8_polish_ci zapisane we własnej definicji i to ono ustala kolejność w zapytaniu 1.
Typowe błędy
Literówka w nazwie bazy odcina też punkty za skrypty (R.5.1).
Krok 2: Import tabel z pliku wyprawy.sql
Kryteria:
R.1.1 Wykonany import tabel do bazy danych wyprawy , czynność udokumentowana w pliku import.png
ok. 2 min
Polecenie z arkusza
Z rozpakowanego archiwum zaimportuj tabele z pliku wyprawy.sql do utworzonej bazy.
Zaznacz bazę wyprawy, karta Import, plik wyprawy.sql, Wykonaj. Tabele miejsca (21 miejsc, link_obraz = nazwa pliku zdjęcia) i wycieczki (52 wycieczki, id_miejsca to klucz obcy).
Krok 3: Zrzut ekranu import.png
Kryteria:
R.1.1 Wykonany import tabel do bazy danych wyprawy , 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ń.
Cały ekran z paskiem zadań, zapis jako import.png. W tym arkuszu wszystkie zrzuty są w PNG.
Typowe błędy
Wykadrowany zrzut nie jest oceniany.
Krok 4: Zapytanie 1: nazwy miejscowości rosnąco
Kryteria: ,
R.1.2 Zapisany plik kwerendy.txt zawierający co najmniej jedno zapytanie SQL, wynikające z treści zadania
R.1.3 Wykonane zapytanie 1 wybierające jedynie nazwy wszystkich miejscowości posortowane rosnąco. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa FROM miejsca ORDER BY nazwa; oraz wynik działania zapytania udokumentowany zrzutem, na którym widocznych jest dokładnie 21 rekordów: pierwszy Brno, ostatni Wiedeń
ok. 2 min
Polecenie z arkusza
Zapytanie 1: wybierające jedynie nazwy wszystkich miejscowości posortowane rosnąco.
Jedna kolumna, kierunek sortowania wpisany jawnie (ASC). Wynik: 21 miejsc od Brna do Wiednia (R.1.3), zapytanie do kwerendy.txt, zrzut kw1.png. Ta lista wypełni listę rozwijaną i posłuży jako biała lista miejsc w skrypcie 2.
kwerendy.txtTekstnowy plik
-- Zapytanie 1SELECT nazwaFROM miejscaORDER BY nazwa ASC;
Podgląd strony po tym kroku
Krok 5: Zapytanie 2: cena wycieczki do Malborka
Kryteria:
R.1.4 Wykonane zapytanie 2 wybierające jedynie cenę wycieczki do Malborka. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT cena FROM miejsca WHERE nazwa = "Malbork"; oraz wynik działania zapytania udokumentowany zrzutem, na którym widoczna jest wartość 2000.00
ok. 1 min
Polecenie z arkusza
Zapytanie 2: wybierające jedynie cenę wycieczki do Malborka.
Napis w apostrofach - standard SQL (cudzysłów jako napis rozumie tylko MySQL). Wynik: 2000.00 (R.1.4), zrzut kw2.png.
kwerendy.txtTekstzmienione: 5 linii
-- Zapytanie 1SELECT nazwaFROM miejscaORDER BY nazwa ASC;-- Zapytanie 2SELECT cenaFROM miejscaWHERE nazwa = 'Malbork';
Podgląd strony po tym kroku
Krok 6: Zapytanie 3: miejsca ze zdjęciem o nazwie od zera
Kryteria:
R.1.5 Wykonane zapytanie 3 wybierające jedynie nazwę, cenę i link obrazu z tabeli miejsca dla wierszy, w których link obrazu rozpoczyna się od 0 (zero). W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa, cena, link_obraz FROM miejsca WHERE link_obraz LIKE "0%"; oraz wynik działania zapytania udokumentowany zrzutem, na którym widocznych jest dokładnie 9 rekordów, pierwszy Malbork 2000, 01.jpg ; ostatni Olsztyn 660 09.jpg
ok. 2 min
Polecenie z arkusza
Zapytanie 3: wybierające jedynie nazwę, cenę i link obrazu z tabeli miejsca dla wierszy, w których link obrazu rozpoczyna się od 0 (zero).
LIKE '0%' - zero na początku, potem cokolwiek (% tylko na końcu wzorca). Wynik: 9 miejsc, od Malborka (01.jpg) do Olsztyna (09.jpg) (R.1.5), zrzut kw3.png.
Typowe błędy
LIKE '%0%' znajdzie też 10.jpg i 20.jpg.
kwerendy.txtTekstzmienione: 5 linii
-- Zapytanie 1SELECT nazwaFROM miejscaORDER BY nazwa ASC;-- Zapytanie 2SELECT cenaFROM miejscaWHERE nazwa = 'Malbork';-- Zapytanie 3SELECT nazwa, cena, link_obrazFROM miejscaWHERE link_obraz LIKE '0%';
Podgląd strony po tym kroku
Krok 7: Zapytanie 4: średnia liczba dorosłych na miejsce
Kryteria:
R.1.6 Wykonane zapytanie 4 wybierające jedynie nazwy wszystkich miejsc i odpowiadające im obliczone średnie liczby osób dorosłych, zgrupowane według nazw miejsc. Średnia liczba osób jest zaokrąglona do liczby całkowitej. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa, ROUND(AVG(liczba_doroslych)) FROM miejsca JOIN wycieczki USING(id_miejsca) GROUP BY nazwa; (lub porównanie kluczy po ON lub WHERE, dowolna metoda zaokrąglenia do liczby całkowitej) oraz wynik działania zapytania udokumentowany zrzutem, na którym widocznych jest dokładnie 16 rekordów dla Brna, Gdańska, ..., Warszawy, Wenecji
ok. 4 min
Polecenie z arkusza
Zapytanie 4: wybierające jedynie nazwy wszystkich miejsc i odpowiadające im obliczone średnie liczby osób dorosłych, zgrupowane według nazw miejsc. Średnia liczba osób jest zaokrąglona do liczby całkowitej. Należy posłużyć się relacją.
Złączenie z aliasami tabel (miejsca AS m, wycieczki AS w) i alias kolumny wyniku: AS srednio_doroslych - bez niego nagłówek kolumny brzmiałby ROUND(AVG(w.liczba_doroslych)), a w PHP trudno byłoby się do niej odwołać.
ROUND(AVG(...)) - najpierw średnia, potem zaokrąglenie wyniku,
GROUP BY m.id_miejsca, m.nazwa - grupa to jedno miejsce (po kluczu; dwa miejsca o tej samej nazwie by się nie zlały), arkusz każe grupować według nazwy, więc nazwa też jest w GROUP BY,
ORDER BY m.nazwa - kolejność alfabetyczna wprost, nie przypadkowa.
Wynik: 16 miejsc od Brna do Wenecji (R.1.6) - pięć miejsc bez żadnej wycieczki nie ma pary w złączeniu. Zrzut kw4.png.
Typowe błędy
AVG(ROUND(...)) zaokrągla liczby przed liczeniem średniej - średnia zostaje ułamkiem.
kwerendy.txtTekstzmienione: 7 linii
-- Zapytanie 1SELECT nazwaFROM miejscaORDER BY nazwa ASC;-- Zapytanie 2SELECT cenaFROM miejscaWHERE nazwa = 'Malbork';-- Zapytanie 3SELECT nazwa, cena, link_obrazFROM miejscaWHERE link_obraz LIKE '0%';-- Zapytanie 4SELECT m.nazwa, ROUND(AVG(w.liczba_doroslych)) AS srednio_doroslychFROM miejsca AS mJOIN wycieczki AS w ON w.id_miejsca = m.id_miejscaGROUP BY m.id_miejsca, m.nazwaORDER BY m.nazwa;
Podgląd strony po tym kroku
Krok 8: Zapytanie 5: nowa tabela klienci
Kryteria:
R.1.7 Wykonane zapytanie 5 tworzące tabelę klienci z kolumnami: Klucz podstawowy: id_klienta jako liczba całkowita dodatnia, autoinkrementowany, nie może przyjmować wartości NULL, imie typu tekstowego o długości 20 znaków, nazwisko typu tekstowego o długości 40 znaków, data_ur typu daty. W pliku z kwerendami lub na zrzucie istnieje zapis np.: CREATE TABLE klienci ( id_klienta INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, imie VARCHAR(20), nazwisko VARCHAR(40), data_ur DATE ); oraz wynik działania zapytania udokumentowany zrzutem, na którym jest komunikat o poprawnym wykonaniu zapytania lub struktura tabeli klienci
ok. 3 min
Polecenie z arkusza
Zapytanie 5: tworzące tabelę klienci z kolumnami: id_klienta jako liczba całkowita dodatnia, klucz podstawowy, autoinkrementowany, nie może przyjmować wartości NULL; imie typu tekstowego o długości do 20 znaków; nazwisko typu tekstowego o długości do 40 znaków; data_ur typu data.
Kolumny jedna pod drugą, a klucz podstawowy jako osobne ograniczenie tabeli na końcu: PRIMARY KEY (id_klienta). To ten sam klucz co dopisek przy kolumnie, ale ten zapis pozwala później zbudować klucz z dwóch kolumn i od razu widać, co jest kluczem. Typy: INT UNSIGNED NOT NULL AUTO_INCREMENT, VARCHAR(20), VARCHAR(40), DATE (R.1.7). Zrzut kw5.png.
Typowe błędy
AUTO_INCREMENT w kolumnie, która nie jest kluczem, to błąd MySQL.
kwerendy.txtTekstzmienione: 9 linii
-- Zapytanie 1SELECT nazwaFROM miejscaORDER BY nazwa ASC;-- Zapytanie 2SELECT cenaFROM miejscaWHERE nazwa = 'Malbork';-- Zapytanie 3SELECT nazwa, cena, link_obrazFROM miejscaWHERE link_obraz LIKE '0%';-- Zapytanie 4SELECT m.nazwa, ROUND(AVG(w.liczba_doroslych)) AS srednio_doroslychFROM miejsca AS mJOIN wycieczki AS w ON w.id_miejsca = m.id_miejscaGROUP BY m.id_miejsca, m.nazwaORDER BY m.nazwa;-- Zapytanie 5CREATE TABLE klienci ( id_klienta INT UNSIGNED NOT NULL AUTO_INCREMENT, imie VARCHAR(20), nazwisko VARCHAR(40), data_ur DATE, PRIMARY KEY (id_klienta));
Podgląd strony po tym kroku
Etap 2: Grafika i strona HTML
Kroki 9-27 · po tym etapie masz 19 z 42 kryteriów (pkt)
Krok 9: Grafika 02.jpg: szerokość 200 px i odbicie pionowe
Kryteria:
R.2.1 Grafika 02.jpg przeskalowana z zachowaniem proporcji do szerokości 200 px, odpowiadająca temu wysokość wynosi 134 px (± 5 px)
ok. 3 min
Polecenie z arkusza
Cechy grafiki 02.jpg: grafika przeskalowana z zachowaniem proporcji do szerokości 200 px. Grafika odbita pionowo.
02.jpg (800 × 536 px): Skaluj obraz do szerokości 200 px przy zamkniętym łańcuchu (134 px wysokości), potem Odbij pionowo (góra-dół), eksport pod tą samą nazwą (R.2.1).
Typowe błędy
„Odbij poziomo” to lustro lewo-prawo - arkusz mówi „pionowo”.
Składa się ze strony głównej index.php oraz podstron wczasy.html, wycieczki.html, allinclusive.html. Należy utworzyć trzy pliki podstron i zapisać w nich „Strona w trakcie budowy”.
Trzy podstrony ze szkieletem HTML5 (język, kodowanie, tytuł) i napisem z arkusza w paragrafie - bez <meta charset> „Strona w trakcie budowy” mogłaby wyświetlić się z krzakami. Odnośniki z nawigacji mają dokąd prowadzić (R.3.3).
allinclusive.htmlHTMLnowy plik
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>All inclusive</title></head><body> <p>Strona w trakcie budowy</p></body></html>
wczasy.htmlHTMLnowy plik
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Wczasy</title></head><body> <p>Strona w trakcie budowy</p></body></html>
wycieczki.htmlHTMLnowy plik
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Wycieczki</title></head><body> <p>Strona w trakcie budowy</p></body></html>
Podgląd strony po tym kroku (wczasy.html)
Krok 11: Plik index.php w HTML5
Kryteria:
R.2.2 W pliku index.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
Cechy strony index.php: zapisana w języku HTML5.
Plik index.php w folderze zdającego. <!DOCTYPE html> włącza tryb standardów HTML5, dalej szkielet: <html>, <head> i <body>. Rozszerzenie .php, bo na początku pliku stanie kod PHP.
R.2.2 W pliku index.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.
lang="pl" w <html> - czytnik ekranu czyta polską wymową, przeglądarka dobiera słownik i dzielenie wyrazów.
R.2.2 W pliku index.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 pierwszy element <head>. Ten sam standard ustawi połączenie z bazą (charset=utf8mb4 w PDO), więc polskie litery przejdą bez zmian całą drogę: baza → PHP → przeglądarka.
<title>Biuro turystyczne</title> - napis na karcie przeglądarki. Obok <meta name="viewport">: bez niego telefon pokazałby stronę pomniejszoną jak na monitorze. Arkusz tego nie wymaga, a w każdym współczesnym szablonie ta linia jest.
R.2.4 W sekcji body zdefiniowany jest podział na bloki za pomocą semantycznych znaczników HTML 5: nav, main->aside+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 i sekcja.
Znaczniki z klucza R.2.4: <nav>, <main> z <aside> i <section>, <footer>. Każdy występuje raz, więc CSS sięgnie po nie selektorami znaczników, bez identyfikatorów.
Typowe błędy
<aside> i <section> poza <main> nie spełniają R.2.4.
Zawartość bloku nawigacyjnego: lista punktowana zawierająca trzy elementy: odnośnik do podstrony wczasy.html o treści „Wczasy”, odnośnik do podstrony wycieczki.html o treści „Wycieczki”, odnośnik do podstrony allinclusive.html o treści „All inclusive”.
Menu jako lista - czytnik ekranu zapowie „lista, trzy elementy”, więc użytkownik wie, ile jest pozycji (R.2.6, R.3.3).
R.2.5 Umieszczony w bloku bocznym <h3>: „Twój cel wyprawy” i <h4>: „Koszt wycieczki", w sekcji <h3>: „Wycieczki” oraz paragraf z numerem zdającego w stopce
ok. 1 min
Polecenie z arkusza
Zawartość bloku bocznego: nagłówek trzeciego stopnia o treści: „Twój cel wyprawy”.
R.2.7 W bloku bocznym umieszczony formularz z listą rozwijaną, 3 polami edycyjnymi i przyciskiem „Symulacja ceny”
R.3.4 Formularz wysyła dane metodą POST do tego samego pliku (poprawne również bez atrybutu action)
ok. 1 min
Polecenie z arkusza
Formularz zgodny z ilustracją 2, wysyłający dane do tego samego pliku metodą bezpieczną, z zawartością: etykiety, lista rozwijana, pola edycyjne i przycisk.
method="post" bez action (R.2.7, R.3.4). Elementy ustawi jeden pod drugim siatka w CSS - bez znaczników <br>.
Typowe błędy
Brak method to GET - skrypt czytający dane POST nic nie dostanie.
R.2.7 W bloku bocznym umieszczony formularz z listą rozwijaną, 3 polami edycyjnymi i przyciskiem „Symulacja ceny”
R.3.6 Przynajmniej jedna etykieta jest powiązana z kontrolką, dwa pola edycyjne są typu number, jedno typu date
ok. 1 min
Polecenie z arkusza
Etykieta „Ile dorosłych?” powiązana z pierwszym polem edycyjnym. Pole edycyjne typu liczbowego.
type="number" z min="0" i step="1" - przeglądarka przyjmie tylko liczby całkowite od zera (R.3.6). Ten sam warunek sprawdzi PHP, bo żądanie można wysłać z pominięciem formularza.
R.2.5 Umieszczony w bloku bocznym <h3>: „Twój cel wyprawy” i <h4>: „Koszt wycieczki", w sekcji <h3>: „Wycieczki” oraz paragraf z numerem zdającego w stopce
ok. 1 min
Polecenie z arkusza
Nagłówek czwartego stopnia o treści „Koszt wycieczki”. Efekt działania skryptu 2.
<h4> pod formularzem; wynik skryptu 2 stanie pod nim (R.2.5).
R.2.5 Umieszczony w bloku bocznym <h3>: „Twój cel wyprawy” i <h4>: „Koszt wycieczki", w sekcji <h3>: „Wycieczki” oraz paragraf z numerem zdającego w stopce
ok. 1 min
Polecenie z arkusza
Zawartość sekcji: nagłówek trzeciego stopnia o treści: „Wycieczki”. Efekt działania skryptu 3.
<h3> w sekcji; oferty wygeneruje skrypt 3 (R.2.5).
R.2.5 Umieszczony w bloku bocznym <h3>: „Twój cel wyprawy” i <h4>: „Koszt wycieczki", w sekcji <h3>: „Wycieczki” oraz paragraf z numerem zdającego w stopce
ok. 1 min
Polecenie z arkusza
Zawartość stopki: paragraf o treści „Autor: ”, dalej wstawiony numer zdającego.
Tu nazwisko autora rozwiązania - na egzaminie wpisujesz swój numer zdającego (PESEL), bo tego sprawdza klucz R.2.5.
Kroki 28-37 · po tym etapie masz 30 z 42 kryteriów (pkt)
Krok 28: Plik styl.css: zmienne kolorów, krój i wyrównanie
Kryteria:
R.4.1 Ustawione domyślnie dla wszystkich selektorów (*): krój czcionki "Comic Sans MS", sans- serif (tylko taka kolejność ) oraz wyrównanie tekstu do środka
ok. 2 min
Polecenie z arkusza
Domyślnie dla wszystkich selektorów: krój czcionki Comic Sans MS, w przypadku braku sans-serif, wyrównanie tekstu do środka.
Na początku dwa kolory arkusza jako zmienne CSS: morski #00838F (tło nawigacji, stopki i ofert) i miętowy #B2DFDB (tekst nawigacji, odnośniki, blok boczny). Każdy występuje w kilku regułach - zmienna trzyma go w jednym miejscu. Potem * z krojem "Comic Sans MS", sans-serif i wyśrodkowaniem (R.4.1).
Typowe błędy
Inspektor przeglądarki pokazuje kolory obliczone ze zmiennych. Jeśli wolisz nie ryzykować sporu przy czytaniu samego pliku, wpisz kolory wprost - jak w wersji podstawowej.
styl.cssCSSnowy plik
:root {--teal: #00838F;--mint: #B2DFDB;}* {font-family: "Comic Sans MS", sans-serif;text-align: center;}
Podgląd strony po tym kroku (index.php)
Krok 29: Nawigacja i stopka: tło, kolor tekstu, margines wewnętrzny
Kryteria: , ,
R.4.2 Ustawiony kolor tła dla bloku nawigacyjnego i stopki #00838F, dla bloku bocznego #B2DFDB
R.4.3 Ustawiony kolor czcionki dla bloku nawigacyjnego, stopki i selektora a #B2DFDB
R.4.6 Ustawione marginesy wewnętrzne (padding) dla bloku nawigacyjnego i stopki 10px oraz marginesy zewnętrzne (margin): 7px dla selektora img i jedynie lewy 50px dla odnośnika
ok. 1 min
Polecenie z arkusza
Dla bloku nawigacyjnego i stopki: kolor tła #00838F, kolor czcionki #B2DFDB, marginesy wewnętrzne 10 px.
Kolory ze zmiennych, padding: 10px (R.4.2, R.4.3, R.4.6).
Krok 30: Blok boczny i sekcja: siatka, wysokość, paski przewijania
Kryteria: , ,
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)
R.4.4 Ustawione dla bloku bocznego i sekcji wysokość 600px oraz szerokość 50% (width lub grid-template-columns lub flex)
R.4.5 Ustawiona dla bloku bocznego i sekcji właściwość overflow: auto;
ok. 2 min
Polecenie z arkusza
Wspólne dla bloku bocznego i sekcji: szerokość 50%, wysokość 600 px, paski przewijania pojawiające się tylko w przypadku przepełnienia bloku.
<main> to siatka o dwóch kolumnach po 50% - blok boczny i sekcja od razu stoją obok siebie, a stopka poza <main> zostaje pod nimi bez clear (R.3.1). Szerokość 50% zapisana w grid-template-columns - klucz R.4.4 wymienia tę właściwość obok width. Wspólna reguła dla obu bloków to już tylko wysokość i overflow: auto (R.4.4, R.4.5).
Ten jeden krok zastępuje dwa z wersji podstawowej (float: left w obu blokach i clear: both w stopce).
Typowe błędy
width: 50% w bloku umieszczonym w kolumnie siatki liczy się od kolumny (25% strony) - szerokość daje się raz, w siatce.
Formularz zgodny z ilustracją 2, wysyłający dane do tego samego pliku metodą bezpieczną, z zawartością: etykiety, lista rozwijana, pola edycyjne i przycisk.
Formularz to siatka o jednej kolumnie: każda etykieta, pole i przycisk w osobnym wierszu, wyśrodkowane, z równym odstępem gap - jak na ilustracji 4, bez <br> w HTML. Klucz tego nie ocenia.
Krok 33: Klasa wycieczka: tło z przezroczystością, wymiary, zaokrąglenie
Kryteria:
R.4.9 Ustawiony styl klasy dla wycieczki: kolor tła #00838F z przezroczystością 0.7 lub RGBA(0,131,143,0.7) lub #00838fb3, wysokość 150px, marginesy zewnętrzne: górny i dolny 10px, boczne 100px, zaokrąglenie rogów 30px
ok. 3 min
Polecenie z arkusza
Dla klasy wycieczka, przypisanej do bloków tworzonych przez skrypt 3: kolor tła #00838F z przezroczystością 0.7, wysokość 150 px, marginesy zewnętrzne: górny i dolny 10 px, boczne 100 px, zaokrąglenie rogów 30 px.
rgb(0 131 143 / 0.7) - nowa składnia koloru: składowe oddzielone spacjami, a po ukośniku krycie. To ten sam kolor co rgba(0, 131, 143, 0.7) (inspektor tak go pokaże); 00 → 0, 83 → 131, 8F → 143 to #00838F w systemie dziesiętnym (R.4.9),
margin-block: 10px i margin-inline: 100px - marginesy „w kierunku bloku” (góra i dół) i „w kierunku tekstu” (lewa i prawa). Nazwy mówią wprost, czego dotyczą, zamiast skrótu 10px 100px, w którym trzeba pamiętać kolejność,
transition - przy najechaniu kursorem kolor zmieni się płynnie przez 0,2 s, a nie skokiem. Arkusz tego nie wymaga.
Typowe błędy
opacity: 0.7 przyciemniłby też obraz i tekst w bloku - przezroczyste ma być tylko tło.
R.4.10 Gdy kursor znajdzie się na odnośniku, jego kolor czcionki zmienia się na biały i dodawane jest podkreślenie lub gdy kursor znajdzie się na bloku ostylowanym klasą wycieczka, przezroczystość wynosi 1
ok. 1 min
Polecenie z arkusza
Gdy kursor znajdzie się na bloku z przypisaną klasą wycieczka, przezroczystość wynosi 1.
Ten sam kolor z kryciem 1 - pełny, nieprzezroczysty (R.4.10).
R.4.6 Ustawione marginesy wewnętrzne (padding) dla bloku nawigacyjnego i stopki 10px oraz marginesy zewnętrzne (margin): 7px dla selektora img i jedynie lewy 50px dla odnośnika
R.4.7 Ustawione dla obrazu opływanie po jego lewej stronie (float: right;) oraz zaokrąglenie rogów 30px
ok. 1 min
Polecenie z arkusza
Dla selektora obrazu: opływanie po jego lewej stronie (obraz jest po prawej stronie), zaokrąglenie rogów 30 px, marginesy zewnętrzne 7 px.
Tu nowoczesny układ ustępuje kluczowi: arkusz mówi o opływaniu, a klucz R.4.7 wymaga wprost float: right. Ofertę dałoby się ułożyć siatką (obraz w prawej kolumnie, tekst w lewej), ale za to punktu by nie było. float robi tu dokładnie to, do czego powstał - tekst opływa obraz (R.4.6, R.4.7).
Typowe błędy
float: left stawia obraz po lewej - nawias w arkuszu mówi: obraz po prawej.
Krok 36: Element listy: liniowo-blokowy, bez punktorów
Kryteria:
R.4.8 Ustawione dla selektora li: display: inline-block; punktory nie są widoczne oraz dla odnośnika styl bez podkreślenia text-decoration: none;
ok. 1 min
Polecenie z arkusza
Dla selektora elementu listy: sposób wyświetlania liniowo-blokowy, bez punktorów.
„Liniowo-blokowy” to dosłownie display: inline-block (R.4.8) - też forma narzucona przez klucz; menu w jednej linii dałoby się zrobić display: flex na liście, ale kryterium sprawdza właściwość elementu li.
Krok 37: Odnośniki: kolor, bez podkreślenia, lewy margines, stan pod kursorem
Kryteria: , , ,
R.4.3 Ustawiony kolor czcionki dla bloku nawigacyjnego, stopki i selektora a #B2DFDB
R.4.6 Ustawione marginesy wewnętrzne (padding) dla bloku nawigacyjnego i stopki 10px oraz marginesy zewnętrzne (margin): 7px dla selektora img i jedynie lewy 50px dla odnośnika
R.4.8 Ustawione dla selektora li: display: inline-block; punktory nie są widoczne oraz dla odnośnika styl bez podkreślenia text-decoration: none;
R.4.10 Gdy kursor znajdzie się na odnośniku, jego kolor czcionki zmienia się na biały i dodawane jest podkreślenie lub gdy kursor znajdzie się na bloku ostylowanym klasą wycieczka, przezroczystość wynosi 1
ok. 2 min
Polecenie z arkusza
Dla selektora odnośnika: kolor czcionki #B2DFDB, bez podkreślenia, jedynie margines zewnętrzny lewy 50 px. Gdy kursor znajdzie się na odnośniku, jego kolor czcionki zmienia się na biały i dodawane jest podkreślenie.
margin-left - „jedynie” lewy margines, więc nie margin-inline (to byłyby oba boki). Pod kursorem biały kolor i podkreślenie (R.4.3, R.4.6, R.4.8, R.4.10).
Kroki 38-54 · po tym etapie masz 42 z 42 kryteriów (pkt)
Krok 38: Dane połączenia w pliku config.php
Kryteria:
R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy wyprawy oraz ostatnią operacją na bazie jest jej zamknięcie
ok. 2 min
Polecenie z arkusza
Łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie wyprawy.
Dane logowania do bazy w osobnym pliku config.php, który tylko zwraca tablicę (return [...]). W prawdziwym projekcie hasło do bazy jest inne na każdym serwerze i nie trafia do repozytorium razem z kodem strony - wystarczy podmienić jeden plik. Arkusz dopuszcza dodatkowe pliki („ewentualnie inne przygotowane pliki”).
DSN (mysql:host=localhost;dbname=wyprawy;charset=utf8mb4) to adres bazy dla PDO w jednym napisie: rodzaj serwera, host, nazwa bazy i kodowanie połączenia. Plik bez HTML-u nie ma końcowego ?> - nic po nim nie wypisze się przypadkiem.
Typowe błędy
Plik config.php musi trafić do folderu z rezultatami - bez niego strona się nie połączy, a egzaminator nie znajdzie nazwy bazy.
config.phpPHPnowy plik
<?php// Dane połączenia z bazą - w jednym miejscu, poza plikiem stronyreturn ['dsn' => 'mysql:host=localhost;dbname=wyprawy;charset=utf8mb4','user' => 'root','password' => '',];
R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy wyprawy 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. 3 min
Polecenie z arkusza
Napisane w języku PHP. Łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie wyprawy.
Kod PHP stoi na samym początku pliku, przed <!DOCTYPE html>: najpierw wszystkie operacje na bazie, potem szablon strony, który tylko wypisuje gotowe dane. Klucz R.5 przyjmuje PDO tak samo jak MySQLi.
require 'config.php' - dane połączenia trafiają do $config,
new PDO(...) - połączenie i wybór bazy; opcja ERRMODE_EXCEPTION sprawia, że każdy błąd SQL zatrzymuje skrypt wyjątkiem, zamiast po cichu zwracać false, a FETCH_ASSOC - że wiersze przychodzą jako tablice z nazwami kolumn ($row['nazwa'] zamiast $row[1]),
try … catch (PDOException $error) - gdy baza nie działa, użytkownik widzi zwykły komunikat, a nie treść błędu z nazwą serwera i ścieżkami; http_response_code(500) mówi przeglądarce, że to błąd serwera.
Od tego kroku podgląd to prawdziwy wynik strony z serwera - na razie bez zmian.
Krok 40: Funkcja e(): bezpieczne wypisywanie tekstu
ok. 2 min
Polecenie z arkusza
Należy stosować znaczące nazewnictwo zmiennych i funkcji w języku polskim lub angielskim.
Każdy napis z bazy albo z formularza, który trafi do HTML-u, przejdzie przez e() (od escape). htmlspecialchars zamienia <, >, & i - dzięki ENT_QUOTES - oba cudzysłowy na encje, więc napis <script> zapisany kiedyś w bazie wyświetli się jako tekst, a nie wykona (ochrona przed XSS). Escapowanie robi się na wyjściu, tuż przed wypisaniem - w bazie zostają dane surowe.
Krótka nazwa to świadomy wyjątek od „znaczącego nazewnictwa”: e() występuje w szablonie wiele razy i jest powszechnie znaną konwencją (tak samo nazywa się w Laravelu). Typy string przy parametrze i wyniku mówią, czego funkcja oczekuje; liczby PHP zamieni na tekst sam.
R.5.3 Skrypt 1 wyświetla kolejne miejsca wycieczki jako elementy listy rozwijanej
ok. 2 min
Polecenie z arkusza
Każde zwrócone zapytaniem miejsce wycieczki wypisuje jako element listy rozwijanej.
Pętla w składni szablonu, jeden <option> na miejsce (R.5.3). Bez value formularz wyśle nazwę miejsca - skrypt 2 szuka ceny właśnie po nazwie. Lista ma 21 miejsc.
index.phpPHPzmienione: 3 linie
<?php$config = require'config.php';try {$db = new PDO($config['dsn'], $config['user'], $config['password'], [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$places = $db->query('SELECT nazwa FROM miejsca ORDER BY nazwa')->fetchAll(PDO::FETCH_COLUMN);} catch (PDOException $error) {http_response_code(500);exit('Baza danych jest chwilowo niedostępna. Spróbuj ponownie później.');}functione(string $text): string{returnhtmlspecialchars($text, ENT_QUOTES, 'UTF-8');}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Biuro turystyczne</title> <linkrel="stylesheet"href="styl.css"></head><body> <nav> <ul> <li><ahref="wczasy.html">Wczasy</a></li> <li><ahref="wycieczki.html">Wycieczki</a></li> <li><ahref="allinclusive.html">All inclusive</a></li> </ul> </nav> <main> <aside> <h3>Twój cel wyprawy</h3> <formmethod="post"> <labelfor="place">Miejsce wycieczki</label> <selectid="place"name="place"><?phpforeach ($placesas$placeName): ?> <option><?= e($placeName) ?></option><?phpendforeach; ?> </select> <labelfor="adults">Ile dorosłych?</label> <inputtype="number"id="adults"name="adults"min="0"step="1"> <labelfor="children">Ile dzieci?</label> <inputtype="number"id="children"name="children"min="0"step="1"> <labelfor="date">Termin</label> <inputtype="date"id="date"name="date"> <buttontype="submit">Symulacja ceny</button> </form> <h4>Koszt wycieczki</h4> </aside> <section> <h3>Wycieczki</h3> </section> </main> <footer> <p>Autor: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (index.php)
Krok 43: Skrypt 2: czy wysłano poprawne dane
ok. 4 min
Polecenie z arkusza
Skrypt 2: sprawdza, czy z formularza zostały wysłane dane, jeżeli tak, to: wysyła zapytanie 2, wylicza wartość wycieczki, wyświetla dwa paragrafy.
„Wysłane dane” to tu cztery wartości, każda sprawdzona na wejściu:
miejsce - biała lista: in_array($place, $places, true) przepuszcza tylko nazwę, która jest w bazie (lista ze skryptu 1),
liczby osób - filter_input z FILTER_VALIDATE_INT i min_range 0: liczba całkowita albo false/null; puste pole czy „-2” nie przejdą, a rachunek nie skończy się błędem TypeError,
termin: DateTimeImmutable::createFromFormat('!Y-m-d', ...) czyta datę w formacie z pola date, a porównanie z powrotem z tekstem odrzuca daty nieistniejące - „2024-02-31” PHP zamieniłby po cichu na 2 marca.
Dopiero gdy wszystkie cztery są poprawne, $cost przestanie być null. Przy pierwszym wejściu na stronę żaden warunek nie jest spełniony.
Krok 44: Skrypt 2: zapytanie 2 przygotowane z miejscem z formularza
Kryteria:
R.5.4 Skrypt 2 zawiera instrukcję wysyłającą do bazy danych zmodyfikowane zapytanie 2, w ten sposób, że sprawdzane jest miejsce wycieczki wysłane z formularza
ok. 3 min
Polecenie z arkusza
Wysyła do bazy danych zapytanie 2, zmodyfikowane w ten sposób, że sprawdzane jest miejsce wycieczki wysłane z formularza.
Zapytanie 2 z parametrem :place w miejscu „Malbork” (R.5.4). fetchColumn() zwraca samą cenę; kolumna decimal przychodzi jako napis „22530.00”, więc (float) zamienia ją jawnie na liczbę przed rachunkiem.
R.5.5 Skrypt 2 wylicza wartość wycieczki na podstawie wysłanej z formularza liczby dorosłych i dzieci oraz ceny zwróconej zapytaniem, przy czym dzieci płacą połowę ceny wycieczki
ok. 2 min
Polecenie z arkusza
Wylicza wartość wycieczki na podstawie wysłanej z formularza liczby dorosłych i dzieci oraz ceny zwróconej zapytaniem, przy czym dzieci płacą połowę ceny wycieczki.
R.5.7 Skrypt 2 wyświetla tekst „<wartość> złotych”, gdzie pole <> jest wyliczoną wartością kosztu wycieczki
ok. 2 min
Polecenie z arkusza
Wyświetla paragraf o treści <wartość> złotych, gdzie pole <> jest wyliczoną wartością wycieczki.
number_format($cost, 2, ',', ' ') zapisuje kwotę po polsku: dwa miejsca po przecinku, przecinek dziesiętny, spacja między tysiącami - „90 120,00 złotych”. Ilustracja 4 pokazuje gołą liczbę „90120”; klucz R.5.7 sprawdza wartość i słowo „złotych”, więc obie postacie są poprawne, a sformatowana czyta się lepiej i nie gubi groszy przy cenach nieparzystych (połowa 1 999 zł to 999,50).
Typowe błędy
number_format zwraca napis - formatuj dopiero wynik, nie liczby przed rachunkiem.
Formularz zgodny z ilustracją 2, wysyłający dane do tego samego pliku metodą bezpieczną.
Na ilustracji 4 po symulacji formularz nadal pokazuje Tokio, 2, 4 i datę. Po wysłaniu strona ładuje się od nowa, więc pola trzeba wypełnić z powrotem: opcja równa wysłanemu miejscu dostaje selected, a pola - value z wysłaną wartością. Każda wartość przez e(): to dane od użytkownika wstawiane w atrybut, a ENT_QUOTES w e() nie pozwoli cudzysłowowi zamknąć atrybutu. Klucz tego nie ocenia - to wygoda z ilustracji.
Krok 50: Skrypt 3: blok z klasą wycieczka dla każdego wiersza
Kryteria: ,
R.2.8 W sekcji umieszczony przynajmniej jeden blok z przypisaną klasą wycieczka (w skrypcie lub HTML)
R.5.8 Skrypt 3 dla każdego zwróconego zapytaniem wiersza tworzy blok. W bloku wyświetlane są miejsce i cena wycieczki
ok. 2 min
Polecenie z arkusza
Dla każdego zwróconego zapytaniem wiersza tworzy blok z przypisaną klasą CSS wycieczka zawierający: obraz, nagłówek drugiego stopnia i paragraf.
Blok oferty to <article> - samodzielna treść, która miałaby sens także wyjęta ze strony (jak wpis czy karta produktu). Arkusz mówi „blok z klasą wycieczka”, nie narzuca znacznika; <div> też by wystarczył, ale nic by nie mówił (R.2.8, R.5.8).
Krok 55 · po tym etapie masz 42 z 42 kryteriów (pkt)
Krok 55: Co jest inaczej i którą wersję wybrać na egzaminie
ok. 2 min
Obie wersje dają komplet 42 punktów. Różnice:
wersja podstawowa
wersja nowoczesna
układ
float: left + clear: both
main jako siatka 50% / 50%
kolory
wpisane w każdej regule
zmienne CSS, rgb(0 131 143 / 0.7)
oferta
<div class="wycieczka">
<article class="wycieczka">
baza
mysqli_*, miejsce sklejone z SQL
PDO, zapytanie przygotowane, config.php
dane z formularza
isset i is_numeric
biała lista, filter_input, kontrola daty
kwota
„90120 złotych”
„90 120,00 złotych” (number_format)
formularz po symulacji
pusty
pamięta dane
Klucz wymusza starsze formy w dwóch miejscach: float: right dla obrazu (R.4.7) i display: inline-block dla elementów listy (R.4.8) - obie wersje ich używają. Na egzaminie wersja podstawowa ma mniej kodu do napisania; nowoczesna wymaga znajomości PDO i DateTimeImmutable na pamięć. Symulacja ceny niczego nie zapisuje, więc w prawdziwej aplikacji formularz poszedłby metodą GET (wynik można wtedy zapisać w zakładce) - arkusz wymaga POST.
Krok 56 · po tym etapie masz 42 z 42 kryteriów (pkt)
Krok 56: 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 folderze z numerem zdającego: 01.jpg … 21.jpg, allinclusive.html, config.php, import.png, index.php, kw1.png … kw5.png, kwerendy.txt, przeglądarka.txt, styl.css, wczasy.html, wycieczki.html. Płytę sprawdź po nagraniu.
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.