Przejdź do głównej treści
EE.09-03-22.01-SGStyczeń 2022 · 47 kroków · ok. 88 min · 34 kryteria z klucza

Rozwiązanie krok po kroku: Dodawanie zespołu pogotowia ratunkowego (Nowoczesny kod (flex, PDO))

To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza ee09 z czterema zapytaniami, zdjęcie karetki o wysokości 100 px, strona ratownicy.html z formularzem dodania zespołu i skrypt dodanie.php, który zapisuje zespół i wypisuje wysłane zapytanie - i tak samo spełnia każde kryterium klucza. Inna jest droga:

  • semantyczne znaczniki: baner w <header>, blok główny w <main>, stopka w <footer> - w banerze i stopce po trzy <section>,
  • flexbox zamiast float: <header> i <footer> są kontenerami display: flex, a szerokości 55%, 25% i 20% zostają na blokach - tak, jak ocenia je klucz,
  • zmienne CSS dla kolorów i wysokości z arkusza, etykiety <label for>, przyciski <button>, pola z required,
  • PHP na PDO z zapytaniem przygotowanym, sprawdzeniem numeru karetki i pustych pól, obsługą błędów w try / catch i zamknięciem połączenia w finally,
  • wypisane zapytanie złożone tak samo, jak składa je PDO, i porównane z dziennikiem serwera bazy - na ekranie jest dokładnie to, co dostała baza, przepuszczone przez htmlspecialchars,
  • strona z komunikatem w wyglądzie formularza oraz przekierowanie po zapisie - odświeżenie nie doda drugiego zespołu.

To arkusz EE.09 ze stycznia 2022 - kwalifikacji formuły 2017, którą zastąpiła INF.03. Baza i zdjęcie pochodzą z oryginalnego archiwum CKE pliki3.zip (załącznik na stronie arkusza), zapytania i zrzuty są policzone na MariaDB 10.4, a podgląd od kroków z PHP to wynik prawdziwego uruchomienia kodu w PHP 8.2 (klucz każe sprawdzać w XAMPP 8.0.7 - użyte konstrukcje działają w obu wersjach).

Jak liczą się punkty. Klucz ma 34 kryteria (R.1.1 - R.5.5), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 26 punktów. Przy każdym kroku jest napisane, które kryteria spełnia; klucz przyjmuje zarówno mysqli, jak i PDO.

  1. Etap 1: Baza danych

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

    Krok 1: Folder zdającego i archiwum pliki3.zip

    ok. 2 min
    Polecenie z arkusza

    Należy utworzyć folder o nazwie odpowiadającej numerowi PESEL zdającego lub w przypadku jego braku innemu numerowi, którym został podpisany arkusz. Archiwum należy rozpakować do tego folderu.

    Folder na pulpicie nazwany numerem PESEL (w tym rozwiązaniu w jego miejscu stoi Bartosz Bryniarski), do niego archiwum pliki3.zip rozpakowane z hasłem ##Pogotowi3$. W środku: baza3.sql i obraz.jpg. Do tego folderu trafiają wszystkie rezultaty pracy.

  2. Krok 2: XAMPP i baza ee09

    ok. 2 min
    Polecenie z arkusza

    Uruchom usługi MySQL i Apache za pomocą XAMPP Control Panel. Za pomocą narzędzia phpMyAdmin wykonaj podane operacje na bazie danych: Utwórz bazę danych o nazwie ee09

    Start przy Apache i MySQL, potem http://localhost/phpmyadmin, Nowa, nazwa ee09, porównywanie np. utf8mb4_polish_ci.

  3. Krok 3: Import pliku baza3.sql i zrzut import.png

    Kryteria:

    R.1.1 Wykonano import tabel do bazy danych ee09 , czynność udokumentowano plikiem o nazwie import w formacie PNG

    ok. 3 min
    Polecenie z arkusza

    Do bazy ee09 zaimportuj tabele z pliku baza3.sql z rozpakowanego archiwum. Wykonaj zrzut ekranu po imporcie. Zrzut zapisz w folderze z numerem PESEL lub w przypadku jego braku w folderze z innym numerem, którym został podpisany arkusz, w formacie PNG i nazwij 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

    Baza ee09, karta Import, plik baza3.sql, Wykonaj. Po komunikacie „Import zakończył się sukcesem” w lewym panelu jest 12 tabel; zadanie używa trzech: ratownicy, dyspozytorzy i zgloszenia. Zrzut całego ekranu z paskiem zadań: import.png.

    Typowe błędy

    Ten zrzut zapisz jako PNG, zrzuty zapytań (kw1 - kw4) - jako JPEG.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 3
  4. Krok 4: Zapytanie 1: nowy zespół karetki 20

    Kryteria: ,

    R.1.2 Zapisano plik tekstowy o nazwie kwerendy zawierający co najmniej jedno zapytanie SQL, wynikające z treści zadania

    R.1.3 Utworzono zapytanie 1 dodające rekord do tabeli ratownicy o kolejnych wartościach pól : NULL, 20, Adam Nowak, Jan Kowalski, Anna Nowakowska. Możliwe również zamiast NULL pominięcie pola id. W pliku z kwerendami lub na zrzucie istnieje zapis np.: INSERT INTO ratownicy VALUES (NULL, 20, 'Adam Nowak', 'Jan Kowalski', 'Anna Nowakowska'); oraz wynik działania zapytania udokumentowano zrzutem, na którym jest widoczne poprawne wykonane zapytanie lub tabela z dodanymi danymi

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 1: dodające rekord do tabeli ratownicy o wartościach: nrKaretki - 20, ratownicy, kolejno - Adam Nowak, Jan Kowalski, Anna Nowakowska. Baza powinna sama nadać wartość pola id dla tabeli

    Wartości dla wszystkich pięciu kolumn w kolejności z tabeli. NULL w kolumnie id z AUTO_INCREMENT - numer nada baza (nowy zespół dostaje 5). Numer karetki jako liczba, imiona i nazwiska w apostrofach. Zapytanie trafia do kwerendy.txt (R.1.2), zrzut z dodanym rekordem - do kw1.jpg. To samo zapytanie, z parametrami w miejscu danych, wykona później skrypt.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    INSERT INTO ratownicy VALUES (NULL, 20, 'Adam Nowak', 'Jan Kowalski', 'Anna Nowakowska');
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: zgłoszenia dyspozytora o id 4

    Kryteria:

    R.1.4 Utworzono zapytanie 2 wybierające jedynie pola id, adres oraz ratownicy_id z tabeli zgloszenia, dla pola dyspozytorzy_id = 4. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT id, adres, ratownicy_id FROM zgloszenia WHERE dyspozytorzy_id = 4; oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczne są jedynie pola id, adres i ratownicy_id dla dokładnie 3 rekordów o id = 1, 2, 6

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 2: wybierające jedynie pola id, adres oraz ratownicy_id z tabeli zgloszenia, dla zgłoszeń przyjętych przez dyspozytora o id 4

    Trzy kolumny, warunek na kluczu obcym dyspozytorzy_id = 4 (nie na id zgłoszenia). Wynik: zgłoszenia 1, 2 i 6. Zrzut: kw2.jpg. Każda klauzula w osobnej linii - przy dłuższych zapytaniach łatwiej wtedy znaleźć błąd.

    kwerendy.txt Tekst zmienione: 5 linii
    -- Zapytanie 1
    INSERT INTO ratownicy VALUES (NULL, 20, 'Adam Nowak', 'Jan Kowalski', 'Anna Nowakowska');
    
    -- Zapytanie 2
    SELECT id, adres, ratownicy_id
    FROM zgloszenia
    WHERE dyspozytorzy_id = 4;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: użytkownik adam z hasłem

    Kryteria:

    R.1.5 Utworzono zapytanie 3 tworzące użytkownika adam z hasłem Adam6& W pliku z kwerendami lub na zrzucie istnieje zapis np.: CREATE USER 'adam' IDENTIFIED BY 'Adam6&'; (możliwe też z @'localhost') oraz wynik działania zapytania udokumentowano zrzutem wskazującym na dodanie tego użytkownika z hasłem

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 3: tworzące użytkownika adam z hasłem Adam6&

    CREATE USER 'adam' IDENTIFIED BY 'Adam6&' - bez podanego hosta konto dostaje host % (klucz przyjmuje też 'adam'@'localhost'). Żeby zrzut kw3.jpg pokazywał konto z hasłem, pod zapytaniem w tej samej karcie SQL dopisz SELECT User, Host, Password FROM mysql.user WHERE User = 'adam'; - do kwerendy.txt trafia tylko zapytanie 3.

    Typowe błędy

    Adam6& to hasło do ćwiczeń - na prawdziwym serwerze konto dostaje długie losowe hasło i host localhost.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    INSERT INTO ratownicy VALUES (NULL, 20, 'Adam Nowak', 'Jan Kowalski', 'Anna Nowakowska');
    
    -- Zapytanie 2
    SELECT id, adres, ratownicy_id
    FROM zgloszenia
    WHERE dyspozytorzy_id = 4;
    
    -- Zapytanie 3
    CREATE USER 'adam' IDENTIFIED BY 'Adam6&';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: prawa CREATE, ALTER i DROP do tabeli ratownicy

    Kryteria:

    R.1.6 Utworzono zapytanie 4 nadające dla użytkownika adam zestaw praw CREATE, ALTER, DROP albo zestaw praw INSERT, UPDATE, DELETE dla tabeli ratownicy . W pliku z kwerendami lub na zrzucie istnieje zapis np.: GRANT CREATE, ALTER, DROP ON ratownicy TO 'adam'; lub GRANT INSERT, UPDATE, DELETE ON ratownicy TO 'adam'; oraz wynik działania zapytania udokumentowano zrzutem wskazującym dodanie zestawu praw na tabeli ratownicy dla użytkownika adam

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 4: nadające dla użytkownika adam prawa tworzenia obiektu, edycji struktury obiektu i usuwania obiektu dla tabeli ratownicy

    Tworzenie obiektu to CREATE, edycja struktury - ALTER, usuwanie - DROP; ON ratownicy przy zaznaczonej bazie ee09 oznacza tabelę ee09.ratownicy. Zrzut kw4.jpg z wynikiem SHOW GRANTS FOR 'adam'; dopisanym pod zapytaniem. Klucz przyjmuje też zestaw INSERT, UPDATE, DELETE.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    INSERT INTO ratownicy VALUES (NULL, 20, 'Adam Nowak', 'Jan Kowalski', 'Anna Nowakowska');
    
    -- Zapytanie 2
    SELECT id, adres, ratownicy_id
    FROM zgloszenia
    WHERE dyspozytorzy_id = 4;
    
    -- Zapytanie 3
    CREATE USER 'adam' IDENTIFIED BY 'Adam6&';
    
    -- Zapytanie 4
    GRANT CREATE, ALTER, DROP ON ratownicy TO 'adam';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Grafika i strona HTML

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

    Krok 8: Zdjęcie karetki: wysokość 100 px

    Kryteria:

    R.2.1 Plik obraz.jpg przeskalowano z zachowaniem proporcji tak, że jego wysokość wynosi dokładnie 100 px, odpowiadająca temu szerokość wynosi 167 px ±5 px

    ok. 2 min
    Polecenie z arkusza

    Plik obraz.jpg, wypakowany z archiwum, należy przeskalować z zachowaniem proporcji tak, aby jego wysokość wynosiła dokładnie 100 px

    GIMP, Obraz → Skaluj obraz, łańcuch zamknięty, wysokość 100 px - szerokość przelicza się na 167 px (566 × 100 / 339). Plik → Nadpisz obraz.jpg.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 8
  9. Krok 9: Plik ratownicy.html

    ok. 1 min
    Polecenie z arkusza

    Składa się ze strony o nazwie ratownicy.html oraz skryptu dodanie.php. Poniższe wymagania dotyczą tylko pliku ratownicy.html

    Szkielet HTML5 w pliku ratownicy.html. <meta name="viewport"> każe telefonom wyświetlić stronę w naturalnej szerokości ekranu, zamiast pomniejszać widok komputerowy.

    ratownicy.html HTML nowy plik
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (ratownicy.html)
  10. Krok 10: Kodowanie polskich znaków

    Kryteria:

    R.2.2 Witrynę internetową zapisano w pliku HTML o nazwie ratownicy oraz zastosowano właściwy standard kodowania polskich znaków

    ok. 1 min
    Polecenie z arkusza

    Zastosowany właściwy standard kodowania polskich znaków

    <meta charset="UTF-8"> jako pierwsza linia <head> i plik zapisany w UTF-8 - z nazwą pliku to R.2.2.

    ratownicy.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (ratownicy.html)
  11. Krok 11: Tytuł strony

    Kryteria:

    R.2.3 Nadano tytuł strony: "Zespoły ratownicze”

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony widoczny na karcie przeglądarki: „Zespoły ratownicze”

    <title>Zespoły ratownicze</title> (R.2.3).

    ratownicy.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zespoły ratownicze</title>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (ratownicy.html)
  12. Krok 12: Podpięcie arkusza stylów styl3.css

    Kryteria:

    R.3.5 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl3.css , formatowanie pochodzi jedynie z tego arkusza Uwaga: kryterium to nie jest spełnione również, gdy źle przypisano co najmniej jeden selektor do bloku (np. kropka zamiast #)

    ok. 1 min
    Polecenie z arkusza

    Arkusz stylów w pliku o nazwie styl3.css prawidłowo połączony z kodem strony

    <link rel="stylesheet" href="styl3.css"> (R.3.5).

    ratownicy.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zespoły ratownicze</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (ratownicy.html)
  13. Krok 13: Bloki: header i footer z trzema sekcjami, main

    Kryteria:

    R.2.4 Wygląd strony zdefiniowano dzieląc ją na bloki: trzy bloki banera, blok główny, trzy bloki stopki. Zastosowano znaczniki sekcji

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki: na górze trzy bloki banera: lewy, środkowy, prawy, poniżej blok główny, na dole trzy bloki stopki: lewy, środkowy, prawy. Podział zrealizowany za pomocą znaczników sekcji tak, aby po uruchomieniu w przeglądarce wygląd układu bloków był zgodny z Obrazem 2

    Każda grupa bloków dostaje znacznik, który mówi, czym jest:

    • <header> - baner strony; w środku trzy bloki banera,
    • <main> - główna treść, czyli formularz; na stronie jest jeden <main>, więc nie potrzebuje identyfikatora,
    • <footer> - stopka z trzema blokami.

    Bloki to <section> z klasami left, middle i right. Te same klasy w banerze i w stopce, bo arkusz daje lewym, środkowym i prawym blokom obu rzędów te same szerokości - jedna reguła CSS obsłuży dwa bloki (R.2.4). <header> i <footer> posłużą też jako kontenery, które ułożą bloki w rzędzie.

    ratownicy.html HTML zmienione: 18 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zespoły ratownicze</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <section class="left">
            </section>
            <section class="middle">
            </section>
            <section class="right">
            </section>
        </header>
        <main>
        </main>
        <footer>
            <section class="left">
            </section>
            <section class="middle">
            </section>
            <section class="right">
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (ratownicy.html)
  14. Krok 14: Lewy blok banera: nagłówek drugiego stopnia

    ok. 1 min
    Polecenie z arkusza

    Zawartość lewego bloku banera: nagłówek drugiego stopnia o treści „Dodanie zespołu ratowniczego”

    <h2> w pierwszej sekcji <header>. Punkt R.2.5 przyjdzie razem z nagłówkiem <h5> w stopce.

    ratownicy.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zespoły ratownicze</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <section class="left">
                <h2>Dodanie zespołu ratowniczego</h2>
            </section>
            <section class="middle">
            </section>
            <section class="right">
            </section>
        </header>
        <main>
        </main>
        <footer>
            <section class="left">
            </section>
            <section class="middle">
            </section>
            <section class="right">
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (ratownicy.html)
  15. Krok 15: Środkowy blok banera: kontakt z łamaniem linii

    ok. 1 min
    Polecenie z arkusza

    Zawartość środkowego bloku banera: akapit (paragraf) o treści „Kontakt: 022 222 11 333”, łamanie linii po słowie „Kontakt:”

    Jeden <p> ze znacznikiem <br> po dwukropku. Tu <br> jest na miejscu - łamanie linii należy do treści, a nie do układu.

    ratownicy.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zespoły ratownicze</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <section class="left">
                <h2>Dodanie zespołu ratowniczego</h2>
            </section>
            <section class="middle">
                <p>Kontakt:<br>022 222 11 333</p>
            </section>
            <section class="right">
            </section>
        </header>
        <main>
        </main>
        <footer>
            <section class="left">
            </section>
            <section class="middle">
            </section>
            <section class="right">
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (ratownicy.html)
  16. Krok 16: Prawy blok banera: zdjęcie z tekstem alternatywnym

    Kryteria:

    R.2.7 W prawym bloku banera umieszczono obraz.jpg wraz z tekstem alternatywnym "Ratownicy"

    ok. 1 min
    Polecenie z arkusza

    Zawartość prawego bloku banera: obraz.jpg z tekstem alternatywnym o treści: „Ratownicy”

    <img> z alt="Ratownicy" (R.2.7). Atrybuty width i height z wymiarami pliku po skalowaniu rezerwują miejsce na zdjęcie, zanim się wczyta - strona nie „skacze” przy ładowaniu.

    ratownicy.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zespoły ratownicze</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <section class="left">
                <h2>Dodanie zespołu ratowniczego</h2>
            </section>
            <section class="middle">
                <p>Kontakt:<br>022 222 11 333</p>
            </section>
            <section class="right">
                <img src="obraz.jpg" alt="Ratownicy" width="167" height="100">
            </section>
        </header>
        <main>
        </main>
        <footer>
            <section class="left">
            </section>
            <section class="middle">
            </section>
            <section class="right">
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (ratownicy.html)
  17. Krok 17: Blok główny: nagłówek trzeciego stopnia

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku głównego: Nagłówek trzeciego stopnia o treści: „Dodaj nowy zespół”

    <h3> na początku <main>.

    ratownicy.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zespoły ratownicze</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <section class="left">
                <h2>Dodanie zespołu ratowniczego</h2>
            </section>
            <section class="middle">
                <p>Kontakt:<br>022 222 11 333</p>
            </section>
            <section class="right">
                <img src="obraz.jpg" alt="Ratownicy" width="167" height="100">
            </section>
        </header>
        <main>
            <h3>Dodaj nowy zespół</h3>
        </main>
        <footer>
            <section class="left">
            </section>
            <section class="middle">
            </section>
            <section class="right">
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (ratownicy.html)
  18. Krok 18: Formularz wysyłający dane metodą POST do dodanie.php

    Kryteria:

    R.3.4 Formularz wysyła dane metodą POST do pliku dodanie.php

    ok. 2 min
    Polecenie z arkusza

    Formularz wysyłający dane metodą POST do pliku dodanie.php zawierający pola

    action="dodanie.php" - dokąd, method="post" - jak: dane jadą w treści żądania i trafią do $_POST (R.3.4). Stronę otwierasz przez http://localhost/…, żeby serwer wykonał skrypt.

    ratownicy.html HTML zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zespoły ratownicze</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <section class="left">
                <h2>Dodanie zespołu ratowniczego</h2>
            </section>
            <section class="middle">
                <p>Kontakt:<br>022 222 11 333</p>
            </section>
            <section class="right">
                <img src="obraz.jpg" alt="Ratownicy" width="167" height="100">
            </section>
        </header>
        <main>
            <h3>Dodaj nowy zespół</h3>
            <form action="dodanie.php" method="post">
            </form>
        </main>
        <footer>
            <section class="left">
            </section>
            <section class="middle">
            </section>
            <section class="right">
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (ratownicy.html)
  19. Krok 19: Pole „Numer karetki:” typu number z etykietą

    ok. 2 min
    Polecenie z arkusza

    Pole edycyjne typu numerycznego podpisane powyżej tekstem: „Numer karetki:”

    Napis to <label for="ambulance">, a pole ma ten sam id - kliknięcie napisu ustawia kursor w polu, a czytnik ekranu odczytuje napis jako nazwę pola. Nad polem ustawi go reguła CSS dla wiersza .row, bez <br>.

    • type="number" - pole na liczbę, ze strzałkami (R.3.3 wymaga tego typu w pierwszym polu),
    • name="nrKaretki" - klucz w $_POST, taki jak nazwa kolumny,
    • min="1" i required - przeglądarka nie wyśle pustego pola ani numeru 0 czy ujemnego. Skrypt i tak sprawdzi to jeszcze raz.
    ratownicy.html HTML zmienione: 4 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zespoły ratownicze</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <section class="left">
                <h2>Dodanie zespołu ratowniczego</h2>
            </section>
            <section class="middle">
                <p>Kontakt:<br>022 222 11 333</p>
            </section>
            <section class="right">
                <img src="obraz.jpg" alt="Ratownicy" width="167" height="100">
            </section>
        </header>
        <main>
            <h3>Dodaj nowy zespół</h3>
            <form action="dodanie.php" method="post">
                <div class="row">
                    <label for="ambulance">Numer karetki:</label>
                    <input type="number" id="ambulance" name="nrKaretki" min="1" required>
                </div>
            </form>
        </main>
        <footer>
            <section class="left">
            </section>
            <section class="middle">
            </section>
            <section class="right">
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (ratownicy.html)
  20. Krok 20: Trzy pola na imiona i nazwiska ratowników

    ok. 2 min
    Polecenie z arkusza

    Trzy pola edycyjne podpisane powyżej tekstem, kolejno: „Imię i nazwisko pierwszego ratownika:”, „Imię i nazwisko drugiego ratownika:”, „Imię i nazwisko trzeciego ratownika:”

    Trzy takie same wiersze: etykieta z for, pole tekstowe z pasującym id i name równym nazwie kolumny (ratownik1 - ratownik3), required. Identyfikatory po angielsku, np. rescuer1, a nazwy pól - jak w bazie.

    ratownicy.html HTML zmienione: 12 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zespoły ratownicze</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <section class="left">
                <h2>Dodanie zespołu ratowniczego</h2>
            </section>
            <section class="middle">
                <p>Kontakt:<br>022 222 11 333</p>
            </section>
            <section class="right">
                <img src="obraz.jpg" alt="Ratownicy" width="167" height="100">
            </section>
        </header>
        <main>
            <h3>Dodaj nowy zespół</h3>
            <form action="dodanie.php" method="post">
                <div class="row">
                    <label for="ambulance">Numer karetki:</label>
                    <input type="number" id="ambulance" name="nrKaretki" min="1" required>
                </div>
                <div class="row">
                    <label for="rescuer1">Imię i nazwisko pierwszego ratownika:</label>
                    <input type="text" id="rescuer1" name="ratownik1" required>
                </div>
                <div class="row">
                    <label for="rescuer2">Imię i nazwisko drugiego ratownika:</label>
                    <input type="text" id="rescuer2" name="ratownik2" required>
                </div>
                <div class="row">
                    <label for="rescuer3">Imię i nazwisko trzeciego ratownika:</label>
                    <input type="text" id="rescuer3" name="ratownik3" required>
                </div>
            </form>
        </main>
        <footer>
            <section class="left">
            </section>
            <section class="middle">
            </section>
            <section class="right">
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (ratownicy.html)
  21. Krok 21: Przycisk „CZYŚĆ”

    ok. 1 min
    Polecenie z arkusza

    Przycisk czyszczący zawartość formularza z etykietą „CZYŚĆ”

    <button type="reset"> - napis stoi między znacznikami. Typ trzeba podać jawnie: <button> bez type w formularzu domyślnie wysyła dane.

    ratownicy.html HTML zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zespoły ratownicze</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <section class="left">
                <h2>Dodanie zespołu ratowniczego</h2>
            </section>
            <section class="middle">
                <p>Kontakt:<br>022 222 11 333</p>
            </section>
            <section class="right">
                <img src="obraz.jpg" alt="Ratownicy" width="167" height="100">
            </section>
        </header>
        <main>
            <h3>Dodaj nowy zespół</h3>
            <form action="dodanie.php" method="post">
                <div class="row">
                    <label for="ambulance">Numer karetki:</label>
                    <input type="number" id="ambulance" name="nrKaretki" min="1" required>
                </div>
                <div class="row">
                    <label for="rescuer1">Imię i nazwisko pierwszego ratownika:</label>
                    <input type="text" id="rescuer1" name="ratownik1" required>
                </div>
                <div class="row">
                    <label for="rescuer2">Imię i nazwisko drugiego ratownika:</label>
                    <input type="text" id="rescuer2" name="ratownik2" required>
                </div>
                <div class="row">
                    <label for="rescuer3">Imię i nazwisko trzeciego ratownika:</label>
                    <input type="text" id="rescuer3" name="ratownik3" required>
                </div>
                <div class="buttons">
                    <button type="reset">CZYŚĆ</button>
                </div>
            </form>
        </main>
        <footer>
            <section class="left">
            </section>
            <section class="middle">
            </section>
            <section class="right">
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (ratownicy.html)
  22. Krok 22: Przycisk „DODAJ”

    Kryteria: ,

    R.2.8 W bloku głównym umieszczono formularz z czterema polami edycyjnymi oraz dwoma przyciskami "CZYŚĆ" i "DODAJ"

    R.3.3 Pierwsze pole formularza jest typu number oraz pierwszy przycisk czyści zawartość formularza, drugi przycisk wysyła dane z formularza (input z typem submit lub button)

    ok. 1 min
    Polecenie z arkusza

    Przycisk wysyłający zawartość formularza z etykietą „DODAJ”

    <button type="submit"> - drugi przycisk wysyła formularz. Klucz wprost przyjmuje button (R.3.3). Formularz ma komplet: cztery pola i dwa przyciski (R.2.8).

    ratownicy.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zespoły ratownicze</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <section class="left">
                <h2>Dodanie zespołu ratowniczego</h2>
            </section>
            <section class="middle">
                <p>Kontakt:<br>022 222 11 333</p>
            </section>
            <section class="right">
                <img src="obraz.jpg" alt="Ratownicy" width="167" height="100">
            </section>
        </header>
        <main>
            <h3>Dodaj nowy zespół</h3>
            <form action="dodanie.php" method="post">
                <div class="row">
                    <label for="ambulance">Numer karetki:</label>
                    <input type="number" id="ambulance" name="nrKaretki" min="1" required>
                </div>
                <div class="row">
                    <label for="rescuer1">Imię i nazwisko pierwszego ratownika:</label>
                    <input type="text" id="rescuer1" name="ratownik1" required>
                </div>
                <div class="row">
                    <label for="rescuer2">Imię i nazwisko drugiego ratownika:</label>
                    <input type="text" id="rescuer2" name="ratownik2" required>
                </div>
                <div class="row">
                    <label for="rescuer3">Imię i nazwisko trzeciego ratownika:</label>
                    <input type="text" id="rescuer3" name="ratownik3" required>
                </div>
                <div class="buttons">
                    <button type="reset">CZYŚĆ</button>
                    <button type="submit">DODAJ</button>
                </div>
            </form>
        </main>
        <footer>
            <section class="left">
            </section>
            <section class="middle">
            </section>
            <section class="right">
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (ratownicy.html)
  23. Krok 23: Lewy blok stopki: odnośnik do kwerendy.txt

    Kryteria:

    R.3.2 Po wybraniu odnośnika "Zobacz kwerendy" otwiera się lub pobiera plik kwerendy

    ok. 1 min
    Polecenie z arkusza

    Zawartość lewego bloku stopki: odnośnik o treści „Zobacz kwerendy”, jego kliknięcie otwiera lub pobiera plik kwerendy.txt

    Ścieżka względna do pliku w tym samym folderze (R.3.2).

    ratownicy.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zespoły ratownicze</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <section class="left">
                <h2>Dodanie zespołu ratowniczego</h2>
            </section>
            <section class="middle">
                <p>Kontakt:<br>022 222 11 333</p>
            </section>
            <section class="right">
                <img src="obraz.jpg" alt="Ratownicy" width="167" height="100">
            </section>
        </header>
        <main>
            <h3>Dodaj nowy zespół</h3>
            <form action="dodanie.php" method="post">
                <div class="row">
                    <label for="ambulance">Numer karetki:</label>
                    <input type="number" id="ambulance" name="nrKaretki" min="1" required>
                </div>
                <div class="row">
                    <label for="rescuer1">Imię i nazwisko pierwszego ratownika:</label>
                    <input type="text" id="rescuer1" name="ratownik1" required>
                </div>
                <div class="row">
                    <label for="rescuer2">Imię i nazwisko drugiego ratownika:</label>
                    <input type="text" id="rescuer2" name="ratownik2" required>
                </div>
                <div class="row">
                    <label for="rescuer3">Imię i nazwisko trzeciego ratownika:</label>
                    <input type="text" id="rescuer3" name="ratownik3" required>
                </div>
                <div class="buttons">
                    <button type="reset">CZYŚĆ</button>
                    <button type="submit">DODAJ</button>
                </div>
            </form>
        </main>
        <footer>
            <section class="left">
                <a href="kwerendy.txt">Zobacz kwerendy</a>
            </section>
            <section class="middle">
            </section>
            <section class="right">
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (ratownicy.html)
  24. Krok 24: Środkowy blok stopki: nagłówek piątego stopnia

    Kryteria:

    R.2.5 W lewym bloku banera zapisano nagłówek h2: "Dodanie zespołu ratowniczego" oraz w bloku głównym zapisano nagłówek h3: "Dodaj nowy zespół" oraz w środkowym bloku stopki nagłówek h5: "Przypominamy numery alarmowe" oraz zapisano przynajmniej jeden paragraf

    ok. 1 min
    Polecenie z arkusza

    Zawartość środkowego bloku stopki: Nagłówek piątego stopnia o treści: „Przypominamy numery alarmowe”

    <h5> w środkowej sekcji <footer>. Z nagłówkami <h2>, <h3> i paragrafem w banerze daje to R.2.5.

    ratownicy.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zespoły ratownicze</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <section class="left">
                <h2>Dodanie zespołu ratowniczego</h2>
            </section>
            <section class="middle">
                <p>Kontakt:<br>022 222 11 333</p>
            </section>
            <section class="right">
                <img src="obraz.jpg" alt="Ratownicy" width="167" height="100">
            </section>
        </header>
        <main>
            <h3>Dodaj nowy zespół</h3>
            <form action="dodanie.php" method="post">
                <div class="row">
                    <label for="ambulance">Numer karetki:</label>
                    <input type="number" id="ambulance" name="nrKaretki" min="1" required>
                </div>
                <div class="row">
                    <label for="rescuer1">Imię i nazwisko pierwszego ratownika:</label>
                    <input type="text" id="rescuer1" name="ratownik1" required>
                </div>
                <div class="row">
                    <label for="rescuer2">Imię i nazwisko drugiego ratownika:</label>
                    <input type="text" id="rescuer2" name="ratownik2" required>
                </div>
                <div class="row">
                    <label for="rescuer3">Imię i nazwisko trzeciego ratownika:</label>
                    <input type="text" id="rescuer3" name="ratownik3" required>
                </div>
                <div class="buttons">
                    <button type="reset">CZYŚĆ</button>
                    <button type="submit">DODAJ</button>
                </div>
            </form>
        </main>
        <footer>
            <section class="left">
                <a href="kwerendy.txt">Zobacz kwerendy</a>
            </section>
            <section class="middle">
                <h5>Przypominamy numery alarmowe</h5>
            </section>
            <section class="right">
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (ratownicy.html)
  25. Krok 25: Lista numerowana numerów alarmowych

    Kryteria:

    R.2.6 W środkowym bloku stopki umieszczono listę numerowaną z dwoma elementami, zastosowano znaczniki <ol>, <li> oraz ich zamknięcia

    ok. 1 min
    Polecenie z arkusza

    Zawartość środkowego bloku stopki: Lista numerowana (uporządkowana) zawierająca elementy: 112, 999

    <ol> z dwoma <li> (R.2.6). Litery zamiast cyfr ustawi CSS.

    ratownicy.html HTML zmienione: 4 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zespoły ratownicze</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <section class="left">
                <h2>Dodanie zespołu ratowniczego</h2>
            </section>
            <section class="middle">
                <p>Kontakt:<br>022 222 11 333</p>
            </section>
            <section class="right">
                <img src="obraz.jpg" alt="Ratownicy" width="167" height="100">
            </section>
        </header>
        <main>
            <h3>Dodaj nowy zespół</h3>
            <form action="dodanie.php" method="post">
                <div class="row">
                    <label for="ambulance">Numer karetki:</label>
                    <input type="number" id="ambulance" name="nrKaretki" min="1" required>
                </div>
                <div class="row">
                    <label for="rescuer1">Imię i nazwisko pierwszego ratownika:</label>
                    <input type="text" id="rescuer1" name="ratownik1" required>
                </div>
                <div class="row">
                    <label for="rescuer2">Imię i nazwisko drugiego ratownika:</label>
                    <input type="text" id="rescuer2" name="ratownik2" required>
                </div>
                <div class="row">
                    <label for="rescuer3">Imię i nazwisko trzeciego ratownika:</label>
                    <input type="text" id="rescuer3" name="ratownik3" required>
                </div>
                <div class="buttons">
                    <button type="reset">CZYŚĆ</button>
                    <button type="submit">DODAJ</button>
                </div>
            </form>
        </main>
        <footer>
            <section class="left">
                <a href="kwerendy.txt">Zobacz kwerendy</a>
            </section>
            <section class="middle">
                <h5>Przypominamy numery alarmowe</h5>
                <ol>
                    <li>112</li>
                    <li>999</li>
                </ol>
            </section>
            <section class="right">
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (ratownicy.html)
  26. Krok 26: Prawy blok stopki: autor strony

    ok. 1 min
    Polecenie z arkusza

    Zawartość prawego bloku stopki: akapit (paragraf) o treści: „Autor”, łamanie linii, dalej wstawiony numer PESEL zdającego lub w przypadku jego braku inny numer, którym został podpisany arkusz

    Na egzaminie po <br> wpisujesz PESEL; tu w jego miejscu stoi Bartosz Bryniarski. Strona HTML jest gotowa.

    ratownicy.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zespoły ratownicze</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <section class="left">
                <h2>Dodanie zespołu ratowniczego</h2>
            </section>
            <section class="middle">
                <p>Kontakt:<br>022 222 11 333</p>
            </section>
            <section class="right">
                <img src="obraz.jpg" alt="Ratownicy" width="167" height="100">
            </section>
        </header>
        <main>
            <h3>Dodaj nowy zespół</h3>
            <form action="dodanie.php" method="post">
                <div class="row">
                    <label for="ambulance">Numer karetki:</label>
                    <input type="number" id="ambulance" name="nrKaretki" min="1" required>
                </div>
                <div class="row">
                    <label for="rescuer1">Imię i nazwisko pierwszego ratownika:</label>
                    <input type="text" id="rescuer1" name="ratownik1" required>
                </div>
                <div class="row">
                    <label for="rescuer2">Imię i nazwisko drugiego ratownika:</label>
                    <input type="text" id="rescuer2" name="ratownik2" required>
                </div>
                <div class="row">
                    <label for="rescuer3">Imię i nazwisko trzeciego ratownika:</label>
                    <input type="text" id="rescuer3" name="ratownik3" required>
                </div>
                <div class="buttons">
                    <button type="reset">CZYŚĆ</button>
                    <button type="submit">DODAJ</button>
                </div>
            </form>
        </main>
        <footer>
            <section class="left">
                <a href="kwerendy.txt">Zobacz kwerendy</a>
            </section>
            <section class="middle">
                <h5>Przypominamy numery alarmowe</h5>
                <ol>
                    <li>112</li>
                    <li>999</li>
                </ol>
            </section>
            <section class="right">
                <p>Autor<br>Bartosz Bryniarski</p>
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (ratownicy.html)
  27. Etap 3: Styl CSS

    Kroki 27-37 · po tym etapie masz 29 z 34 kryteriów (pkt)

    Krok 27: Plik styl3.css i zmienne z kolorami arkusza

    ok. 2 min
    Polecenie z arkusza

    Cechy formatowania CSS: Są zawarte wyłącznie w zewnętrznym arkuszu stylów

    Nowy plik styl3.css. Na początku zmienne CSS w :root: kolor SaddleBrown powtarza się w tle bloków, w ramce i w kolorze odnośnika, a Snow - w tle bloku głównego i odnośnika. Każdą wartość wpisujesz raz, a reguły odwołują się do niej przez var(--brown) - zmiana koloru to zmiana jednej linii. Wartości to nazwy z arkusza, więc egzaminator znajdzie je w pliku.

    styl3.css CSS nowy plik
    :root {
        --brown: SaddleBrown;
        --snow: Snow;
        --text-light: white;
        --bar-height: 100px;
    }
    Podgląd strony po tym kroku (ratownicy.html)
  28. Krok 28: Krój Arial dla całej strony

    Kryteria:

    R.4.1 Ustawiono domyślny krój czcionki dla całej strony na Arial (dla selektora * lub body lub html lub kontenera całej strony)

    ok. 1 min
    Polecenie z arkusza

    Domyślne dla całej strony: krój czcionki Arial

    Selektor * - obejmuje też pola i przyciski, które nie dziedziczą kroju po body (R.4.1).

    styl3.css CSS zmienione: 4 linie
    :root {
        --brown: SaddleBrown;
        --snow: Snow;
        --text-light: white;
        --bar-height: 100px;
    }
    
    * {
        font-family: Arial;
    }
    Podgląd strony po tym kroku (ratownicy.html)
  29. Krok 29: Wspólny styl bloków banera i stopki

    Kryteria: ,

    R.4.3 Ustawiono biały kolor czcionki dla wszystkich bloków banera i stopki

    R.4.4 Ustawiono wysokość dla wszystkich bloków banera i stopki na 100px

    ok. 2 min
    Polecenie z arkusza

    Wspólne dla wszystkich bloków banera i stopki: kolor tła SaddleBrown, biały kolor czcionki, wysokość 100 px

    Selektory potomka header section i footer section trafiają w sześć bloków naraz - bez wyliczania identyfikatorów. Biały tekst (R.4.3) i wysokość 100 px (R.4.4) we wszystkich blokach banera i stopki.

    styl3.css CSS zmienione: 7 linii
    :root {
        --brown: SaddleBrown;
        --snow: Snow;
        --text-light: white;
        --bar-height: 100px;
    }
    
    * {
        font-family: Arial;
    }
    
    header section,
    footer section {
        background-color: var(--brown);
        color: var(--text-light);
        height: var(--bar-height);
    }
    Podgląd strony po tym kroku (ratownicy.html)
  30. Krok 30: Baner i stopka jako kontenery flex

    Kryteria:

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

    ok. 2 min
    Polecenie z arkusza

    Podział zrealizowany za pomocą znaczników sekcji tak, aby po uruchomieniu w przeglądarce wygląd układu bloków był zgodny z Obrazem 2

    display: flex na <header> i <footer> ustawia ich dzieci - po trzy sekcje - w jednym rzędzie, bez float i bez clear (R.3.1). <main> stoi między nimi jako zwykły blok na całą szerokość. Szerokości dostaną same bloki w kolejnych krokach: arkusz z 2022 roku i jego klucz oceniają szerokość bloków (55%, 25%, 20%), więc tam zostaje width, a kontenery tylko je układają. Flex nie zawija bloków do nowego wiersza, więc nawet zaokrąglenia pikseli nie zepchną prawego bloku niżej.

    styl3.css CSS zmienione: 5 linii
    :root {
        --brown: SaddleBrown;
        --snow: Snow;
        --text-light: white;
        --bar-height: 100px;
    }
    
    * {
        font-family: Arial;
    }
    
    header section,
    footer section {
        background-color: var(--brown);
        color: var(--text-light);
        height: var(--bar-height);
    }
    
    header,
    footer {
        display: flex;
    }
    Podgląd strony po tym kroku (ratownicy.html)
  31. Krok 31: Lewe bloki: szerokość 55% i większa czcionka

    Kryteria:

    R.4.8 Ustawiono rozmiar czcionki 120% dla lewych bloków banera i stopki

    ok. 1 min
    Polecenie z arkusza

    Dodatkowo dla lewego bloku banera i lewego bloku stopki: szerokość 55%, rozmiar czcionki 120%

    Klasa .left jest na lewym bloku banera i lewym bloku stopki - jedna reguła dla obu (R.4.8). width: 55% liczy się od szerokości kontenera, czyli od szerokości strony.

    styl3.css CSS zmienione: 5 linii
    :root {
        --brown: SaddleBrown;
        --snow: Snow;
        --text-light: white;
        --bar-height: 100px;
    }
    
    * {
        font-family: Arial;
    }
    
    header section,
    footer section {
        background-color: var(--brown);
        color: var(--text-light);
        height: var(--bar-height);
    }
    
    header,
    footer {
        display: flex;
    }
    
    .left {
        width: 55%;
        font-size: 120%;
    }
    Podgląd strony po tym kroku (ratownicy.html)
  32. Krok 32: Środkowe bloki: szerokość 25%

    ok. 1 min
    Polecenie z arkusza

    Dodatkowo dla środkowego bloku banera i środkowego bloku stopki: szerokość 25%

    Klasa .middle - dwa bloki, jedna właściwość.

    styl3.css CSS zmienione: 4 linie
    :root {
        --brown: SaddleBrown;
        --snow: Snow;
        --text-light: white;
        --bar-height: 100px;
    }
    
    * {
        font-family: Arial;
    }
    
    header section,
    footer section {
        background-color: var(--brown);
        color: var(--text-light);
        height: var(--bar-height);
    }
    
    header,
    footer {
        display: flex;
    }
    
    .left {
        width: 55%;
        font-size: 120%;
    }
    
    .middle {
        width: 25%;
    }
    Podgląd strony po tym kroku (ratownicy.html)
  33. Krok 33: Prawe bloki: szerokość 20% i tekst do prawej

    Kryteria: ,

    R.4.5 Ustawiono szerokość lewych bloków banera i stopki na 55%, środkowych bloków banera i stopki na 25% oraz prawych bloków banera i stopki na 20%

    R.4.6 Ustawiono wyrównanie tekstu do prawej strony dla prawych bloków banera i stopki

    ok. 1 min
    Polecenie z arkusza

    Dodatkowo dla prawego bloku banera i prawego bloku stopki: szerokość 20%, wyrównanie tekstu do prawej strony

    55 + 25 + 20 = 100% w każdym rzędzie (R.4.5). text-align: right dociska do prawej krawędzi tekst stopki i zdjęcie karetki (R.4.6).

    styl3.css CSS zmienione: 5 linii
    :root {
        --brown: SaddleBrown;
        --snow: Snow;
        --text-light: white;
        --bar-height: 100px;
    }
    
    * {
        font-family: Arial;
    }
    
    header section,
    footer section {
        background-color: var(--brown);
        color: var(--text-light);
        height: var(--bar-height);
    }
    
    header,
    footer {
        display: flex;
    }
    
    .left {
        width: 55%;
        font-size: 120%;
    }
    
    .middle {
        width: 25%;
    }
    
    .right {
        width: 20%;
        text-align: right;
    }
    Podgląd strony po tym kroku (ratownicy.html)
  34. Krok 34: Blok główny: tło Snow i marginesy wewnętrzne

    Kryteria: ,

    R.4.2 Ustawiono kolor tła SaddleBrown dla wszystkich bloków banera i stopki oraz kolor Snow dla bloku głównego

    R.4.7 Ustawiono dla bloku głównego marginesy wewnętrzne (padding): 70px

    ok. 1 min
    Polecenie z arkusza

    Dla bloku głównego: kolor tła Snow, marginesy wewnętrzne 70 px

    Selektor znacznika main. Kolory tła wszystkich bloków (R.4.2) i padding: 70px (R.4.7).

    styl3.css CSS zmienione: 5 linii
    :root {
        --brown: SaddleBrown;
        --snow: Snow;
        --text-light: white;
        --bar-height: 100px;
    }
    
    * {
        font-family: Arial;
    }
    
    header section,
    footer section {
        background-color: var(--brown);
        color: var(--text-light);
        height: var(--bar-height);
    }
    
    header,
    footer {
        display: flex;
    }
    
    .left {
        width: 55%;
        font-size: 120%;
    }
    
    .middle {
        width: 25%;
    }
    
    .right {
        width: 20%;
        text-align: right;
    }
    
    main {
        background-color: var(--snow);
        padding: 70px;
    }
    Podgląd strony po tym kroku (ratownicy.html)
  35. Krok 35: Odnośnik: kropkowana ramka, tło i wysokość linii

    Kryteria:

    R.4.9 Ustawiono styl odnośnika: obramowanie 2px dotted SaddleBrown, kolor tła Snow, kolor czcionki SaddleBrown, padding: 20px, wysokość linii 100px (dla selektora a lub indentyfikatora lub klasy)

    ok. 1 min
    Polecenie z arkusza

    Dla odnośnika: obramowanie linią kropkowaną o szerokości 2 px i kolorze SaddleBrown, kolor tła Snow, kolor czcionki SaddleBrown, marginesy wewnętrzne 20 px, wysokość linii 100 px

    border: 2px dotted z kolorem ze zmiennej, tło i tekst w odwróconych kolorach bloku, padding: 20px i line-height: 100px - tyle co wysokość stopki (R.4.9).

    styl3.css CSS zmienione: 8 linii
    :root {
        --brown: SaddleBrown;
        --snow: Snow;
        --text-light: white;
        --bar-height: 100px;
    }
    
    * {
        font-family: Arial;
    }
    
    header section,
    footer section {
        background-color: var(--brown);
        color: var(--text-light);
        height: var(--bar-height);
    }
    
    header,
    footer {
        display: flex;
    }
    
    .left {
        width: 55%;
        font-size: 120%;
    }
    
    .middle {
        width: 25%;
    }
    
    .right {
        width: 20%;
        text-align: right;
    }
    
    main {
        background-color: var(--snow);
        padding: 70px;
    }
    
    a {
        border: 2px dotted var(--brown);
        background-color: var(--snow);
        color: var(--brown);
        padding: 20px;
        line-height: 100px;
    }
    Podgląd strony po tym kroku (ratownicy.html)
  36. Krok 36: Lista numerowana: małe litery jako punktory

    Kryteria:

    R.4.10 Ustawiono styl listy (list-style-type): mała litera jako punktor

    ok. 1 min
    Polecenie z arkusza

    Dla listy numerowanej: punktor jako mała litera

    list-style-type: lower-alpha - „a.”, „b.” (R.4.10).

    styl3.css CSS zmienione: 4 linie
    :root {
        --brown: SaddleBrown;
        --snow: Snow;
        --text-light: white;
        --bar-height: 100px;
    }
    
    * {
        font-family: Arial;
    }
    
    header section,
    footer section {
        background-color: var(--brown);
        color: var(--text-light);
        height: var(--bar-height);
    }
    
    header,
    footer {
        display: flex;
    }
    
    .left {
        width: 55%;
        font-size: 120%;
    }
    
    .middle {
        width: 25%;
    }
    
    .right {
        width: 20%;
        text-align: right;
    }
    
    main {
        background-color: var(--snow);
        padding: 70px;
    }
    
    a {
        border: 2px dotted var(--brown);
        background-color: var(--snow);
        color: var(--brown);
        padding: 20px;
        line-height: 100px;
    }
    
    ol {
        list-style-type: lower-alpha;
    }
    Podgląd strony po tym kroku (ratownicy.html)
  37. Krok 37: Etykiety nad polami formularza

    ok. 2 min
    Polecenie z arkusza

    Pole edycyjne typu numerycznego podpisane powyżej tekstem: „Numer karetki:”

    Etykieta i pole są elementami w linii - bez stylu stanęłyby obok siebie. Wiersz .row jako kolumna flex (flex-direction: column) układa je jeden pod drugim, jak na obrazie 2, a align-items: flex-start zostawia polom naturalną szerokość (bez tej linii rozciągnęłyby się na całą szerokość bloku). Arkusz nie podaje stylu formularza, a obraz 2 pokazuje pola pod napisami - ta reguła tylko to odtwarza.

    styl3.css CSS zmienione: 6 linii
    :root {
        --brown: SaddleBrown;
        --snow: Snow;
        --text-light: white;
        --bar-height: 100px;
    }
    
    * {
        font-family: Arial;
    }
    
    header section,
    footer section {
        background-color: var(--brown);
        color: var(--text-light);
        height: var(--bar-height);
    }
    
    header,
    footer {
        display: flex;
    }
    
    .left {
        width: 55%;
        font-size: 120%;
    }
    
    .middle {
        width: 25%;
    }
    
    .right {
        width: 20%;
        text-align: right;
    }
    
    main {
        background-color: var(--snow);
        padding: 70px;
    }
    
    a {
        border: 2px dotted var(--brown);
        background-color: var(--snow);
        color: var(--brown);
        padding: 20px;
        line-height: 100px;
    }
    
    ol {
        list-style-type: lower-alpha;
    }
    
    .row {
        display: flex;
        flex-direction: column;
        align-items: flex-start;
    }
    Podgląd strony po tym kroku (ratownicy.html)
  38. Etap 4: Skrypt PHP

    Kroki 38-45 · po tym etapie masz 34 z 34 kryteriów (pkt)

    Krok 38: Plik dodanie.php, metoda żądania i połączenie PDO z bazą

    Kryteria:

    R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy ee09

    ok. 3 min
    Polecenie z arkusza

    Napisany w języku PHP, w pliku dodanie.php. Skrypt łączy się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie ee09

    Plik dodanie.php zaczyna się od sprawdzenia, jak go otwarto:

    • $_SERVER['REQUEST_METHOD'] !== 'POST' - ktoś wszedł na adres skryptu wprost (z historii, z zakładki, robot). Wtedy header('Location: ratownicy.html') odsyła go do formularza, a exit kończy skrypt - sam nagłówek go nie zatrzymuje. Sprawdzone: takie wejście dostaje odpowiedź 302 i nic nie trafia do bazy,
    • new PDO - adres serwera localhost, baza ee09 i kodowanie w jednym napisie, potem użytkownik root i puste hasło (R.5.1). ERRMODE_EXCEPTION - każdy błąd bazy rzuca wyjątek, który obsłuży jeden blok catch.

    PDO zamiast funkcji mysqli z tabeli 1 - klucz przyjmuje obie biblioteki. Podgląd do kroku z zapytaniem pokazuje formularz.

    dodanie.php PHP nowy plik
    <?php
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        header('Location: ratownicy.html');
        exit;
    }
    
    $pdo = new PDO('mysql:host=localhost;dbname=ee09;charset=utf8mb4', 'root', '', [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    ]);

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

  39. Krok 39: Dane z formularza w tablicy $_POST

    Kryteria:

    R.5.3 Skrypt pobiera dane z formularza za pomocą tablicy $_POST (lub za pomocą $_REQUEST gdy sprawdzono, że REQUEST_METHOD jest równe POST)

    ok. 2 min
    Polecenie z arkusza

    Skrypt działa na danych przesłanych z formularza

    Cztery wartości z $_POST (R.5.3), od razu sprawdzone i oczyszczone:

    • filter_var(..., FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]) - numer karetki zamienia się na liczbę całkowitą tylko wtedy, gdy naprawdę nią jest i jest większy od zera. Dla pustego pola, tekstu, 2.5 czy -3 wynikiem jest false,
    • ?? '' - gdy pola w ogóle nie wysłano, zmienna dostaje pusty napis zamiast ostrzeżenia „Undefined array key”,
    • trim() - zdejmuje spacje z brzegów imion i nazwisk.

    Nazwy zmiennych po angielsku ($ambulance, $rescuer1), klucze tablicy - takie, jak atrybuty name w formularzu.

    dodanie.php PHP zmienione: 5 linii
    <?php
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        header('Location: ratownicy.html');
        exit;
    }
    
    $ambulance = filter_var($_POST['nrKaretki'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
    $rescuer1 = trim($_POST['ratownik1'] ?? '');
    $rescuer2 = trim($_POST['ratownik2'] ?? '');
    $rescuer3 = trim($_POST['ratownik3'] ?? '');
    
    $pdo = new PDO('mysql:host=localhost;dbname=ee09;charset=utf8mb4', 'root', '', [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    ]);

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

  40. Krok 40: Zapytanie 1 jako zapytanie przygotowane

    Kryteria:

    R.5.4 Skrypt wysyła do bazy danych zmodyfikowane zapytanie 1, jego wartości wypełnione są danymi z formularza. Należy sprawdzić w phpMyAdmin czy rekord został wstawiony

    ok. 3 min
    Polecenie z arkusza

    Skrypt wysyła do bazy danych zapytanie wstawiające rekord do tabeli ratownicy z danymi pobranymi z formularza (zmodyfikowane zapytanie 1, opisane w punkcie „Operacje na bazie danych”)

    Zapytanie 1, w którym numer karetki i nazwiska zastępują parametry :ambulance, :rescuer1, :rescuer2, :rescuer3. Treść stoi w zmiennej $sql - przyda się w następnym kroku:

    • $pdo->prepare($sql) - przygotowanie zapytania z parametrami,
    • bindValue - wartość dla każdego parametru; numer karetki z typem PDO::PARAM_INT, więc trafi do zapytania jako liczba, nazwiska jako napisy,
    • execute() - wykonanie.

    Dane nigdy nie staną się kodem SQL. Sprawdzone: nazwisko O'Neill, przydomek w cudzysłowie i wpisany w pole trzeciego ratownika fragment '), (NULL, 1, 'X', 'Y', 'Z') -- zapisały jeden rekord, z tym tekstem jako nazwiskiem. Sprawdzenie, jak każe klucz (R.5.4): formularz z danymi 7, Marek Lis, Ewa Sowa, Piotr Kos i w tabeli nowy wiersz 5.

    Typowe błędy

    Tabela z archiwum ma kodowanie latin1 - litery ą, ć, ę, ł, ń, ś, ź, ż zapisują się jako „?” także przez PDO z charset=utf8mb4. Klucz sprawdza import pliku bez zmian, więc kodowania tabeli nie zmieniasz.

    dodanie.php PHP zmienione: 7 linii
    <?php
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        header('Location: ratownicy.html');
        exit;
    }
    
    $ambulance = filter_var($_POST['nrKaretki'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
    $rescuer1 = trim($_POST['ratownik1'] ?? '');
    $rescuer2 = trim($_POST['ratownik2'] ?? '');
    $rescuer3 = trim($_POST['ratownik3'] ?? '');
    $sql = 'INSERT INTO ratownicy VALUES (NULL, :ambulance, :rescuer1, :rescuer2, :rescuer3);';
    
    $pdo = new PDO('mysql:host=localhost;dbname=ee09;charset=utf8mb4', 'root', '', [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    ]);
    $statement = $pdo->prepare($sql);
    $statement->bindValue('ambulance', $ambulance, PDO::PARAM_INT);
    $statement->bindValue('rescuer1', $rescuer1);
    $statement->bindValue('rescuer2', $rescuer2);
    $statement->bindValue('rescuer3', $rescuer3);
    $statement->execute();
    Podgląd strony po tym kroku (ratownicy.html)
    Podgląd strony po kroku 40
  41. Krok 41: Komunikat z treścią wysłanego zapytania

    Kryteria:

    R.5.5 Skrypt wyświetla tekst: „Do bazy zostało wysłane zapytanie: <zapytanie>”, gdzie <zapytanie> oznacza treść zapytania, które zostało wysłane skryptem do bazy danych

    ok. 4 min
    Polecenie z arkusza

    Skrypt wypisuje tekst: „Do bazy zostało wysłane zapytanie: <zapytanie>”, gdzie <zapytanie> oznacza treść zapytania, które zostało wysłane skryptem do bazy danych

    Przy zapytaniu przygotowanym w kodzie nie ma gotowego tekstu z danymi - jest $sql z parametrami. Skąd wziąć „treść zapytania, które zostało wysłane”?

    PDO dla MySQL i MariaDB domyślnie samo składa zapytanie przed wysłaniem: w miejsce parametru wstawia liczbę (PARAM_INT) albo napis w apostrofach, ze znakami ucieczki przed apostrofami i ukośnikami. Skrypt robi to samo jawnie:

    • $pdo->quote() - napis w apostrofach, zabezpieczony tak samo, jak robi to PDO przy bindValue,
    • (string) $ambulance - liczba bez apostrofów,
    • strtr podmienia w $sql nazwy parametrów na te wartości.

    Sprawdzone w dzienniku serwera bazy (general log MariaDB): do bazy dotarło dokładnie INSERT INTO ratownicy VALUES (NULL, 7, 'Marek Lis', 'Ewa Sowa', 'Piotr Kos') - znak w znak to, co wypisuje skrypt; różni się tylko końcowy średnik, którego serwer nie zapisuje w dzienniku (R.5.5). To samo dla trudnych danych: O'Neill przyszło jako 'Ewa O\'Neill', a skrypt wypisał to samo.

    Przed echo treść przechodzi przez e() - funkcję z htmlspecialchars. Zapytanie zawiera dane wpisane przez użytkownika, a <script> w nazwisku nie może wykonać się na stronie. Podgląd: wynik dla zespołu 7.

    Typowe błędy

    Gdyby w opcjach PDO wyłączyć składanie zapytań po stronie PHP (PDO::ATTR_EMULATE_PREPARES => false), baza dostałaby osobno treść z parametrami i osobno dane - wtedy uczciwą odpowiedzią na „wysłane zapytanie” jest sama treść $sql z parametrami.

    dodanie.php PHP zmienione: 12 linii
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        header('Location: ratownicy.html');
        exit;
    }
    
    $ambulance = filter_var($_POST['nrKaretki'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
    $rescuer1 = trim($_POST['ratownik1'] ?? '');
    $rescuer2 = trim($_POST['ratownik2'] ?? '');
    $rescuer3 = trim($_POST['ratownik3'] ?? '');
    $sql = 'INSERT INTO ratownicy VALUES (NULL, :ambulance, :rescuer1, :rescuer2, :rescuer3);';
    
    $pdo = new PDO('mysql:host=localhost;dbname=ee09;charset=utf8mb4', 'root', '', [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    ]);
    $statement = $pdo->prepare($sql);
    $statement->bindValue('ambulance', $ambulance, PDO::PARAM_INT);
    $statement->bindValue('rescuer1', $rescuer1);
    $statement->bindValue('rescuer2', $rescuer2);
    $statement->bindValue('rescuer3', $rescuer3);
    $statement->execute();
    $query = strtr($sql, [
        ':ambulance' => (string) $ambulance,
        ':rescuer1' => $pdo->quote($rescuer1),
        ':rescuer2' => $pdo->quote($rescuer2),
        ':rescuer3' => $pdo->quote($rescuer3),
    ]);
    echo 'Do bazy zostało wysłane zapytanie: ' . e($query);
    Podgląd strony po tym kroku (dodanie.php)
  42. Krok 42: Obsługa błędów i zamknięcie połączenia w finally

    Kryteria:

    R.5.2 Ostatnią operacją na bazie jest jej zamknięcie (zgodne ze składnią)

    ok. 3 min
    Polecenie z arkusza

    Na końcu działania skrypt zamyka połączenie z serwerem.

    Połączenie, zapis i złożenie treści zapytania trafiają do bloku try, a komunikat - do zmiennej $message:

    • catch (PDOException $exception) - gdy baza nie działa albo zapytanie się nie powiedzie, $message dostaje zwykłe zdanie dla użytkownika zamiast komunikatu PHP z danymi serwera. Sprawdzone na złym haśle: skrypt wypisuje „Nie udało się dodać zespołu, spróbuj ponownie za chwilę.”,
    • finally wykonuje się zawsze - po udanym zapisie i po wyjątku. Stoją w nim $statement = null i $pdo = null: PDO nie ma funkcji close, połączenie zamyka się, gdy znika ostatnie odwołanie do obiektu. Zamknięcie jest więc ostatnią operacją na bazie w każdej gałęzi (R.5.2).

    $query powstaje w try, przed zamknięciem - quote() potrzebuje otwartego połączenia. Wypisanie komunikatu jest już po finally. Podgląd: dla zespołu 7 bez zmian.

    dodanie.php PHP zmienione: 24 linie
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        header('Location: ratownicy.html');
        exit;
    }
    
    $ambulance = filter_var($_POST['nrKaretki'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
    $rescuer1 = trim($_POST['ratownik1'] ?? '');
    $rescuer2 = trim($_POST['ratownik2'] ?? '');
    $rescuer3 = trim($_POST['ratownik3'] ?? '');
    $sql = 'INSERT INTO ratownicy VALUES (NULL, :ambulance, :rescuer1, :rescuer2, :rescuer3);';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=ee09;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        ]);
        $statement = $pdo->prepare($sql);
        $statement->bindValue('ambulance', $ambulance, PDO::PARAM_INT);
        $statement->bindValue('rescuer1', $rescuer1);
        $statement->bindValue('rescuer2', $rescuer2);
        $statement->bindValue('rescuer3', $rescuer3);
        $statement->execute();
        $query = strtr($sql, [
            ':ambulance' => (string) $ambulance,
            ':rescuer1' => $pdo->quote($rescuer1),
            ':rescuer2' => $pdo->quote($rescuer2),
            ':rescuer3' => $pdo->quote($rescuer3),
        ]);
        $message = 'Do bazy zostało wysłane zapytanie: ' . $query;
    } catch (PDOException $exception) {
        $message = 'Nie udało się dodać zespołu, spróbuj ponownie za chwilę.';
    } finally {
        $statement = null;
        $pdo = null;
    }
    echo e($message);

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

  43. Krok 43: Strona z komunikatem w wyglądzie formularza

    ok. 3 min

    Dodatek spoza arkusza - klucz go nie ocenia. Sam tekst na białym tle wygląda jak błąd. Tu pod skryptem stoi krótka strona w tym samym wyglądzie co formularz (ten sam <header>, <main> i styl3.css): nagłówek, komunikat z $message przepuszczonym przez e() i odnośnik do formularza, żeby dodać kolejny zespół. Kod PHP zostaje na górze pliku, w HTML-u jest tylko jedno wypisanie zmiennej <?= e($message) ?>.

    Podgląd: strona po dodaniu zespołu karetki 7.

    dodanie.php PHP zmienione: 28 linii
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        header('Location: ratownicy.html');
        exit;
    }
    
    $ambulance = filter_var($_POST['nrKaretki'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
    $rescuer1 = trim($_POST['ratownik1'] ?? '');
    $rescuer2 = trim($_POST['ratownik2'] ?? '');
    $rescuer3 = trim($_POST['ratownik3'] ?? '');
    $sql = 'INSERT INTO ratownicy VALUES (NULL, :ambulance, :rescuer1, :rescuer2, :rescuer3);';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=ee09;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        ]);
        $statement = $pdo->prepare($sql);
        $statement->bindValue('ambulance', $ambulance, PDO::PARAM_INT);
        $statement->bindValue('rescuer1', $rescuer1);
        $statement->bindValue('rescuer2', $rescuer2);
        $statement->bindValue('rescuer3', $rescuer3);
        $statement->execute();
        $query = strtr($sql, [
            ':ambulance' => (string) $ambulance,
            ':rescuer1' => $pdo->quote($rescuer1),
            ':rescuer2' => $pdo->quote($rescuer2),
            ':rescuer3' => $pdo->quote($rescuer3),
        ]);
        $message = 'Do bazy zostało wysłane zapytanie: ' . $query;
    } catch (PDOException $exception) {
        $message = 'Nie udało się dodać zespołu, spróbuj ponownie za chwilę.';
    } finally {
        $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>Zespoły ratownicze</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <section class="left">
                <h2>Dodanie zespołu ratowniczego</h2>
            </section>
            <section class="middle">
                <p>Kontakt:<br>022 222 11 333</p>
            </section>
            <section class="right">
                <img src="obraz.jpg" alt="Ratownicy" width="167" height="100">
            </section>
        </header>
        <main>
            <h3>Dodaj nowy zespół</h3>
            <p><?= e($message) ?></p>
            <a href="ratownicy.html">Dodaj kolejny zespół</a>
        </main>
    </body>
    </html>
    Podgląd strony po tym kroku (dodanie.php)
  44. Krok 44: Zły numer karetki i puste pola nie trafiają do bazy

    ok. 3 min

    Pola mają required i min="1", ale to zabezpieczenie przeglądarki - dane da się wysłać z pominięciem formularza (np. poleceniem curl albo po usunięciu atrybutów w narzędziach przeglądarki). Dlatego skrypt sprawdza je jeszcze raz, zanim połączy się z bazą: gdy filter_var zwróciło false albo któreś nazwisko jest puste po trim(), zamiast zapisu ustawia komunikat z tym, co trzeba poprawić.

    Sprawdzone: pusty numer, 0, 2.5 i nazwisko z samych spacji nie dodają rekordu. W wersji podstawowej pusty numer wywraca zapytanie (VALUES (NULL, , …). Podgląd: komunikat dla nazwiska ze spacji.

    dodanie.php PHP zmienione: 26 linii
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        header('Location: ratownicy.html');
        exit;
    }
    
    $ambulance = filter_var($_POST['nrKaretki'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
    $rescuer1 = trim($_POST['ratownik1'] ?? '');
    $rescuer2 = trim($_POST['ratownik2'] ?? '');
    $rescuer3 = trim($_POST['ratownik3'] ?? '');
    $sql = 'INSERT INTO ratownicy VALUES (NULL, :ambulance, :rescuer1, :rescuer2, :rescuer3);';
    
    if ($ambulance === false || $rescuer1 === '' || $rescuer2 === '' || $rescuer3 === '') {
        $message = 'Wpisz numer karetki (liczba całkowita większa od zera) oraz imiona i nazwiska trzech ratowników.';
    } else {
        try {
            $pdo = new PDO('mysql:host=localhost;dbname=ee09;charset=utf8mb4', 'root', '', [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            ]);
            $statement = $pdo->prepare($sql);
            $statement->bindValue('ambulance', $ambulance, PDO::PARAM_INT);
            $statement->bindValue('rescuer1', $rescuer1);
            $statement->bindValue('rescuer2', $rescuer2);
            $statement->bindValue('rescuer3', $rescuer3);
            $statement->execute();
            $query = strtr($sql, [
                ':ambulance' => (string) $ambulance,
                ':rescuer1' => $pdo->quote($rescuer1),
                ':rescuer2' => $pdo->quote($rescuer2),
                ':rescuer3' => $pdo->quote($rescuer3),
            ]);
            $message = 'Do bazy zostało wysłane zapytanie: ' . $query;
        } catch (PDOException $exception) {
            $message = 'Nie udało się dodać zespołu, spróbuj ponownie za chwilę.';
        } finally {
            $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>Zespoły ratownicze</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <section class="left">
                <h2>Dodanie zespołu ratowniczego</h2>
            </section>
            <section class="middle">
                <p>Kontakt:<br>022 222 11 333</p>
            </section>
            <section class="right">
                <img src="obraz.jpg" alt="Ratownicy" width="167" height="100">
            </section>
        </header>
        <main>
            <h3>Dodaj nowy zespół</h3>
            <p><?= e($message) ?></p>
            <a href="ratownicy.html">Dodaj kolejny zespół</a>
        </main>
    </body>
    </html>
    Podgląd strony po tym kroku (dodanie.php)
  45. Krok 45: Odświeżenie po zapisie nie dodaje drugiego zespołu

    ok. 4 min

    Strona z komunikatem jest wynikiem żądania POST, więc odświeżenie (F5) pyta, czy wysłać dane ponownie - a ponowne wysłanie dopisałoby drugi identyczny zespół. Wzorzec PRG (Post/Redirect/Get):

    • session_start() na początku pliku - sesja przechowa komunikat między dwoma żądaniami,
    • po udanym zapisie ($saved = true ustawiane zaraz po złożeniu komunikatu) i po bloku finally komunikat trafia do $_SESSION, a header('Location: dodanie.php') z exit każe przeglądarce otworzyć skrypt jeszcze raz zwykłym żądaniem GET,
    • żądanie GET z komunikatem w sesji wyświetla go i od razu usuwa (unset); GET bez komunikatu - jak wcześniej - wraca do formularza.

    Sprawdzone na prawdziwym PHP: wysłanie formularza daje odpowiedź 302, potem stronę z wysłanym zapytaniem, a kolejne odświeżenie - powrót do formularza, bez drugiego rekordu w tabeli. Błąd (zły numer) nie przekierowuje - komunikat zostaje na stronie. Podgląd: stan po przekierowaniu.

    dodanie.php PHP zmienione: 49 linii
    <?php
    session_start();
    
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
        // po przekierowaniu: komunikat zapisany w sesji wyświetla się jeden raz
        if (!isset($_SESSION['message'])) {
            header('Location: ratownicy.html');
            exit;
        }
        $message = $_SESSION['message'];
        unset($_SESSION['message']);
    } else {
        $ambulance = filter_var($_POST['nrKaretki'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
        $rescuer1 = trim($_POST['ratownik1'] ?? '');
        $rescuer2 = trim($_POST['ratownik2'] ?? '');
        $rescuer3 = trim($_POST['ratownik3'] ?? '');
        $sql = 'INSERT INTO ratownicy VALUES (NULL, :ambulance, :rescuer1, :rescuer2, :rescuer3);';
    
        if ($ambulance === false || $rescuer1 === '' || $rescuer2 === '' || $rescuer3 === '') {
            $message = 'Wpisz numer karetki (liczba całkowita większa od zera) oraz imiona i nazwiska trzech ratowników.';
        } else {
            $saved = false;
            try {
                $pdo = new PDO('mysql:host=localhost;dbname=ee09;charset=utf8mb4', 'root', '', [
                    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                ]);
                $statement = $pdo->prepare($sql);
                $statement->bindValue('ambulance', $ambulance, PDO::PARAM_INT);
                $statement->bindValue('rescuer1', $rescuer1);
                $statement->bindValue('rescuer2', $rescuer2);
                $statement->bindValue('rescuer3', $rescuer3);
                $statement->execute();
                $query = strtr($sql, [
                    ':ambulance' => (string) $ambulance,
                    ':rescuer1' => $pdo->quote($rescuer1),
                    ':rescuer2' => $pdo->quote($rescuer2),
                    ':rescuer3' => $pdo->quote($rescuer3),
                ]);
                $message = 'Do bazy zostało wysłane zapytanie: ' . $query;
                $saved = true;
            } catch (PDOException $exception) {
                $message = 'Nie udało się dodać zespołu, spróbuj ponownie za chwilę.';
            } finally {
                $statement = null;
                $pdo = null;
            }
    
            if ($saved) {
                // PRG: komunikat do sesji i przekierowanie - odświeżenie strony nie doda zespołu drugi raz
                $_SESSION['message'] = $message;
                header('Location: dodanie.php');
                exit;
            }
        }
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Zespoły ratownicze</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <section class="left">
                <h2>Dodanie zespołu ratowniczego</h2>
            </section>
            <section class="middle">
                <p>Kontakt:<br>022 222 11 333</p>
            </section>
            <section class="right">
                <img src="obraz.jpg" alt="Ratownicy" width="167" height="100">
            </section>
        </header>
        <main>
            <h3>Dodaj nowy zespół</h3>
            <p><?= e($message) ?></p>
            <a href="ratownicy.html">Dodaj kolejny zespół</a>
        </main>
    </body>
    </html>
    Podgląd strony po tym kroku (dodanie.php)
  46. Etap 5: Porównanie z wersją podstawową

    Krok 46 · po tym etapie masz 34 z 34 kryteriów (pkt)

    Krok 46: Co jest inaczej i którą wersję wybrać na egzaminie

    ok. 3 min

    Obie wersje dają komplet 34 punktów. Różnice:

    wersja podstawowawersja nowoczesna
    blokisiedem section z id jeden po drugimheader i footer z trzema section, main
    układfloat: left i clear: bothheader i footer jako display: flex, width na blokach
    style blokówsześć identyfikatorów w jednej reguleheader section, footer section i klasy .left, .middle, .right
    kolorywpisane w każdej regulezmienne --brown, --snow
    formularznapis, <br>, pole, input type="reset"label for, wiersze w div, button, required, min
    bibliotekamysqli (tabela 1 arkusza)PDO z wyjątkami
    numer karetkiwklejony do zapytania, puste pole psuje składnięfilter_var - liczba całkowita od 1 albo komunikat
    INSERTdane wklejone w treść zapytaniaparametry i bindValue
    wypisane zapytaniezmienna $query bez escapowaniata sama treść, którą dostała baza, przez e()
    zamknięcie bazymysqli_close na końcu$pdo = null w finally
    po zapisiesam tekst, F5 dodaje drugi zespółstrona w wyglądzie formularza, przekierowanie

    Na egzaminie wersja podstawowa trzyma się funkcji z tabeli 1 i łatwiej ją napisać bez dokumentacji. Nowoczesna nie wywraca się na apostrofie ani pustym numerze, nie da się jej wstrzyknąć kodu SQL ani skryptu i nie dubluje zespołów po odświeżeniu - a klucz przyjmuje obie. Na prawdziwym serwerze skrypt nie łączyłby się kontem root bez hasła, tylko kontem z prawem INSERT do jednej tabeli, a dane logowania leżałyby w osobnym pliku poza katalogiem strony.

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

  47. Etap 6: Oddanie pracy

    Krok 47 · po tym etapie masz 34 z 34 kryteriów (pkt)

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

    ok. 5 min
    Polecenie z arkusza

    UWAGA: po zakończeniu pracy utwórz w folderze z numerem PESEL lub w przypadku jego braku w folderze z innym numerem, którym został podpisany arkusz plik tekstowy o nazwie przeglądarka.txt. Zapisz w nim nazwę przeglądarki internetowej, w której weryfikowano poprawność działania witryny. Nagraj płytę z rezultatami pracy.

    W przeglądarka.txt nazwa przeglądarki, w której sprawdzana była strona. W folderze z numerem zdającego: dodanie.php, import.png, kw1.jpg - kw4.jpg, kwerendy.txt, obraz.jpg, przeglądarka.txt, ratownicy.html i styl3.css. Nagraj płytę, sprawdź jej odczyt i opisz ją numerem zdającego.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

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

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.

Potwierdzenie

Informacja