Przejdź do głównej treści
INF.03-09-25.01-SGStyczeń 2025 · 30 kroków · ok. 73 min · 40 kryteriów z klucza

Rozwiązanie krok po kroku: Portal zdobywców gór (Nowoczesny kod (grid, PDO))

To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza zdobywcy z czterema zapytaniami, logo i strona zdobywcy.php, która dopisuje do bazy osobę z formularza i wypisuje członków koła w tabeli - i tak samo spełnia każde kryterium klucza. Inna jest droga:

  • siatka CSS: bloki lewy i prawy we wspólnym kontenerze z kolumnami 30% 70% w grid-template-columns (klucz R.4.4 przyjmuje je wprost) zamiast float: left i clear: both; blok lewy to <aside>, prawy - <main>,
  • PHP na PDO nad HTML-em: najpierw zapis z formularza (Skrypt 2), potem odczyt listy (Skrypt 1), połączenie zamknięte, zanim zacznie się strona,
  • bezpieczny zapis: zapytanie przygotowane, sprawdzenie e-maila po stronie serwera, funkcja tylko z listy dozwolonych, nazwisko z apostrofem (O'Neill) zapisuje się bez błędu,
  • przekierowanie po zapisie (wzorzec POST/Redirect/GET) od pierwszej wersji: odświeżenie strony nie dopisuje osoby drugi raz, a po zapisie pojawia się potwierdzenie,
  • formularz pamięta wpisane dane, gdy czegoś brakuje, a lista funkcji powstaje z tej samej stałej, której używa sprawdzenie.

Na końcu krótkie porównanie z wersją podstawową (zakładka obok). Baza i logo pochodzą z prawdziwego archiwum pliki4, zapytania zostały wykonane na tej bazie w MariaDB z XAMPP-a, a podgląd strony to wynik prawdziwego uruchomienia kodu z danego kroku w PHP 8.2 na świeżo zaimportowanej bazie - od kroku z zapisem po wysłaniu formularza z danymi M., Miłosz, uczestnik, jak w ostatnim wierszu tabeli na ilustracji 2.

Jak liczą się punkty. Klucz ma 40 kryteriów (R.1.1 - R.5.8), każde za 1 punkt. Próg zdania to 75%, czyli 30 punktów. Przy każdym kroku jest napisane, które kryteria spełnia.

  1. Etap 1: Baza danych

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

    Krok 1: Baza danych zdobywcy

    ok. 2 min
    Polecenie z arkusza

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

    Archiwum pliki4.7z rozpakuj hasłem $ZdobyWcy& do folderu z numerem zdającego: baza.sql i logo.jpg. W phpMyAdmin Nowa → zdobywcy, porównywanie utf8_unicode_ci, albo:

    CREATE DATABASE zdobywcy CHARACTER SET utf8 COLLATE utf8_unicode_ci;
    
    Typowe błędy

    Nazwa bazy musi być dokładnie zdobywcy - skrypty łączą się z nią po nazwie (R.5.2).

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

    ok. 2 min
    Polecenie z arkusza

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

    Import → baza.sql. Trzy tabele z ilustracji 1: gory (309 szczytów z pasmem i wysokością), osoby (15 członków koła) i osiagniecia (kto i kiedy zdobył który szczyt - pola gora_id, osoba_id, data_zdobycia).

  3. Krok 3: Zrzut ekranu import.png

    Kryteria:

    R.1.1 Wykonany import tabeli do bazy danych zdobywcy , co jest udokumentowane w pliku import.png

    ok. 2 min
    Polecenie z arkusza

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

    Cały ekran z paskiem zadań, zapis jako import.png.

    Typowe błędy

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

  4. Krok 4: Zapytanie 1: dziesięć najwyższych szczytów

    Kryteria: ,

    R.1.2 Zapisany plik kwerendy.txt zawierający co najmniej jedno zapytanie SQL, wynikające z treści zadania

    R.1.3 Wykonane zapytanie 1 wybierające jedynie pasmo, nazwę góry i jej wysokość z tabeli gory i wyświetlające pierwszych 10 rekordów posortowanych malejąco według wysokości. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT pasmo, nazwa_gory, wysokosc FROM gory ORDER BY wysokosc DESC LIMIT 10; oraz wynik działania zapytania jest udokumentowany zrzutem, na którym widocznych jest dokładnie 10 rekordów zawierających jedynie pola pasmo, nazwa_gory, wysokosc

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 1: wybierające jedynie pasmo, nazwę góry i jej wysokość z tabeli gory i wyświetlające pierwszych 10 rekordów posortowanych malejąco według wysokości.

    ORDER BY wysokosc DESC - malejąco, od najwyższego; LIMIT 10 działa na wynik już posortowany, więc stoi na końcu. Wynik zaczyna się od Rysów (2499 m). Zapytania zapisuj w kwerendy.txt, zrzut jako kw1.png.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT pasmo, nazwa_gory, wysokosc
    FROM gory
    ORDER BY wysokosc DESC
    LIMIT 10;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: członkowie koła

    Kryteria:

    R.1.4 Wykonane zapytanie 2 wybierające jedynie nazwisko, imię, funkcję i email z tabeli osoby . W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwisko, imie, funkcja, email FROM osoby; oraz wynik działania zapytania jest udokumentowany zrzutem, na którym widocznych jest 15 rekordów lub więcej (zależnie od wywołania zapytania 4) zawierających jedynie pola nazwisko, imie, funkcja, email

    ok. 1 min
    Polecenie z arkusza

    Zapytanie 2: wybierające jedynie nazwisko, imię, funkcję i email z tabeli osoby.

    Cztery kolumny, bez klucza id_osoby. Wynik: 15 osób (16, jeśli wcześniej wykonano zapytanie 4 - klucz to uwzględnia). Zrzut: kw2.png. To zapytanie wypełni tabelę na stronie.

    kwerendy.txt Tekst zmienione: 4 linie
    -- Zapytanie 1
    SELECT pasmo, nazwa_gory, wysokosc
    FROM gory
    ORDER BY wysokosc DESC
    LIMIT 10;
    
    -- Zapytanie 2
    SELECT nazwisko, imie, funkcja, email
    FROM osoby;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: nowe osiągnięcie

    Kryteria:

    R.1.5 Wykonane zapytanie 3 wstawiające osiągnięcie w postaci zdobycia góry „Łysica - Skała Agaty” przez uczestnika B. Urszula w dniu 2024-06-08. W pliku z kwerendami lub na zrzucie istnieje zapis np.: INSERT INTO osiagniecia VALUES (NULL, 182, 4, "2024-06-08"); (lub inne formy zapytania, wartość klucza pominięta lub NULL) oraz wynik działania zapytania jest udokumentowany zrzutem, na którym widoczny jest komunikat potwierdzający wykonanie zapytania lub tabela z dodanym rekordem

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 3: wstawiające osiągnięcie w postaci zdobycia góry „Łysica - Skała Agaty” (id=182) przez uczestnika B. Urszula (id=4) w dniu 2024-06-08, klucz główny uzyskiwany automatycznie.

    INSERT z listą kolumn: (gora_id, osoba_id, data_zdobycia) - klucz główny pominięty, więc baza nada go sama (AUTO_INCREMENT, tu id 12). Lista kolumn sprawia, że zapytanie nie zależy od kolejności kolumn w tabeli i nie przestanie działać, gdy ktoś dopisze do niej nową kolumnę - wersja VALUES (NULL, 182, 4, …) musiałaby wtedy dostać kolejną wartość. Klucz przyjmuje obie formy.

    Do osiągnięcia wpisujesz identyfikatory, nie nazwy - nazwę góry i osoby baza zna z pozostałych tabel. Zrzut: kw3.png (tu: wstawiony rekord złączony z nazwą góry i osobą).

    kwerendy.txt Tekst zmienione: 4 linie
    -- Zapytanie 1
    SELECT pasmo, nazwa_gory, wysokosc
    FROM gory
    ORDER BY wysokosc DESC
    LIMIT 10;
    
    -- Zapytanie 2
    SELECT nazwisko, imie, funkcja, email
    FROM osoby;
    
    -- Zapytanie 3
    INSERT INTO osiagniecia (gora_id, osoba_id, data_zdobycia)
    VALUES (182, 4, '2024-06-08');
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: nowa osoba

    Kryteria:

    R.1.6 Wykonane zapytanie 4 wstawiające nową osobę o danych: klucz główny uzyskiwany automatycznie, M., Miłosz, uczestnik, [email protected] do tabeli osoby. W pliku z kwerendami lub na zrzucie istnieje zapis np.: INSERT INTO osoby VALUES (NULL, 'M.', 'Miłosz', 'uczestnik', '[email protected]'); (lub inne formy zapytania, wartość klucza pominięta lub NULL) oraz wynik działania zapytania jest udokumentowany zrzutem, na którym widoczny jest komunikat potwierdzający wykonanie zapytania lub tabela z dodanym rekordem

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 4: wstawiające nową osobę: M., Miłosz, uczestnik, [email protected] do tabeli osoby, klucz główny uzyskiwany automatycznie.

    To samo dla tabeli osoby: cztery kolumny i cztery napisy w apostrofach, klucz nada baza (id 16). Zrzut: kw4.png. To zapytanie - z parametrami zamiast stałych - wyśle Skrypt 2.

    kwerendy.txt Tekst zmienione: 4 linie
    -- Zapytanie 1
    SELECT pasmo, nazwa_gory, wysokosc
    FROM gory
    ORDER BY wysokosc DESC
    LIMIT 10;
    
    -- Zapytanie 2
    SELECT nazwisko, imie, funkcja, email
    FROM osoby;
    
    -- Zapytanie 3
    INSERT INTO osiagniecia (gora_id, osoba_id, data_zdobycia)
    VALUES (182, 4, '2024-06-08');
    
    -- Zapytanie 4
    INSERT INTO osoby (nazwisko, imie, funkcja, email)
    VALUES ('M.', 'Miłosz', 'uczestnik', '[email protected]');
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Grafika i strona HTML

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

    Krok 8: Logo: przezroczyste tło, napis, 600 px

    Kryteria: ,

    R.2.1 Grafika logo.png ma przezroczyste tło oraz jest przeskalowana do szerokości 600 px, odpowiadająca temu wysokość to 500 px (± 5 px)

    R.2.2 Grafika zawiera tekst o treści „ZDOBYWCY GÓR” zapisany czcionką szeryfową w kolorze zielonym. Tekst nie wychodzi poza obszar mapy

    ok. 6 min
    Polecenie z arkusza

    Cechy grafiki: Nazwa pliku logo.png, grafika wykonana na podstawie pliku logo.jpg. Tło przezroczyste. Umieszczony na środku mapy tekst o treści „ZDOBYWCY GÓR” zapisany czcionką szeryfową, w kolorze zielonym. Tekst nie może wychodzić poza obszar mapy. Przeskalowana z zachowaniem proporcji do szerokości 600 px.

    Grafika jest taka sama w obu wersjach. W GIMP-ie:

    1. Warstwa → Przezroczystość → Dodaj kanał alfa, Różdżka (zaznaczanie ciągłe) na białym tle i Delete - różdżka zaznacza tylko biel połączoną z klikniętym miejscem, więc śnieg na szczytach zostaje,
    2. Tekst: „ZDOBYWCY GÓR” w dwóch wierszach, czcionka szeryfowa (np. Times New Roman Bold), kolor zielony, na środku zwoju mapy, w całości na mapie,
    3. Obraz → Skaluj obraz: szerokość 600 z zamkniętym łańcuchem - wysokość wychodzi 500 px (583 × 600 / 700),
    4. Eksportuj jako logo.png (R.2.1, R.2.2).
    Typowe błędy

    Kolor na alfę z białym zamienia też śnieg na szczytach w przezroczystość - na tle strony góry zrobią się „dziurawe”.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 8
  9. Krok 9: Plik zdobywcy.php: HTML5, język, kodowanie

    Kryteria:

    R.2.3 Witryna internetowa zapisana w pliku zdobywcy.php oraz jest zapisany jawnie standard kodowania polskich znaków, deklaracja HTML5 <!DOCTYPE HTML>, jest zadeklarowany język witryny: polski np. <html lang="pl"> oraz nadany tytuł stronie "ZDOBYWCY GÓR”. Strona zawiera sekcje head i body

    ok. 1 min
    Polecenie z arkusza

    Cechy witryny: Składa się ze strony o nazwie zdobywcy.php. Zapisana w języku HTML5. Zadeklarowany polski język zawartości witryny. Jawnie zastosowany właściwy standard kodowania polskich znaków.

    Szkielet od razu z lang="pl" i <meta charset="UTF-8"> jako pierwszym elementem <head> (R.2.3). Bez kodu PHP podgląd pokazuje plik wprost.

    zdobywcy.php PHP nowy plik
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (zdobywcy.php)
  10. Krok 10: Tytuł strony i arkusz stylów

    Kryteria: ,

    R.2.3 Witryna internetowa zapisana w pliku zdobywcy.php oraz jest zapisany jawnie standard kodowania polskich znaków, deklaracja HTML5 <!DOCTYPE HTML>, jest zadeklarowany język witryny: polski np. <html lang="pl"> oraz nadany tytuł stronie "ZDOBYWCY GÓR”. Strona zawiera sekcje head i body

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

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony „ZDOBYWCY GÓR” widoczny na karcie przeglądarki. Arkusz stylów w pliku o nazwie styles.css prawidłowo połączony z kodem strony.

    <title> (R.2.3) i <link> do styles.css - z „s” na końcu (R.3.2) - oraz viewport dla telefonów.

    Typowe błędy

    style.css albo styl.css (jak w innych arkuszach) odcina wszystkie style.

    zdobywcy.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>ZDOBYWCY GÓR</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (zdobywcy.php)
  11. Krok 11: Bloki strony

    Kryteria:

    R.2.4 Zdefiniowany układ strony dzielący ją na bloki: nagłówkowy, nawigacji, lewy, prawy i stopki przy pomocy semantycznych znaczników sekcji języka HTML5 zgodnie z ich znaczeniem, w tym do części nagłówkowej - header, do stopki - footer. Dopuszcza się dodatkowe użycie bloków div lub section

    ok. 2 min
    Polecenie z arkusza

    Podział strony na blok nagłówkowy, poniżej blok nawigacji, poniżej obok siebie bloki lewy i prawy, na dole blok stopki. Podział zrealizowany za pomocą semantycznych znaczników sekcji języka HTML5 tak, aby po uruchomieniu w przeglądarce wygląd układu bloków był zgodny z ilustracją 2.

    Znaczniki dobrane do treści bloków (R.2.4):

    • <header>, <nav> i <footer> - jak w każdym arkuszu,
    • <aside> - blok lewy z logo i listą to treść poboczna, uzupełnienie strony,
    • <main> - blok prawy z formularzem i tabelą to główna treść, po którą przychodzi użytkownik,
    • <div class="columns"> - kontener bez znaczenia, tylko po to, żeby bloki lewy i prawy miały wspólnego rodzica, w którym CSS ustawi dwie kolumny (klucz dopuszcza dodatkowe div).
    zdobywcy.php PHP zmienione: 12 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZDOBYWCY GÓR</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
        </header>
        <nav>
        </nav>
        <div class="columns">
            <aside>
            </aside>
            <main>
            </main>
        </div>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zdobywcy.php)
  12. Krok 12: Nagłówek

    Kryteria:

    R.2.5 Umieszczony w bloku nagłówkowym nagłówek <h1>: „Klub zdobywców gór polskich”, w bloku lewym <h3>: „razem z nami:", w bloku prawym <h2>: „Dołącz do naszego zespołu!” oraz przynajmniej jeden paragraf

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku nagłówkowego: nagłówek pierwszego stopnia o treści: „Klub zdobywców gór polskich”.

    <h1>.

    zdobywcy.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>ZDOBYWCY GÓR</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Klub zdobywców gór polskich</h1>
        </header>
        <nav>
        </nav>
        <div class="columns">
            <aside>
            </aside>
            <main>
            </main>
        </div>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zdobywcy.php)
  13. Krok 13: Nawigacja: odnośniki do zrzutów

    Kryteria:

    R.3.3 Utworzone 4 odnośniki nazwane: kwerenda1, kwerenda2, kwerenda3, kwerenda4. Po wybraniu przynajmniej jednego odnośnika otwiera/pobiera się plik graficzny

    ok. 2 min
    Polecenie z arkusza

    Zawartość bloku nawigacji: odnośniki do plików zawierających zrzuty ekranu wykonanych kwerend z treścią: kwerenda1, kwerenda2, kwerenda3, kwerenda4.

    Odnośnik może prowadzić do dowolnego pliku, także obrazu - kliknięcie otworzy zrzut w przeglądarce. Zrzuty kw1.png - kw4.png leżą w tym samym folderze, więc wystarczy sama nazwa (R.3.3). aria-label mówi czytnikowi ekranu, czego dotyczy ta nawigacja. W podglądzie kliknięcia są zablokowane.

    zdobywcy.php PHP zmienione: 5 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZDOBYWCY GÓR</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Klub zdobywców gór polskich</h1>
        </header>
        <nav aria-label="Zrzuty zapytań">
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <div class="columns">
            <aside>
            </aside>
            <main>
            </main>
        </div>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zdobywcy.php)
  14. Krok 14: Blok lewy: logo, nagłówek, lista

    Kryteria: , ,

    R.2.5 Umieszczony w bloku nagłówkowym nagłówek <h1>: „Klub zdobywców gór polskich”, w bloku lewym <h3>: „razem z nami:", w bloku prawym <h2>: „Dołącz do naszego zespołu!” oraz przynajmniej jeden paragraf

    R.2.6 W bloku lewym umieszczona grafika z logiem z tekstem alternatywnym "logo zdobywcy"

    R.2.7 W bloku lewym umieszczona jedna lista punktowana z pięcioma elementami, zastosowane znaczniki <ul>, <li>

    ok. 2 min
    Polecenie z arkusza

    Zawartość bloku lewego: Grafika logo.png z tekstem alternatywnym o treści: „logo zdobywcy”. Nagłówek trzeciego stopnia o treści: „razem z nami:”. Lista punktowana (nieuporządkowana) zawierająca elementy: „wyjazdy”, „szkolenia”, „rekreacja”, „wypoczynek”, „wyzwania”.

    Obraz z tekstem alternatywnym, <h3> i <ul> z pięcioma <li> (R.2.5, R.2.6, R.2.7).

    zdobywcy.php PHP zmienione: 9 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZDOBYWCY GÓR</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Klub zdobywców gór polskich</h1>
        </header>
        <nav aria-label="Zrzuty zapytań">
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <div class="columns">
            <aside>
                <img src="logo.png" alt="logo zdobywcy">
                <h3>razem z nami:</h3>
                <ul>
                    <li>wyjazdy</li>
                    <li>szkolenia</li>
                    <li>rekreacja</li>
                    <li>wypoczynek</li>
                    <li>wyzwania</li>
                </ul>
            </aside>
            <main>
            </main>
        </div>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zdobywcy.php)
  15. Krok 15: Blok prawy: nagłówek i formularz zapisu

    Kryteria: , , , ,

    R.2.5 Umieszczony w bloku nagłówkowym nagłówek <h1>: „Klub zdobywców gór polskich”, w bloku lewym <h3>: „razem z nami:", w bloku prawym <h2>: „Dołącz do naszego zespołu!” oraz przynajmniej jeden paragraf

    R.2.10 W bloku prawym umieszczony formularz z trzema polami edycyjnymi, polem listy rozwijalnej i przyciskiem oraz przynajmniej z jednym polem powiązana etykieta <label>

    R.3.4 Formularz wysyła dane metodą POST do tego samego pliku (poprawne również bez atrybutu action)

    R.3.5 Przycisk realizuje funkcję wysłania danych z formularza (input lub button z typem submit lub button bez typu)

    R.3.6 Pole do wpisywania adresu email jest typu email np. <input type="email">

    ok. 4 min
    Polecenie z arkusza

    Zawartość bloku prawego: Nagłówek drugiego stopnia o treści: „Dołącz do naszego zespołu!”. Paragraf zawierający tekst „Wpisz swoje dane do formularza:”. Formularz wysyłający dane do tego samego pliku metodą bezpieczną, zawierający: pole edycyjne poprzedzone etykietą „Nazwisko: ”, pole edycyjne poprzedzone etykietą „Imię: ”, pole listy rozwijanej poprzedzone etykietą „Funkcja: ”, o elementach: „uczestnik”, „przewodnik”, „zaopatrzeniowiec”, „organizator”, „ratownik”, pole edycyjne przeznaczone do wpisywania adresu email, poprzedzone etykietą „Email: ”, przycisk „Dodaj” wysyłający dane z formularza do skryptu 2.

    • method="post" bez action - dane wracają do zdobywcy.php (R.3.4); „metoda bezpieczna” w rozumieniu arkusza to POST - dane idą w treści żądania, a nie w adresie strony. W specyfikacji HTTP „bezpieczne” są co prawda metody, które niczego nie zmieniają na serwerze (GET i HEAD), ale punkt daje method="post",
    • etykiety powiązane z polami przez for/id, nazwy name to klucze w $_POST,
    • type="email" (R.3.6) i required - przeglądarka sprawdzi pola przed wysłaniem; serwer i tak sprawdzi je drugi raz, bo żądanie da się wysłać z pominięciem formularza,
    • lista funkcji <select> z pięcioma opcjami, przycisk type="submit" (R.3.5).
    zdobywcy.php PHP zmienione: 19 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZDOBYWCY GÓR</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Klub zdobywców gór polskich</h1>
        </header>
        <nav aria-label="Zrzuty zapytań">
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <div class="columns">
            <aside>
                <img src="logo.png" alt="logo zdobywcy">
                <h3>razem z nami:</h3>
                <ul>
                    <li>wyjazdy</li>
                    <li>szkolenia</li>
                    <li>rekreacja</li>
                    <li>wypoczynek</li>
                    <li>wyzwania</li>
                </ul>
            </aside>
            <main>
                <h2>Dołącz do naszego zespołu!</h2>
                <p>Wpisz swoje dane do formularza:</p>
                <form method="post">
                    <label for="surname">Nazwisko: </label>
                    <input type="text" id="surname" name="surname" required>
                    <label for="name">Imię: </label>
                    <input type="text" id="name" name="name" required>
                    <label for="role">Funkcja: </label>
                    <select id="role" name="role">
                        <option>uczestnik</option>
                        <option>przewodnik</option>
                        <option>zaopatrzeniowiec</option>
                        <option>organizator</option>
                        <option>ratownik</option>
                    </select>
                    <label for="email">Email: </label>
                    <input type="email" id="email" name="email" required>
                    <button type="submit">Dodaj</button>
                </form>
            </main>
        </div>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zdobywcy.php)
  16. Krok 16: Tabela: część nagłówkowa

    Kryteria:

    R.2.9 Pierwszy wiersz tabeli zawiera komórki nagłówkowe, zastosowane znaczniki <th>

    ok. 1 min
    Polecenie z arkusza

    Tabela o 4 kolumnach, pierwszy jej wiersz zawiera komórki nagłówkowe: Nazwisko, Imię, Funkcja, Email. Tabela jest wypełniana za pomocą skryptu 1.

    Wiersz <th> w <thead>, a pod nim puste <tbody> - tam Skrypt 1 wpisze członków koła (R.2.9).

    zdobywcy.php PHP zmienione: 12 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZDOBYWCY GÓR</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Klub zdobywców gór polskich</h1>
        </header>
        <nav aria-label="Zrzuty zapytań">
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <div class="columns">
            <aside>
                <img src="logo.png" alt="logo zdobywcy">
                <h3>razem z nami:</h3>
                <ul>
                    <li>wyjazdy</li>
                    <li>szkolenia</li>
                    <li>rekreacja</li>
                    <li>wypoczynek</li>
                    <li>wyzwania</li>
                </ul>
            </aside>
            <main>
                <h2>Dołącz do naszego zespołu!</h2>
                <p>Wpisz swoje dane do formularza:</p>
                <form method="post">
                    <label for="surname">Nazwisko: </label>
                    <input type="text" id="surname" name="surname" required>
                    <label for="name">Imię: </label>
                    <input type="text" id="name" name="name" required>
                    <label for="role">Funkcja: </label>
                    <select id="role" name="role">
                        <option>uczestnik</option>
                        <option>przewodnik</option>
                        <option>zaopatrzeniowiec</option>
                        <option>organizator</option>
                        <option>ratownik</option>
                    </select>
                    <label for="email">Email: </label>
                    <input type="email" id="email" name="email" required>
                    <button type="submit">Dodaj</button>
                </form>
                <table>
                    <thead>
                        <tr>
                            <th>Nazwisko</th>
                            <th>Imię</th>
                            <th>Funkcja</th>
                            <th>Email</th>
                        </tr>
                    </thead>
                    <tbody>
                    </tbody>
                </table>
            </main>
        </div>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zdobywcy.php)
  17. Krok 17: Stopka

    Kryteria:

    R.2.5 Umieszczony w bloku nagłówkowym nagłówek <h1>: „Klub zdobywców gór polskich”, w bloku lewym <h3>: „razem z nami:", w bloku prawym <h2>: „Dołącz do naszego zespołu!” oraz przynajmniej jeden paragraf

    ok. 1 min
    Polecenie z arkusza

    Zawartość stopki: paragraf „Stronę wykonał: ”, dalej wstawiony numer zdającego.

    Na egzaminie po dwukropku wpisujesz swój numer zdającego (PESEL).

    zdobywcy.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>ZDOBYWCY GÓR</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Klub zdobywców gór polskich</h1>
        </header>
        <nav aria-label="Zrzuty zapytań">
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <div class="columns">
            <aside>
                <img src="logo.png" alt="logo zdobywcy">
                <h3>razem z nami:</h3>
                <ul>
                    <li>wyjazdy</li>
                    <li>szkolenia</li>
                    <li>rekreacja</li>
                    <li>wypoczynek</li>
                    <li>wyzwania</li>
                </ul>
            </aside>
            <main>
                <h2>Dołącz do naszego zespołu!</h2>
                <p>Wpisz swoje dane do formularza:</p>
                <form method="post">
                    <label for="surname">Nazwisko: </label>
                    <input type="text" id="surname" name="surname" required>
                    <label for="name">Imię: </label>
                    <input type="text" id="name" name="name" required>
                    <label for="role">Funkcja: </label>
                    <select id="role" name="role">
                        <option>uczestnik</option>
                        <option>przewodnik</option>
                        <option>zaopatrzeniowiec</option>
                        <option>organizator</option>
                        <option>ratownik</option>
                    </select>
                    <label for="email">Email: </label>
                    <input type="email" id="email" name="email" required>
                    <button type="submit">Dodaj</button>
                </form>
                <table>
                    <thead>
                        <tr>
                            <th>Nazwisko</th>
                            <th>Imię</th>
                            <th>Funkcja</th>
                            <th>Email</th>
                        </tr>
                    </thead>
                    <tbody>
                    </tbody>
                </table>
            </main>
        </div>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zdobywcy.php)
  18. Etap 3: Styl CSS

    Kroki 18-22 · po tym etapie masz 31 z 40 kryteriów (pkt)

    Krok 18: Plik styles.css: zmienne, ciało strony i lista

    Kryteria: ,

    R.4.1 Ustawione dla selektora body: kolor tła # DDEEDD, krój czcionki Tahoma, wyrównanie tekstu do środka

    R.4.7 Ustawione dla selektora ul typ punktora kwadrat oraz dla selektora li wyrównanie tekstu do lewej

    ok. 2 min
    Polecenie z arkusza

    Dla selektora ciała strony: kolor tła #DDEEDD, krój czcionki Tahoma, wyrównanie tekstu do środka. Dla selektora listy punktowanej: typ punktora kwadrat. Dla selektora elementu listy: wyrównanie tekstu do lewej.

    Kolory bloków z arkusza w zmiennych CSS o nazwach mówiących, co to za barwa. Potem selektory znaczników body, ul i li - uwaga arkusza wymaga ich wprost (R.4.1, R.4.7). Punktor kwadrat to square, a elementy listy stoją przy lewej krawędzi, choć cała strona jest wyśrodkowana.

    styles.css CSS nowy plik
    :root {
        --blue: #409FFF;
        --grey: #A0A0A0;
        --sand: #E4C0A8;
        --green: #009900;
    }
    
    body {
        background-color: #DDEEDD;
        font-family: Tahoma;
        text-align: center;
    }
    
    ul {
        list-style-type: square;
    }
    
    li {
        text-align: left;
    }
    Podgląd strony po tym kroku (zdobywcy.php)
  19. Krok 19: Nagłówek i stopka

    Kryteria: , , ,

    R.4.2 Ustawiony kolor tła dla bloków: nagłówkowego #409FFF, nawigacji - biały, lewego #E4C0A8, stopki #A0A0A0

    R.4.3 Ustawiony kolor czcionki: biały dla bloku nagłówkowego i stopki oraz #009900 dla selektora a

    R.4.6 Ustawione marginesy wewnętrzne (padding) dla bloku nagłówkowego, nawigacji i stopki 10px oraz ustawione dla tabeli margin-top: 20px; lub margin: auto;

    R.4.10 Ustawiony rozmiar czcionki 120% dla bloku nagłówkowego i stopki

    ok. 2 min
    Polecenie z arkusza

    Wspólne dla bloku nagłówkowego i stopki: biały kolor czcionki, rozmiar czcionki 120%, marginesy wewnętrzne 10 px. Dodatkowo dla bloku nagłówkowego: kolor tła #409FFF. Dodatkowo dla stopki: kolor tła #A0A0A0.

    Wspólne cechy w jednej regule, kolory tła osobno (R.4.2, R.4.3, R.4.6, R.4.10).

    styles.css CSS zmienione: 15 linii
    :root {
        --blue: #409FFF;
        --grey: #A0A0A0;
        --sand: #E4C0A8;
        --green: #009900;
    }
    
    body {
        background-color: #DDEEDD;
        font-family: Tahoma;
        text-align: center;
    }
    
    ul {
        list-style-type: square;
    }
    
    li {
        text-align: left;
    }
    
    header,
    footer {
        color: white;
        font-size: 120%;
        padding: 10px;
    }
    
    header {
        background-color: var(--blue);
    }
    
    footer {
        background-color: var(--grey);
    }
    Podgląd strony po tym kroku (zdobywcy.php)
  20. Krok 20: Nawigacja: odstępy między wyrazami

    Kryteria: , , ,

    R.4.2 Ustawiony kolor tła dla bloków: nagłówkowego #409FFF, nawigacji - biały, lewego #E4C0A8, stopki #A0A0A0

    R.4.3 Ustawiony kolor czcionki: biały dla bloku nagłówkowego i stopki oraz #009900 dla selektora a

    R.4.6 Ustawione marginesy wewnętrzne (padding) dla bloku nagłówkowego, nawigacji i stopki 10px oraz ustawione dla tabeli margin-top: 20px; lub margin: auto;

    R.4.8 Ustawione dla bloku nawigacji odstępy między wyrazami 30px

    ok. 2 min
    Polecenie z arkusza

    Dla bloku nawigacji: kolor tła #FFFFFF, odstępy między wyrazami 30 px, marginesy wewnętrzne 10 px. Dla selektora odnośnika: kolor czcionki #009900.

    word-spacing powiększa odstęp między wyrazami - każdy odnośnik to jedno słowo, więc rozsuwają się o 30 px (R.4.2, R.4.3, R.4.6, R.4.8). Tu gap z siatki byłby naturalniejszy, ale klucz R.4.8 ocenia właśnie word-spacing.

    styles.css CSS zmienione: 10 linii
    :root {
        --blue: #409FFF;
        --grey: #A0A0A0;
        --sand: #E4C0A8;
        --green: #009900;
    }
    
    body {
        background-color: #DDEEDD;
        font-family: Tahoma;
        text-align: center;
    }
    
    ul {
        list-style-type: square;
    }
    
    li {
        text-align: left;
    }
    
    header,
    footer {
        color: white;
        font-size: 120%;
        padding: 10px;
    }
    
    header {
        background-color: var(--blue);
    }
    
    footer {
        background-color: var(--grey);
    }
    
    nav {
        background-color: #FFFFFF;
        word-spacing: 30px;
        padding: 10px;
    }
    
    a {
        color: var(--green);
    }
    Podgląd strony po tym kroku (zdobywcy.php)
  21. Krok 21: Bloki lewy i prawy: siatka dwóch kolumn

    Kryteria: , , ,

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

    R.4.2 Ustawiony kolor tła dla bloków: nagłówkowego #409FFF, nawigacji - biały, lewego #E4C0A8, stopki #A0A0A0

    R.4.4 Ustawiona wysokość bloku lewego i prawego 650px oraz szerokość bloku lewego 30%, bloku prawego 70% (width lub grid-template-columns lub flex), selektora img 90%

    R.4.5 Ustawione paski przewijania na wartość auto dla bloku prawego

    ok. 3 min
    Polecenie z arkusza

    Dla bloku lewego: kolor tła #E4C0A8, szerokość 30%, wysokość 650 px. Dla bloku prawego: szerokość 70%, wysokość 650 px, paski przewijania widoczne tylko gdy tekst nie mieści się w bloku.

    Kontener .columns staje się siatką o dwóch kolumnach: grid-template-columns: 30% 70% to szerokości bloku lewego i prawego naraz - klucz R.4.4 przyjmuje je zapisane właśnie tu. Bloki stają obok siebie bez float, a stopka pod nimi bez clear, bo siatka nie wyjmuje elementów z przepływu (R.3.1).

    Wysokość 650 px dla obu bloków, tło lewego ze zmiennej (R.4.2, R.4.4), overflow: auto dla prawego - pasek tylko przy długiej tabeli (R.4.5).

    Typowe błędy

    width: 30% i width: 70% dopisane do bloków w siatce 30% 70% zwężają je do 9% i 49% strony - procent szerokości elementu siatki liczy się od jego kolumny.

    styles.css CSS zmienione: 15 linii
    :root {
        --blue: #409FFF;
        --grey: #A0A0A0;
        --sand: #E4C0A8;
        --green: #009900;
    }
    
    body {
        background-color: #DDEEDD;
        font-family: Tahoma;
        text-align: center;
    }
    
    ul {
        list-style-type: square;
    }
    
    li {
        text-align: left;
    }
    
    header,
    footer {
        color: white;
        font-size: 120%;
        padding: 10px;
    }
    
    header {
        background-color: var(--blue);
    }
    
    footer {
        background-color: var(--grey);
    }
    
    nav {
        background-color: #FFFFFF;
        word-spacing: 30px;
        padding: 10px;
    }
    
    a {
        color: var(--green);
    }
    
    .columns {
        display: grid;
        grid-template-columns: 30% 70%;
    }
    
    aside {
        background-color: var(--sand);
        height: 650px;
    }
    
    main {
        height: 650px;
        overflow: auto;
    }
    Podgląd strony po tym kroku (zdobywcy.php)
  22. Krok 22: Tabela: ramki i marginesy, obraz

    Kryteria: , ,

    R.4.4 Ustawiona wysokość bloku lewego i prawego 650px oraz szerokość bloku lewego 30%, bloku prawego 70% (width lub grid-template-columns lub flex), selektora img 90%

    R.4.6 Ustawione marginesy wewnętrzne (padding) dla bloku nagłówkowego, nawigacji i stopki 10px oraz ustawione dla tabeli margin-top: 20px; lub margin: auto;

    R.4.9 Ustawione dla tabeli i komórek tabeli obramowanie 1px solid black

    ok. 2 min
    Polecenie z arkusza

    Wspólne dla tabeli i wszystkich komórek tabeli: obramowanie ciągłe koloru czarnego o grubości 1 px. Dodatkowo dla tabeli: margines zewnętrzny górny 20 px, marginesy zewnętrzne automatycznie wyliczane przez przeglądarkę. Dla selektora obrazu: szerokość 90%.

    „Wszystkie komórki” to th i td (R.4.9). Marginesy tabeli w jednym skrócie margin: 20px auto 0 - kolejno: góra 20 px, boki auto (wyśrodkowanie), dół 0 - zamiast dwóch linii, w których kolejność decyduje o wyniku (R.4.6). Obraz 90% szerokości bloku (R.4.4).

    Typowe błędy

    Dwie osobne linie w złej kolejności (margin-top: 20px; przed margin: auto;) - skrót nadpisze górny margines na auto, czyli 0.

    styles.css CSS zmienione: 14 linii
    :root {
        --blue: #409FFF;
        --grey: #A0A0A0;
        --sand: #E4C0A8;
        --green: #009900;
    }
    
    body {
        background-color: #DDEEDD;
        font-family: Tahoma;
        text-align: center;
    }
    
    ul {
        list-style-type: square;
    }
    
    li {
        text-align: left;
    }
    
    header,
    footer {
        color: white;
        font-size: 120%;
        padding: 10px;
    }
    
    header {
        background-color: var(--blue);
    }
    
    footer {
        background-color: var(--grey);
    }
    
    nav {
        background-color: #FFFFFF;
        word-spacing: 30px;
        padding: 10px;
    }
    
    a {
        color: var(--green);
    }
    
    .columns {
        display: grid;
        grid-template-columns: 30% 70%;
    }
    
    aside {
        background-color: var(--sand);
        height: 650px;
    }
    
    main {
        height: 650px;
        overflow: auto;
    }
    
    table,
    th,
    td {
        border: 1px solid black;
    }
    
    table {
        margin: 20px auto 0;
    }
    
    img {
        width: 90%;
    }
    Podgląd strony po tym kroku (zdobywcy.php)
  23. Etap 4: Skrypty PHP

    Kroki 23-28 · po tym etapie masz 40 z 40 kryteriów (pkt)

    Krok 23: Połączenie PDO na górze pliku

    Kryteria: , , ,

    R.5.1 Skrypt jest napisany w języku PHP i zawiera przynajmniej jedną zgodną z treścią zadania instrukcję

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

    R.5.3 Ostatnią operacją na bazie jest jej zamknięcie

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

    ok. 4 min
    Polecenie z arkusza

    Napisane w języku PHP. Należy stosować znaczące nazewnictwo zmiennych i funkcji w języku polskim lub angielskim. Łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie zdobywcy. Na końcu jest zamykane połączenie z serwerem.

    Oba skrypty będą stały przed <!DOCTYPE html>, w jednym bloku z jednym połączeniem: najpierw zapis z formularza, potem odczyt listy - dzięki tej kolejności nowa osoba od razu jest w tabeli.

    • DSN 'mysql:host=localhost;dbname=zdobywcy;charset=utf8mb4', użytkownik root, puste hasło (R.5.1, R.5.2),
    • ERRMODE_EXCEPTION i FETCH_ASSOC - wyjątki przy błędach, wiersze z nazwami kolumn,
    • $pdo = null zamyka połączenie; zapytania trafią przed tę linię (R.5.3),
    • catch (PDOException $exception) - szczegóły do dziennika serwera, dla użytkownika ogólny komunikat,
    • funkcja e() - krótka nazwa htmlspecialchars(..., ENT_QUOTES).

    Nazwy po angielsku mówią, co przechowują (R.5.8). Od tego kroku podgląd to wynik prawdziwego uruchomienia.

    zdobywcy.php PHP zmienione: 20 linii
    <?php
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    $members = [];
    $message = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=zdobywcy;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $message = 'Baza klubu jest chwilowo niedostępna. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZDOBYWCY GÓR</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Klub zdobywców gór polskich</h1>
        </header>
        <nav aria-label="Zrzuty zapytań">
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <div class="columns">
            <aside>
                <img src="logo.png" alt="logo zdobywcy">
                <h3>razem z nami:</h3>
                <ul>
                    <li>wyjazdy</li>
                    <li>szkolenia</li>
                    <li>rekreacja</li>
                    <li>wypoczynek</li>
                    <li>wyzwania</li>
                </ul>
            </aside>
            <main>
                <h2>Dołącz do naszego zespołu!</h2>
                <p>Wpisz swoje dane do formularza:</p>
                <form method="post">
                    <label for="surname">Nazwisko: </label>
                    <input type="text" id="surname" name="surname" required>
                    <label for="name">Imię: </label>
                    <input type="text" id="name" name="name" required>
                    <label for="role">Funkcja: </label>
                    <select id="role" name="role">
                        <option>uczestnik</option>
                        <option>przewodnik</option>
                        <option>zaopatrzeniowiec</option>
                        <option>organizator</option>
                        <option>ratownik</option>
                    </select>
                    <label for="email">Email: </label>
                    <input type="email" id="email" name="email" required>
                    <button type="submit">Dodaj</button>
                </form>
                <table>
                    <thead>
                        <tr>
                            <th>Nazwisko</th>
                            <th>Imię</th>
                            <th>Funkcja</th>
                            <th>Email</th>
                        </tr>
                    </thead>
                    <tbody>
                    </tbody>
                </table>
            </main>
        </div>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zdobywcy.php)
  24. Krok 24: Skrypt 1: zapytanie 2

    Kryteria:

    R.5.4 Skrypt 1 zawiera instrukcję wysyłającą do bazy danych zapytanie 2

    ok. 1 min
    Polecenie z arkusza

    Skrypt 1: wysyła do bazy danych zapytanie 2.

    $pdo->query(...) wysyła zapytanie 2, a ->fetchAll() odbiera 15 osób do tablicy $members (R.5.4). Zwykłe query wystarcza - w tym zapytaniu nie ma danych od użytkownika.

    zdobywcy.php PHP zmienione: 2 linie
    <?php
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    $members = [];
    $message = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=zdobywcy;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        $members = $pdo->query('SELECT nazwisko, imie, funkcja, email FROM osoby')->fetchAll();
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $message = 'Baza klubu jest chwilowo niedostępna. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZDOBYWCY GÓR</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Klub zdobywców gór polskich</h1>
        </header>
        <nav aria-label="Zrzuty zapytań">
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <div class="columns">
            <aside>
                <img src="logo.png" alt="logo zdobywcy">
                <h3>razem z nami:</h3>
                <ul>
                    <li>wyjazdy</li>
                    <li>szkolenia</li>
                    <li>rekreacja</li>
                    <li>wypoczynek</li>
                    <li>wyzwania</li>
                </ul>
            </aside>
            <main>
                <h2>Dołącz do naszego zespołu!</h2>
                <p>Wpisz swoje dane do formularza:</p>
                <form method="post">
                    <label for="surname">Nazwisko: </label>
                    <input type="text" id="surname" name="surname" required>
                    <label for="name">Imię: </label>
                    <input type="text" id="name" name="name" required>
                    <label for="role">Funkcja: </label>
                    <select id="role" name="role">
                        <option>uczestnik</option>
                        <option>przewodnik</option>
                        <option>zaopatrzeniowiec</option>
                        <option>organizator</option>
                        <option>ratownik</option>
                    </select>
                    <label for="email">Email: </label>
                    <input type="email" id="email" name="email" required>
                    <button type="submit">Dodaj</button>
                </form>
                <table>
                    <thead>
                        <tr>
                            <th>Nazwisko</th>
                            <th>Imię</th>
                            <th>Funkcja</th>
                            <th>Email</th>
                        </tr>
                    </thead>
                    <tbody>
                    </tbody>
                </table>
            </main>
        </div>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

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

  25. Krok 25: Skrypt 1: wiersze tabeli

    Kryteria: ,

    R.2.8 W bloku prawym umieszczona tabela o 4 kolumnach (w skrypcie lub HTML) zastosowane znaczniki <table>, <tr>, <td>

    R.5.5 Skrypt 1 wyświetla w wierszach tabeli dane zwrócone zapytaniem, dane są wpisane do odpowiednich pól tabeli

    ok. 3 min
    Polecenie z arkusza

    W każdym wierszu tabeli, w odpowiednich komórkach, wyświetlane są zwrócone zapytaniem kolejne wiersze z bazy.

    W <tbody> pętla foreach ($members as $member): i wiersz z czterema komórkami - wartości po nazwach kolumn, każda przez e(); e-mail od użytkownika może zawierać znaki, które przeglądarka wzięłaby za HTML. Podgląd: 15 członków koła (R.2.8, R.5.5).

    zdobywcy.php PHP zmienione: 8 linii
    <?php
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    $members = [];
    $message = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=zdobywcy;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        $members = $pdo->query('SELECT nazwisko, imie, funkcja, email FROM osoby')->fetchAll();
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $message = 'Baza klubu jest chwilowo niedostępna. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZDOBYWCY GÓR</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Klub zdobywców gór polskich</h1>
        </header>
        <nav aria-label="Zrzuty zapytań">
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <div class="columns">
            <aside>
                <img src="logo.png" alt="logo zdobywcy">
                <h3>razem z nami:</h3>
                <ul>
                    <li>wyjazdy</li>
                    <li>szkolenia</li>
                    <li>rekreacja</li>
                    <li>wypoczynek</li>
                    <li>wyzwania</li>
                </ul>
            </aside>
            <main>
                <h2>Dołącz do naszego zespołu!</h2>
                <p>Wpisz swoje dane do formularza:</p>
                <form method="post">
                    <label for="surname">Nazwisko: </label>
                    <input type="text" id="surname" name="surname" required>
                    <label for="name">Imię: </label>
                    <input type="text" id="name" name="name" required>
                    <label for="role">Funkcja: </label>
                    <select id="role" name="role">
                        <option>uczestnik</option>
                        <option>przewodnik</option>
                        <option>zaopatrzeniowiec</option>
                        <option>organizator</option>
                        <option>ratownik</option>
                    </select>
                    <label for="email">Email: </label>
                    <input type="email" id="email" name="email" required>
                    <button type="submit">Dodaj</button>
                </form>
                <table>
                    <thead>
                        <tr>
                            <th>Nazwisko</th>
                            <th>Imię</th>
                            <th>Funkcja</th>
                            <th>Email</th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php foreach ($members as $member): ?>
                            <tr>
                                <td><?= e($member['nazwisko']) ?></td>
                                <td><?= e($member['imie']) ?></td>
                                <td><?= e($member['funkcja']) ?></td>
                                <td><?= e($member['email']) ?></td>
                            </tr>
                        <?php endforeach; ?>
                    </tbody>
                </table>
            </main>
        </div>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zdobywcy.php)
  26. Krok 26: Skrypt 2: dopisanie osoby z formularza

    Kryteria: ,

    R.5.6 Skrypt 2 wysyła do bazy danych zmodyfikowane zapytanie 4, w ten sposób, że dane do zapytania są pobrane z formularza (sprawdzić w phpMyAdmin )

    R.5.7 Skrypt 2 zawiera instrukcję pobierającą dane z tablicy globalnej odpowiadającej zastosowanej metodzie formularza ($_POST, $_GET, $_REQUEST) oraz jest zabezpieczony na wypadek braku danych w formularzu (wystarczy, że jest sprawdzone dla jednego pola)

    ok. 5 min
    Polecenie z arkusza

    Skrypt 2 wysyła do bazy danych zmodyfikowane zapytanie 4, w ten sposób, że dane do zapytania są pobrane z formularza. Zapytanie jest wysyłane tylko wtedy, gdy podano dane w formularzu.

    Skrypt 2 stoi przed Skryptem 1 w tym samym bloku try:

    • field('surname') - mała funkcja, która czyta pole z $_POST (tablicy, której szuka klucz R.5.7), daje pusty napis, gdy pola nie ma, i obcina spacje; spreparowane żądanie z tablicą zamiast tekstu też kończy się pustym napisem, a nie błędem,
    • filter_var(..., FILTER_VALIDATE_EMAIL) - poprawny adres albo false; type="email" sprawdza tylko przeglądarka,
    • funkcja musi być jedną z ROLES - stałej z pięcioma funkcjami z arkusza (in_array z true porównuje też typ),
    • dopiero gdy wszystko się zgadza, zapytanie przygotowane: zapytanie 4 z parametrami :surname, :name, :role, :email, a wartości osobno w execute (R.5.6, R.5.7).

    Sprawdzone: dane z arkusza dopisują Miłosza (id 16), nazwisko O'Neill zapisuje się poprawnie (w wersji podstawowej apostrof kończy się błędem SQL), a zły e-mail, funkcja spoza listy i same spacje nie dopisują niczego. Podgląd: stan po wysłaniu formularza z danymi M., Miłosz, uczestnik.

    zdobywcy.php PHP zmienione: 23 linie
    <?php
    const ROLES = ['uczestnik', 'przewodnik', 'zaopatrzeniowiec', 'organizator', 'ratownik'];
    
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    function field(string $name): string
    {
        $value = $_POST[$name] ?? '';
        return is_string($value) ? trim($value) : '';
    }
    
    $members = [];
    $message = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=zdobywcy;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            $surname = field('surname');
            $name = field('name');
            $role = field('role');
            $email = filter_var(field('email'), FILTER_VALIDATE_EMAIL);
    
            if ($surname !== '' && $name !== '' && $email !== false && in_array($role, ROLES, true)) {
                $statement = $pdo->prepare(
                    'INSERT INTO osoby (nazwisko, imie, funkcja, email)
                     VALUES (:surname, :name, :role, :email)'
                );
                $statement->execute(['surname' => $surname, 'name' => $name, 'role' => $role, 'email' => $email]);
            }
        }
    
        $members = $pdo->query('SELECT nazwisko, imie, funkcja, email FROM osoby')->fetchAll();
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $message = 'Baza klubu jest chwilowo niedostępna. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZDOBYWCY GÓR</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Klub zdobywców gór polskich</h1>
        </header>
        <nav aria-label="Zrzuty zapytań">
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <div class="columns">
            <aside>
                <img src="logo.png" alt="logo zdobywcy">
                <h3>razem z nami:</h3>
                <ul>
                    <li>wyjazdy</li>
                    <li>szkolenia</li>
                    <li>rekreacja</li>
                    <li>wypoczynek</li>
                    <li>wyzwania</li>
                </ul>
            </aside>
            <main>
                <h2>Dołącz do naszego zespołu!</h2>
                <p>Wpisz swoje dane do formularza:</p>
                <form method="post">
                    <label for="surname">Nazwisko: </label>
                    <input type="text" id="surname" name="surname" required>
                    <label for="name">Imię: </label>
                    <input type="text" id="name" name="name" required>
                    <label for="role">Funkcja: </label>
                    <select id="role" name="role">
                        <option>uczestnik</option>
                        <option>przewodnik</option>
                        <option>zaopatrzeniowiec</option>
                        <option>organizator</option>
                        <option>ratownik</option>
                    </select>
                    <label for="email">Email: </label>
                    <input type="email" id="email" name="email" required>
                    <button type="submit">Dodaj</button>
                </form>
                <table>
                    <thead>
                        <tr>
                            <th>Nazwisko</th>
                            <th>Imię</th>
                            <th>Funkcja</th>
                            <th>Email</th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php foreach ($members as $member): ?>
                            <tr>
                                <td><?= e($member['nazwisko']) ?></td>
                                <td><?= e($member['imie']) ?></td>
                                <td><?= e($member['funkcja']) ?></td>
                                <td><?= e($member['email']) ?></td>
                            </tr>
                        <?php endforeach; ?>
                    </tbody>
                </table>
            </main>
        </div>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zdobywcy.php)
  27. Krok 27: Przekierowanie po zapisie i potwierdzenie

    ok. 4 min
    Polecenie z arkusza

    Zapytanie jest wysyłane tylko wtedy, gdy podano dane w formularzu.

    Po udanym zapisie skrypt nie buduje strony, tylko odsyła przeglądarkę pod adres strony - wzorzec POST/Redirect/GET:

    • $pdo = null - zamknięcie połączenia zostaje ostatnią operacją na bazie także w tej gałęzi,
    • header('Location: zdobywcy.php?added=1', true, 303) - kod 303 każe przeglądarce pobrać wskazany adres zwykłym GET; dopisek ?added=1 niesie informację o udanym zapisie,
    • exit - header tylko ustawia nagłówek i nie zatrzymuje skryptu; bez exit PHP wykonywałby dalszy kod.

    Odświeżenie strony (F5) powtarza teraz GET, a nie wysłanie formularza - sprawdzone: po zapisie i dwóch odświeżeniach Miłosz jest w tabeli raz (w wersji podstawowej F5 dopisuje go drugi raz). Na stronie z ?added=1 pod akapitem stoi potwierdzenie z role="status", które czytnik ekranu przeczyta sam. Wszystko to dzieje się przed pierwszym znakiem HTML-u - header po wypisaniu czegokolwiek kończy się ostrzeżeniem „headers already sent”.

    zdobywcy.php PHP zmienione: 7 linii
    <?php
    const ROLES = ['uczestnik', 'przewodnik', 'zaopatrzeniowiec', 'organizator', 'ratownik'];
    
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    function field(string $name): string
    {
        $value = $_POST[$name] ?? '';
        return is_string($value) ? trim($value) : '';
    }
    
    $members = [];
    $message = isset($_GET['added']) ? 'Dziękujemy - nowa osoba jest już w zespole.' : '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=zdobywcy;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            $surname = field('surname');
            $name = field('name');
            $role = field('role');
            $email = filter_var(field('email'), FILTER_VALIDATE_EMAIL);
    
            if ($surname !== '' && $name !== '' && $email !== false && in_array($role, ROLES, true)) {
                $statement = $pdo->prepare(
                    'INSERT INTO osoby (nazwisko, imie, funkcja, email)
                     VALUES (:surname, :name, :role, :email)'
                );
                $statement->execute(['surname' => $surname, 'name' => $name, 'role' => $role, 'email' => $email]);
                $pdo = null;
                header('Location: zdobywcy.php?added=1', true, 303);
                exit;
            }
        }
    
        $members = $pdo->query('SELECT nazwisko, imie, funkcja, email FROM osoby')->fetchAll();
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $message = 'Baza klubu jest chwilowo niedostępna. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZDOBYWCY GÓR</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Klub zdobywców gór polskich</h1>
        </header>
        <nav aria-label="Zrzuty zapytań">
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <div class="columns">
            <aside>
                <img src="logo.png" alt="logo zdobywcy">
                <h3>razem z nami:</h3>
                <ul>
                    <li>wyjazdy</li>
                    <li>szkolenia</li>
                    <li>rekreacja</li>
                    <li>wypoczynek</li>
                    <li>wyzwania</li>
                </ul>
            </aside>
            <main>
                <h2>Dołącz do naszego zespołu!</h2>
                <p>Wpisz swoje dane do formularza:</p>
                <?php if ($message !== ''): ?>
                    <p role="status"><strong><?= e($message) ?></strong></p>
                <?php endif; ?>
                <form method="post">
                    <label for="surname">Nazwisko: </label>
                    <input type="text" id="surname" name="surname" required>
                    <label for="name">Imię: </label>
                    <input type="text" id="name" name="name" required>
                    <label for="role">Funkcja: </label>
                    <select id="role" name="role">
                        <option>uczestnik</option>
                        <option>przewodnik</option>
                        <option>zaopatrzeniowiec</option>
                        <option>organizator</option>
                        <option>ratownik</option>
                    </select>
                    <label for="email">Email: </label>
                    <input type="email" id="email" name="email" required>
                    <button type="submit">Dodaj</button>
                </form>
                <table>
                    <thead>
                        <tr>
                            <th>Nazwisko</th>
                            <th>Imię</th>
                            <th>Funkcja</th>
                            <th>Email</th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php foreach ($members as $member): ?>
                            <tr>
                                <td><?= e($member['nazwisko']) ?></td>
                                <td><?= e($member['imie']) ?></td>
                                <td><?= e($member['funkcja']) ?></td>
                                <td><?= e($member['email']) ?></td>
                            </tr>
                        <?php endforeach; ?>
                    </tbody>
                </table>
            </main>
        </div>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zdobywcy.php)
  28. Krok 28: Formularz pamięta dane, lista funkcji z jednej stałej

    ok. 4 min
    Polecenie z arkusza

    Pole listy rozwijanej poprzedzone etykietą „Funkcja: ”, o elementach: „uczestnik”, „przewodnik”, „zaopatrzeniowiec”, „organizator”, „ratownik”.

    Gdy czegoś brakuje, zapis się nie odbywa, a formularz wraca z komunikatem „Uzupełnij nazwisko, imię i poprawny adres e-mail.” i z wpisanymi danymi - tablica $form trzyma wartości pól, a szablon wstawia je w value (przez e()) i zaznacza wybraną funkcję atrybutem selected. Sprawdzone: przy adresie m.milosz@ w polach zostają „M.”, „Miłosz”, a na liście „ratownik”.

    Opcje listy powstają teraz pętlą z tej samej stałej ROLES, którą sprawdza Skrypt 2 - dopisanie szóstej funkcji to jedna zmiana w jednym miejscu, a lista i sprawdzenie nie mogą się rozjechać. Nazwy funkcji nadal widać w kodzie strony (R.2.10). Awaria bazy daje komunikat w tym samym miejscu, bez nazwy bazy i kodu błędu. Podgląd: strona po zapisie i przekierowaniu.

    zdobywcy.php PHP zmienione: 9 linii
    <?php
    const ROLES = ['uczestnik', 'przewodnik', 'zaopatrzeniowiec', 'organizator', 'ratownik'];
    
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    function field(string $name): string
    {
        $value = $_POST[$name] ?? '';
        return is_string($value) ? trim($value) : '';
    }
    
    $members = [];
    $form = ['surname' => '', 'name' => '', 'role' => ROLES[0], 'email' => ''];
    $message = isset($_GET['added']) ? 'Dziękujemy - nowa osoba jest już w zespole.' : '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=zdobywcy;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        if ($_SERVER['REQUEST_METHOD'] === 'POST') {
            $surname = field('surname');
            $name = field('name');
            $role = field('role');
            $email = filter_var(field('email'), FILTER_VALIDATE_EMAIL);
            $form = ['surname' => $surname, 'name' => $name, 'role' => $role, 'email' => field('email')];
    
            if ($surname !== '' && $name !== '' && $email !== false && in_array($role, ROLES, true)) {
                $statement = $pdo->prepare(
                    'INSERT INTO osoby (nazwisko, imie, funkcja, email)
                     VALUES (:surname, :name, :role, :email)'
                );
                $statement->execute(['surname' => $surname, 'name' => $name, 'role' => $role, 'email' => $email]);
                $pdo = null;
                header('Location: zdobywcy.php?added=1', true, 303);
                exit;
            }
            $message = 'Uzupełnij nazwisko, imię i poprawny adres e-mail.';
        }
    
        $members = $pdo->query('SELECT nazwisko, imie, funkcja, email FROM osoby')->fetchAll();
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $message = 'Baza klubu jest chwilowo niedostępna. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>ZDOBYWCY GÓR</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Klub zdobywców gór polskich</h1>
        </header>
        <nav aria-label="Zrzuty zapytań">
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <div class="columns">
            <aside>
                <img src="logo.png" alt="logo zdobywcy">
                <h3>razem z nami:</h3>
                <ul>
                    <li>wyjazdy</li>
                    <li>szkolenia</li>
                    <li>rekreacja</li>
                    <li>wypoczynek</li>
                    <li>wyzwania</li>
                </ul>
            </aside>
            <main>
                <h2>Dołącz do naszego zespołu!</h2>
                <p>Wpisz swoje dane do formularza:</p>
                <?php if ($message !== ''): ?>
                    <p role="status"><strong><?= e($message) ?></strong></p>
                <?php endif; ?>
                <form method="post">
                    <label for="surname">Nazwisko: </label>
                    <input type="text" id="surname" name="surname" value="<?= e($form['surname']) ?>" required>
                    <label for="name">Imię: </label>
                    <input type="text" id="name" name="name" value="<?= e($form['name']) ?>" required>
                    <label for="role">Funkcja: </label>
                    <select id="role" name="role">
                        <?php foreach (ROLES as $roleName): ?>
                            <option<?= $roleName === $form['role'] ? ' selected' : '' ?>><?= e($roleName) ?></option>
                        <?php endforeach; ?>
                    </select>
                    <label for="email">Email: </label>
                    <input type="email" id="email" name="email" value="<?= e($form['email']) ?>" required>
                    <button type="submit">Dodaj</button>
                </form>
                <table>
                    <thead>
                        <tr>
                            <th>Nazwisko</th>
                            <th>Imię</th>
                            <th>Funkcja</th>
                            <th>Email</th>
                        </tr>
                    </thead>
                    <tbody>
                        <?php foreach ($members as $member): ?>
                            <tr>
                                <td><?= e($member['nazwisko']) ?></td>
                                <td><?= e($member['imie']) ?></td>
                                <td><?= e($member['funkcja']) ?></td>
                                <td><?= e($member['email']) ?></td>
                            </tr>
                        <?php endforeach; ?>
                    </tbody>
                </table>
            </main>
        </div>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zdobywcy.php)
  29. Etap 5: Porównanie z wersją podstawową

    Krok 29 · po tym etapie masz 40 z 40 kryteriów (pkt)

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

    ok. 2 min

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

    wersja podstawowawersja nowoczesna
    układfloat: left w sekcjach + clear: both<div class="columns"> z siatką 30% 70%
    znacznikidwie <section><aside> (logo, lista) i <main> (formularz, tabela)
    bazamysqli w dwóch blokach PHP rozrzuconych po HTML-uPDO w jednym bloku nad HTML-em, połączenie zamknięte przed stroną
    zapis!empty i dane wklejone w zapytaniefield() z $_POST, filter_var, ROLES, zapytanie przygotowane
    po zapisieF5 dopisuje drugi raz (w dobrych praktykach PRG)przekierowanie 303 i potwierdzenie od początku
    zły e-mail, apostrofzapis bez sprawdzenia, apostrof to błąd SQLkomunikat, formularz z danymi, apostrof zapisany

    Na egzaminie wersja podstawowa jest krótsza i stawia Skrypt 2 tam, gdzie widać go w treści strony. Nowoczesna to sposób, w jaki pisze się formularze w prawdziwych serwisach - z jednym zastrzeżeniem: header() działa tylko przed pierwszym znakiem HTML-u, więc kto przeniesie logikę na górę pliku, musi uważać, żeby nad <?php nie została choćby pusta linia.

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

  30. Etap 6: Oddanie pracy

    Krok 30 · po tym etapie masz 40 z 40 kryteriów (pkt)

    Krok 30: 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 folderze z numerem zdającego, powinny znajdować się pliki: import.png, kw1.png, kw2.png, kw3.png, kw4.png, kwerendy.txt, logo.png, styles.css, zdobywcy.php, przeglądarka.txt oraz ewentualnie inne przygotowane pliki.

    W przeglądarka.txt wpisz nazwę przeglądarki. Zrzuty kw1.png - kw4.png muszą leżeć obok strony - prowadzą do nich odnośniki w nawigacji (R.3.3). Nagraj płytę i sprawdź jej odczyt.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

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

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.