INF.03-10-25.06-SGCzerwiec 2025 · 47 kroków · ok. 84 min · 39 kryteriów z klucza
Rozwiązanie krok po kroku: Portal szkoleniowy z bazą danych (Wersja podstawowa)
Rozwiązanie buduje portal szkoleniowy: baza szkolenia z trzema tabelami i czterema operacjami (sortowanie, prosty SELECT, INSERT z automatycznym kluczem, COUNT z GROUP BY i aliasem), zdjęcie kursu przeskalowane do 150 px, strona index.php z tabelą kursów wypełnianą z bazy i formularzem zapisu (trzy pola, lista rozwijana, przycisk), style w styl.css z cieniem bloku i odstępem między literami oraz trzy skrypty PHP: tabela kursów ze zdjęciami, lista kursów do listy rozwijanej i zapis uczestnika do bazy z komunikatem.
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: zapytanie przygotowane z bindowaniem dla INSERT, escapowanie danych i walidacja tego, co przychodzi z formularza. W tym arkuszu 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 zapytaniami przygotowanymi od pierwszej linii - obie wersje są zgodne z kluczem.
Baza i zdjęcia pochodzą z prawdziwego archiwum pliki9 (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 39 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 - 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 39 kryteriów (pkt)
Krok 1: Baza danych szkolenia
Kryteria:
R.1.1 Wykonany import tabel do bazy danych szkolenia , czynność udokumentowana w pliku import.png
ok. 2 min
Polecenie z arkusza
Utwórz bazę danych o nazwie szkolenia, z zestawem polskich znaków (np. utf8_polish_ci).
W przeglądarce otwierasz http://localhost/phpmyadmin, klikasz Nowa w lewym panelu, wpisujesz nazwę szkolenia i z listy metod porównywania wybierasz utf8_polish_ci. Uwaga na nazwy: baza nazywa się szkolenia, a plik do importu szkolimy.sql - to celowo różne słowa, żeby sprawdzić, czy czytasz uważnie.
Typowe błędy
Nazwa bazy musi być dokładnie szkolenia - skrypty PHP łączą się z bazą po nazwie, więc literówka odetnie punkty także za skrypty (R.5.1).
Krok 2: Import tabel z pliku szkolimy.sql
Kryteria:
R.1.1 Wykonany import tabel do bazy danych szkolenia , czynność udokumentowana w pliku import.png
ok. 2 min
Polecenie z arkusza
Z rozpakowanego archiwum zaimportuj tabele z pliku szkolimy.sql do utworzonej bazy.
Wejdź do bazy szkolenia (musi być zaznaczona w lewym panelu), karta Import, Wybierz plik → szkolimy.sql z rozpakowanego archiwum, Wykonaj.
Po imporcie są trzy tabele z ilustracji 1: kursy (kod, nazwa, cena, max_liczba_miejsc - 8 kursów; kod to liczba 1-8 i zarazem nazwa pliku zdjęcia), uczestnicy (id_uczestnika, imie, nazwisko, wiek - 29 osób) i kursy_uczestnicy (kod_kursu, id_uczestnika, data - 46 zapisów). Tabela łącząca kursy_uczestnicy nie ma własnego identyfikatora - jej klucz to para kluczy obcych z datą.
Typowe błędy
Import bez wcześniejszego wejścia do bazy szkolenia tworzy tabele w innej bazie (np. test) - skrypty nie znajdą wtedy tabeli kursy.
Krok 3: Zrzut ekranu import.png
Kryteria:
R.1.1 Wykonany import tabel do bazy danych szkolenia , 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. Uwaga na formaty w tym arkuszu: zrzut importu w PNG, zrzuty kwerend w JPEG - odwrotnie niż w wielu innych arkuszach. 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. Zły format pliku to też strata punktu.
Krok 4: Zapytanie 1: kursy od najtańszego
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 kod, nazwę i cenę kursów posortowane rosnąco według ceny. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT kod, nazwa, cena FROM kursy ORDER BY cena; (ASC opcjonalne) oraz wynik działania zapytania udokumentowany zrzutem, na którym widocznych jest dokładnie 8 rekordów dla wszystkich kursów, pierwszy rekord 5 gastronomia 1000, ostatni 7 spawanie 3200
ok. 2 min
Polecenie z arkusza
Zapytanie 1: wybierające jedynie kod, nazwę i cenę kursów posortowane rosnąco według ceny.
Zakładka SQL w phpMyAdmin (przy zaznaczonej bazie szkolenia). Klauzula po klauzuli:
SELECT kod, nazwa, cena - trzy kolumny w kolejności z arkusza; „jedynie” wyklucza SELECT * (nie chcesz max_liczba_miejsc).
FROM kursy - tabela kursów.
ORDER BY cena - sortowanie po cenie, rosnąco (domyślne ASC).
Wynik: 8 kursów, pierwszy 5 gastronomia 1000, ostatni 7 spawanie 3200 - dokładnie jak w kluczu. Zapytanie kopiujesz do pliku kwerendy.txt i robisz zrzut: kw1.jpg. To samo zapytanie wyśle skrypt 1 do tabeli na stronie - kolejność kolumn (kod, nazwa, cena) jest w nim istotna.
Typowe błędy
ORDER BY nazwa to nie to samo - arkusz każe sortować według ceny. Klucz sprawdza pierwszy i ostatni rekord.
kwerendy.txtTekstnowy plik
-- Zapytanie 1SELECT kod, nazwa, cena FROM kursy ORDER BY cena;
Podgląd strony po tym kroku
Krok 5: Zapytanie 2: nazwy wszystkich kursów
Kryteria:
R.1.4 Wykonane zapytanie 2 wybierające jedynie nazwy wszystkich kursów. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa FROM kursy; oraz wynik działania zapytania udokumentowany zrzutem, na którym widocznych jest dokładnie 8 rekordów: pierwszy jest język angielski, ostatni rysunek techniczny
ok. 1 min
Polecenie z arkusza
Zapytanie 2: wybierające jedynie nazwy wszystkich kursów.
Najprostsze zapytanie w arkuszu: SELECT nazwa FROM kursy - jedna kolumna, bez warunku i bez sortowania („wszystkich”). Wynik: 8 nazw w kolejności z tabeli, od języka angielskiego do rysunku technicznego. Zrzut: kw2.jpg. Skrypt 2 wyśle to zapytanie do listy rozwijanej, więc lista pokaże kursy w tej samej kolejności (ilustracja 4).
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT kod, nazwa, cena FROM kursy ORDER BY cena;-- Zapytanie 2SELECT nazwa FROM kursy;
Podgląd strony po tym kroku
Krok 6: Zapytanie 3: nowy uczestnik Tadeusz Wysocki
Kryteria:
R.1.5 Wykonane zapytanie 3 wstawiające rekord do tabeli uczestnicy o danych: Tadeusz Wysocki, wiek 36 lat. Klucz główny nadawany automatycznie. W pliku z kwerendami lub na zrzucie istnieje zapis np.: INSERT INTO `uczestnicy`(`imie`, `nazwisko`, `wiek`) VALUES ('Tadeusz','Wysocki','36'); oraz wynik działania zapytania udokumentowany zrzutem, na którym widoczny jest komunikat o poprawnym wstawieniu wiersza lub tabela ze wstawionym wierszem
ok. 3 min
Polecenie z arkusza
Zapytanie 3: wstawiające rekord do tabeli uczestnicy o danych: Tadeusz Wysocki, wiek 36 lat. Klucz główny nadawany automatycznie.
To nie SELECT, tylko INSERT - zapytanie zmienia dane:
INSERT INTO uczestnicy (imie, nazwisko, wiek) - do której tabeli i do których kolumn; „klucz główny nadawany automatycznie” znaczy, że kolumnę id_uczestnikapomijasz - ma AUTO_INCREMENT, więc baza sama nada kolejny numer (tu 30, bo w tabeli jest 29 osób).
VALUES ('Tadeusz', 'Wysocki', 36) - wartości w tej samej kolejności co kolumny; napisy w apostrofach, liczba bez.
phpMyAdmin odpowiada komunikatem „1 wiersz wstawiony” - to jest zrzut kw3.jpg (klucz przyjmuje też zrzut tabeli z nowym wierszem). Skrypt 3 wyśle to samo zapytanie z danymi z formularza.
Typowe błędy
Podanie własnego id_uczestnika (np. 30) nie spełnia „nadawany automatycznie”. VALUES bez listy kolumn wymagałoby czterech wartości, w tym klucza.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT kod, nazwa, cena FROM kursy ORDER BY cena;-- Zapytanie 2SELECT nazwa FROM kursy;-- Zapytanie 3INSERT INTO uczestnicy (imie, nazwisko, wiek) VALUES ('Tadeusz', 'Wysocki', 36);
Podgląd strony po tym kroku
Krok 7: Zapytanie 4: liczba zapisanych na każdy kurs
Kryteria:
R.1.6 Wykonane zapytanie 4 tworzące zestawienie, na którym widoczne są nazwy wszystkich kursów oraz liczby osób zapisanych na te kursy. Nazwa kolumny z liczbą osób (alias): „Zapisanych”. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT COUNT(kod_kursu) AS 'Zapisanych', nazwa FROM kursy JOIN kursy_uczestnicy ON kod = kod_kursu GROUP BY nazwa; (AS opcjonalne) oraz wynik działania zapytania udokumentowany zrzutem, na którym widoczne są dane: 6 florystyka, 12 gastronomia, 7 informatyka, 2 język angielski, 6 język francuski, 6 język niemiecki, 4 rysunek techniczny, 3 spawanie
ok. 4 min
Polecenie z arkusza
Zapytanie 4: tworzące zestawienie, na którym widoczne są nazwy wszystkich kursów oraz liczby osób zapisanych na te kursy. Nazwa kolumny z liczbą osób (alias): „Zapisanych”.
Nazwa jest w kursy, zapisy w kursy_uczestnicy - trzeba złączyć tabele i policzyć wiersze w grupach:
SELECT nazwa, COUNT(kod_kursu) AS "Zapisanych" - funkcja COUNT liczy wiersze; AS nadaje kolumnie alias z arkusza.
FROM kursy JOIN kursy_uczestnicy ON kod = kod_kursu - do każdego zapisu dokładasz kurs; nazwy kolumn są różne w obu tabelach, więc nie trzeba ich poprzedzać nazwą tabeli.
GROUP BY nazwa - zapisy tego samego kursu zlewają się w jedną grupę i COUNT liczy osobno dla każdej.
Wynik: 8 wierszy, od florystyki (6) do spawania (3); gastronomia ma najwięcej - 12. Zrzut: kw4.jpg. Plik kwerendy.txt ma komplet czterech zapytań.
Typowe błędy
COUNT bez GROUP BY zwróci jeden wiersz z liczbą wszystkich zapisów (46). Grupowanie jest tym, co robi z tego „zestawienie”.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT kod, nazwa, cena FROM kursy ORDER BY cena;-- Zapytanie 2SELECT nazwa FROM kursy;-- Zapytanie 3INSERT INTO uczestnicy (imie, nazwisko, wiek) VALUES ('Tadeusz', 'Wysocki', 36);-- Zapytanie 4SELECT nazwa, COUNT(kod_kursu) AS "Zapisanych" FROM kursy JOIN kursy_uczestnicy ON kod = kod_kursu GROUP BY nazwa;
Podgląd strony po tym kroku
Etap 2: Grafika i strona HTML
Kroki 8-24 · po tym etapie masz 17 z 39 kryteriów (pkt)
Krok 8: Grafika 1.jpg: szerokość 150 px
Kryteria:
R.2.1 Grafika 1.jpg przeskalowana z zachowaniem proporcji do szerokości 150 px, odpowiadająca temu wysokość wynosi 100 px (± 5 px)
ok. 3 min
Polecenie z arkusza
Cechy grafiki 1.jpg: przeskalowana z zachowaniem proporcji do szerokości 150 px.
Siedem zdjęć z archiwum (2.jpg - 8.jpg) ma już 150 × 100 px, tylko 1.jpg (flaga brytyjska, kurs języka angielskiego) jest większe: 450 × 300 px. W GIMP-ie: Obraz → Skaluj obraz, szerokość 150 px z zamkniętym łańcuchem proporcji - wysokość wychodzi 100 px (klucz: 100 ±5). Eksport pod tą samą nazwą 1.jpg. Wszystkie zdjęcia mają wtedy ten sam rozmiar i tabela na stronie wygląda równo.
Typowe błędy
Skalowanie bez proporcji (150 × 300) nie spełnia kryterium - klucz sprawdza wysokość 100 px.
Podgląd strony po tym kroku
Krok 9: 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. 2 min
Polecenie z arkusza
Składa się ze strony o nazwie index.php. Zapisana w języku HTML5.
W edytorze zaznaczającym składnię tworzysz plik index.php w folderze zdającego, obok zdjęć. 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.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.
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.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 pierwsza rzecz w <head>. „Jawnie” znaczy: wpisane w kodzie, nie zdane na domysł przeglądarki. Bez tej linii „język francuski” z bazy i „Wprowadź wszystkie dane” mogłyby wyświetlić się jako krzaki. Plik trzeba też zapisać w UTF-8.
<title> w <head> - napis na karcie przeglądarki, nie nagłówek na stronie. Klucz R.2.3 sprawdza tekst „Szkolenia i kursy”.
index.phpPHPzmienione: 1 linia
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Szkolenia i kursy</title></head><body></body></html>
Podgląd strony po tym kroku (index.php)
Krok 13: Podpięcie arkusza stylów
Kryteria:
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”.
index.phpPHPzmienione: 1 linia
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Szkolenia i kursy</title> <linkrel="stylesheet"href="styl.css"></head><body></body></html>
Podgląd strony po tym kroku (index.php)
Krok 14: Bloki semantyczne
Kryteria:
R.2.4 W sekcji body zdefiniowany jest podział na bloki za pomocą semantycznych znaczników HTML 5: header, main->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: sekcja lewa i sekcja prawa.
Z ilustracji 3: blok nagłówkowy na górze, pod nim blok główny z dwiema sekcjami obok siebie, na dole stopka. Klucz R.2.4 wymienia znaczniki wprost: <header>, <main> z dwiema <section>, <footer> (nazwy w tabeli 3 arkusza).
Sekcje dostają identyfikatory left i right, bo arkusz formatuje każdą inaczej (lewa 50% z paskiem przewijania, prawa 40% z cieniem) - 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 (index.php, styl.css, kolumny bazy) zostają.
Typowe błędy
<div> zamiast <section> nie jest znacznikiem semantycznym - klucz wymaga main → section + section.
Krok 15: Blok nagłówkowy: nagłówek pierwszego stopnia
Kryteria:
R.2.5 Umieszczony w bloku nagłówkowym <h1>: "SZKOLENIA”, w sekcji prawej <h2>: "Zapisy na kursy" oraz paragraf z numerem zdającego w stopce
ok. 1 min
Polecenie z arkusza
Zawartość bloku nagłówkowego: nagłówek pierwszego stopnia o treści: „SZKOLENIA”.
Pierwszego stopnia to <h1>, w <header>, wielkimi literami jak w arkuszu (klucz dopuszcza błędy wielkości liter, ale nie ma powodu ich robić). Rozstrzelone litery z ilustracji 2 zrobi CSS, nie spacje w tekście.
Krok 16: Sekcja lewa: tabela z wierszem nagłówkowym
Kryteria: ,
R.2.7 W sekcji lewej umieszczona tabela o 3 kolumnach (w skrypcie lub HTML) , zastosowane znaczniki <table>, <tr>, <td>
R.2.8 Pierwszy wiersz tabeli zawiera komórki nagłówkowe, zastosowane znaczniki <tr>, <th>
ok. 2 min
Polecenie z arkusza
Zawartość sekcji lewej: tabela o trzech kolumnach. Pierwszy wiersz zawiera komórki nagłówkowe: Kurs, Nazwa, Cena. Tabela wypełniona jest przez skrypt 1.
<table> z jednym wierszem <tr>, a w nim trzy komórki nagłówkowe <th> (klucz R.2.8 sprawdza właśnie th, nie td z pogrubieniem). Kolejne wiersze - zdjęcie, nazwa, cena dla każdego kursu - dopisze skrypt 1, więc w HTML tabela kończy się na nagłówku. Przeglądarka domyślnie pogrubia i wyśrodkowuje <th>, jak na ilustracji 2.
R.2.9 W sekcji prawej umieszczony formularz z 4 etykietami, 3 polami edycyjnymi, listą rozwijaną i przyciskiem. Przynajmniej jedna etykieta powiązana jest z kontrolką
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 zgodny z ilustracją 2, wysyłający dane do tego samego pliku metodą bezpieczną z polami: etykiety, pola edycyjne, lista rozwijana i przycisk.
„Metoda bezpieczna” w rozumieniu arkusza to POST - dane nie pojawiają się w adresie strony. W specyfikacji HTTP „bezpieczne” są co prawda metody, które niczego nie zmieniają na serwerze (GET i HEAD), ale arkusz używa tego słowa w innym sensie, a klucz wymaga POST. „Do tego samego pliku” to brak atrybutu action (przeglądarka wysyła formularz na adres bieżącej strony) albo action="index.php" - klucz R.3.3 przyjmuje oba warianty. Na ilustracji 2 każda etykieta stoi nad swoją kontrolką - stąd <br> po każdym elemencie w kolejnych krokach.
Typowe błędy
method="get" (albo brak method) wyśle dane w adresie - arkusz i klucz wymagają tu POST, a skrypt czytający $_POST nic nie dostanie.
R.2.9 W sekcji prawej umieszczony formularz z 4 etykietami, 3 polami edycyjnymi, listą rozwijaną i przyciskiem. Przynajmniej jedna etykieta powiązana jest z kontrolką
ok. 1 min
Polecenie z arkusza
Etykieta dla pierwszego pola edycyjnego: „Imię”. Pole edycyjne.
Etykieta <label> jest „powiązana” z polem przez atrybut for równy id pola (klucz R.2.9 sprawdza przynajmniej jedno takie powiązanie - kliknięcie etykiety ustawia kursor w polu). <input type="text"> to zwykłe pole edycyjne; name="name" to klucz, pod którym wartość trafi do $_POST w PHP.
Typowe błędy
Pole bez name nie jest wysyłane z formularzem - skrypt 3 nie dostanie imienia, choć pole wygląda poprawnie.
R.2.9 W sekcji prawej umieszczony formularz z 4 etykietami, 3 polami edycyjnymi, listą rozwijaną i przyciskiem. Przynajmniej jedna etykieta powiązana jest z kontrolką
ok. 1 min
Polecenie z arkusza
Etykieta dla drugiego pola edycyjnego: „Nazwisko”. Pole edycyjne.
Ten sam wzorzec: <label for="surname"> i pole tekstowe z id i name równymi surname.
Krok 21: Formularz: etykieta „Wiek” i pole liczbowe
Kryteria: ,
R.2.9 W sekcji prawej umieszczony formularz z 4 etykietami, 3 polami edycyjnymi, listą rozwijaną i przyciskiem. Przynajmniej jedna etykieta powiązana jest z kontrolką
R.3.4 Pole edycyjne do wpisania wieku jest typu number oraz przycisk realizuje funkcję wysłania danych z formularza (input lub button z typem submit lub button bez typu)
ok. 1 min
Polecenie z arkusza
Etykieta dla trzeciego pola edycyjnego: „Wiek”. Pole edycyjne typu numerycznego.
„Typ numeryczny” to type="number" - przeglądarka nie pozwoli wpisać liter i pokazuje strzałki jak na ilustracji 5 (klucz R.3.4 sprawdza, że pole wieku jest typu number). Wartość pójdzie do kolumny wiek, która jest liczbą.
Krok 22: Formularz: etykieta „Rodzaj kursu” i lista rozwijana
Kryteria:
R.2.9 W sekcji prawej umieszczony formularz z 4 etykietami, 3 polami edycyjnymi, listą rozwijaną i przyciskiem. Przynajmniej jedna etykieta powiązana jest z kontrolką
ok. 1 min
Polecenie z arkusza
Etykieta dla listy rozwijanej: „Rodzaj kursu”. Lista rozwijana wypełniona przez skrypt 2.
Czwarta etykieta, powiązana z <select id="course">. Opcje zostawiasz puste - wstawi je skrypt 2, w pętli po wyniku zapytania 2. Arkusz nie każe zapisywać wybranego kursu do bazy (zapytanie 3 wstawia tylko imię, nazwisko i wiek), więc lista służy do wyboru, ale skrypt 3 jej nie czyta.
R.2.9 W sekcji prawej umieszczony formularz z 4 etykietami, 3 polami edycyjnymi, listą rozwijaną i przyciskiem. Przynajmniej jedna etykieta powiązana jest z kontrolką
R.3.4 Pole edycyjne do wpisania wieku jest typu number oraz 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 „Dodaj dane” wysyłający dane z formularza do skryptu 3.
<button type="submit">Dodaj dane</button> wewnątrz formularza wysyła go po kliknięciu. Klucz R.3.4 przyjmuje też <input type="submit" value="Dodaj dane">. Komplet z klucza R.2.9: 4 etykiety, 3 pola edycyjne, lista rozwijana i przycisk. „Efekt działania skryptu 3” (paragraf z komunikatem) pojawi się pod formularzem.
R.2.5 Umieszczony w bloku nagłówkowym <h1>: "SZKOLENIA”, w sekcji prawej <h2>: "Zapisy na kursy" oraz paragraf z numerem zdającego w stopce
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.5. Struktura HTML jest kompletna - dalej style.
Kroki 25-32 · po tym etapie masz 28 z 39 kryteriów (pkt)
Krok 25: Plik styl.css: ciało strony
Kryteria:
R.4.1 Ustawiony dla selektora body: krój czcionki "Century Gothic", sans-serif (tylko taka kolejność) , wyrównanie tekstu do środka, kolor tła #FCE4EC
ok. 2 min
Polecenie z arkusza
Dla selektora ciała strony: krój czcionki Century Gothic, w przypadku braku sans-serif, wyrównanie tekstu do środka, kolor tła #FCE4EC.
Utwórz plik styl.css obok index.php. Selektor body - arkusz podkreśla w uwadze, że ciało strony i tabelę formatujesz wyłącznie selektorem znacznika (bez klas i identyfikatorów). font-family to lista: nazwa z dwóch słów w cudzysłowie ("Century Gothic"), potem rodzina zapasowa sans-serif - klucz R.4.1 mówi: tylko taka kolejność. text-align: center dziedziczą wszystkie elementy, więc etykiety i nagłówki od razu stają na środku. Różowe tło z arkusza.
styl.cssCSSnowy plik
body {font-family: "Century Gothic", sans-serif;text-align: center;background-color: #FCE4EC;}
R.4.2 Ustawiony kolor tła dla bloku nagłówkowego, prawej sekcji i stopki #880E4F
R.4.3 Ustawiony biały kolor czcionki dla bloku nagłówkowego, prawej sekcji i stopki
R.4.6 Ustawione marginesy wewnętrzne (padding) dla bloku nagłówkowego i stopki 10px oraz ustawione marginesy zewnętrzne (margin): dla tabeli auto, dla prawej sekcji 35px i dla przynajmniej jednej kontrolki 5px
R.4.7 Ustawiona dla bloku nagłówkowego i stopki odległość między literami 20px
ok. 2 min
Polecenie z arkusza
Dla bloku nagłówkowego i stopki: kolor tła #880E4F, biały kolor czcionki, marginesy wewnętrzne 10 px, odległość między literami 20 px.
Wspólne cechy dwóch bloków piszesz raz - header, footer. Bordowe tło, biały tekst, padding: 10px z każdej strony. „Odległość między literami” to letter-spacing: 20px - to ona daje rozstrzelony napis S Z K O L E N I A z ilustracji 2 (klucz R.4.7), bez żadnych spacji w HTML.
Typowe błędy
word-spacing rozsuwa wyrazy, nie litery. Arkusz mówi o odległości między literami - letter-spacing.
R.4.4 Ustawiona wysokość sekcji lewej 600px, prawej 500px oraz szerokość sekcji lewej 50%, prawej 40% (width lub grid-template-columns lub flex)
R.4.9 Ustawiony dla lewej sekcji styl overflow: auto;
ok. 1 min
Polecenie z arkusza
Dla lewej sekcji: szerokość 50%, wysokość 600 px, paski przewijania tylko w momencie, gdy zawartość nie mieści się w bloku.
Selektor identyfikatora #left. „Paski tylko, gdy zawartość się nie mieści” to overflow: auto (klucz R.4.9). Osiem wierszy tabeli ze zdjęciami po 100 px to ponad 600 px - pojawi się pionowy pasek jak na ilustracji 2.
R.4.2 Ustawiony kolor tła dla bloku nagłówkowego, prawej sekcji i stopki #880E4F
R.4.3 Ustawiony biały kolor czcionki dla bloku nagłówkowego, prawej sekcji i stopki
R.4.4 Ustawiona wysokość sekcji lewej 600px, prawej 500px oraz szerokość sekcji lewej 50%, prawej 40% (width lub grid-template-columns lub flex)
R.4.6 Ustawione marginesy wewnętrzne (padding) dla bloku nagłówkowego i stopki 10px oraz ustawione marginesy zewnętrzne (margin): dla tabeli auto, dla prawej sekcji 35px i dla przynajmniej jednej kontrolki 5px
R.4.8 Ustawiony dla prawej sekcji cień box-shadow: 10px 10px 15px DimGray;
ok. 2 min
Polecenie z arkusza
Dla prawej sekcji: kolor tła #880E4F, biały kolor czcionki, szerokość 40%, wysokość 500 px, marginesy zewnętrzne 35 px, cień bloku o przesunięciu 10 px w obu osiach, rozmyciu 15 px i kolorze DimGray.
Selektor #right. Cień bloku to box-shadow z tabeli 2 arkusza: h-offset v-offset blur color - czyli 10px 10px 15px DimGray (przesunięcie w prawo, przesunięcie w dół, rozmycie, kolor; parametry spread i inset pomijasz). Klucz R.4.8 sprawdza dokładnie ten zapis. margin: 35px odsuwa blok od sekcji lewej i od krawędzi - na ilustracji 2 widać ten odstęp.
Typowe błędy
text-shadow (druga właściwość z tabeli 2) to cień tekstu, nie bloku - arkusz mówi „cień bloku”.
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. 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.
Sekcje mają już szerokości (50% i 40%), ale jako bloki stoją jedna pod drugą. display: flex na <main> ustawia jego dzieci w jednym wierszu, obok siebie - a stopka, która jest poza <main>, zostaje pod spodem bez żadnego clear. Klucz R.3.1 przyjmuje też float z clear i grid; flex jest tu najkrótszy, bo nie trzeba sprzątać po pływających blokach.
Typowe błędy
float: left na obu sekcjach też działa, ale bez clear: both na stopce stopka wjeżdża obok sekcji.
R.4.5 Ustawiona szerokość selektora table oraz wszystkich kontrolek (pola edycyjne, lista rozwijana, przycisk) 70%
R.4.6 Ustawione marginesy wewnętrzne (padding) dla bloku nagłówkowego i stopki 10px oraz ustawione marginesy zewnętrzne (margin): dla tabeli auto, dla prawej sekcji 35px i dla przynajmniej jednej kontrolki 5px
ok. 1 min
Polecenie z arkusza
Dla selektora tabeli: marginesy zewnętrzne automatycznie wyliczane przez przeglądarkę, szerokość 70%.
Selektor table - zgodnie z uwagą arkusza bez klas i identyfikatorów. margin: auto przy ustalonej szerokości to klasyczny sposób na wyśrodkowanie bloku: przeglądarka dzieli wolne miejsce po równo na lewo i prawo (klucz R.4.6). text-align: center z body wyśrodkowuje tekst w komórkach, ale nie samą tabelę - do tego potrzebny jest właśnie margin: auto.
Krok 31: Kontrolki formularza: margines i szerokość
Kryteria: ,
R.4.5 Ustawiona szerokość selektora table oraz wszystkich kontrolek (pola edycyjne, lista rozwijana, przycisk) 70%
R.4.6 Ustawione marginesy wewnętrzne (padding) dla bloku nagłówkowego i stopki 10px oraz ustawione marginesy zewnętrzne (margin): dla tabeli auto, dla prawej sekcji 35px i dla przynajmniej jednej kontrolki 5px
ok. 1 min
Polecenie z arkusza
Dla wszystkich kontrolek (pola edycyjne, lista rozwijana, przycisk) marginesy zewnętrzne 5 px, szerokość 70%.
Trzy znaczniki kontrolek w jednej regule: input, select, button. Szerokość 70% liczy się od sekcji prawej, więc pola są równe i wyśrodkowane (jako elementy liniowe podlegają text-align: center), jak na ilustracji 2. Margines 5 px rozsuwa je w pionie.
R.4.10 Gdy kursor znajdzie się na wierszu tabeli jego kolor tła zmienia się na #F8BBD0
ok. 1 min
Polecenie z arkusza
Gdy kursor znajdzie się na wierszu tabeli jego kolor tła zmienia się na #F8BBD0.
Pseudoklasa :hover na tr - podświetla cały wiersz, w którym jest kursor. Na razie w tabeli jest tylko wiersz nagłówkowy (najedź na niego w podglądzie); kolejne dopisze skrypt 1 i też będą się podświetlać. Arkusz stylów jest kompletny.
Kroki 33-43 · po tym etapie masz 39 z 39 kryteriów (pkt)
Krok 33: Połączenie z bazą danych
Kryteria: ,
R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy szkolenia
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 szkolenia.
Kod PHP piszesz między <?php a ?> - serwer wykonuje go, zanim strona trafi do przeglądarki. Połączenie otwierasz raz, na samym początku pliku, i trzymasz 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.
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.
Typowe błędy
Kolejność argumentów ma znaczenie: mysqli_connect('localhost', 'root', 'szkolenia') podaje „szkolenia” 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 zawiera instrukcję wysyłającą do bazy danych zapytanie 2
ok. 2 min
Polecenie z arkusza
Skrypt 1: wysyła do bazy danych zapytanie 1.
Skrypt 1 piszesz wewnątrz<table>, zaraz po wierszu nagłówkowym - tam mają trafić kolejne wiersze. mysqli_query($connection, ...) wysyła zapytanie i zwraca wynik, który zapisujesz w $courses (klucz R.5.3). Treść to dokładnie zapytanie 1 z kwerendy.txt - kursy od najtańszego.
Krok 35: Skrypt 1: wiersz z obrazem dla każdego kursu
Kryteria: ,
R.2.6 W sekcji lewej umieszczona przynajmniej jedna grafika z tekstem alternatywnym "Kurs" (w skrypcie lub HTML)
R.5.5 Skrypt 1 wyświetla w I kolumnie tabeli wszystkie obrazy, których nazwy to kody zwrócone z bazy, a rozszerzenia to .jpg
ok. 3 min
Polecenie z arkusza
Dla każdego zwróconego przez bazę rekordu, w kolejnych wierszach tabeli wstawiane są: do pierwszej komórki: obraz o nazwie utworzonej ze zwróconego przez bazę kodu i rozszerzenia .jpg z tekstem alternatywnym: „kurs” (dla kodu 1 jest to 1.jpg).
„Dla każdego rekordu” to pętla: mysqli_fetch_row($courses) (tabela 1) oddaje kolejny wiersz jako tablicę od zera, a gdy wierszy zabraknie - false, co kończy while. Każdy obieg otwiera <tr>, wypisuje pierwszą komórkę i zamyka wiersz. Nazwa pliku powstaje ze sklejenia kodu z rozszerzeniem: w napisie '$row[0].jpg' PHP podstawia $row[0] (np. 5), a .jpg zostaje dosłownie - wychodzi 5.jpg. Tekst alternatywny z arkusza (klucz R.2.6 przyjmuje alt w skrypcie). Podgląd: osiem zdjęć w pierwszej kolumnie, od gastronomii.
Typowe błędy
src='$row[0]' bez .jpg to nazwa pliku bez rozszerzenia - obraz się nie załaduje. src='$row[1].jpg' wziąłby nazwę kursu zamiast kodu.
Krok 36: Skrypt 1: nazwa i cena w kolejnych komórkach
Kryteria:
R.5.4 Skrypt 1 dla każdego zwróconego przez bazę rekordu, wyświetla w II i III kolumnie tabeli kolejne nazwy i ceny kursów
ok. 1 min
Polecenie z arkusza
Do kolejnych komórek: zwrócone z bazy nazwa i cena kursu.
Przed zamknięciem wiersza dokładasz dwie komórki: $row[1] to nazwa, $row[2] cena - kolejność z zapytania 1 (kod, nazwa, cena). Tabela wygląda jak na ilustracji 2: zdjęcie, nazwa, cena, a wiersze podświetlają się pod kursorem dzięki tr:hover z CSS. Osiem wierszy po 100 px przekracza 600 px sekcji - pojawia się pasek przewijania z overflow: auto.
R.5.3 Skrypt 1 zawiera instrukcję wysyłającą do bazy danych zapytanie 1 lub Skrypt 2 zawiera instrukcję wysyłającą do bazy danych zapytanie 2
ok. 1 min
Polecenie z arkusza
Skrypt 2: wysyła do bazy danych zapytanie 2.
Skrypt 2 piszesz wewnątrz<select>, bo tam mają trafić opcje. Zapytanie 2 z kwerendy.txt - same nazwy kursów - trafia do $names. Osobna zmienna, bo $courses ze skryptu 1 jest już przeczytana do końca.
index.phpPHPzmienione: 3 linie
<?php$connection = mysqli_connect('localhost', 'root', '', 'szkolenia');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Szkolenia i kursy</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>SZKOLENIA</h1> </header> <main> <sectionid="left"> <table> <tr> <th>Kurs</th> <th>Nazwa</th> <th>Cena</th> </tr><?php$courses = mysqli_query($connection, 'SELECT kod, nazwa, cena FROM kursy ORDER BY cena;');while ($row = mysqli_fetch_row($courses)) {echo'<tr>';echo"<td><img src='$row[0].jpg' alt='kurs'></td>";echo"<td>$row[1]</td><td>$row[2]</td>";echo'</tr>';}?> </table> </section> <sectionid="right"> <h2>Zapisy na kursy</h2> <formmethod="post"> <labelfor="name">Imię</label><br> <inputtype="text"id="name"name="name"><br> <labelfor="surname">Nazwisko</label><br> <inputtype="text"id="surname"name="surname"><br> <labelfor="age">Wiek</label><br> <inputtype="number"id="age"name="age"><br> <labelfor="course">Rodzaj kursu</label><br> <selectid="course"name="course"><?php$names = mysqli_query($connection, 'SELECT nazwa FROM kursy;');?> </select><br> <buttontype="submit">Dodaj dane</button> </form> </section> </main> <footer> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
R.5.6 Skrypt 2 wyświetla nazwy kursów jako elementy listy rozwijanej w znaczniku <option>
ok. 2 min
Polecenie z arkusza
Wszystkie zwrócone nazwy kursów są zapisywane jako elementy listy rozwijanej.
Ta sama pętla co w skrypcie 1, tylko wypisuje <option> z nazwą (klucz R.5.6 sprawdza znacznik option). Podgląd: lista ma 8 kursów w kolejności z tabeli, od języka angielskiego - jak na ilustracji 4.
Typowe błędy
echo '<option>$row[0]</option>' w apostrofach wypisze dosłownie „$row[0]” - PHP nie podstawia zmiennych w apostrofach.
index.phpPHPzmienione: 3 linie
<?php$connection = mysqli_connect('localhost', 'root', '', 'szkolenia');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Szkolenia i kursy</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>SZKOLENIA</h1> </header> <main> <sectionid="left"> <table> <tr> <th>Kurs</th> <th>Nazwa</th> <th>Cena</th> </tr><?php$courses = mysqli_query($connection, 'SELECT kod, nazwa, cena FROM kursy ORDER BY cena;');while ($row = mysqli_fetch_row($courses)) {echo'<tr>';echo"<td><img src='$row[0].jpg' alt='kurs'></td>";echo"<td>$row[1]</td><td>$row[2]</td>";echo'</tr>';}?> </table> </section> <sectionid="right"> <h2>Zapisy na kursy</h2> <formmethod="post"> <labelfor="name">Imię</label><br> <inputtype="text"id="name"name="name"><br> <labelfor="surname">Nazwisko</label><br> <inputtype="text"id="surname"name="surname"><br> <labelfor="age">Wiek</label><br> <inputtype="number"id="age"name="age"><br> <labelfor="course">Rodzaj kursu</label><br> <selectid="course"name="course"><?php$names = mysqli_query($connection, 'SELECT nazwa FROM kursy;');while ($row = mysqli_fetch_row($names)) {echo"<option>$row[0]</option>";}?> </select><br> <buttontype="submit">Dodaj dane</button> </form> </section> </main> <footer> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (index.php)
Krok 39: Skrypt 3: czy wpisano imię, nazwisko i wiek
Kryteria:
R.5.9 Skrypt zawiera instrukcję pobierającą dane z tablicy globalnej odpowiadającej zastosowanej metodzie formularza ($_POST, $_GET, $_REQUEST)
ok. 2 min
Polecenie z arkusza
Skrypt 3: wykonywany po kliknięciu przycisku „Dodaj dane”, jeżeli imię, nazwisko i wiek zostały wprowadzone w formularzu.
Skrypt 3 piszesz pod formularzem - tam ma się pojawić komunikat. Warunek „zostały wprowadzone” to więcej niż isset z tabeli 1: po kliknięciu przycisku klucze name, surname i agezawsze są w $_POST, nawet gdy pola zostały puste. Dlatego !empty() - prawdziwe tylko wtedy, gdy wartość istnieje i nie jest pustym napisem; trzy warunki łączy && (wszystkie naraz). Klucz R.5.9 sprawdza, że dane są czytane z $_POST - tablicy odpowiadającej metodzie formularza.
Typowe błędy
Samo isset($_POST['name']) jest prawdziwe po każdym kliknięciu przycisku, także z pustymi polami - do bazy trafiłby uczestnik bez imienia.
Krok 40: Skrypt 3: zapytanie 3 z danymi z formularza
Kryteria:
R.5.7 Skrypt 3 sprawdza czy dane zostały wysłane z formularza, jeśli tak, to wysyła do bazy danych zmodyfikowane zapytanie 3, z wstawionymi danymi z formularza oraz wyświetla tekst „Dane uczestnika <imię> <nazwisko> zostały dodane”, gdzie pola <> są wysłane z formularza (sposób alternatywny z atrybutem required, wstawienie rekordu sprawdzić w phpMyAdmin)
ok. 3 min
Polecenie z arkusza
Wysyła do bazy danych zapytanie 3, zmodyfikowane tak, że dane wysłane z formularza są wstawione do zapytania.
Trzy wartości z $_POST do zmiennych o znaczących nazwach, a potem zapytanie 3 z kwerendy.txt dostaje je w miejsce „Tadeusz”, „Wysocki” i 36: napisy w apostrofach wewnątrz SQL, liczba bez. INSERT nic nie zwraca do czytania, więc mysqli_query wywołujesz bez zapisywania wyniku. Podgląd pokazuje stan po wysłaniu formularza z ilustracji 5 (Magdalena Janowska, 35) - wiersz jest już w bazie, ale strona jeszcze o tym nie mówi.
R.5.7 Skrypt 3 sprawdza czy dane zostały wysłane z formularza, jeśli tak, to wysyła do bazy danych zmodyfikowane zapytanie 3, z wstawionymi danymi z formularza oraz wyświetla tekst „Dane uczestnika <imię> <nazwisko> zostały dodane”, gdzie pola <> są wysłane z formularza (sposób alternatywny z atrybutem required, wstawienie rekordu sprawdzić w phpMyAdmin)
ok. 1 min
Polecenie z arkusza
Wyświetla paragraf o treści „Dane uczestnika <imię><nazwisko> zostały dodane”, gdzie pola <> oznaczają wartości wysłane z formularza.
echo z paragrafem składa komunikat z imienia i nazwiska z formularza - tych samych zmiennych, które poszły do bazy. Podgląd: „Dane uczestnika Magdalena Janowska zostały dodane”, jak na ilustracji 5.
R.5.8 Skrypt 3 w przeciwnym do R.5.7 przypadku wyświetla tekst „Wprowadź wszystkie dane”
ok. 1 min
Polecenie z arkusza
W przeciwnym wypadku wyświetla paragraf „Wprowadź wszystkie dane”.
Gałąź else wykonuje się, gdy którekolwiek z trzech pól jest puste - a także przy pierwszym wejściu na stronę, gdy formularza nikt jeszcze nie wysłał. Dlatego na ilustracji 2 komunikat „Wprowadź wszystkie dane” widać od razu po otwarciu strony (klucz R.5.8). Podgląd wraca do tego stanu.
index.phpPHPzmienione: 2 linie
<?php$connection = mysqli_connect('localhost', 'root', '', 'szkolenia');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Szkolenia i kursy</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>SZKOLENIA</h1> </header> <main> <sectionid="left"> <table> <tr> <th>Kurs</th> <th>Nazwa</th> <th>Cena</th> </tr><?php$courses = mysqli_query($connection, 'SELECT kod, nazwa, cena FROM kursy ORDER BY cena;');while ($row = mysqli_fetch_row($courses)) {echo'<tr>';echo"<td><img src='$row[0].jpg' alt='kurs'></td>";echo"<td>$row[1]</td><td>$row[2]</td>";echo'</tr>';}?> </table> </section> <sectionid="right"> <h2>Zapisy na kursy</h2> <formmethod="post"> <labelfor="name">Imię</label><br> <inputtype="text"id="name"name="name"><br> <labelfor="surname">Nazwisko</label><br> <inputtype="text"id="surname"name="surname"><br> <labelfor="age">Wiek</label><br> <inputtype="number"id="age"name="age"><br> <labelfor="course">Rodzaj kursu</label><br> <selectid="course"name="course"><?php$names = mysqli_query($connection, 'SELECT nazwa FROM kursy;');while ($row = mysqli_fetch_row($names)) {echo"<option>$row[0]</option>";}?> </select><br> <buttontype="submit">Dodaj dane</button> </form><?phpif (!empty($_POST['name']) && !empty($_POST['surname']) && !empty($_POST['age'])) {$name = $_POST['name'];$surname = $_POST['surname'];$age = $_POST['age']; mysqli_query($connection, "INSERT INTO uczestnicy (imie, nazwisko, wiek) VALUES ('$name', '$surname', $age);");echo"<p>Dane uczestnika $name$surname zostały dodane</p>";} else {echo'<p>Wprowadź wszystkie dane</p>';}?> </section> </main> <footer> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (index.php)
Krok 43: Zamknięcie połączenia z bazą
Kryteria:
R.5.2 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 - lista kursów zostaje pusta, a zapis nie działa.
index.phpPHPzmienione: 3 linie
<?php$connection = mysqli_connect('localhost', 'root', '', 'szkolenia');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Szkolenia i kursy</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>SZKOLENIA</h1> </header> <main> <sectionid="left"> <table> <tr> <th>Kurs</th> <th>Nazwa</th> <th>Cena</th> </tr><?php$courses = mysqli_query($connection, 'SELECT kod, nazwa, cena FROM kursy ORDER BY cena;');while ($row = mysqli_fetch_row($courses)) {echo'<tr>';echo"<td><img src='$row[0].jpg' alt='kurs'></td>";echo"<td>$row[1]</td><td>$row[2]</td>";echo'</tr>';}?> </table> </section> <sectionid="right"> <h2>Zapisy na kursy</h2> <formmethod="post"> <labelfor="name">Imię</label><br> <inputtype="text"id="name"name="name"><br> <labelfor="surname">Nazwisko</label><br> <inputtype="text"id="surname"name="surname"><br> <labelfor="age">Wiek</label><br> <inputtype="number"id="age"name="age"><br> <labelfor="course">Rodzaj kursu</label><br> <selectid="course"name="course"><?php$names = mysqli_query($connection, 'SELECT nazwa FROM kursy;');while ($row = mysqli_fetch_row($names)) {echo"<option>$row[0]</option>";}?> </select><br> <buttontype="submit">Dodaj dane</button> </form><?phpif (!empty($_POST['name']) && !empty($_POST['surname']) && !empty($_POST['age'])) {$name = $_POST['name'];$surname = $_POST['surname'];$age = $_POST['age']; mysqli_query($connection, "INSERT INTO uczestnicy (imie, nazwisko, wiek) VALUES ('$name', '$surname', $age);");echo"<p>Dane uczestnika $name$surname zostały dodane</p>";} else {echo'<p>Wprowadź wszystkie dane</p>';}?> </section> </main> <footer> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer><?phpmysqli_close($connection);?></body></html>
Kroki 44-46 · po tym etapie masz 39 z 39 kryteriów (pkt)
Krok 44: Refaktor: zapytanie przygotowane dla INSERT
Kryteria:
R.5.7 Skrypt 3 sprawdza czy dane zostały wysłane z formularza, jeśli tak, to wysyła do bazy danych zmodyfikowane zapytanie 3, z wstawionymi danymi z formularza oraz wyświetla tekst „Dane uczestnika <imię> <nazwisko> zostały dodane”, gdzie pola <> są wysłane z formularza (sposób alternatywny z atrybutem required, wstawienie rekordu sprawdzić w phpMyAdmin)
ok. 4 min
Polecenie z arkusza
Wysyła do bazy danych zapytanie 3, zmodyfikowane tak, że dane wysłane z formularza są wstawione do zapytania.
Imię i nazwisko z formularza szły prosto do INSERT - to SQL injection, i to w najgroźniejszej odmianie, bo zapytanie zapisuje: nazwisko Nowak'); DROP TABLE uczestnicy; -- dokleiłoby drugie polecenie. W prawdziwych projektach dane trafiają do zapytania przez zapytanie przygotowane: mysqli_prepare z trzema ?, mysqli_stmt_bind_param z typami 'ssi' (dwa napisy i liczba całkowita) i mysqli_stmt_execute. Stara linia zostaje w komentarzu. Podgląd bez zmian.
Typowe błędy
Liczba liter w 'ssi' musi równać się liczbie znaków ? w zapytaniu i liczbie zmiennych - inaczej mysqli_stmt_bind_param zgłosi błąd i wiersz nie trafi do bazy.
Krok 45: Refaktor: escapowanie danych przed wypisaniem
Kryteria: ,
R.5.4 Skrypt 1 dla każdego zwróconego przez bazę rekordu, wyświetla w II i III kolumnie tabeli kolejne nazwy i ceny kursów
R.5.6 Skrypt 2 wyświetla nazwy kursów jako elementy listy rozwijanej w znaczniku <option>
ok. 3 min
Polecenie z arkusza
Wyświetla paragraf o treści „Dane uczestnika <imię><nazwisko> zostały dodane”, gdzie pola <> oznaczają wartości wysłane z formularza.
Skrypt 3 wypisuje do HTML imię i nazwisko wprost z formularza, a skrypty 1 i 2 nazwy kursów z bazy, które kiedyś też ktoś wpisał. Napis <script>…</script> w polu „Imię” wykonałby się w przeglądarce (XSS). htmlspecialchars() zamienia <, >, ", & na encje, więc tekst wyświetla się dosłownie i nie staje się kodem. Do bazy idą dane surowe ($name), na ekran ich bezpieczne kopie ($safeName) - escapujesz na wyjściu, nie przy zapisie. Ceny zostają - liczby nie niosą znaczników. Podgląd bez zmian.
Typowe błędy
htmlspecialchars przed INSERT zapisałoby do bazy < zamiast < - zniekształcone dane, które później trzeba odkręcać.
Krok 46: Refaktor: oczyszczenie i rzutowanie danych
ok. 2 min
Polecenie z arkusza
Skrypt 3 działa tylko wtedy, gdy imię, nazwisko i wiek zostały wprowadzone w formularzu.
Dwie poprawki na to, co przychodzi z formularza:
trim() zdejmuje spacje z początku i końca imienia i nazwiska - bez tego „ Magdalena ” ze spacją trafiłaby do bazy ze spacją i nie znalazłoby jej później żadne wyszukiwanie,
(int) rzutuje wiek na liczbę całkowitą: type="number" to tylko wygoda w przeglądarce, a żądanie POST można wysłać bez niej. Po rzutowaniu typ 'i' w bindowaniu dostaje naprawdę liczbę.
Na egzaminie wystarczy wersja z kroków wcześniejszych - klucz tego nie sprawdza. To już kod gotowy do prawdziwego użycia.
Typowe błędy
Walidacja po stronie przeglądarki nie zastępuje walidacji w PHP - formularz można wysłać z dowolnymi wartościami. Uwaga na kolejność: !empty sprawdza tu wartość przedtrim(), więc pole z samymi spacjami przejdzie warunek i trafi do bazy jako pusty napis - pełna walidacja sprawdza wartość już po trim() (tak robi wersja nowoczesna).
Krok 47 · po tym etapie masz 39 z 39 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. 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ć: obrazy 1.jpg … 8.jpg, import.png, index.php, kw1.jpg … kw4.jpg, kwerendy.txt, przeglądarka.txt, styl.css. 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.