Przejdź do głównej treści
INF.03-05-26.01-SGStyczeń 2026 · 52 kroki · ok. 97 min · 39 kryteriów z klucza

Rozwiązanie krok po kroku: Portal zgłoszeń wypadków (Nowoczesny kod (grid, PDO))

To drugi sposób rozwiązania tego samego arkusza: portal zgłoszeń wypadków, baza zgloszenia, jedna strona index.php z tabelą personelu wybieranego polem opcji, listą osób bez zgłoszeń i formularzem nowego zgłoszenia. Obie wersje spełniają wszystkie 39 kryteriów klucza - różni je sposób pisania kodu, nie wynik.

Czym ta wersja różni się od podstawowej:

  • siatka CSS w bloku głównym (grid-template-columns: 50% 50%), kolory w zmiennych CSS, tabela z <thead> i <tbody>,
  • PHP na PDO z wyjątkami, cała logika na górze pliku, a w HTML tylko pętle foreach i funkcja escape(),
  • opcja z formularza z białej listy i zapytanie przygotowane - wartość spoza „policjant/ratownik” nie dotrze do SQL,
  • zapytanie 3 przez NOT EXISTS zamiast NOT IN - z wyjaśnieniem, kiedy NOT IN zawodzi,
  • zapis zgłoszenia z filter_var, obsługą błędu klucza obcego (osoba spoza bazy) i wzorcem PRG: po zapisie przekierowanie, więc lista od razu jest aktualna, a F5 nie dopisze zgłoszenia drugi raz.

Kroki nadal idą zdanie po zdaniu za treścią arkusza. Podgląd w każdym kroku z PHP to wynik prawdziwego uruchomienia kodu z tego kroku w PHP 8.2 na bazie z arkusza; od kroku z polem opcji pokazuje stan z ilustracji 2 - po wybraniu „Ratownik” i kliknięciu „Pokaż”. Baza pochodzi z prawdziwego archiwum pliki7.

Jak liczą się punkty. Klucz ma 39 kryteriów (R.1.1 - R.5.9), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 30 punktów. Przy każdym kroku jest napisane, które kryteria spełnia; egzaminator ocenia spełnienie kryteriów z klucza, nie zgodność z tym kodem.

  1. Etap 1: Baza danych

    Kroki 1-7 · po tym etapie masz 6 z 39 kryteriów (pkt)

    Krok 1: Baza danych zgloszenia

    ok. 2 min
    Polecenie z arkusza

    Utwórz bazę danych o nazwie zgloszenia, z zestawem polskich znaków (np. utf8_polish_ci)

    Archiwum pliki7.7z ma hasło @LarMowe^ (małpa na początku, daszek na końcu). W phpMyAdmin: Nowa, nazwa zgloszenia - bez „ł”, dokładnie jak w arkuszu - porównywanie utf8_polish_ci. To samo daje polecenie w zakładce SQL:

    CREATE DATABASE zgloszenia CHARACTER SET utf8 COLLATE utf8_polish_ci;
    
    Typowe błędy

    Nazwa z polską literą (zgłoszenia) to inna baza niż ta, z którą łączą się skrypty - przepada punkt za połączenie (R.5.1).

  2. Krok 2: Import tabel z pliku zgloszenia.sql

    ok. 2 min
    Polecenie z arkusza

    Z rozpakowanego archiwum zaimportuj tabele z pliku zgloszenia.sql do utworzonej bazy

    Zaznacz bazę zgloszenia, potem Import → zgloszenia.sql → Wykonaj. Powstają trzy tabele z ilustracji 1: personel (18 osób, status „policjant” albo „ratownik”), pojazdy (30 pojazdów, typ „K” - karetka, „R” - radiowóz) i rejestr (29 zgłoszeń). rejestr kluczami obcymi wskazuje osobę i pojazd - baza nie przyjmie zgłoszenia dla osoby, której nie ma w personel, co przyda się w Skrypcie 3.

  3. Krok 3: Zrzut ekranu import.png

    Kryteria:

    R.1.1 Wykonany import tabel do bazy danych zgloszenia - plik import.png

    ok. 2 min
    Polecenie z arkusza

    Wykonaj zrzut ekranu po imporcie. Zapisz zrzut w formacie PNG pod nazwą import. Nie kadruj zrzutu. Powinien on obejmować cały ekran monitora, z widocznym paskiem zadań. Na zrzucie powinny być widoczne elementy wskazujące na poprawnie wykonany import tabel

    Print Screen (cały ekran z paskiem zadań), wklej do edytora grafiki i zapisz jako import.png. Uwaga na formaty: import w PNG, zrzuty zapytań w JPEG.

    Typowe błędy

    Wykadrowany zrzut albo zrzut bez paska zadań nie jest oceniany (uwaga w kluczu przy R.1).

  4. Krok 4: Zapytanie 1: policjanci

    Kryteria: ,

    R.1.2 W pliku kwerendy.txt zapisane co najmniej jedno zapytanie SQL, wynikające z treści zadania

    R.1.3 Wykonane zapytanie 1 wybierające jedynie identyfikatory, imiona i nazwiska tych osób spośród personelu, dla których status to policjant. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT id, imie, nazwisko FROM personel WHERE status = "policjant"; oraz na zrzucie widocznych jest dokładnie 11 rekordów z id: 1, 2, 5, 6, 7, 8, 10, 11, 14, 16, 18

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 1: wybierające jedynie identyfikatory, imiona i nazwiska tych osób spośród personelu, których status to policjant

    • SELECT id, imie, nazwisko - trzy pola, bez statusu,
    • WHERE status = 'policjant' - tekst w apostrofach.

    Wynik: 11 wierszy o identyfikatorach 1, 2, 5, 6, 7, 8, 10, 11, 14, 16, 18 (R.1.3). Zapytania zapisuj w kwerendy.txt z komentarzem -- nad każdym, zrzut jako kw1.jpg. W Skrypcie 1 w miejscu słowa „policjant” stanie parametr z opcją z formularza.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT id, imie, nazwisko
    FROM personel
    WHERE status = 'policjant';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: liczba karetek i radiowozów

    Kryteria:

    R.1.4 Wykonane zapytanie 2 wybierające jedynie typ i zliczające liczbę pojazdów zgrupowanych według typu. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT typ, COUNT(*) FROM pojazdy GROUP BY typ; oraz na zrzucie widoczne są dane: K 6; R 24

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 2: wybierające jedynie typ i zliczające liczbę pojazdów zgrupowanych według typu (zapytanie wybiera liczbę karetek i radiowozów)

    • GROUP BY typ - wiersze o tym samym typie tworzą jedną grupę,
    • COUNT(*) AS liczba - liczba wierszy w grupie, z aliasem, żeby kolumna wyniku nie nazywała się COUNT(*).

    Wynik: K 6 i R 24 (R.1.4). Zrzut: kw2.jpg.

    Typowe błędy

    SELECT COUNT(*) FROM pojazdy bez grupowania zwraca jedną liczbę 30 - nie widać, ile jest karetek, a ile radiowozów.

    kwerendy.txt Tekst zmienione: 5 linii
    -- Zapytanie 1
    SELECT id, imie, nazwisko
    FROM personel
    WHERE status = 'policjant';
    
    -- Zapytanie 2
    SELECT typ, COUNT(*) AS liczba
    FROM pojazdy
    GROUP BY typ;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: osoby bez żadnego zgłoszenia

    Kryteria:

    R.1.5 Wykonane zapytanie 3 wybierające jedynie te identyfikatory i nazwiska osób spośród personelu, których id nie pojawia się w rejestrze. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT id, nazwisko FROM personel WHERE id NOT IN (SELECT id_personel FROM rejestr); lub SELECT personel.id, nazwisko FROM personel LEFT JOIN rejestr ON personel.id = id_personel GROUP BY personel.id HAVING COUNT(data) = 0; (lub inne rozwiązania dające uniwersalny wynik) oraz na zrzucie widoczne są dokładnie 3 rekordy: 2 Nowak, 3 Nosowska, 4 Mizera. (Baza musi być przywrócona do stanu początkowego)

    ok. 4 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie te identyfikatory i nazwiska osób spośród personelu, których id nie pojawia się w rejestrze

    • FROM personel AS p - alias p do odwołań z podzapytania,
    • WHERE NOT EXISTS (SELECT 1 FROM rejestr AS r WHERE r.id_personel = p.id) - zostaje osoba, dla której nie istnieje ani jeden wiersz rejestru z jej identyfikatorem. SELECT 1 niczego nie wybiera - podzapytanie odpowiada tylko „jest / nie ma”.

    Wynik: 2 Nowak, 3 Nosowska, 4 Mizera (R.1.5); klucz przyjmuje „inne rozwiązania dające uniwersalny wynik”. Klucz podaje też wersję z NOT IN (SELECT id_personel FROM rejestr) - daje tu ten sam wynik, ale ma pułapkę: gdyby w kolumnie podzapytania trafił się choć jeden NULL, NOT IN zwróciłoby zero wierszy, bo porównanie z NULL nie jest ani prawdą, ani fałszem. W tej bazie id_personel ma NOT NULL, więc problem nie wystąpi, a NOT EXISTS jest odporne zawsze. Zrzut: kw3.jpg.

    Typowe błędy

    Zapytanie 3 wykonaj przed zapytaniem 4 albo na bazie w stanie początkowym (uwaga w kluczu) - po dopisaniu zgłoszeń wynik się zmienia.

    kwerendy.txt Tekst zmienione: 7 linii
    -- Zapytanie 1
    SELECT id, imie, nazwisko
    FROM personel
    WHERE status = 'policjant';
    
    -- Zapytanie 2
    SELECT typ, COUNT(*) AS liczba
    FROM pojazdy
    GROUP BY typ;
    
    -- Zapytanie 3
    SELECT p.id, p.nazwisko
    FROM personel AS p
    WHERE NOT EXISTS (
        SELECT 1 FROM rejestr AS r WHERE r.id_personel = p.id
    );
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: nowe zgłoszenie z dzisiejszą datą

    Kryteria:

    R.1.6 Wykonane zapytanie 4 wstawiające rekord do tabeli rejestr : identyfikator personelu: 1, identyfikator pojazdu: 14, automatycznie wpisywana aktualna data, klucz generowany automatycznie. W pliku z kwerendami lub na zrzucie istnieje zapis np.: INSERT INTO rejestr(data, id_personel, id_pojazd) VALUES (CURDATE(), 1, 14); ( możliwe też CURRENT_DATE() i NOW()) oraz na zrzucie jest komunikat o poprawnie wykonanym zapytaniu lub widoczny wstawiony wiersz

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 4: wstawiające rekord do tabeli rejestr: identyfikator personelu: 1, identyfikator pojazdu: 14, automatycznie wpisywana aktualna data, klucz generowany automatycznie

    Lista kolumn bez id - AUTO_INCREMENT nada numer 30. CURDATE() wstawia datę serwera w chwili wykonania zapytania (klucz przyjmuje też CURRENT_DATE() i NOW(); NOW() niesie godzinę, którą kolumna DATE i tak odetnie). Zrzut kw4.jpg: komunikat o wstawieniu albo tabela z nowym wierszem (R.1.6).

    Typowe błędy

    Data wpisana ręcznie ('2026-01-12') nie jest „automatycznie wpisywaną aktualną datą” - jutro to samo zapytanie wstawi wczorajszy dzień.

    kwerendy.txt Tekst zmienione: 4 linie
    -- Zapytanie 1
    SELECT id, imie, nazwisko
    FROM personel
    WHERE status = 'policjant';
    
    -- Zapytanie 2
    SELECT typ, COUNT(*) AS liczba
    FROM pojazdy
    GROUP BY typ;
    
    -- Zapytanie 3
    SELECT p.id, p.nazwisko
    FROM personel AS p
    WHERE NOT EXISTS (
        SELECT 1 FROM rejestr AS r WHERE r.id_personel = p.id
    );
    
    -- Zapytanie 4
    INSERT INTO rejestr (data, id_personel, id_pojazd)
    VALUES (CURDATE(), 1, 14);
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Strona HTML

    Kroki 8-26 · po tym etapie masz 17 z 39 kryteriów (pkt)

    Krok 8: Plik index.php w HTML5

    ok. 2 min
    Polecenie z arkusza

    Składa się ze strony o nazwie index.php. Zapisana w języku HTML5

    <!DOCTYPE html> i szkielet strony. Rozszerzenie .php, bo dojdą skrypty; plik leży w htdocs XAMPP-a i otwiera się go przez http://localhost/....

    index.php PHP nowy plik
    <!DOCTYPE html>
    <html>
    <head>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  9. Krok 9: Język strony

    Kryteria:

    R.2.1 W pliku index.php znajduje się deklaracja HTML5 <!DOCTYPE HTML> i deklaracja języka zawartości witryny: polski np. <html lang="pl">

    ok. 1 min
    Polecenie z arkusza

    Zadeklarowany polski język zawartości witryny

    lang="pl" w <html> - czytnik ekranu czyta stronę polską wymową. Z <!DOCTYPE html> to jedno kryterium (R.2.1).

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  10. Krok 10: Kodowanie 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> - przed tytułem z literą „Ł”.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  11. Krok 11: Tytuł strony i viewport

    Kryteria:

    R.2.2 W sekcji head znajduje się jawnie podany standard kodowania polskich znaków i tytuł strony "ZGŁOSZENIA”

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony „ZGŁOSZENIA”

    <title> wielkimi literami, jak w arkuszu (z kodowaniem - R.2.2). <meta name="viewport"> pokazuje stronę na telefonie w jego prawdziwej szerokości; arkusz tego nie wymaga.

    index.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZGŁOSZENIA</title>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  12. Krok 12: 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 HTML5 bez ukośnika />. Całe formatowanie pochodzi z tego pliku (R.3.2).

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZGŁOSZENIA</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  13. Krok 13: Bloki semantyczne

    Kryteria:

    R.2.3 W sekcji body zdefiniowany jest podział na bloki za pomocą semantycznych znaczników HTML5: header, main->section+section, footer (dopuszcza się zastosowanie bloków pomocniczych)

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki zrealizowany za pomocą semantycznych znaczników bloków języka HTML5 tak, aby po uruchomieniu w przeglądarce układ bloków na stronie był zgodny z ilustracją 3

    Klucz R.2.3 wymienia znaczniki: header, main z dwiema section, footer. Sekcje są jedynymi dziećmi <main>, więc siatka na <main> ustawi je obok siebie.

    Identyfikatory opisują zawartość sekcji, nie ich miejsce: staff (personel) i new-report (nowe zgłoszenie). left/right przestałyby być prawdą po pierwszej zmianie układu, np. na telefonie, gdzie sekcje stanęłyby jedna pod drugą.

    index.php PHP zmienione: 10 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZGŁOSZENIA</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
        </header>
        <main>
            <section id="staff">
            </section>
            <section id="new-report">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  14. Krok 14: Nagłówek strony

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku nagłówkowego: nagłówek pierwszego stopnia o treści „Zgłoszenia wydarzeń”

    <h1> - jedyny nagłówek pierwszego stopnia na stronie.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZGŁOSZENIA</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zgłoszenia wydarzeń</h1>
        </header>
        <main>
            <section id="staff">
            </section>
            <section id="new-report">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  15. Krok 15: Sekcja personelu: nagłówek

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku sekcji lewej: Nagłówek drugiego stopnia o treści „Personel”

    <h2> na początku sekcji staff.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZGŁOSZENIA</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zgłoszenia wydarzeń</h1>
        </header>
        <main>
            <section id="staff">
                <h2>Personel</h2>
            </section>
            <section id="new-report">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  16. Krok 16: Formularz wyboru wysyłany metodą POST

    Kryteria: ,

    R.2.5 Na stronie umieszczony przynajmniej jeden formularz, zastosowany znacznik <form>

    R.3.3 Przynajmniej jeden formularz wysyła dane metodą POST do tego samego pliku (poprawne również bez atrybutu action)

    ok. 2 min
    Polecenie z arkusza

    Formularz wysyłający dane do tego samego pliku metodą bezpieczną

    <form method="post"> bez action - brak action to „ten sam adres” (R.2.5, R.3.3). „Metoda bezpieczna” w arkuszu to POST: dane w treści żądania, a nie w adresie. Ten formularz tylko wybiera, co pokazać, więc w prawdziwym serwisie pasowałby do niego GET (adres z wybraną opcją dałoby się zapisać w zakładkach) - ale arkusz i klucz chcą POST, a klucz ocenia właśnie to.

    index.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZGŁOSZENIA</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zgłoszenia wydarzeń</h1>
        </header>
        <main>
            <section id="staff">
                <h2>Personel</h2>
                <form method="post">
                </form>
            </section>
            <section id="new-report">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  17. Krok 17: Pole opcji „Policjant”, domyślnie zaznaczone

    ok. 2 min
    Polecenie z arkusza

    Pole opcji z treścią „Policjant”, domyślnie zaznaczone

    <input type="radio"> z checked - zaznaczone przy pierwszym wejściu. Treść „Policjant” jest w <label for="police">, więc kliknięcie napisu też zaznacza pole, a czytnik ekranu czyta napis jako nazwę pola. value="policjant" - małymi literami, tak jak status jest zapisany w bazie; tę wartość dostanie skrypt.

    index.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZGŁOSZENIA</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zgłoszenia wydarzeń</h1>
        </header>
        <main>
            <section id="staff">
                <h2>Personel</h2>
                <form method="post">
                    <input type="radio" id="police" name="option" value="policjant" checked>
                    <label for="police">Policjant</label>
                </form>
            </section>
            <section id="new-report">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  18. Krok 18: Pole opcji „Ratownik”

    ok. 1 min
    Polecenie z arkusza

    Pole opcji z treścią „Ratownik”

    Drugie pole tego samego typu, value="ratownik", bez checked. Identyfikator po angielsku (rescuer), jak wszystkie w projekcie.

    index.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZGŁOSZENIA</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zgłoszenia wydarzeń</h1>
        </header>
        <main>
            <section id="staff">
                <h2>Personel</h2>
                <form method="post">
                    <input type="radio" id="police" name="option" value="policjant" checked>
                    <label for="police">Policjant</label>
                    <input type="radio" id="rescuer" name="option" value="ratownik">
                    <label for="rescuer">Ratownik</label>
                </form>
            </section>
            <section id="new-report">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  19. Krok 19: Tylko jedno pole opcji naraz

    ok. 1 min
    Polecenie z arkusza

    W jednym momencie można zaznaczyć tylko jedno pole opcji

    Kod z dwóch poprzednich kroków już to zapewnia: oba pola mają ten sam name="option", a przeglądarka traktuje pola opcji o tej samej nazwie jako jedną grupę - zaznaczenie „Ratownik” odznacza „Policjant”. Z tym krokiem jest komplet R.3.5 (z polem liczbowym, które dojdzie niżej). Dla czytnika ekranu grupę można by jeszcze opisać <fieldset> z <legend>; na ilustracji 2 nie ma ramki ani podpisu grupy, więc zostaje bez nich.

    Typowe błędy

    Różne name w polach opcji - obie kropki da się zaznaczyć naraz, a skrypt dostanie tylko jedną wartość.

    index.php PHP zmienione: 0 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZGŁOSZENIA</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zgłoszenia wydarzeń</h1>
        </header>
        <main>
            <section id="staff">
                <h2>Personel</h2>
                <form method="post">
                    <input type="radio" id="police" name="option" value="policjant" checked>
                    <label for="police">Policjant</label>
                    <input type="radio" id="rescuer" name="option" value="ratownik">
                    <label for="rescuer">Ratownik</label>
                </form>
            </section>
            <section id="new-report">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>

    Podgląd strony bez zmian - taki jak po kroku 18.

  20. Krok 20: Przycisk „Pokaż”

    Kryteria:

    R.3.4 Przynajmniej jeden 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 „Pokaż”, wysyłający dane z formularza do skryptu 1

    <button type="submit"> wysyła formularz (R.3.4); Skrypt 1 odczyta wybraną opcję po przeładowaniu strony.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZGŁOSZENIA</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zgłoszenia wydarzeń</h1>
        </header>
        <main>
            <section id="staff">
                <h2>Personel</h2>
                <form method="post">
                    <input type="radio" id="police" name="option" value="policjant" checked>
                    <label for="police">Policjant</label>
                    <input type="radio" id="rescuer" name="option" value="ratownik">
                    <label for="rescuer">Ratownik</label>
                    <button type="submit">Pokaż</button>
                </form>
            </section>
            <section id="new-report">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  21. Krok 21: Tabela: wiersz nagłówkowy w thead

    Kryteria:

    R.2.9 Pierwszy wiersz tabeli zawiera komórki nagłówkowe „Id”, „Imię”, „Nazwisko”, zastosowane znaczniki <tr> i <th>

    ok. 2 min
    Polecenie z arkusza

    Tabela z trzema kolumnami, w której pierwszy wiersz zawiera komórki nagłówkowe: „Id”, „Imię”, „Nazwisko”. Pozostałe wiersze wypełnione są przez skrypt 1

    Pierwszy wiersz to <tr> z trzema komórkami nagłówkowymi <th> (R.2.9). Wiersz nagłówkowy stoi w <thead>, a miejsce na dane - w <tbody>: przeglądarka wie, który wiersz opisuje kolumny (przy drukowaniu długiej tabeli powtórzy go na każdej stronie), a skrypt będzie dopisywał wiersze do <tbody>, nie mieszając ich z nagłówkiem.

    index.php PHP zmienione: 11 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZGŁOSZENIA</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zgłoszenia wydarzeń</h1>
        </header>
        <main>
            <section id="staff">
                <h2>Personel</h2>
                <form method="post">
                    <input type="radio" id="police" name="option" value="policjant" checked>
                    <label for="police">Policjant</label>
                    <input type="radio" id="rescuer" name="option" value="ratownik">
                    <label for="rescuer">Ratownik</label>
                    <button type="submit">Pokaż</button>
                </form>
                <table>
                    <thead>
                        <tr>
                            <th>Id</th>
                            <th>Imię</th>
                            <th>Nazwisko</th>
                        </tr>
                    </thead>
                    <tbody>
                    </tbody>
                </table>
            </section>
            <section id="new-report">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  22. Krok 22: Sekcja nowego zgłoszenia: nagłówek i lista

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku sekcji prawej: Nagłówek drugiego stopnia o treści „Nowe zgłoszenie”. Lista numerowana (uporządkowana) wypełniona przez skrypt 2

    <h2> i pusta lista uporządkowana <ol> - elementy <li> wypisze Skrypt 2. Numerów 1., 2., 3. na ilustracji 2 nie ma: zdejmie je CSS, bo przed nazwiskiem i tak stoi identyfikator osoby.

    index.php PHP zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZGŁOSZENIA</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zgłoszenia wydarzeń</h1>
        </header>
        <main>
            <section id="staff">
                <h2>Personel</h2>
                <form method="post">
                    <input type="radio" id="police" name="option" value="policjant" checked>
                    <label for="police">Policjant</label>
                    <input type="radio" id="rescuer" name="option" value="ratownik">
                    <label for="rescuer">Ratownik</label>
                    <button type="submit">Pokaż</button>
                </form>
                <table>
                    <thead>
                        <tr>
                            <th>Id</th>
                            <th>Imię</th>
                            <th>Nazwisko</th>
                        </tr>
                    </thead>
                    <tbody>
                    </tbody>
                </table>
            </section>
            <section id="new-report">
                <h2>Nowe zgłoszenie</h2>
                <ol>
                </ol>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  23. Krok 23: Drugi formularz: nowe zgłoszenie

    ok. 1 min
    Polecenie z arkusza

    Formularz wysyłający dane do tego samego pliku metodą bezpieczną

    Osobny <form method="post"> - każdy formularz wysyła tylko swoje pola, więc po „Dodaj zgłoszenie” w $_POST będzie sam identyfikator osoby, bez opcji z lewej sekcji. Tak skrypt rozpozna, który formularz kliknięto. Ten formularz zmienia dane w bazie, więc tu POST jest jedynym właściwym wyborem.

    index.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZGŁOSZENIA</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zgłoszenia wydarzeń</h1>
        </header>
        <main>
            <section id="staff">
                <h2>Personel</h2>
                <form method="post">
                    <input type="radio" id="police" name="option" value="policjant" checked>
                    <label for="police">Policjant</label>
                    <input type="radio" id="rescuer" name="option" value="ratownik">
                    <label for="rescuer">Ratownik</label>
                    <button type="submit">Pokaż</button>
                </form>
                <table>
                    <thead>
                        <tr>
                            <th>Id</th>
                            <th>Imię</th>
                            <th>Nazwisko</th>
                        </tr>
                    </thead>
                    <tbody>
                    </tbody>
                </table>
            </section>
            <section id="new-report">
                <h2>Nowe zgłoszenie</h2>
                <ol>
                </ol>
                <form method="post">
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  24. Krok 24: Etykieta i pole na identyfikator

    Kryteria:

    R.3.5 Pierwsze pole opcji jest domyślnie zaznaczone (checked) , w jednym momencie można wybrać tylko jedno pole opcji, pole edycyjne jest typu number

    ok. 2 min
    Polecenie z arkusza

    Pole etykiety „Wybierz id osoby z listy: ”, powiązane z polem edycyjnym. Pole edycyjne przeznaczone do wpisywania liczb

    <label for="person"> wskazuje id pola. Pole type="number" (R.3.5) z min="1", step="1" i required - identyfikatory to dodatnie liczby całkowite, a pusty formularz nie wyjdzie z przeglądarki. To wygoda, nie zabezpieczenie: Skrypt 3 i tak sprawdzi wartość. name="person" to klucz w $_POST.

    index.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZGŁOSZENIA</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zgłoszenia wydarzeń</h1>
        </header>
        <main>
            <section id="staff">
                <h2>Personel</h2>
                <form method="post">
                    <input type="radio" id="police" name="option" value="policjant" checked>
                    <label for="police">Policjant</label>
                    <input type="radio" id="rescuer" name="option" value="ratownik">
                    <label for="rescuer">Ratownik</label>
                    <button type="submit">Pokaż</button>
                </form>
                <table>
                    <thead>
                        <tr>
                            <th>Id</th>
                            <th>Imię</th>
                            <th>Nazwisko</th>
                        </tr>
                    </thead>
                    <tbody>
                    </tbody>
                </table>
            </section>
            <section id="new-report">
                <h2>Nowe zgłoszenie</h2>
                <ol>
                </ol>
                <form method="post">
                    <label for="person">Wybierz id osoby z listy: </label>
                    <input type="number" id="person" name="person" min="1" step="1" required>
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  25. Krok 25: Przycisk „Dodaj zgłoszenie”

    Kryteria:

    R.2.6 Na stronie umieszczone kontrolki: dwa pola opcji, dwa przyciski, pole edycyjne powiązane z etykietą

    ok. 1 min
    Polecenie z arkusza

    Przycisk „Dodaj zgłoszenie”, wysyłający dane z formularza do skryptu 3

    Drugi <button type="submit">. Na stronie są wszystkie kontrolki z R.2.6: dwa pola opcji, dwa przyciski i pole edycyjne z etykietą.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZGŁOSZENIA</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zgłoszenia wydarzeń</h1>
        </header>
        <main>
            <section id="staff">
                <h2>Personel</h2>
                <form method="post">
                    <input type="radio" id="police" name="option" value="policjant" checked>
                    <label for="police">Policjant</label>
                    <input type="radio" id="rescuer" name="option" value="ratownik">
                    <label for="rescuer">Ratownik</label>
                    <button type="submit">Pokaż</button>
                </form>
                <table>
                    <thead>
                        <tr>
                            <th>Id</th>
                            <th>Imię</th>
                            <th>Nazwisko</th>
                        </tr>
                    </thead>
                    <tbody>
                    </tbody>
                </table>
            </section>
            <section id="new-report">
                <h2>Nowe zgłoszenie</h2>
                <ol>
                </ol>
                <form method="post">
                    <label for="person">Wybierz id osoby z listy: </label>
                    <input type="number" id="person" name="person" min="1" step="1" required>
                    <button type="submit">Dodaj zgłoszenie</button>
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  26. Krok 26: Stopka

    Kryteria:

    R.2.4 W bloku nagłówkowym znajduje się <h1>: „Zgłoszenia wydarzeń”, w sekcji lewej <h2>: „Personel", w sekcji prawej <h2>: „Nowe zgłoszenie” oraz <p> 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

    Akapit w <footer>. Tu stoi imię i nazwisko autora rozwiązania - na egzaminie wpisujesz swój numer zdającego (PESEL). Są już nagłówki i akapit z R.2.4: <h1>, dwa <h2> i <p> w stopce.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZGŁOSZENIA</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zgłoszenia wydarzeń</h1>
        </header>
        <main>
            <section id="staff">
                <h2>Personel</h2>
                <form method="post">
                    <input type="radio" id="police" name="option" value="policjant" checked>
                    <label for="police">Policjant</label>
                    <input type="radio" id="rescuer" name="option" value="ratownik">
                    <label for="rescuer">Ratownik</label>
                    <button type="submit">Pokaż</button>
                </form>
                <table>
                    <thead>
                        <tr>
                            <th>Id</th>
                            <th>Imię</th>
                            <th>Nazwisko</th>
                        </tr>
                    </thead>
                    <tbody>
                    </tbody>
                </table>
            </section>
            <section id="new-report">
                <h2>Nowe zgłoszenie</h2>
                <ol>
                </ol>
                <form method="post">
                    <label for="person">Wybierz id osoby z listy: </label>
                    <input type="number" id="person" name="person" min="1" step="1" required>
                    <button type="submit">Dodaj zgłoszenie</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  27. Etap 3: Styl CSS

    Kroki 27-38 · po tym etapie masz 28 z 39 kryteriów (pkt)

    Krok 27: Zmienne CSS z kolorami arkusza

    ok. 2 min
    Polecenie z arkusza

    Styl CSS zdefiniowany jest w całości w zewnętrznym pliku o nazwie styl.css.

    Maroon pojawi się w sześciu miejscach: tło nagłówka, stopki i przycisków, ramki tabeli i komórek, napis przycisku pod kursorem. Wpisany raz w --color-maroon, w regułach jest używany jako var(--color-maroon). Tła sekcji też dostają zmienne z nazwami od zawartości sekcji.

    styl.css CSS nowy plik
    :root {
        --color-maroon: Maroon;
        --color-staff: LightSalmon;
        --color-report: DarkSalmon;
    }
    Podgląd strony po tym kroku (index.php)
  28. Krok 28: Krój Tahoma dla wszystkich selektorów

    Kryteria:

    R.4.1 domyślne dla wszystkich selektorów (*): krój czcionki Tahoma

    ok. 1 min
    Polecenie z arkusza

    Domyślnie dla wszystkich selektorów: krój czcionki Tahoma

    Selektor * (R.4.1) obejmuje też przyciski i pola, które domyślnie mają krój systemowy. Arkusz nie podaje rodziny zapasowej, a klucz sprawdza samą Tahomę, więc zostaje bez dopisku sans-serif.

    styl.css CSS zmienione: 4 linie
    :root {
        --color-maroon: Maroon;
        --color-staff: LightSalmon;
        --color-report: DarkSalmon;
    }
    
    * {
        font-family: Tahoma;
    }
    Podgląd strony po tym kroku (index.php)
  29. Krok 29: Nagłówek i stopka

    ok. 1 min
    Polecenie z arkusza

    Wspólne dla bloku nagłówkowego i stopki: kolor tła Maroon, biały kolor czcionki, marginesy wewnętrzne 10 px

    Jedna reguła dla dwóch selektorów. Wyrównania do środka tu nie ma - arkusz każe wyśrodkować nagłówki h1-h3, a nie cały blok, dlatego podpis w stopce stoi przy lewej krawędzi, jak na ilustracji 2.

    styl.css CSS zmienione: 7 linii
    :root {
        --color-maroon: Maroon;
        --color-staff: LightSalmon;
        --color-report: DarkSalmon;
    }
    
    * {
        font-family: Tahoma;
    }
    
    header,
    footer {
        background-color: var(--color-maroon);
        color: white;
        padding: 10px;
    }
    Podgląd strony po tym kroku (index.php)
  30. Krok 30: Sekcje: wysokość

    ok. 1 min
    Polecenie z arkusza

    Wspólne dla bloku sekcji lewej i prawej: szerokość 50%, wysokość 500 px

    Selektor section obejmuje obie sekcje. Szerokość 50% wyznaczy siatka w kroku układu - klucz R.4.4 dopuszcza ją w grid-template-columns.

    styl.css CSS zmienione: 4 linie
    :root {
        --color-maroon: Maroon;
        --color-staff: LightSalmon;
        --color-report: DarkSalmon;
    }
    
    * {
        font-family: Tahoma;
    }
    
    header,
    footer {
        background-color: var(--color-maroon);
        color: white;
        padding: 10px;
    }
    
    section {
        height: 500px;
    }
    Podgląd strony po tym kroku (index.php)
  31. Krok 31: Kolory tła sekcji

    ok. 1 min
    Polecenie z arkusza

    Dodatkowo kolor tła dla bloku sekcji lewej – LightSalmon, dla prawej – DarkSalmon

    Identyfikatory nadane sekcjom w HTML: #staff i #new-report. Selektor identyfikatora dokłada tylko tło - wysokość z reguły section zostaje.

    styl.css CSS zmienione: 8 linii
    :root {
        --color-maroon: Maroon;
        --color-staff: LightSalmon;
        --color-report: DarkSalmon;
    }
    
    * {
        font-family: Tahoma;
    }
    
    header,
    footer {
        background-color: var(--color-maroon);
        color: white;
        padding: 10px;
    }
    
    section {
        height: 500px;
    }
    
    #staff {
        background-color: var(--color-staff);
    }
    
    #new-report {
        background-color: var(--color-report);
    }
    Podgląd strony po tym kroku (index.php)
  32. Krok 32: Układ: siatka w bloku głównym

    Kryteria:

    R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z ilustracją 3 w arkuszu egzaminacyjnym (prawidłowo zastosowano właściwości CSS decydujące o układzie bloków na stronie, np. float i clear albo display flex albo grid albo position)

    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

    <main> staje się siatką o dwóch kolumnach po 50%: sekcja personelu w pierwszej, nowe zgłoszenie w drugiej. Nagłówek i stopka są poza siatką, więc stoją nad i pod nią - bez float i clear (R.3.1). Szerokości sekcji w grid-template-columns zamykają R.4.4 razem z wysokością 500 px i szerokością tabeli, która dojdzie za chwilę.

    Typowe błędy

    Odstęp gap przy kolumnach 50% + 50% wypchnąłby siatkę poza ekran - przy procentach z arkusza odstępów nie ma.

    styl.css CSS zmienione: 5 linii
    :root {
        --color-maroon: Maroon;
        --color-staff: LightSalmon;
        --color-report: DarkSalmon;
    }
    
    * {
        font-family: Tahoma;
    }
    
    header,
    footer {
        background-color: var(--color-maroon);
        color: white;
        padding: 10px;
    }
    
    section {
        height: 500px;
    }
    
    #staff {
        background-color: var(--color-staff);
    }
    
    #new-report {
        background-color: var(--color-report);
    }
    
    main {
        display: grid;
        grid-template-columns: 50% 50%;
    }
    Podgląd strony po tym kroku (index.php)
  33. Krok 33: Nagłówki wyśrodkowane

    Kryteria:

    R.4.5 wyrównanie tekstu do środka: selektory h1, h2, h3

    ok. 1 min
    Polecenie z arkusza

    Wspólne dla selektorów nagłówka pierwszego, drugiego i trzeciego stopnia: wyrównanie tekstu do środka

    Selektory znaczników h1, h2, h3, jak każe uwaga arkusza (R.4.5). <h3> wypisze dopiero Skrypt 1 - reguła już na niego czeka.

    styl.css CSS zmienione: 6 linii
    :root {
        --color-maroon: Maroon;
        --color-staff: LightSalmon;
        --color-report: DarkSalmon;
    }
    
    * {
        font-family: Tahoma;
    }
    
    header,
    footer {
        background-color: var(--color-maroon);
        color: white;
        padding: 10px;
    }
    
    section {
        height: 500px;
    }
    
    #staff {
        background-color: var(--color-staff);
    }
    
    #new-report {
        background-color: var(--color-report);
    }
    
    main {
        display: grid;
        grid-template-columns: 50% 50%;
    }
    
    h1,
    h2,
    h3 {
        text-align: center;
    }
    Podgląd strony po tym kroku (index.php)
  34. Krok 34: Obramowanie tabeli i komórek

    ok. 1 min
    Polecenie z arkusza

    Wspólne dla selektora tabeli i selektora komórki tabeli: obramowanie linią ciągłą o szerokości 1 px i kolorze Maroon

    Skrót border: grubość, styl (solid - linia ciągła) i kolor. Komórka tabeli to td; komórek danych jeszcze nie ma, obramowanie pojawi się po Skrypcie 1.

    styl.css CSS zmienione: 5 linii
    :root {
        --color-maroon: Maroon;
        --color-staff: LightSalmon;
        --color-report: DarkSalmon;
    }
    
    * {
        font-family: Tahoma;
    }
    
    header,
    footer {
        background-color: var(--color-maroon);
        color: white;
        padding: 10px;
    }
    
    section {
        height: 500px;
    }
    
    #staff {
        background-color: var(--color-staff);
    }
    
    #new-report {
        background-color: var(--color-report);
    }
    
    main {
        display: grid;
        grid-template-columns: 50% 50%;
    }
    
    h1,
    h2,
    h3 {
        text-align: center;
    }
    
    table,
    td {
        border: 1px solid var(--color-maroon);
    }
    Podgląd strony po tym kroku (index.php)
  35. Krok 35: Tabela: szerokość, jedno obramowanie, wyśrodkowanie

    Kryteria: ,

    R.4.4 wysokość: blok sekcji lewej i prawej - 500px, szerokość: blok sekcji lewej i prawej - 50% (width lub grid-template-columns lub flex) , selektor table - 85%

    R.4.8 obramowanie: tabeli i selektora td - border: 1px solid Maroon; oraz dla tabeli krawędzie połączone: border-collapse: collapse;

    ok. 2 min
    Polecenie z arkusza

    Dodatkowo dla selektora tabeli: szerokość 85%, krawędzie połączone w jedno obramowanie, marginesy zewnętrzne automatycznie wyliczone przez przeglądarkę

    border-collapse: collapse skleja ramki tabeli i komórek w jedną linię (R.4.8). margin: auto rozdziela wolne miejsce po równo z lewej i prawej - tabela o szerokości 85% staje na środku sekcji. Komplet wymiarów R.4.4.

    styl.css CSS zmienione: 6 linii
    :root {
        --color-maroon: Maroon;
        --color-staff: LightSalmon;
        --color-report: DarkSalmon;
    }
    
    * {
        font-family: Tahoma;
    }
    
    header,
    footer {
        background-color: var(--color-maroon);
        color: white;
        padding: 10px;
    }
    
    section {
        height: 500px;
    }
    
    #staff {
        background-color: var(--color-staff);
    }
    
    #new-report {
        background-color: var(--color-report);
    }
    
    main {
        display: grid;
        grid-template-columns: 50% 50%;
    }
    
    h1,
    h2,
    h3 {
        text-align: center;
    }
    
    table,
    td {
        border: 1px solid var(--color-maroon);
    }
    
    table {
        width: 85%;
        border-collapse: collapse;
        margin: auto;
    }
    Podgląd strony po tym kroku (index.php)
  36. Krok 36: Lista bez punktora

    Kryteria:

    R.4.7 styl listy numerowanej brak punktora: list-style lub list-style-type: none;

    ok. 1 min
    Polecenie z arkusza

    Dla listy numerowanej: brak punktora

    list-style-type: none zdejmuje numery (R.4.7). Lista nadal jest <ol> - czytnik ekranu zapowie ją jako listę, tylko bez wizualnych numerów.

    styl.css CSS zmienione: 4 linie
    :root {
        --color-maroon: Maroon;
        --color-staff: LightSalmon;
        --color-report: DarkSalmon;
    }
    
    * {
        font-family: Tahoma;
    }
    
    header,
    footer {
        background-color: var(--color-maroon);
        color: white;
        padding: 10px;
    }
    
    section {
        height: 500px;
    }
    
    #staff {
        background-color: var(--color-staff);
    }
    
    #new-report {
        background-color: var(--color-report);
    }
    
    main {
        display: grid;
        grid-template-columns: 50% 50%;
    }
    
    h1,
    h2,
    h3 {
        text-align: center;
    }
    
    table,
    td {
        border: 1px solid var(--color-maroon);
    }
    
    table {
        width: 85%;
        border-collapse: collapse;
        margin: auto;
    }
    
    ol {
        list-style-type: none;
    }
    Podgląd strony po tym kroku (index.php)
  37. Krok 37: Przyciski: kolory, margines lewy, zaokrąglenie

    Kryteria: , , ,

    R.4.2 kolor tła: blok nagłówkowy, stopka i oba przyciski - Maroon, blok sekcji lewej - LightSalmon, blok sekcji prawej - DarkSalmon

    R.4.3 kolor czcionki: blok nagłówkowy, stopka, oba przyciski - biały

    R.4.6 marginesy wewnętrzne (padding) : blok nagłówkowy, stopka, oba przyciski - 10px marginesy zewnętrzne (margin) : tabela - auto, jedynie margin-left dla obu przycisków - 20px

    R.4.9 styl obu przycisków: bez obramowania np. border: none; oraz border-radius: 20px;

    ok. 2 min
    Polecenie z arkusza

    Dla obu przycisków: kolor tła Maroon, biały kolor czcionki, jedynie margines zewnętrzny lewy 20 px, marginesy wewnętrzne 10 px, bez obramowania, zaokrąglenie rogów 20 px

    Selektor button obejmuje oba przyciski. „Jedynie margines zewnętrzny lewy” to margin-left: 20px - margin: 20px odsunąłby przycisk ze wszystkich stron. Ta reguła zamyka kolory tła (R.4.2), kolor czcionki (R.4.3), marginesy (R.4.6) i styl przycisków (R.4.9).

    styl.css CSS zmienione: 9 linii
    :root {
        --color-maroon: Maroon;
        --color-staff: LightSalmon;
        --color-report: DarkSalmon;
    }
    
    * {
        font-family: Tahoma;
    }
    
    header,
    footer {
        background-color: var(--color-maroon);
        color: white;
        padding: 10px;
    }
    
    section {
        height: 500px;
    }
    
    #staff {
        background-color: var(--color-staff);
    }
    
    #new-report {
        background-color: var(--color-report);
    }
    
    main {
        display: grid;
        grid-template-columns: 50% 50%;
    }
    
    h1,
    h2,
    h3 {
        text-align: center;
    }
    
    table,
    td {
        border: 1px solid var(--color-maroon);
    }
    
    table {
        width: 85%;
        border-collapse: collapse;
        margin: auto;
    }
    
    ol {
        list-style-type: none;
    }
    
    button {
        background-color: var(--color-maroon);
        color: white;
        margin-left: 20px;
        padding: 10px;
        border: none;
        border-radius: 20px;
    }
    Podgląd strony po tym kroku (index.php)
  38. Krok 38: Przycisk pod kursorem i z klawiatury

    Kryteria:

    R.4.10 styl, gdy kursor znajdzie się na przycisku jego kolor tła zamienia się na biały, a kolor czcionki na Maroon

    ok. 1 min
    Polecenie z arkusza

    Gdy kursor znajdzie się na przycisku jego kolor tła zmienia się na biały, a kolor czcionki na Maroon

    Pseudoklasa :hover zamienia kolory miejscami (R.4.10). Ta sama zamiana dla :focus-visible: przycisk bez ramki (border: none) po wybraniu klawiszem Tab musi być jakoś wyróżniony, inaczej osoba korzystająca z klawiatury nie wie, gdzie jest.

    styl.css CSS zmienione: 6 linii
    :root {
        --color-maroon: Maroon;
        --color-staff: LightSalmon;
        --color-report: DarkSalmon;
    }
    
    * {
        font-family: Tahoma;
    }
    
    header,
    footer {
        background-color: var(--color-maroon);
        color: white;
        padding: 10px;
    }
    
    section {
        height: 500px;
    }
    
    #staff {
        background-color: var(--color-staff);
    }
    
    #new-report {
        background-color: var(--color-report);
    }
    
    main {
        display: grid;
        grid-template-columns: 50% 50%;
    }
    
    h1,
    h2,
    h3 {
        text-align: center;
    }
    
    table,
    td {
        border: 1px solid var(--color-maroon);
    }
    
    table {
        width: 85%;
        border-collapse: collapse;
        margin: auto;
    }
    
    ol {
        list-style-type: none;
    }
    
    button {
        background-color: var(--color-maroon);
        color: white;
        margin-left: 20px;
        padding: 10px;
        border: none;
        border-radius: 20px;
    }
    
    button:hover,
    button:focus-visible {
        background-color: white;
        color: var(--color-maroon);
    }
    Podgląd strony po tym kroku (index.php)
  39. Etap 4: Skrypty PHP

    Kroki 39-49 · po tym etapie masz 39 z 39 kryteriów (pkt)

    Krok 39: Połączenie PDO z obsługą błędu i zamknięcie

    Kryteria:

    R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy zgloszenia oraz ostatnią operacją na bazie jest jej zamknięcie

    ok. 4 min
    Polecenie z arkusza

    Łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie zgloszenia. Na końcu zamykane jest połączenie z serwerem

    Cały PHP stoi na górze pliku, przed <!DOCTYPE html> - za chwilę dojdzie przekierowanie, a nagłówki HTTP wysyła się przed treścią strony. new PDO(...) z charset=utf8mb4 i dwiema opcjami: ERRMODE_EXCEPTION (błąd SQL rzuca wyjątek, który da się złapać) i FETCH_ASSOC (wiersze z nazwami kolumn). try/catch zamienia nieudane połączenie na ogólny komunikat z kodem 500.

    Na dole bloku zamknięcie: $statement = null; (obiekt zapytania trzyma połączenie) i $pdo = null;. Zapytania będą dopisywane nad tymi liniami, więc zamknięcie zostaje ostatnią operacją na bazie (R.5.1). Od tego kroku podgląd to wynik prawdziwego uruchomienia w PHP 8.2.

    Typowe błędy

    PDO nie ma w tabeli 1 arkusza - nazwy stałych trzeba znać na pamięć. Kto nie jest pewny składni, pisze funkcjami mysqli_* z tabeli 1 (wersja podstawowa).

    index.php PHP zmienione: 16 linii
    <?php
    // Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumn
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=zgloszenia;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $exception) {
        http_response_code(500);
        exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');
    }
    
    // Wszystkie dane pobrane - zamknięcie połączenia
    $statement = null;
    $pdo = null;
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZGŁOSZENIA</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zgłoszenia wydarzeń</h1>
        </header>
        <main>
            <section id="staff">
                <h2>Personel</h2>
                <form method="post">
                    <input type="radio" id="police" name="option" value="policjant" checked>
                    <label for="police">Policjant</label>
                    <input type="radio" id="rescuer" name="option" value="ratownik">
                    <label for="rescuer">Ratownik</label>
                    <button type="submit">Pokaż</button>
                </form>
                <table>
                    <thead>
                        <tr>
                            <th>Id</th>
                            <th>Imię</th>
                            <th>Nazwisko</th>
                        </tr>
                    </thead>
                    <tbody>
                    </tbody>
                </table>
            </section>
            <section id="new-report">
                <h2>Nowe zgłoszenie</h2>
                <ol>
                </ol>
                <form method="post">
                    <label for="person">Wybierz id osoby z listy: </label>
                    <input type="number" id="person" name="person" min="1" step="1" required>
                    <button type="submit">Dodaj zgłoszenie</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  40. Krok 40: Funkcja escape() do bezpiecznego wypisywania

    ok. 2 min
    Polecenie z arkusza

    Należy stosować znaczące nazewnictwo zmiennych i funkcji w języku polskim lub angielskim

    escape() przepuszcza tekst przez htmlspecialchars z ENT_QUOTES: <, >, & i oba cudzysłowy zamieniają się na encje. Każde nazwisko z bazy i każdy komunikat przechodzi przez nią przed wypisaniem - wpis <script> w tabeli personel pokaże się jako tekst, zamiast wykonać się w przeglądarce (XSS). Nazwa to pełne słowo, a nie frameworkowy skrót e(), bo arkusz wymaga nazw mówiących, do czego służą.

    index.php PHP zmienione: 5 linii
    <?php
    // Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumn
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=zgloszenia;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $exception) {
        http_response_code(500);
        exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');
    }
    
    function escape(string|int|float|null $value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
    }
    
    // Wszystkie dane pobrane - zamknięcie połączenia
    $statement = null;
    $pdo = null;
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZGŁOSZENIA</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zgłoszenia wydarzeń</h1>
        </header>
        <main>
            <section id="staff">
                <h2>Personel</h2>
                <form method="post">
                    <input type="radio" id="police" name="option" value="policjant" checked>
                    <label for="police">Policjant</label>
                    <input type="radio" id="rescuer" name="option" value="ratownik">
                    <label for="rescuer">Ratownik</label>
                    <button type="submit">Pokaż</button>
                </form>
                <table>
                    <thead>
                        <tr>
                            <th>Id</th>
                            <th>Imię</th>
                            <th>Nazwisko</th>
                        </tr>
                    </thead>
                    <tbody>
                    </tbody>
                </table>
            </section>
            <section id="new-report">
                <h2>Nowe zgłoszenie</h2>
                <ol>
                </ol>
                <form method="post">
                    <label for="person">Wybierz id osoby z listy: </label>
                    <input type="number" id="person" name="person" min="1" step="1" required>
                    <button type="submit">Dodaj zgłoszenie</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

    Podgląd strony bez zmian - taki jak po kroku 39.

  41. Krok 41: Skrypt 1: opcja z białej listy i zapytanie 1

    Kryteria: ,

    R.5.3 Skrypt 1 zawiera instrukcję wysyłającą do bazy zmodyfikowane zapytanie 1 w ten sposób, że wybierane są rekordy z opcją wysłaną z formularza (policjant lub ratownik) lub w stanie początkowym z opcją policjant

    R.5.8 Przynajmniej jeden skrypt zawiera instrukcję pobierającą dane z tablicy globalnej odpowiadającej zastosowanej metodzie formularza ($_POST, $_GET, $_REQUEST)

    ok. 4 min
    Polecenie z arkusza

    Wysyła do bazy danych zapytanie 1, zmodyfikowane w ten sposób, że wybierane są rekordy z opcją wysłaną z formularza (policjant lub ratownik) lub w stanie początkowym z opcją policjant

    Wybrane pole opcji jest w $_POST['option'] (R.5.8). Operator ?? podstawia „policjant”, gdy formularza nikt nie wysłał - to stan początkowy z arkusza.

    Formularz pozwala wybrać tylko dwie wartości, ale żądanie POST da się wysłać z pominięciem formularza. Stąd biała lista: in_array($option, $allowedOptions, true) sprawdza, czy wartość jest jedną z dwóch dozwolonych (true - porównanie ścisłe, także typu), a inna wraca do „policjant”. Do tego zapytanie przygotowane z parametrem :status (R.5.3) - dwie warstwy: biała lista pilnuje sensu, parametr pilnuje składni SQL. W wersji podstawowej z egzaminu wartość ' OR '1'='1 pokazywała cały personel.

    Podgląd od tego kroku: strona po wybraniu „Ratownik” i kliknięciu „Pokaż”, jak na ilustracji 2 - wiersze dojdą w kolejnym kroku.

    index.php PHP zmienione: 10 linii
    <?php
    // Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumn
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=zgloszenia;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $exception) {
        http_response_code(500);
        exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');
    }
    
    function escape(string|int|float|null $value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
    }
    
    // Skrypt 1: personel o statusie wybranym w formularzu (zapytanie 1)
    $allowedOptions = ['policjant', 'ratownik'];
    $option = $_POST['option'] ?? 'policjant';
    if (!in_array($option, $allowedOptions, true)) {
        $option = 'policjant';
    }
    $statement = $pdo->prepare('SELECT id, imie, nazwisko FROM personel WHERE status = :status');
    $statement->execute(['status' => $option]);
    $staff = $statement->fetchAll();
    
    // Wszystkie dane pobrane - zamknięcie połączenia
    $statement = null;
    $pdo = null;
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZGŁOSZENIA</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zgłoszenia wydarzeń</h1>
        </header>
        <main>
            <section id="staff">
                <h2>Personel</h2>
                <form method="post">
                    <input type="radio" id="police" name="option" value="policjant" checked>
                    <label for="police">Policjant</label>
                    <input type="radio" id="rescuer" name="option" value="ratownik">
                    <label for="rescuer">Ratownik</label>
                    <button type="submit">Pokaż</button>
                </form>
                <table>
                    <thead>
                        <tr>
                            <th>Id</th>
                            <th>Imię</th>
                            <th>Nazwisko</th>
                        </tr>
                    </thead>
                    <tbody>
                    </tbody>
                </table>
            </section>
            <section id="new-report">
                <h2>Nowe zgłoszenie</h2>
                <ol>
                </ol>
                <form method="post">
                    <label for="person">Wybierz id osoby z listy: </label>
                    <input type="number" id="person" name="person" min="1" step="1" required>
                    <button type="submit">Dodaj zgłoszenie</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

    Podgląd strony bez zmian - taki jak po kroku 39.

  42. Krok 42: Skrypt 1: nagłówek z wybraną opcją

    Kryteria:

    R.5.4 Skrypt 1 wyświetla <h3> o treści „Wybrano opcję: <opcja>”, gdzie <> oznacza wybraną w formularzu opcję

    ok. 1 min
    Polecenie z arkusza

    Wypisuje nagłówek trzeciego stopnia o treści „Wybrano opcję: <opcja>”, gdzie <opcja> oznacza wybraną w formularzu opcję. Element ten należy wypisać przed tabelą.

    <h3> stoi w HTML między formularzem a tabelą, więc jest „przed tabelą” (R.5.4). Wypisuje $option po białej liście - „ratownik” małą literą, jak na ilustracji 2. Wyśrodkowanie dała już reguła h3.

    index.php PHP zmienione: 1 linia
    <?php
    // Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumn
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=zgloszenia;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $exception) {
        http_response_code(500);
        exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');
    }
    
    function escape(string|int|float|null $value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
    }
    
    // Skrypt 1: personel o statusie wybranym w formularzu (zapytanie 1)
    $allowedOptions = ['policjant', 'ratownik'];
    $option = $_POST['option'] ?? 'policjant';
    if (!in_array($option, $allowedOptions, true)) {
        $option = 'policjant';
    }
    $statement = $pdo->prepare('SELECT id, imie, nazwisko FROM personel WHERE status = :status');
    $statement->execute(['status' => $option]);
    $staff = $statement->fetchAll();
    
    // Wszystkie dane pobrane - zamknięcie połączenia
    $statement = null;
    $pdo = null;
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZGŁOSZENIA</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zgłoszenia wydarzeń</h1>
        </header>
        <main>
            <section id="staff">
                <h2>Personel</h2>
                <form method="post">
                    <input type="radio" id="police" name="option" value="policjant" checked>
                    <label for="police">Policjant</label>
                    <input type="radio" id="rescuer" name="option" value="ratownik">
                    <label for="rescuer">Ratownik</label>
                    <button type="submit">Pokaż</button>
                </form>
                <h3>Wybrano opcję: <?= escape($option) ?></h3>
                <table>
                    <thead>
                        <tr>
                            <th>Id</th>
                            <th>Imię</th>
                            <th>Nazwisko</th>
                        </tr>
                    </thead>
                    <tbody>
                    </tbody>
                </table>
            </section>
            <section id="new-report">
                <h2>Nowe zgłoszenie</h2>
                <ol>
                </ol>
                <form method="post">
                    <label for="person">Wybierz id osoby z listy: </label>
                    <input type="number" id="person" name="person" min="1" step="1" required>
                    <button type="submit">Dodaj zgłoszenie</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  43. Krok 43: Skrypt 1: wiersze tabeli

    Kryteria: ,

    R.5.5 Skrypt 1 wypełnia tabelę zwróconymi zapytaniem wierszami, odpowiednie pola w kolumnach, wyświetlone są wszystkie zwrócone wiersze

    R.2.8 W sekcji lewej umieszczona tabela o 3 kolumnach (w skrypcie lub HTML) zastosowane znaczniki <table>, <tr>, <td>

    ok. 3 min
    Polecenie z arkusza

    Wszystkie zwrócone zapytaniem wiersze wyświetla jako kolejne wiersze tabeli zgodnie z ilustracją 2

    Pętla foreach ($staff as $employee): ... endforeach; w <tbody>: jeden <tr> na osobę, trzy <td> w kolejności kolumn (R.5.5, R.2.8). Podgląd jak lewa sekcja ilustracji 2: Joanna Nosowska, Karolina Mizera, Leon Zawodowiec i dalej - 7 ratowników. Po wybraniu „Policjant” albo przy pierwszym wejściu tabela ma 11 wierszy.

    Zmienna pętli nazywa się $employee (pracownik), a nie $row - w szablonie od razu widać, co jest w środku.

    index.php PHP zmienione: 7 linii
    <?php
    // Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumn
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=zgloszenia;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $exception) {
        http_response_code(500);
        exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');
    }
    
    function escape(string|int|float|null $value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
    }
    
    // Skrypt 1: personel o statusie wybranym w formularzu (zapytanie 1)
    $allowedOptions = ['policjant', 'ratownik'];
    $option = $_POST['option'] ?? 'policjant';
    if (!in_array($option, $allowedOptions, true)) {
        $option = 'policjant';
    }
    $statement = $pdo->prepare('SELECT id, imie, nazwisko FROM personel WHERE status = :status');
    $statement->execute(['status' => $option]);
    $staff = $statement->fetchAll();
    
    // Wszystkie dane pobrane - zamknięcie połączenia
    $statement = null;
    $pdo = null;
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZGŁOSZENIA</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zgłoszenia wydarzeń</h1>
        </header>
        <main>
            <section id="staff">
                <h2>Personel</h2>
                <form method="post">
                    <input type="radio" id="police" name="option" value="policjant" checked>
                    <label for="police">Policjant</label>
                    <input type="radio" id="rescuer" name="option" value="ratownik">
                    <label for="rescuer">Ratownik</label>
                    <button type="submit">Pokaż</button>
                </form>
                <h3>Wybrano opcję: <?= escape($option) ?></h3>
                <table>
                    <thead>
                        <tr>
                            <th>Id</th>
                            <th>Imię</th>
                            <th>Nazwisko</th>
                        </tr>
                    </thead>
                    <tbody>
    <?php foreach ($staff as $employee): ?>
                        <tr>
                            <td><?= escape($employee['id']) ?></td>
                            <td><?= escape($employee['imie']) ?></td>
                            <td><?= escape($employee['nazwisko']) ?></td>
                        </tr>
    <?php endforeach; ?>
                    </tbody>
                </table>
            </section>
            <section id="new-report">
                <h2>Nowe zgłoszenie</h2>
                <ol>
                </ol>
                <form method="post">
                    <label for="person">Wybierz id osoby z listy: </label>
                    <input type="number" id="person" name="person" min="1" step="1" required>
                    <button type="submit">Dodaj zgłoszenie</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  44. Krok 44: Skrypt 1: zaznaczona opcja zostaje po wysłaniu

    ok. 2 min
    Polecenie z arkusza

    Pole opcji z treścią „Policjant”, domyślnie zaznaczone

    Po kliknięciu „Pokaż” strona ładuje się od nowa i zwykłe checked wróciłoby na „Policjant” - nad tabelą ratowników. Na ilustracji 2 widać właśnie taki stan, bo tak działa najprostsza wersja. Tu checked dostaje pole, którego wartość jest w $option: przy pierwszym wejściu to „policjant” (domyślne zaznaczenie z arkusza i R.3.5), po wybraniu ratowników - „ratownik”. Użytkownik zawsze widzi, co wybrał.

    index.php PHP zmienione: 2 linie
    <?php
    // Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumn
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=zgloszenia;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $exception) {
        http_response_code(500);
        exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');
    }
    
    function escape(string|int|float|null $value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
    }
    
    // Skrypt 1: personel o statusie wybranym w formularzu (zapytanie 1)
    $allowedOptions = ['policjant', 'ratownik'];
    $option = $_POST['option'] ?? 'policjant';
    if (!in_array($option, $allowedOptions, true)) {
        $option = 'policjant';
    }
    $statement = $pdo->prepare('SELECT id, imie, nazwisko FROM personel WHERE status = :status');
    $statement->execute(['status' => $option]);
    $staff = $statement->fetchAll();
    
    // Wszystkie dane pobrane - zamknięcie połączenia
    $statement = null;
    $pdo = null;
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZGŁOSZENIA</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zgłoszenia wydarzeń</h1>
        </header>
        <main>
            <section id="staff">
                <h2>Personel</h2>
                <form method="post">
                    <input type="radio" id="police" name="option" value="policjant"<?= $option === 'policjant' ? ' checked' : '' ?>>
                    <label for="police">Policjant</label>
                    <input type="radio" id="rescuer" name="option" value="ratownik"<?= $option === 'ratownik' ? ' checked' : '' ?>>
                    <label for="rescuer">Ratownik</label>
                    <button type="submit">Pokaż</button>
                </form>
                <h3>Wybrano opcję: <?= escape($option) ?></h3>
                <table>
                    <thead>
                        <tr>
                            <th>Id</th>
                            <th>Imię</th>
                            <th>Nazwisko</th>
                        </tr>
                    </thead>
                    <tbody>
    <?php foreach ($staff as $employee): ?>
                        <tr>
                            <td><?= escape($employee['id']) ?></td>
                            <td><?= escape($employee['imie']) ?></td>
                            <td><?= escape($employee['nazwisko']) ?></td>
                        </tr>
    <?php endforeach; ?>
                    </tbody>
                </table>
            </section>
            <section id="new-report">
                <h2>Nowe zgłoszenie</h2>
                <ol>
                </ol>
                <form method="post">
                    <label for="person">Wybierz id osoby z listy: </label>
                    <input type="number" id="person" name="person" min="1" step="1" required>
                    <button type="submit">Dodaj zgłoszenie</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  45. Krok 45: Skrypt 2: zapytanie 3

    Kryteria:

    R.5.2 Przynajmniej jeden skrypt zawiera instrukcję wysyłającą do bazy danych zapytanie

    ok. 2 min
    Polecenie z arkusza

    Skrypt 2: wysyła do bazy danych zapytanie 3

    Zapytanie 3 z kwerendy.txt (NOT EXISTS) przez $pdo->query(...) - bez wartości od użytkownika, więc bez prepare (R.5.2). Wynik od razu w tablicy $withoutReports; nazwa mówi, co jest w środku.

    index.php PHP zmienione: 6 linii
    <?php
    // Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumn
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=zgloszenia;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $exception) {
        http_response_code(500);
        exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');
    }
    
    function escape(string|int|float|null $value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
    }
    
    // Skrypt 1: personel o statusie wybranym w formularzu (zapytanie 1)
    $allowedOptions = ['policjant', 'ratownik'];
    $option = $_POST['option'] ?? 'policjant';
    if (!in_array($option, $allowedOptions, true)) {
        $option = 'policjant';
    }
    $statement = $pdo->prepare('SELECT id, imie, nazwisko FROM personel WHERE status = :status');
    $statement->execute(['status' => $option]);
    $staff = $statement->fetchAll();
    
    // Skrypt 2: osoby bez żadnego zgłoszenia (zapytanie 3)
    $withoutReports = $pdo->query(
        'SELECT p.id, p.nazwisko FROM personel AS p
         WHERE NOT EXISTS (SELECT 1 FROM rejestr AS r WHERE r.id_personel = p.id)'
    )->fetchAll();
    
    // Wszystkie dane pobrane - zamknięcie połączenia
    $statement = null;
    $pdo = null;
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZGŁOSZENIA</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zgłoszenia wydarzeń</h1>
        </header>
        <main>
            <section id="staff">
                <h2>Personel</h2>
                <form method="post">
                    <input type="radio" id="police" name="option" value="policjant"<?= $option === 'policjant' ? ' checked' : '' ?>>
                    <label for="police">Policjant</label>
                    <input type="radio" id="rescuer" name="option" value="ratownik"<?= $option === 'ratownik' ? ' checked' : '' ?>>
                    <label for="rescuer">Ratownik</label>
                    <button type="submit">Pokaż</button>
                </form>
                <h3>Wybrano opcję: <?= escape($option) ?></h3>
                <table>
                    <thead>
                        <tr>
                            <th>Id</th>
                            <th>Imię</th>
                            <th>Nazwisko</th>
                        </tr>
                    </thead>
                    <tbody>
    <?php foreach ($staff as $employee): ?>
                        <tr>
                            <td><?= escape($employee['id']) ?></td>
                            <td><?= escape($employee['imie']) ?></td>
                            <td><?= escape($employee['nazwisko']) ?></td>
                        </tr>
    <?php endforeach; ?>
                    </tbody>
                </table>
            </section>
            <section id="new-report">
                <h2>Nowe zgłoszenie</h2>
                <ol>
                </ol>
                <form method="post">
                    <label for="person">Wybierz id osoby z listy: </label>
                    <input type="number" id="person" name="person" min="1" step="1" required>
                    <button type="submit">Dodaj zgłoszenie</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

    Podgląd strony bez zmian - taki jak po kroku 44.

  46. Krok 46: Skrypt 2: elementy listy

    Kryteria: ,

    R.5.6 Skrypt 2 wyświetla wszystkie zwrócone zapytaniem 3 wiersze jako elementy <li> (id i nazwisko)

    R.2.7 W sekcji prawej umieszczona jedna lista numerowana z elementami, zastosowane znaczniki <ol>, <li> (w skrypcie lub HTML)

    ok. 2 min
    Polecenie z arkusza

    Wszystkie zwrócone zapytaniem wiersze wyświetla jako elementy listy zgodnie z ilustracją 2

    Jeden <li> na osobę: identyfikator, spacja, nazwisko (R.5.6, R.2.7). Podgląd: 2 Nowak, 3 Nosowska, 4 Mizera - jak na ilustracji 2. To identyfikatory, które użytkownik wpisze w pole pod listą.

    index.php PHP zmienione: 3 linie
    <?php
    // Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumn
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=zgloszenia;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $exception) {
        http_response_code(500);
        exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');
    }
    
    function escape(string|int|float|null $value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
    }
    
    // Skrypt 1: personel o statusie wybranym w formularzu (zapytanie 1)
    $allowedOptions = ['policjant', 'ratownik'];
    $option = $_POST['option'] ?? 'policjant';
    if (!in_array($option, $allowedOptions, true)) {
        $option = 'policjant';
    }
    $statement = $pdo->prepare('SELECT id, imie, nazwisko FROM personel WHERE status = :status');
    $statement->execute(['status' => $option]);
    $staff = $statement->fetchAll();
    
    // Skrypt 2: osoby bez żadnego zgłoszenia (zapytanie 3)
    $withoutReports = $pdo->query(
        'SELECT p.id, p.nazwisko FROM personel AS p
         WHERE NOT EXISTS (SELECT 1 FROM rejestr AS r WHERE r.id_personel = p.id)'
    )->fetchAll();
    
    // Wszystkie dane pobrane - zamknięcie połączenia
    $statement = null;
    $pdo = null;
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZGŁOSZENIA</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zgłoszenia wydarzeń</h1>
        </header>
        <main>
            <section id="staff">
                <h2>Personel</h2>
                <form method="post">
                    <input type="radio" id="police" name="option" value="policjant"<?= $option === 'policjant' ? ' checked' : '' ?>>
                    <label for="police">Policjant</label>
                    <input type="radio" id="rescuer" name="option" value="ratownik"<?= $option === 'ratownik' ? ' checked' : '' ?>>
                    <label for="rescuer">Ratownik</label>
                    <button type="submit">Pokaż</button>
                </form>
                <h3>Wybrano opcję: <?= escape($option) ?></h3>
                <table>
                    <thead>
                        <tr>
                            <th>Id</th>
                            <th>Imię</th>
                            <th>Nazwisko</th>
                        </tr>
                    </thead>
                    <tbody>
    <?php foreach ($staff as $employee): ?>
                        <tr>
                            <td><?= escape($employee['id']) ?></td>
                            <td><?= escape($employee['imie']) ?></td>
                            <td><?= escape($employee['nazwisko']) ?></td>
                        </tr>
    <?php endforeach; ?>
                    </tbody>
                </table>
            </section>
            <section id="new-report">
                <h2>Nowe zgłoszenie</h2>
                <ol>
    <?php foreach ($withoutReports as $person): ?>
                    <li><?= escape($person['id']) ?> <?= escape($person['nazwisko']) ?></li>
    <?php endforeach; ?>
                </ol>
                <form method="post">
                    <label for="person">Wybierz id osoby z listy: </label>
                    <input type="number" id="person" name="person" min="1" step="1" required>
                    <button type="submit">Dodaj zgłoszenie</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  47. Krok 47: Skrypt 3: zapis zgłoszenia przed odczytami

    Kryteria:

    R.5.7 Skrypt 3 wysyła do bazy zmodyfikowane zapytanie 4 tak, że we wstawianym rekordzie id_personelu ma wartość przesłaną z formularza (sprawdzić w phpMyAdmin )

    ok. 4 min
    Polecenie z arkusza

    Jeżeli wysłano dane z formularza to skrypt wysyła do bazy danych zmodyfikowane zapytanie 4 tak, że w miejscu id personelu jest podana wartość wysłana z formularza

    Skrypt 3 stoi w kodzie przed Skryptem 1 i 2, choć arkusz wymienia go ostatni. PHP wykonuje plik od góry: gdy zapis jest pierwszy, lista osób bez zgłoszeń czyta bazę już po zapisie. W wersji podstawowej z egzaminu po dodaniu zgłoszenia dla osoby 2 lista nadal pokazywała „2 Nowak” - aż do następnego wejścia.

    • isset($_POST['person']) - „jeżeli wysłano dane z formularza”: pole person istnieje tylko po kliknięciu „Dodaj zgłoszenie”.
    • filter_var(..., FILTER_VALIDATE_INT, ...) - liczba całkowita od 1 albo false (pusty napis, „abc”, „2 OR 1=1”); przy błędzie komunikat zamiast zapisu.
    • Zapytanie 4 przygotowane, z parametrem :person w miejscu identyfikatora osoby; pojazd 14 i CURDATE() zostają (R.5.7).

    Sprawdzone w bazie, jak każe klucz: po wpisaniu 2 w tabeli rejestr przybył rekord 31 (osoba 2, pojazd 14, data dzisiejsza), a lista od razu pokazuje tylko Nosowską i Mizerę.

    index.php PHP zmienione: 15 linii
    <?php
    // Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumn
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=zgloszenia;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $exception) {
        http_response_code(500);
        exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');
    }
    
    function escape(string|int|float|null $value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
    }
    
    // Skrypt 3: nowe zgłoszenie z drugiego formularza (zapytanie 4) - przed odczytami
    $message = '';
    if (isset($_POST['person'])) {
        $personId = filter_var($_POST['person'], FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
        if ($personId === false) {
            $message = 'Podaj identyfikator osoby - liczbę całkowitą większą od zera.';
        } else {
            $statement = $pdo->prepare('INSERT INTO rejestr (data, id_personel, id_pojazd) VALUES (CURDATE(), :person, 14)');
            $statement->execute(['person' => $personId]);
        }
    }
    
    // Skrypt 1: personel o statusie wybranym w formularzu (zapytanie 1)
    $allowedOptions = ['policjant', 'ratownik'];
    $option = $_POST['option'] ?? 'policjant';
    if (!in_array($option, $allowedOptions, true)) {
        $option = 'policjant';
    }
    $statement = $pdo->prepare('SELECT id, imie, nazwisko FROM personel WHERE status = :status');
    $statement->execute(['status' => $option]);
    $staff = $statement->fetchAll();
    
    // Skrypt 2: osoby bez żadnego zgłoszenia (zapytanie 3)
    $withoutReports = $pdo->query(
        'SELECT p.id, p.nazwisko FROM personel AS p
         WHERE NOT EXISTS (SELECT 1 FROM rejestr AS r WHERE r.id_personel = p.id)'
    )->fetchAll();
    
    // Wszystkie dane pobrane - zamknięcie połączenia
    $statement = null;
    $pdo = null;
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZGŁOSZENIA</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zgłoszenia wydarzeń</h1>
        </header>
        <main>
            <section id="staff">
                <h2>Personel</h2>
                <form method="post">
                    <input type="radio" id="police" name="option" value="policjant"<?= $option === 'policjant' ? ' checked' : '' ?>>
                    <label for="police">Policjant</label>
                    <input type="radio" id="rescuer" name="option" value="ratownik"<?= $option === 'ratownik' ? ' checked' : '' ?>>
                    <label for="rescuer">Ratownik</label>
                    <button type="submit">Pokaż</button>
                </form>
                <h3>Wybrano opcję: <?= escape($option) ?></h3>
                <table>
                    <thead>
                        <tr>
                            <th>Id</th>
                            <th>Imię</th>
                            <th>Nazwisko</th>
                        </tr>
                    </thead>
                    <tbody>
    <?php foreach ($staff as $employee): ?>
                        <tr>
                            <td><?= escape($employee['id']) ?></td>
                            <td><?= escape($employee['imie']) ?></td>
                            <td><?= escape($employee['nazwisko']) ?></td>
                        </tr>
    <?php endforeach; ?>
                    </tbody>
                </table>
            </section>
            <section id="new-report">
                <h2>Nowe zgłoszenie</h2>
                <ol>
    <?php foreach ($withoutReports as $person): ?>
                    <li><?= escape($person['id']) ?> <?= escape($person['nazwisko']) ?></li>
    <?php endforeach; ?>
                </ol>
                <form method="post">
                    <label for="person">Wybierz id osoby z listy: </label>
                    <input type="number" id="person" name="person" min="1" step="1" required>
                    <button type="submit">Dodaj zgłoszenie</button>
                </form>
    <?php if ($message !== ''): ?>
                <p><?= escape($message) ?></p>
    <?php endif; ?>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  48. Krok 48: Skrypt 3: przekierowanie po zapisie i błąd klucza obcego

    ok. 5 min
    Polecenie z arkusza

    Jeżeli wysłano dane z formularza to skrypt wysyła do bazy danych zmodyfikowane zapytanie 4 tak, że w miejscu id personelu jest podana wartość wysłana z formularza

    Dwa przypadki, których wersja z egzaminu nie obsługuje:

    1. PRG (Post/Redirect/Get). Po udanym zapisie header('Location: index.php?added=2', true, 303) i exit: przeglądarka pobiera stronę od nowa zwykłym GET-em. Odświeżenie (F5) nie wyśle formularza drugi raz, więc nie powstanie zdublowane zgłoszenie. Kod 303 („zobacz inny adres”) mówi wprost: po POST przejdź GET-em. Komunikat „Dodano zgłoszenie…” niesie parametr added w adresie, sprawdzany filter_var jak każde wejście. Przed exit zamknięcie połączenia - to nadal ostatnia operacja na bazie.
    2. Osoba spoza bazy. Identyfikator 99 łamie klucz obcy i baza zgłasza błąd o kodzie SQLSTATE 23000. catch (PDOException ...) zamienia go na komunikat „Nie ma osoby o identyfikatorze 99.”; każdy inny błąd jest rzucany dalej (throw), bo udawanie, że nic się nie stało, ukryłoby awarię.

    Podgląd pokazuje stronę po przekierowaniu: komunikat pod formularzem i lista bez Nowaka.

    Typowe błędy

    header() musi stać przed jakimkolwiek wypisanym znakiem - nawet spacją przed <?php albo BOM-em w pliku. Dlatego cała logika jest nad <!DOCTYPE html>.

    index.php PHP zmienione: 16 linii
    <?php
    // Połączenie z bazą: błędy jako wyjątki, wiersze jako tablice z nazwami kolumn
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=zgloszenia;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $exception) {
        http_response_code(500);
        exit('Nie udało się połączyć z bazą danych. Spróbuj ponownie za chwilę.');
    }
    
    function escape(string|int|float|null $value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
    }
    
    // Skrypt 3: nowe zgłoszenie z drugiego formularza (zapytanie 4) - przed odczytami
    $message = '';
    $addedId = filter_var($_GET['added'] ?? '', FILTER_VALIDATE_INT);
    if ($addedId !== false) {
        $message = "Dodano zgłoszenie dla osoby o identyfikatorze $addedId.";
    }
    if (isset($_POST['person'])) {
        $personId = filter_var($_POST['person'], FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
        if ($personId === false) {
            $message = 'Podaj identyfikator osoby - liczbę całkowitą większą od zera.';
        } else {
            $statement = $pdo->prepare('INSERT INTO rejestr (data, id_personel, id_pojazd) VALUES (CURDATE(), :person, 14)');
            try {
                $statement->execute(['person' => $personId]);
                $statement = null;
                $pdo = null;
                header('Location: index.php?added=' . $personId, true, 303);
                exit;
            } catch (PDOException $exception) {
                if ($exception->getCode() !== '23000') {
                    throw $exception;
                }
                $message = "Nie ma osoby o identyfikatorze $personId.";
            }
        }
    }
    
    // Skrypt 1: personel o statusie wybranym w formularzu (zapytanie 1)
    $allowedOptions = ['policjant', 'ratownik'];
    $option = $_POST['option'] ?? 'policjant';
    if (!in_array($option, $allowedOptions, true)) {
        $option = 'policjant';
    }
    $statement = $pdo->prepare('SELECT id, imie, nazwisko FROM personel WHERE status = :status');
    $statement->execute(['status' => $option]);
    $staff = $statement->fetchAll();
    
    // Skrypt 2: osoby bez żadnego zgłoszenia (zapytanie 3)
    $withoutReports = $pdo->query(
        'SELECT p.id, p.nazwisko FROM personel AS p
         WHERE NOT EXISTS (SELECT 1 FROM rejestr AS r WHERE r.id_personel = p.id)'
    )->fetchAll();
    
    // Wszystkie dane pobrane - zamknięcie połączenia
    $statement = null;
    $pdo = null;
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZGŁOSZENIA</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Zgłoszenia wydarzeń</h1>
        </header>
        <main>
            <section id="staff">
                <h2>Personel</h2>
                <form method="post">
                    <input type="radio" id="police" name="option" value="policjant"<?= $option === 'policjant' ? ' checked' : '' ?>>
                    <label for="police">Policjant</label>
                    <input type="radio" id="rescuer" name="option" value="ratownik"<?= $option === 'ratownik' ? ' checked' : '' ?>>
                    <label for="rescuer">Ratownik</label>
                    <button type="submit">Pokaż</button>
                </form>
                <h3>Wybrano opcję: <?= escape($option) ?></h3>
                <table>
                    <thead>
                        <tr>
                            <th>Id</th>
                            <th>Imię</th>
                            <th>Nazwisko</th>
                        </tr>
                    </thead>
                    <tbody>
    <?php foreach ($staff as $employee): ?>
                        <tr>
                            <td><?= escape($employee['id']) ?></td>
                            <td><?= escape($employee['imie']) ?></td>
                            <td><?= escape($employee['nazwisko']) ?></td>
                        </tr>
    <?php endforeach; ?>
                    </tbody>
                </table>
            </section>
            <section id="new-report">
                <h2>Nowe zgłoszenie</h2>
                <ol>
    <?php foreach ($withoutReports as $person): ?>
                    <li><?= escape($person['id']) ?> <?= escape($person['nazwisko']) ?></li>
    <?php endforeach; ?>
                </ol>
                <form method="post">
                    <label for="person">Wybierz id osoby z listy: </label>
                    <input type="number" id="person" name="person" min="1" step="1" required>
                    <button type="submit">Dodaj zgłoszenie</button>
                </form>
    <?php if ($message !== ''): ?>
                <p><?= escape($message) ?></p>
    <?php endif; ?>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  49. Krok 49: Nazwy zmiennych

    Kryteria:

    R.5.9 W skryptach zastosowana przynajmniej jedna zmienna oraz nazwy wszystkich zmiennych są w języku polskim lub angielskim i odzwierciedlają cel zastosowania

    ok. 1 min
    Polecenie z arkusza

    Należy stosować znaczące nazewnictwo zmiennych i funkcji w języku polskim lub angielskim

    Każda zmienna mówi, co przechowuje (R.5.9): $pdo (połączenie), $allowedOptions, $option, $staff i $employee (personel i jeden pracownik), $withoutReports i $person (osoby bez zgłoszeń), $personId, $addedId, $message, $statement, $exception. Jedna nazwa na jedno znaczenie - w wersji podstawowej $person był raz wierszem tabeli, a raz identyfikatorem z formularza.

    Podgląd strony bez zmian - taki jak po kroku 48.

  50. Etap 5: Porównanie z wersją podstawową

    Kroki 50-51 · po tym etapie masz 39 z 39 kryteriów (pkt)

    Krok 50: Co jest inaczej i dlaczego

    ok. 3 min
    Polecenie z arkusza

    Ocenie będzie podlegać 5 rezultatów: operacje na bazie danych, zawartość witryny internetowej, działanie witryny internetowej, styl CSS witryny internetowej oraz skrypt połączenia z bazą.

    Obie wersje dają te same punkty. Różnice:

    wersja podstawowata wersja
    układdisplay: flex na <main>, width: 50%display: grid, grid-template-columns: 50% 50%
    tabelawiersze w <table><thead> i <tbody>
    bazamysqli_* z tabeli 1PDO, wyjątki, FETCH_ASSOC
    opcja z formularzawstawiona do SQLbiała lista + parametr :status
    zapytanie 3NOT INNOT EXISTS
    zapis zgłoszeniapo wypisaniu listy, bez kontroliprzed odczytami, filter_var, błąd 23000 jako komunikat
    po zapisieta sama strona (F5 dubluje)przekierowanie 303 (PRG)

    Czego tu jeszcze nie ma, a w prawdziwym systemie zgłoszeń by było: token CSRF w formularzu zapisu (losowa wartość z sesji, sprawdzana przy zapisie - blokuje dodanie zgłoszenia z cudzej strony) i logowanie dyspozytora.

    Podgląd strony bez zmian - taki jak po kroku 48.

  51. Krok 51: Którą wersję wybrać na egzaminie

    ok. 2 min
    Polecenie z arkusza

    Czas przeznaczony na wykonanie zadania wynosi 150 minut.

    • Wersja podstawowa, gdy liczy się czas albo PDO nie jest opanowane: funkcje mysqli_* i isset są w tabeli 1, a skrypty stoją tam, gdzie widać ich efekt.
    • Ta wersja, gdy PDO, filter_var i przekierowanie są pisane z pamięci. Przekierowanie ma jedną pułapkę: pusta linia albo spacja przed <?php blokuje header() - wtedy zapis się wykona, ale strona zostanie pusta.

    Mieszanie też daje pełne punkty: siatka z tej wersji i mysqli_* z podstawowej.

    Podgląd strony bez zmian - taki jak po kroku 48.

  52. Etap 6: Oddanie pracy

    Krok 52 · po tym etapie masz 39 z 39 kryteriów (pkt)

    Krok 52: Plik przeglądarka.txt i nagranie płyty

    ok. 5 min
    Polecenie z arkusza

    Po zakończeniu pracy utwórz plik tekstowy o nazwie przeglądarka.txt. Zapisz w nim nazwę przeglądarki internetowej, w której weryfikowana była poprawność działania witryny. Umieść go w folderze z numerem zdającego. Nagraj płytę z rezultatami pracy.

    W przeglądarka.txt nazwa przeglądarki, w której strona była sprawdzana.

    W folderze z numerem zdającego: import.png, index.php, kw1.jpg - kw4.jpg, kwerendy.txt, przeglądarka.txt, styl.css. Po nagraniu sprawdź odczyt płyty.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

    Podgląd strony bez zmian - taki jak po kroku 48.

Podgląd

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: . 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.