Przejdź do głównej treści
INF.03-10-25.06-SGCzerwiec 2025 · 47 kroków · ok. 84 min · 39 kryteriów z klucza

Rozwiązanie krok po kroku: Portal szkoleniowy z bazą danych (Wersja podstawowa)

Rozwiązanie buduje portal szkoleniowy: baza szkolenia z trzema tabelami i czterema operacjami (sortowanie, prosty SELECT, INSERT z automatycznym kluczem, COUNT z GROUP BY i aliasem), zdjęcie kursu przeskalowane do 150 px, strona index.php z tabelą kursów wypełnianą z bazy i formularzem zapisu (trzy pola, lista rozwijana, przycisk), style w styl.css z cieniem bloku i odstępem między literami oraz trzy skrypty PHP: tabela kursów ze zdjęciami, lista kursów do listy rozwijanej i zapis uczestnika do bazy z komunikatem.

Kroki idą zdanie po zdaniu za treścią arkusza - każdy zaczyna się poleceniem z arkusza, a kod pokazuje dokładnie te linie, które je spełniają. Kod PHP jest najpierw pisany funkcjami z tabeli 1 arkusza (proceduralne mysqli), a na końcu zostaje przerobiony na wersję z prawdziwych projektów: zapytanie przygotowane z bindowaniem dla INSERT, escapowanie danych i walidacja tego, co przychodzi z formularza. W tym arkuszu nie ma JavaScriptu.

To jest wersja podstawowa. W zakładce „Nowoczesny kod (grid, PDO)” jest drugi sposób rozwiązania tego samego arkusza: układ na siatce CSS i PHP na PDO z zapytaniami przygotowanymi od pierwszej linii - obie wersje są zgodne z kluczem.

Baza i zdjęcia pochodzą z prawdziwego archiwum pliki9 (załącznik do pobrania na stronie arkusza, hasło w treści zadania). Zrzuty wyników zapytań są odtworzone z tej bazy - na egzaminie robisz je sam, z całego ekranu.

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

  1. Etap 1: Baza danych

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

    Krok 1: Baza danych szkolenia

    Kryteria:

    R.1.1 Wykonany import tabel do bazy danych szkolenia , czynność udokumentowana w pliku import.png

    ok. 2 min
    Polecenie z arkusza

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

    W przeglądarce otwierasz http://localhost/phpmyadmin, klikasz Nowa w lewym panelu, wpisujesz nazwę szkolenia i z listy metod porównywania wybierasz utf8_polish_ci. Uwaga na nazwy: baza nazywa się szkolenia, a plik do importu szkolimy.sql - to celowo różne słowa, żeby sprawdzić, czy czytasz uważnie.

    Typowe błędy

    Nazwa bazy musi być dokładnie szkolenia - skrypty PHP łączą się z bazą po nazwie, więc literówka odetnie punkty także za skrypty (R.5.1).

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

    Kryteria:

    R.1.1 Wykonany import tabel do bazy danych szkolenia , czynność udokumentowana w pliku import.png

    ok. 2 min
    Polecenie z arkusza

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

    Wejdź do bazy szkolenia (musi być zaznaczona w lewym panelu), karta Import, Wybierz plik → szkolimy.sql z rozpakowanego archiwum, Wykonaj.

    Po imporcie są trzy tabele z ilustracji 1: kursy (kod, nazwa, cena, max_liczba_miejsc - 8 kursów; kod to liczba 1-8 i zarazem nazwa pliku zdjęcia), uczestnicy (id_uczestnika, imie, nazwisko, wiek - 29 osób) i kursy_uczestnicy (kod_kursu, id_uczestnika, data - 46 zapisów). Tabela łącząca kursy_uczestnicy nie ma własnego identyfikatora - jej klucz to para kluczy obcych z datą.

    Typowe błędy

    Import bez wcześniejszego wejścia do bazy szkolenia tworzy tabele w innej bazie (np. test) - skrypty nie znajdą wtedy tabeli kursy.

  3. Krok 3: Zrzut ekranu import.png

    Kryteria:

    R.1.1 Wykonany import tabel do bazy danych szkolenia , czynność udokumentowana w pliku import.png

    ok. 2 min
    Polecenie z arkusza

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

    Klawisz Print Screen (cały ekran, nie Alt+PrtScr, który łapie tylko aktywne okno), wklejasz do edytora grafiki i zapisujesz jako import.png w folderze z numerem zdającego. Uwaga na formaty w tym arkuszu: zrzut importu w PNG, zrzuty kwerend w JPEG - odwrotnie niż w wielu innych arkuszach. Na zrzucie ma być widać komunikat phpMyAdmin o pomyślnym imporcie albo trzy tabele w bazie.

    Typowe błędy

    Wykadrowany zrzut (samo okno przeglądarki, bez paska zadań) nie jest oceniany - klucz mówi to wprost. Zły format pliku to też strata punktu.

  4. Krok 4: Zapytanie 1: kursy od najtańszego

    Kryteria: ,

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

    R.1.3 Wykonane zapytanie 1 wybierające jedynie kod, nazwę i cenę kursów posortowane rosnąco według ceny. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT kod, nazwa, cena FROM kursy ORDER BY cena; (ASC opcjonalne) oraz wynik działania zapytania udokumentowany zrzutem, na którym widocznych jest dokładnie 8 rekordów dla wszystkich kursów, pierwszy rekord 5 gastronomia 1000, ostatni 7 spawanie 3200

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 1: wybierające jedynie kod, nazwę i cenę kursów posortowane rosnąco według ceny.

    Zakładka SQL w phpMyAdmin (przy zaznaczonej bazie szkolenia). Klauzula po klauzuli:

    • SELECT kod, nazwa, cena - trzy kolumny w kolejności z arkusza; „jedynie” wyklucza SELECT * (nie chcesz max_liczba_miejsc).
    • FROM kursy - tabela kursów.
    • ORDER BY cena - sortowanie po cenie, rosnąco (domyślne ASC).

    Wynik: 8 kursów, pierwszy 5 gastronomia 1000, ostatni 7 spawanie 3200 - dokładnie jak w kluczu. Zapytanie kopiujesz do pliku kwerendy.txt i robisz zrzut: kw1.jpg. To samo zapytanie wyśle skrypt 1 do tabeli na stronie - kolejność kolumn (kod, nazwa, cena) jest w nim istotna.

    Typowe błędy

    ORDER BY nazwa to nie to samo - arkusz każe sortować według ceny. Klucz sprawdza pierwszy i ostatni rekord.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT kod, nazwa, cena FROM kursy ORDER BY cena;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: nazwy wszystkich kursów

    Kryteria:

    R.1.4 Wykonane zapytanie 2 wybierające jedynie nazwy wszystkich kursów. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa FROM kursy; oraz wynik działania zapytania udokumentowany zrzutem, na którym widocznych jest dokładnie 8 rekordów: pierwszy jest język angielski, ostatni rysunek techniczny

    ok. 1 min
    Polecenie z arkusza

    Zapytanie 2: wybierające jedynie nazwy wszystkich kursów.

    Najprostsze zapytanie w arkuszu: SELECT nazwa FROM kursy - jedna kolumna, bez warunku i bez sortowania („wszystkich”). Wynik: 8 nazw w kolejności z tabeli, od języka angielskiego do rysunku technicznego. Zrzut: kw2.jpg. Skrypt 2 wyśle to zapytanie do listy rozwijanej, więc lista pokaże kursy w tej samej kolejności (ilustracja 4).

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT kod, nazwa, cena FROM kursy ORDER BY cena;
    
    -- Zapytanie 2
    SELECT nazwa FROM kursy;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: nowy uczestnik Tadeusz Wysocki

    Kryteria:

    R.1.5 Wykonane zapytanie 3 wstawiające rekord do tabeli uczestnicy o danych: Tadeusz Wysocki, wiek 36 lat. Klucz główny nadawany automatycznie. W pliku z kwerendami lub na zrzucie istnieje zapis np.: INSERT INTO `uczestnicy`(`imie`, `nazwisko`, `wiek`) VALUES ('Tadeusz','Wysocki','36'); oraz wynik działania zapytania udokumentowany zrzutem, na którym widoczny jest komunikat o poprawnym wstawieniu wiersza lub tabela ze wstawionym wierszem

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 3: wstawiające rekord do tabeli uczestnicy o danych: Tadeusz Wysocki, wiek 36 lat. Klucz główny nadawany automatycznie.

    To nie SELECT, tylko INSERT - zapytanie zmienia dane:

    • INSERT INTO uczestnicy (imie, nazwisko, wiek) - do której tabeli i do których kolumn; „klucz główny nadawany automatycznie” znaczy, że kolumnę id_uczestnika pomijasz - ma AUTO_INCREMENT, więc baza sama nada kolejny numer (tu 30, bo w tabeli jest 29 osób).
    • VALUES ('Tadeusz', 'Wysocki', 36) - wartości w tej samej kolejności co kolumny; napisy w apostrofach, liczba bez.

    phpMyAdmin odpowiada komunikatem „1 wiersz wstawiony” - to jest zrzut kw3.jpg (klucz przyjmuje też zrzut tabeli z nowym wierszem). Skrypt 3 wyśle to samo zapytanie z danymi z formularza.

    Typowe błędy

    Podanie własnego id_uczestnika (np. 30) nie spełnia „nadawany automatycznie”. VALUES bez listy kolumn wymagałoby czterech wartości, w tym klucza.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT kod, nazwa, cena FROM kursy ORDER BY cena;
    
    -- Zapytanie 2
    SELECT nazwa FROM kursy;
    
    -- Zapytanie 3
    INSERT INTO uczestnicy (imie, nazwisko, wiek) VALUES ('Tadeusz', 'Wysocki', 36);
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: liczba zapisanych na każdy kurs

    Kryteria:

    R.1.6 Wykonane zapytanie 4 tworzące zestawienie, na którym widoczne są nazwy wszystkich kursów oraz liczby osób zapisanych na te kursy. Nazwa kolumny z liczbą osób (alias): „Zapisanych”. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT COUNT(kod_kursu) AS 'Zapisanych', nazwa FROM kursy JOIN kursy_uczestnicy ON kod = kod_kursu GROUP BY nazwa; (AS opcjonalne) oraz wynik działania zapytania udokumentowany zrzutem, na którym widoczne są dane: 6 florystyka, 12 gastronomia, 7 informatyka, 2 język angielski, 6 język francuski, 6 język niemiecki, 4 rysunek techniczny, 3 spawanie

    ok. 4 min
    Polecenie z arkusza

    Zapytanie 4: tworzące zestawienie, na którym widoczne są nazwy wszystkich kursów oraz liczby osób zapisanych na te kursy. Nazwa kolumny z liczbą osób (alias): „Zapisanych”.

    Nazwa jest w kursy, zapisy w kursy_uczestnicy - trzeba złączyć tabele i policzyć wiersze w grupach:

    • SELECT nazwa, COUNT(kod_kursu) AS "Zapisanych" - funkcja COUNT liczy wiersze; AS nadaje kolumnie alias z arkusza.
    • FROM kursy JOIN kursy_uczestnicy ON kod = kod_kursu - do każdego zapisu dokładasz kurs; nazwy kolumn są różne w obu tabelach, więc nie trzeba ich poprzedzać nazwą tabeli.
    • GROUP BY nazwa - zapisy tego samego kursu zlewają się w jedną grupę i COUNT liczy osobno dla każdej.

    Wynik: 8 wierszy, od florystyki (6) do spawania (3); gastronomia ma najwięcej - 12. Zrzut: kw4.jpg. Plik kwerendy.txt ma komplet czterech zapytań.

    Typowe błędy

    COUNT bez GROUP BY zwróci jeden wiersz z liczbą wszystkich zapisów (46). Grupowanie jest tym, co robi z tego „zestawienie”.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT kod, nazwa, cena FROM kursy ORDER BY cena;
    
    -- Zapytanie 2
    SELECT nazwa FROM kursy;
    
    -- Zapytanie 3
    INSERT INTO uczestnicy (imie, nazwisko, wiek) VALUES ('Tadeusz', 'Wysocki', 36);
    
    -- Zapytanie 4
    SELECT nazwa, COUNT(kod_kursu) AS "Zapisanych" FROM kursy JOIN kursy_uczestnicy ON kod = kod_kursu GROUP BY nazwa;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Grafika i strona HTML

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

    Krok 8: Grafika 1.jpg: szerokość 150 px

    Kryteria:

    R.2.1 Grafika 1.jpg przeskalowana z zachowaniem proporcji do szerokości 150 px, odpowiadająca temu wysokość wynosi 100 px (± 5 px)

    ok. 3 min
    Polecenie z arkusza

    Cechy grafiki 1.jpg: przeskalowana z zachowaniem proporcji do szerokości 150 px.

    Siedem zdjęć z archiwum (2.jpg - 8.jpg) ma już 150 × 100 px, tylko 1.jpg (flaga brytyjska, kurs języka angielskiego) jest większe: 450 × 300 px. W GIMP-ie: Obraz → Skaluj obraz, szerokość 150 px z zamkniętym łańcuchem proporcji - wysokość wychodzi 100 px (klucz: 100 ±5). Eksport pod tą samą nazwą 1.jpg. Wszystkie zdjęcia mają wtedy ten sam rozmiar i tabela na stronie wygląda równo.

    Typowe błędy

    Skalowanie bez proporcji (150 × 300) nie spełnia kryterium - klucz sprawdza wysokość 100 px.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 8
  9. Krok 9: Plik index.php w HTML5

    Kryteria:

    R.2.2 W pliku index.php zapisana deklaracja HTML5 <!DOCTYPE HTML>, deklaracja języka zawartości: polski np. <html lang="pl">, w sekcji head znajduje się jawnie podany standard kodowania polskich znaków

    ok. 2 min
    Polecenie z arkusza

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

    W edytorze zaznaczającym składnię tworzysz plik index.php w folderze zdającego, obok zdjęć. Rozszerzenie .php, bo dojdą skrypty PHP - na razie to czysty HTML.

    Pierwsza linia <!DOCTYPE html> mówi przeglądarce, że dokument jest w HTML5. Dalej szkielet każdej strony: <html>, a w nim <head> (informacje o stronie) i <body> (to, co widać w oknie).

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

    Kryteria:

    R.2.2 W pliku index.php zapisana deklaracja HTML5 <!DOCTYPE HTML>, deklaracja języka zawartości: polski np. <html lang="pl">, w sekcji head znajduje się jawnie podany standard kodowania polskich znaków

    ok. 1 min
    Polecenie z arkusza

    Zadeklarowany polski język zawartości witryny.

    Atrybut lang="pl" w znaczniku <html>. Czytnik ekranu czyta wtedy tekst polską wymową, a przeglądarka dobiera słownik. Na stronie nic się nie zmienia - to informacja dla programów.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  11. Krok 11: Kodowanie polskich znaków

    Kryteria:

    R.2.2 W pliku index.php zapisana deklaracja HTML5 <!DOCTYPE HTML>, deklaracja języka zawartości: polski np. <html lang="pl">, w sekcji head znajduje się jawnie podany standard kodowania polskich znaków

    ok. 1 min
    Polecenie z arkusza

    Jawnie zastosowany właściwy standard kodowania polskich znaków.

    <meta charset="UTF-8"> jako pierwsza rzecz w <head>. „Jawnie” znaczy: wpisane w kodzie, nie zdane na domysł przeglądarki. Bez tej linii „język francuski” z bazy i „Wprowadź wszystkie dane” mogłyby wyświetlić się jako krzaki. Plik trzeba też zapisać w UTF-8.

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

    Kryteria:

    R.2.3 Stronie nadany tytuł "Szkolenia i kursy"

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony „Szkolenia i kursy”.

    <title> w <head> - napis na karcie przeglądarki, nie nagłówek na stronie. Klucz R.2.3 sprawdza tekst „Szkolenia i kursy”.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Szkolenia i kursy</title>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  13. Krok 13: Podpięcie arkusza stylów

    Kryteria:

    R.3.2 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl.css formatowanie CSS pochodzi jedynie z tego arkusza

    ok. 1 min
    Polecenie z arkusza

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

    <link rel="stylesheet" href="styl.css"> w <head>. W tym arkuszu nazwa to styl.css - sprawdź ją w treści zadania, bo w innych arkuszach bywa style.css albo styles.css. Plik napiszesz po HTML.

    Typowe błędy

    Zła nazwa pliku w href to brak stylów i cały rezultat R.4 przepada, bo „formatowanie CSS pochodzi jedynie z tego arkusza”.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Szkolenia i kursy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  14. Krok 14: Bloki semantyczne

    Kryteria:

    R.2.4 W sekcji body zdefiniowany jest podział na bloki za pomocą semantycznych znaczników HTML 5: header, main->section+section, footer

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki zrealizowany za pomocą semantycznych znaczników bloków języka HTML5 tak, aby po uruchomieniu w przeglądarce układ bloków na stronie był zgodny z ilustracją 3. Zawartość bloku głównego: sekcja lewa i sekcja prawa.

    Z ilustracji 3: blok nagłówkowy na górze, pod nim blok główny z dwiema sekcjami obok siebie, na dole stopka. Klucz R.2.4 wymienia znaczniki wprost: <header>, <main> z dwiema <section>, <footer> (nazwy w tabeli 3 arkusza).

    Sekcje dostają identyfikatory left i right, bo arkusz formatuje każdą inaczej (lewa 50% z paskiem przewijania, prawa 40% z cieniem) - w CSS trzeba je rozróżnić. Nazwy identyfikatorów i zmiennych piszesz w tym rozwiązaniu po angielsku (arkusz dopuszcza polski lub angielski); nazwy narzucone przez arkusz (index.php, styl.css, kolumny bazy) zostają.

    Typowe błędy

    <div> zamiast <section> nie jest znacznikiem semantycznym - klucz wymaga main → section + section.

    index.php PHP zmienione: 10 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Szkolenia i kursy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
        </header>
        <main>
            <section id="left">
            </section>
            <section id="right">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  15. Krok 15: Blok nagłówkowy: nagłówek pierwszego stopnia

    Kryteria:

    R.2.5 Umieszczony w bloku nagłówkowym <h1>: "SZKOLENIA”, w sekcji prawej <h2>: "Zapisy na kursy" oraz paragraf z numerem zdającego w stopce

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku nagłówkowego: nagłówek pierwszego stopnia o treści: „SZKOLENIA”.

    Pierwszego stopnia to <h1>, w <header>, wielkimi literami jak w arkuszu (klucz dopuszcza błędy wielkości liter, ale nie ma powodu ich robić). Rozstrzelone litery z ilustracji 2 zrobi CSS, nie spacje w tekście.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Szkolenia i kursy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>SZKOLENIA</h1>
        </header>
        <main>
            <section id="left">
            </section>
            <section id="right">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  16. Krok 16: Sekcja lewa: tabela z wierszem nagłówkowym

    Kryteria: ,

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

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

    ok. 2 min
    Polecenie z arkusza

    Zawartość sekcji lewej: tabela o trzech kolumnach. Pierwszy wiersz zawiera komórki nagłówkowe: Kurs, Nazwa, Cena. Tabela wypełniona jest przez skrypt 1.

    <table> z jednym wierszem <tr>, a w nim trzy komórki nagłówkowe <th> (klucz R.2.8 sprawdza właśnie th, nie td z pogrubieniem). Kolejne wiersze - zdjęcie, nazwa, cena dla każdego kursu - dopisze skrypt 1, więc w HTML tabela kończy się na nagłówku. Przeglądarka domyślnie pogrubia i wyśrodkowuje <th>, jak na ilustracji 2.

    index.php PHP zmienione: 7 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Szkolenia i kursy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>SZKOLENIA</h1>
        </header>
        <main>
            <section id="left">
                <table>
                    <tr>
                        <th>Kurs</th>
                        <th>Nazwa</th>
                        <th>Cena</th>
                    </tr>
                </table>
            </section>
            <section id="right">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  17. Krok 17: Sekcja prawa: nagłówek „Zapisy na kursy”

    Kryteria:

    R.2.5 Umieszczony w bloku nagłówkowym <h1>: "SZKOLENIA”, w sekcji prawej <h2>: "Zapisy na kursy" oraz paragraf z numerem zdającego w stopce

    ok. 1 min
    Polecenie z arkusza

    Zawartość sekcji prawej: nagłówek drugiego stopnia o treści: „Zapisy na kursy”.

    Drugiego stopnia to <h2>, w sekcji prawej. Klucz R.2.5 sprawdza go razem z <h1> i paragrafem stopki.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Szkolenia i kursy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>SZKOLENIA</h1>
        </header>
        <main>
            <section id="left">
                <table>
                    <tr>
                        <th>Kurs</th>
                        <th>Nazwa</th>
                        <th>Cena</th>
                    </tr>
                </table>
            </section>
            <section id="right">
                <h2>Zapisy na kursy</h2>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  18. Krok 18: Sekcja prawa: formularz

    Kryteria: ,

    R.2.9 W sekcji prawej umieszczony formularz z 4 etykietami, 3 polami edycyjnymi, listą rozwijaną i przyciskiem. Przynajmniej jedna etykieta powiązana jest z kontrolką

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

    ok. 1 min
    Polecenie z arkusza

    Formularz zgodny z ilustracją 2, wysyłający dane do tego samego pliku metodą bezpieczną z polami: etykiety, pola edycyjne, lista rozwijana i przycisk.

    „Metoda bezpieczna” w rozumieniu arkusza to POST - dane nie pojawiają się w adresie strony. W specyfikacji HTTP „bezpieczne” są co prawda metody, które niczego nie zmieniają na serwerze (GET i HEAD), ale arkusz używa tego słowa w innym sensie, a klucz wymaga POST. „Do tego samego pliku” to brak atrybutu action (przeglądarka wysyła formularz na adres bieżącej strony) albo action="index.php" - klucz R.3.3 przyjmuje oba warianty. Na ilustracji 2 każda etykieta stoi nad swoją kontrolką - stąd <br> po każdym elemencie w kolejnych krokach.

    Typowe błędy

    method="get" (albo brak method) wyśle dane w adresie - arkusz i klucz wymagają tu POST, a skrypt czytający $_POST nic nie dostanie.

    index.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Szkolenia i kursy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>SZKOLENIA</h1>
        </header>
        <main>
            <section id="left">
                <table>
                    <tr>
                        <th>Kurs</th>
                        <th>Nazwa</th>
                        <th>Cena</th>
                    </tr>
                </table>
            </section>
            <section id="right">
                <h2>Zapisy na kursy</h2>
                <form method="post">
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  19. Krok 19: Formularz: etykieta „Imię” i pole

    Kryteria:

    R.2.9 W sekcji prawej umieszczony formularz z 4 etykietami, 3 polami edycyjnymi, listą rozwijaną i przyciskiem. Przynajmniej jedna etykieta powiązana jest z kontrolką

    ok. 1 min
    Polecenie z arkusza

    Etykieta dla pierwszego pola edycyjnego: „Imię”. Pole edycyjne.

    Etykieta <label> jest „powiązana” z polem przez atrybut for równy id pola (klucz R.2.9 sprawdza przynajmniej jedno takie powiązanie - kliknięcie etykiety ustawia kursor w polu). <input type="text"> to zwykłe pole edycyjne; name="name" to klucz, pod którym wartość trafi do $_POST w PHP.

    Typowe błędy

    Pole bez name nie jest wysyłane z formularzem - skrypt 3 nie dostanie imienia, choć pole wygląda poprawnie.

    index.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Szkolenia i kursy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>SZKOLENIA</h1>
        </header>
        <main>
            <section id="left">
                <table>
                    <tr>
                        <th>Kurs</th>
                        <th>Nazwa</th>
                        <th>Cena</th>
                    </tr>
                </table>
            </section>
            <section id="right">
                <h2>Zapisy na kursy</h2>
                <form method="post">
                    <label for="name">Imię</label><br>
                    <input type="text" id="name" name="name"><br>
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  20. Krok 20: Formularz: etykieta „Nazwisko” i pole

    Kryteria:

    R.2.9 W sekcji prawej umieszczony formularz z 4 etykietami, 3 polami edycyjnymi, listą rozwijaną i przyciskiem. Przynajmniej jedna etykieta powiązana jest z kontrolką

    ok. 1 min
    Polecenie z arkusza

    Etykieta dla drugiego pola edycyjnego: „Nazwisko”. Pole edycyjne.

    Ten sam wzorzec: <label for="surname"> i pole tekstowe z id i name równymi surname.

    index.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Szkolenia i kursy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>SZKOLENIA</h1>
        </header>
        <main>
            <section id="left">
                <table>
                    <tr>
                        <th>Kurs</th>
                        <th>Nazwa</th>
                        <th>Cena</th>
                    </tr>
                </table>
            </section>
            <section id="right">
                <h2>Zapisy na kursy</h2>
                <form method="post">
                    <label for="name">Imię</label><br>
                    <input type="text" id="name" name="name"><br>
                    <label for="surname">Nazwisko</label><br>
                    <input type="text" id="surname" name="surname"><br>
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  21. Krok 21: Formularz: etykieta „Wiek” i pole liczbowe

    Kryteria: ,

    R.2.9 W sekcji prawej umieszczony formularz z 4 etykietami, 3 polami edycyjnymi, listą rozwijaną i przyciskiem. Przynajmniej jedna etykieta powiązana jest z kontrolką

    R.3.4 Pole edycyjne do wpisania wieku jest typu number oraz przycisk realizuje funkcję wysłania danych z formularza (input lub button z typem submit lub button bez typu)

    ok. 1 min
    Polecenie z arkusza

    Etykieta dla trzeciego pola edycyjnego: „Wiek”. Pole edycyjne typu numerycznego.

    „Typ numeryczny” to type="number" - przeglądarka nie pozwoli wpisać liter i pokazuje strzałki jak na ilustracji 5 (klucz R.3.4 sprawdza, że pole wieku jest typu number). Wartość pójdzie do kolumny wiek, która jest liczbą.

    index.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Szkolenia i kursy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>SZKOLENIA</h1>
        </header>
        <main>
            <section id="left">
                <table>
                    <tr>
                        <th>Kurs</th>
                        <th>Nazwa</th>
                        <th>Cena</th>
                    </tr>
                </table>
            </section>
            <section id="right">
                <h2>Zapisy na kursy</h2>
                <form method="post">
                    <label for="name">Imię</label><br>
                    <input type="text" id="name" name="name"><br>
                    <label for="surname">Nazwisko</label><br>
                    <input type="text" id="surname" name="surname"><br>
                    <label for="age">Wiek</label><br>
                    <input type="number" id="age" name="age"><br>
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  22. Krok 22: Formularz: etykieta „Rodzaj kursu” i lista rozwijana

    Kryteria:

    R.2.9 W sekcji prawej umieszczony formularz z 4 etykietami, 3 polami edycyjnymi, listą rozwijaną i przyciskiem. Przynajmniej jedna etykieta powiązana jest z kontrolką

    ok. 1 min
    Polecenie z arkusza

    Etykieta dla listy rozwijanej: „Rodzaj kursu”. Lista rozwijana wypełniona przez skrypt 2.

    Czwarta etykieta, powiązana z <select id="course">. Opcje zostawiasz puste - wstawi je skrypt 2, w pętli po wyniku zapytania 2. Arkusz nie każe zapisywać wybranego kursu do bazy (zapytanie 3 wstawia tylko imię, nazwisko i wiek), więc lista służy do wyboru, ale skrypt 3 jej nie czyta.

    index.php PHP zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Szkolenia i kursy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>SZKOLENIA</h1>
        </header>
        <main>
            <section id="left">
                <table>
                    <tr>
                        <th>Kurs</th>
                        <th>Nazwa</th>
                        <th>Cena</th>
                    </tr>
                </table>
            </section>
            <section id="right">
                <h2>Zapisy na kursy</h2>
                <form method="post">
                    <label for="name">Imię</label><br>
                    <input type="text" id="name" name="name"><br>
                    <label for="surname">Nazwisko</label><br>
                    <input type="text" id="surname" name="surname"><br>
                    <label for="age">Wiek</label><br>
                    <input type="number" id="age" name="age"><br>
                    <label for="course">Rodzaj kursu</label><br>
                    <select id="course" name="course">
                    </select><br>
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  23. Krok 23: Formularz: przycisk „Dodaj dane”

    Kryteria: ,

    R.2.9 W sekcji prawej umieszczony formularz z 4 etykietami, 3 polami edycyjnymi, listą rozwijaną i przyciskiem. Przynajmniej jedna etykieta powiązana jest z kontrolką

    R.3.4 Pole edycyjne do wpisania wieku jest typu number oraz przycisk realizuje funkcję wysłania danych z formularza (input lub button z typem submit lub button bez typu)

    ok. 1 min
    Polecenie z arkusza

    Przycisk „Dodaj dane” wysyłający dane z formularza do skryptu 3.

    <button type="submit">Dodaj dane</button> wewnątrz formularza wysyła go po kliknięciu. Klucz R.3.4 przyjmuje też <input type="submit" value="Dodaj dane">. Komplet z klucza R.2.9: 4 etykiety, 3 pola edycyjne, lista rozwijana i przycisk. „Efekt działania skryptu 3” (paragraf z komunikatem) pojawi się pod formularzem.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Szkolenia i kursy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>SZKOLENIA</h1>
        </header>
        <main>
            <section id="left">
                <table>
                    <tr>
                        <th>Kurs</th>
                        <th>Nazwa</th>
                        <th>Cena</th>
                    </tr>
                </table>
            </section>
            <section id="right">
                <h2>Zapisy na kursy</h2>
                <form method="post">
                    <label for="name">Imię</label><br>
                    <input type="text" id="name" name="name"><br>
                    <label for="surname">Nazwisko</label><br>
                    <input type="text" id="surname" name="surname"><br>
                    <label for="age">Wiek</label><br>
                    <input type="number" id="age" name="age"><br>
                    <label for="course">Rodzaj kursu</label><br>
                    <select id="course" name="course">
                    </select><br>
                    <button type="submit">Dodaj dane</button>
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  24. Krok 24: Stopka z numerem zdającego

    Kryteria:

    R.2.5 Umieszczony w bloku nagłówkowym <h1>: "SZKOLENIA”, w sekcji prawej <h2>: "Zapisy na kursy" oraz paragraf z numerem zdającego w stopce

    ok. 1 min
    Polecenie z arkusza

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

    Paragraf <p> w <footer>. W tym rozwiązaniu po dwukropku stoi nazwisko autora - na egzaminie wpisujesz swój numer zdającego (PESEL), bo tego sprawdza klucz R.2.5. Struktura HTML jest kompletna - dalej style.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Szkolenia i kursy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>SZKOLENIA</h1>
        </header>
        <main>
            <section id="left">
                <table>
                    <tr>
                        <th>Kurs</th>
                        <th>Nazwa</th>
                        <th>Cena</th>
                    </tr>
                </table>
            </section>
            <section id="right">
                <h2>Zapisy na kursy</h2>
                <form method="post">
                    <label for="name">Imię</label><br>
                    <input type="text" id="name" name="name"><br>
                    <label for="surname">Nazwisko</label><br>
                    <input type="text" id="surname" name="surname"><br>
                    <label for="age">Wiek</label><br>
                    <input type="number" id="age" name="age"><br>
                    <label for="course">Rodzaj kursu</label><br>
                    <select id="course" name="course">
                    </select><br>
                    <button type="submit">Dodaj dane</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  25. Etap 3: Styl CSS

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

    Krok 25: Plik styl.css: ciało strony

    Kryteria:

    R.4.1 Ustawiony dla selektora body: krój czcionki "Century Gothic", sans-serif (tylko taka kolejność) , wyrównanie tekstu do środka, kolor tła #FCE4EC

    ok. 2 min
    Polecenie z arkusza

    Dla selektora ciała strony: krój czcionki Century Gothic, w przypadku braku sans-serif, wyrównanie tekstu do środka, kolor tła #FCE4EC.

    Utwórz plik styl.css obok index.php. Selektor body - arkusz podkreśla w uwadze, że ciało strony i tabelę formatujesz wyłącznie selektorem znacznika (bez klas i identyfikatorów). font-family to lista: nazwa z dwóch słów w cudzysłowie ("Century Gothic"), potem rodzina zapasowa sans-serif - klucz R.4.1 mówi: tylko taka kolejność. text-align: center dziedziczą wszystkie elementy, więc etykiety i nagłówki od razu stają na środku. Różowe tło z arkusza.

    styl.css CSS nowy plik
    body {
        font-family: "Century Gothic", sans-serif;
        text-align: center;
        background-color: #FCE4EC;
    }
    Podgląd strony po tym kroku (index.php)
  26. Krok 26: Nagłówek i stopka: tło, kolor, margines wewnętrzny, odstęp liter

    Kryteria: , , ,

    R.4.2 Ustawiony kolor tła dla bloku nagłówkowego, prawej sekcji i stopki #880E4F

    R.4.3 Ustawiony biały kolor czcionki dla bloku nagłówkowego, prawej sekcji i stopki

    R.4.6 Ustawione marginesy wewnętrzne (padding) dla bloku nagłówkowego i stopki 10px oraz ustawione marginesy zewnętrzne (margin): dla tabeli auto, dla prawej sekcji 35px i dla przynajmniej jednej kontrolki 5px

    R.4.7 Ustawiona dla bloku nagłówkowego i stopki odległość między literami 20px

    ok. 2 min
    Polecenie z arkusza

    Dla bloku nagłówkowego i stopki: kolor tła #880E4F, biały kolor czcionki, marginesy wewnętrzne 10 px, odległość między literami 20 px.

    Wspólne cechy dwóch bloków piszesz raz - header, footer. Bordowe tło, biały tekst, padding: 10px z każdej strony. „Odległość między literami” to letter-spacing: 20px - to ona daje rozstrzelony napis S Z K O L E N I A z ilustracji 2 (klucz R.4.7), bez żadnych spacji w HTML.

    Typowe błędy

    word-spacing rozsuwa wyrazy, nie litery. Arkusz mówi o odległości między literami - letter-spacing.

    styl.css CSS zmienione: 6 linii
    body {
        font-family: "Century Gothic", sans-serif;
        text-align: center;
        background-color: #FCE4EC;
    }
    header, footer {
        background-color: #880E4F;
        color: white;
        padding: 10px;
        letter-spacing: 20px;
    }
    Podgląd strony po tym kroku (index.php)
  27. Krok 27: Sekcja lewa: wymiary i paski przewijania

    Kryteria: ,

    R.4.4 Ustawiona wysokość sekcji lewej 600px, prawej 500px oraz szerokość sekcji lewej 50%, prawej 40% (width lub grid-template-columns lub flex)

    R.4.9 Ustawiony dla lewej sekcji styl overflow: auto;

    ok. 1 min
    Polecenie z arkusza

    Dla lewej sekcji: szerokość 50%, wysokość 600 px, paski przewijania tylko w momencie, gdy zawartość nie mieści się w bloku.

    Selektor identyfikatora #left. „Paski tylko, gdy zawartość się nie mieści” to overflow: auto (klucz R.4.9). Osiem wierszy tabeli ze zdjęciami po 100 px to ponad 600 px - pojawi się pionowy pasek jak na ilustracji 2.

    styl.css CSS zmienione: 5 linii
    body {
        font-family: "Century Gothic", sans-serif;
        text-align: center;
        background-color: #FCE4EC;
    }
    header, footer {
        background-color: #880E4F;
        color: white;
        padding: 10px;
        letter-spacing: 20px;
    }
    #left {
        width: 50%;
        height: 600px;
        overflow: auto;
    }
    Podgląd strony po tym kroku (index.php)
  28. Krok 28: Sekcja prawa: tło, kolor, wymiary, margines, cień

    Kryteria: , , , ,

    R.4.2 Ustawiony kolor tła dla bloku nagłówkowego, prawej sekcji i stopki #880E4F

    R.4.3 Ustawiony biały kolor czcionki dla bloku nagłówkowego, prawej sekcji i stopki

    R.4.4 Ustawiona wysokość sekcji lewej 600px, prawej 500px oraz szerokość sekcji lewej 50%, prawej 40% (width lub grid-template-columns lub flex)

    R.4.6 Ustawione marginesy wewnętrzne (padding) dla bloku nagłówkowego i stopki 10px oraz ustawione marginesy zewnętrzne (margin): dla tabeli auto, dla prawej sekcji 35px i dla przynajmniej jednej kontrolki 5px

    R.4.8 Ustawiony dla prawej sekcji cień box-shadow: 10px 10px 15px DimGray;

    ok. 2 min
    Polecenie z arkusza

    Dla prawej sekcji: kolor tła #880E4F, biały kolor czcionki, szerokość 40%, wysokość 500 px, marginesy zewnętrzne 35 px, cień bloku o przesunięciu 10 px w obu osiach, rozmyciu 15 px i kolorze DimGray.

    Selektor #right. Cień bloku to box-shadow z tabeli 2 arkusza: h-offset v-offset blur color - czyli 10px 10px 15px DimGray (przesunięcie w prawo, przesunięcie w dół, rozmycie, kolor; parametry spread i inset pomijasz). Klucz R.4.8 sprawdza dokładnie ten zapis. margin: 35px odsuwa blok od sekcji lewej i od krawędzi - na ilustracji 2 widać ten odstęp.

    Typowe błędy

    text-shadow (druga właściwość z tabeli 2) to cień tekstu, nie bloku - arkusz mówi „cień bloku”.

    styl.css CSS zmienione: 8 linii
    body {
        font-family: "Century Gothic", sans-serif;
        text-align: center;
        background-color: #FCE4EC;
    }
    header, footer {
        background-color: #880E4F;
        color: white;
        padding: 10px;
        letter-spacing: 20px;
    }
    #left {
        width: 50%;
        height: 600px;
        overflow: auto;
    }
    #right {
        background-color: #880E4F;
        color: white;
        width: 40%;
        height: 500px;
        margin: 35px;
        box-shadow: 10px 10px 15px DimGray;
    }
    Podgląd strony po tym kroku (index.php)
  29. Krok 29: Układ bloków zgodny z ilustracją 3

    Kryteria:

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

    ok. 2 min
    Polecenie z arkusza

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

    Sekcje mają już szerokości (50% i 40%), ale jako bloki stoją jedna pod drugą. display: flex na <main> ustawia jego dzieci w jednym wierszu, obok siebie - a stopka, która jest poza <main>, zostaje pod spodem bez żadnego clear. Klucz R.3.1 przyjmuje też float z clear i grid; flex jest tu najkrótszy, bo nie trzeba sprzątać po pływających blokach.

    Typowe błędy

    float: left na obu sekcjach też działa, ale bez clear: both na stopce stopka wjeżdża obok sekcji.

    styl.css CSS zmienione: 3 linie
    body {
        font-family: "Century Gothic", sans-serif;
        text-align: center;
        background-color: #FCE4EC;
    }
    header, footer {
        background-color: #880E4F;
        color: white;
        padding: 10px;
        letter-spacing: 20px;
    }
    #left {
        width: 50%;
        height: 600px;
        overflow: auto;
    }
    #right {
        background-color: #880E4F;
        color: white;
        width: 40%;
        height: 500px;
        margin: 35px;
        box-shadow: 10px 10px 15px DimGray;
    }
    main {
        display: flex;
    }
    Podgląd strony po tym kroku (index.php)
  30. Krok 30: Tabela: wyśrodkowanie i szerokość

    Kryteria: ,

    R.4.5 Ustawiona szerokość selektora table oraz wszystkich kontrolek (pola edycyjne, lista rozwijana, przycisk) 70%

    R.4.6 Ustawione marginesy wewnętrzne (padding) dla bloku nagłówkowego i stopki 10px oraz ustawione marginesy zewnętrzne (margin): dla tabeli auto, dla prawej sekcji 35px i dla przynajmniej jednej kontrolki 5px

    ok. 1 min
    Polecenie z arkusza

    Dla selektora tabeli: marginesy zewnętrzne automatycznie wyliczane przez przeglądarkę, szerokość 70%.

    Selektor table - zgodnie z uwagą arkusza bez klas i identyfikatorów. margin: auto przy ustalonej szerokości to klasyczny sposób na wyśrodkowanie bloku: przeglądarka dzieli wolne miejsce po równo na lewo i prawo (klucz R.4.6). text-align: center z body wyśrodkowuje tekst w komórkach, ale nie samą tabelę - do tego potrzebny jest właśnie margin: auto.

    styl.css CSS zmienione: 4 linie
    body {
        font-family: "Century Gothic", sans-serif;
        text-align: center;
        background-color: #FCE4EC;
    }
    header, footer {
        background-color: #880E4F;
        color: white;
        padding: 10px;
        letter-spacing: 20px;
    }
    #left {
        width: 50%;
        height: 600px;
        overflow: auto;
    }
    #right {
        background-color: #880E4F;
        color: white;
        width: 40%;
        height: 500px;
        margin: 35px;
        box-shadow: 10px 10px 15px DimGray;
    }
    main {
        display: flex;
    }
    table {
        margin: auto;
        width: 70%;
    }
    Podgląd strony po tym kroku (index.php)
  31. Krok 31: Kontrolki formularza: margines i szerokość

    Kryteria: ,

    R.4.5 Ustawiona szerokość selektora table oraz wszystkich kontrolek (pola edycyjne, lista rozwijana, przycisk) 70%

    R.4.6 Ustawione marginesy wewnętrzne (padding) dla bloku nagłówkowego i stopki 10px oraz ustawione marginesy zewnętrzne (margin): dla tabeli auto, dla prawej sekcji 35px i dla przynajmniej jednej kontrolki 5px

    ok. 1 min
    Polecenie z arkusza

    Dla wszystkich kontrolek (pola edycyjne, lista rozwijana, przycisk) marginesy zewnętrzne 5 px, szerokość 70%.

    Trzy znaczniki kontrolek w jednej regule: input, select, button. Szerokość 70% liczy się od sekcji prawej, więc pola są równe i wyśrodkowane (jako elementy liniowe podlegają text-align: center), jak na ilustracji 2. Margines 5 px rozsuwa je w pionie.

    styl.css CSS zmienione: 4 linie
    body {
        font-family: "Century Gothic", sans-serif;
        text-align: center;
        background-color: #FCE4EC;
    }
    header, footer {
        background-color: #880E4F;
        color: white;
        padding: 10px;
        letter-spacing: 20px;
    }
    #left {
        width: 50%;
        height: 600px;
        overflow: auto;
    }
    #right {
        background-color: #880E4F;
        color: white;
        width: 40%;
        height: 500px;
        margin: 35px;
        box-shadow: 10px 10px 15px DimGray;
    }
    main {
        display: flex;
    }
    table {
        margin: auto;
        width: 70%;
    }
    input, select, button {
        margin: 5px;
        width: 70%;
    }
    Podgląd strony po tym kroku (index.php)
  32. Krok 32: Wiersz tabeli pod kursorem

    Kryteria:

    R.4.10 Gdy kursor znajdzie się na wierszu tabeli jego kolor tła zmienia się na #F8BBD0

    ok. 1 min
    Polecenie z arkusza

    Gdy kursor znajdzie się na wierszu tabeli jego kolor tła zmienia się na #F8BBD0.

    Pseudoklasa :hover na tr - podświetla cały wiersz, w którym jest kursor. Na razie w tabeli jest tylko wiersz nagłówkowy (najedź na niego w podglądzie); kolejne dopisze skrypt 1 i też będą się podświetlać. Arkusz stylów jest kompletny.

    styl.css CSS zmienione: 3 linie
    body {
        font-family: "Century Gothic", sans-serif;
        text-align: center;
        background-color: #FCE4EC;
    }
    header, footer {
        background-color: #880E4F;
        color: white;
        padding: 10px;
        letter-spacing: 20px;
    }
    #left {
        width: 50%;
        height: 600px;
        overflow: auto;
    }
    #right {
        background-color: #880E4F;
        color: white;
        width: 40%;
        height: 500px;
        margin: 35px;
        box-shadow: 10px 10px 15px DimGray;
    }
    main {
        display: flex;
    }
    table {
        margin: auto;
        width: 70%;
    }
    input, select, button {
        margin: 5px;
        width: 70%;
    }
    tr:hover {
        background-color: #F8BBD0;
    }
    Podgląd strony po tym kroku (index.php)
  33. Etap 4: Skrypty PHP

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

    Krok 33: Połączenie z bazą danych

    Kryteria: ,

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

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

    ok. 3 min
    Polecenie z arkusza

    Napisane w języku PHP. Łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie szkolenia.

    Kod PHP piszesz między <?php a ?> - serwer wykonuje go, zanim strona trafi do przeglądarki. Połączenie otwierasz raz, na samym początku pliku, i trzymasz w zmiennej $connection (znacząca, angielska nazwa - R.5.10), z której skorzystają trzy skrypty. mysqli_connect z tabeli 1 arkusza przyjmuje kolejno: serwer, użytkownika, hasło (puste) i nazwę bazy.

    Od tego kroku podgląd to wynik działania strony po stronie serwera (PHP nie wykonuje się w przeglądarce) - na razie wygląda jak wcześniej.

    Typowe błędy

    Kolejność argumentów ma znaczenie: mysqli_connect('localhost', 'root', 'szkolenia') podaje „szkolenia” jako HASŁO. Nazwa bazy jest czwarta. Plik sprawdzaj przez http://localhost/…, nie otwierając go z dysku.

    index.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'szkolenia');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Szkolenia i kursy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>SZKOLENIA</h1>
        </header>
        <main>
            <section id="left">
                <table>
                    <tr>
                        <th>Kurs</th>
                        <th>Nazwa</th>
                        <th>Cena</th>
                    </tr>
                </table>
            </section>
            <section id="right">
                <h2>Zapisy na kursy</h2>
                <form method="post">
                    <label for="name">Imię</label><br>
                    <input type="text" id="name" name="name"><br>
                    <label for="surname">Nazwisko</label><br>
                    <input type="text" id="surname" name="surname"><br>
                    <label for="age">Wiek</label><br>
                    <input type="number" id="age" name="age"><br>
                    <label for="course">Rodzaj kursu</label><br>
                    <select id="course" name="course">
                    </select><br>
                    <button type="submit">Dodaj dane</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  34. Krok 34: Skrypt 1: zapytanie 1 do bazy

    Kryteria:

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

    ok. 2 min
    Polecenie z arkusza

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

    Skrypt 1 piszesz wewnątrz <table>, zaraz po wierszu nagłówkowym - tam mają trafić kolejne wiersze. mysqli_query($connection, ...) wysyła zapytanie i zwraca wynik, który zapisujesz w $courses (klucz R.5.3). Treść to dokładnie zapytanie 1 z kwerendy.txt - kursy od najtańszego.

    index.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'szkolenia');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Szkolenia i kursy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>SZKOLENIA</h1>
        </header>
        <main>
            <section id="left">
                <table>
                    <tr>
                        <th>Kurs</th>
                        <th>Nazwa</th>
                        <th>Cena</th>
                    </tr>
    <?php
    $courses = mysqli_query($connection, 'SELECT kod, nazwa, cena FROM kursy ORDER BY cena;');
    ?>
                </table>
            </section>
            <section id="right">
                <h2>Zapisy na kursy</h2>
                <form method="post">
                    <label for="name">Imię</label><br>
                    <input type="text" id="name" name="name"><br>
                    <label for="surname">Nazwisko</label><br>
                    <input type="text" id="surname" name="surname"><br>
                    <label for="age">Wiek</label><br>
                    <input type="number" id="age" name="age"><br>
                    <label for="course">Rodzaj kursu</label><br>
                    <select id="course" name="course">
                    </select><br>
                    <button type="submit">Dodaj dane</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

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

  35. Krok 35: Skrypt 1: wiersz z obrazem dla każdego kursu

    Kryteria: ,

    R.2.6 W sekcji lewej umieszczona przynajmniej jedna grafika z tekstem alternatywnym "Kurs" (w skrypcie lub HTML)

    R.5.5 Skrypt 1 wyświetla w I kolumnie tabeli wszystkie obrazy, których nazwy to kody zwrócone z bazy, a rozszerzenia to .jpg

    ok. 3 min
    Polecenie z arkusza

    Dla każdego zwróconego przez bazę rekordu, w kolejnych wierszach tabeli wstawiane są: do pierwszej komórki: obraz o nazwie utworzonej ze zwróconego przez bazę kodu i rozszerzenia .jpg z tekstem alternatywnym: „kurs” (dla kodu 1 jest to 1.jpg).

    „Dla każdego rekordu” to pętla: mysqli_fetch_row($courses) (tabela 1) oddaje kolejny wiersz jako tablicę od zera, a gdy wierszy zabraknie - false, co kończy while. Każdy obieg otwiera <tr>, wypisuje pierwszą komórkę i zamyka wiersz. Nazwa pliku powstaje ze sklejenia kodu z rozszerzeniem: w napisie '$row[0].jpg' PHP podstawia $row[0] (np. 5), a .jpg zostaje dosłownie - wychodzi 5.jpg. Tekst alternatywny z arkusza (klucz R.2.6 przyjmuje alt w skrypcie). Podgląd: osiem zdjęć w pierwszej kolumnie, od gastronomii.

    Typowe błędy

    src='$row[0]' bez .jpg to nazwa pliku bez rozszerzenia - obraz się nie załaduje. src='$row[1].jpg' wziąłby nazwę kursu zamiast kodu.

    index.php PHP zmienione: 5 linii
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'szkolenia');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Szkolenia i kursy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>SZKOLENIA</h1>
        </header>
        <main>
            <section id="left">
                <table>
                    <tr>
                        <th>Kurs</th>
                        <th>Nazwa</th>
                        <th>Cena</th>
                    </tr>
    <?php
    $courses = mysqli_query($connection, 'SELECT kod, nazwa, cena FROM kursy ORDER BY cena;');
    while ($row = mysqli_fetch_row($courses)) {
        echo '<tr>';
        echo "<td><img src='$row[0].jpg' alt='kurs'></td>";
        echo '</tr>';
    }
    ?>
                </table>
            </section>
            <section id="right">
                <h2>Zapisy na kursy</h2>
                <form method="post">
                    <label for="name">Imię</label><br>
                    <input type="text" id="name" name="name"><br>
                    <label for="surname">Nazwisko</label><br>
                    <input type="text" id="surname" name="surname"><br>
                    <label for="age">Wiek</label><br>
                    <input type="number" id="age" name="age"><br>
                    <label for="course">Rodzaj kursu</label><br>
                    <select id="course" name="course">
                    </select><br>
                    <button type="submit">Dodaj dane</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  36. Krok 36: Skrypt 1: nazwa i cena w kolejnych komórkach

    Kryteria:

    R.5.4 Skrypt 1 dla każdego zwróconego przez bazę rekordu, wyświetla w II i III kolumnie tabeli kolejne nazwy i ceny kursów

    ok. 1 min
    Polecenie z arkusza

    Do kolejnych komórek: zwrócone z bazy nazwa i cena kursu.

    Przed zamknięciem wiersza dokładasz dwie komórki: $row[1] to nazwa, $row[2] cena - kolejność z zapytania 1 (kod, nazwa, cena). Tabela wygląda jak na ilustracji 2: zdjęcie, nazwa, cena, a wiersze podświetlają się pod kursorem dzięki tr:hover z CSS. Osiem wierszy po 100 px przekracza 600 px sekcji - pojawia się pasek przewijania z overflow: auto.

    index.php PHP zmienione: 1 linia
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'szkolenia');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Szkolenia i kursy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>SZKOLENIA</h1>
        </header>
        <main>
            <section id="left">
                <table>
                    <tr>
                        <th>Kurs</th>
                        <th>Nazwa</th>
                        <th>Cena</th>
                    </tr>
    <?php
    $courses = mysqli_query($connection, 'SELECT kod, nazwa, cena FROM kursy ORDER BY cena;');
    while ($row = mysqli_fetch_row($courses)) {
        echo '<tr>';
        echo "<td><img src='$row[0].jpg' alt='kurs'></td>";
        echo "<td>$row[1]</td><td>$row[2]</td>";
        echo '</tr>';
    }
    ?>
                </table>
            </section>
            <section id="right">
                <h2>Zapisy na kursy</h2>
                <form method="post">
                    <label for="name">Imię</label><br>
                    <input type="text" id="name" name="name"><br>
                    <label for="surname">Nazwisko</label><br>
                    <input type="text" id="surname" name="surname"><br>
                    <label for="age">Wiek</label><br>
                    <input type="number" id="age" name="age"><br>
                    <label for="course">Rodzaj kursu</label><br>
                    <select id="course" name="course">
                    </select><br>
                    <button type="submit">Dodaj dane</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  37. Krok 37: Skrypt 2: zapytanie 2 do bazy

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

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

    Skrypt 2 piszesz wewnątrz <select>, bo tam mają trafić opcje. Zapytanie 2 z kwerendy.txt - same nazwy kursów - trafia do $names. Osobna zmienna, bo $courses ze skryptu 1 jest już przeczytana do końca.

    index.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'szkolenia');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Szkolenia i kursy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>SZKOLENIA</h1>
        </header>
        <main>
            <section id="left">
                <table>
                    <tr>
                        <th>Kurs</th>
                        <th>Nazwa</th>
                        <th>Cena</th>
                    </tr>
    <?php
    $courses = mysqli_query($connection, 'SELECT kod, nazwa, cena FROM kursy ORDER BY cena;');
    while ($row = mysqli_fetch_row($courses)) {
        echo '<tr>';
        echo "<td><img src='$row[0].jpg' alt='kurs'></td>";
        echo "<td>$row[1]</td><td>$row[2]</td>";
        echo '</tr>';
    }
    ?>
                </table>
            </section>
            <section id="right">
                <h2>Zapisy na kursy</h2>
                <form method="post">
                    <label for="name">Imię</label><br>
                    <input type="text" id="name" name="name"><br>
                    <label for="surname">Nazwisko</label><br>
                    <input type="text" id="surname" name="surname"><br>
                    <label for="age">Wiek</label><br>
                    <input type="number" id="age" name="age"><br>
                    <label for="course">Rodzaj kursu</label><br>
                    <select id="course" name="course">
    <?php
    $names = mysqli_query($connection, 'SELECT nazwa FROM kursy;');
    ?>
                    </select><br>
                    <button type="submit">Dodaj dane</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

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

  38. Krok 38: Skrypt 2: nazwy kursów jako opcje listy

    Kryteria:

    R.5.6 Skrypt 2 wyświetla nazwy kursów jako elementy listy rozwijanej w znaczniku <option>

    ok. 2 min
    Polecenie z arkusza

    Wszystkie zwrócone nazwy kursów są zapisywane jako elementy listy rozwijanej.

    Ta sama pętla co w skrypcie 1, tylko wypisuje <option> z nazwą (klucz R.5.6 sprawdza znacznik option). Podgląd: lista ma 8 kursów w kolejności z tabeli, od języka angielskiego - jak na ilustracji 4.

    Typowe błędy

    echo '<option>$row[0]</option>' w apostrofach wypisze dosłownie „$row[0]” - PHP nie podstawia zmiennych w apostrofach.

    index.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'szkolenia');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Szkolenia i kursy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>SZKOLENIA</h1>
        </header>
        <main>
            <section id="left">
                <table>
                    <tr>
                        <th>Kurs</th>
                        <th>Nazwa</th>
                        <th>Cena</th>
                    </tr>
    <?php
    $courses = mysqli_query($connection, 'SELECT kod, nazwa, cena FROM kursy ORDER BY cena;');
    while ($row = mysqli_fetch_row($courses)) {
        echo '<tr>';
        echo "<td><img src='$row[0].jpg' alt='kurs'></td>";
        echo "<td>$row[1]</td><td>$row[2]</td>";
        echo '</tr>';
    }
    ?>
                </table>
            </section>
            <section id="right">
                <h2>Zapisy na kursy</h2>
                <form method="post">
                    <label for="name">Imię</label><br>
                    <input type="text" id="name" name="name"><br>
                    <label for="surname">Nazwisko</label><br>
                    <input type="text" id="surname" name="surname"><br>
                    <label for="age">Wiek</label><br>
                    <input type="number" id="age" name="age"><br>
                    <label for="course">Rodzaj kursu</label><br>
                    <select id="course" name="course">
    <?php
    $names = mysqli_query($connection, 'SELECT nazwa FROM kursy;');
    while ($row = mysqli_fetch_row($names)) {
        echo "<option>$row[0]</option>";
    }
    ?>
                    </select><br>
                    <button type="submit">Dodaj dane</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  39. Krok 39: Skrypt 3: czy wpisano imię, nazwisko i wiek

    Kryteria:

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

    ok. 2 min
    Polecenie z arkusza

    Skrypt 3: wykonywany po kliknięciu przycisku „Dodaj dane”, jeżeli imię, nazwisko i wiek zostały wprowadzone w formularzu.

    Skrypt 3 piszesz pod formularzem - tam ma się pojawić komunikat. Warunek „zostały wprowadzone” to więcej niż isset z tabeli 1: po kliknięciu przycisku klucze name, surname i age zawsze są w $_POST, nawet gdy pola zostały puste. Dlatego !empty() - prawdziwe tylko wtedy, gdy wartość istnieje i nie jest pustym napisem; trzy warunki łączy && (wszystkie naraz). Klucz R.5.9 sprawdza, że dane są czytane z $_POST - tablicy odpowiadającej metodzie formularza.

    Typowe błędy

    Samo isset($_POST['name']) jest prawdziwe po każdym kliknięciu przycisku, także z pustymi polami - do bazy trafiłby uczestnik bez imienia.

    index.php PHP zmienione: 4 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'szkolenia');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Szkolenia i kursy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>SZKOLENIA</h1>
        </header>
        <main>
            <section id="left">
                <table>
                    <tr>
                        <th>Kurs</th>
                        <th>Nazwa</th>
                        <th>Cena</th>
                    </tr>
    <?php
    $courses = mysqli_query($connection, 'SELECT kod, nazwa, cena FROM kursy ORDER BY cena;');
    while ($row = mysqli_fetch_row($courses)) {
        echo '<tr>';
        echo "<td><img src='$row[0].jpg' alt='kurs'></td>";
        echo "<td>$row[1]</td><td>$row[2]</td>";
        echo '</tr>';
    }
    ?>
                </table>
            </section>
            <section id="right">
                <h2>Zapisy na kursy</h2>
                <form method="post">
                    <label for="name">Imię</label><br>
                    <input type="text" id="name" name="name"><br>
                    <label for="surname">Nazwisko</label><br>
                    <input type="text" id="surname" name="surname"><br>
                    <label for="age">Wiek</label><br>
                    <input type="number" id="age" name="age"><br>
                    <label for="course">Rodzaj kursu</label><br>
                    <select id="course" name="course">
    <?php
    $names = mysqli_query($connection, 'SELECT nazwa FROM kursy;');
    while ($row = mysqli_fetch_row($names)) {
        echo "<option>$row[0]</option>";
    }
    ?>
                    </select><br>
                    <button type="submit">Dodaj dane</button>
                </form>
    <?php
    if (!empty($_POST['name']) && !empty($_POST['surname']) && !empty($_POST['age'])) {
    }
    ?>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  40. Krok 40: Skrypt 3: zapytanie 3 z danymi z formularza

    Kryteria:

    R.5.7 Skrypt 3 sprawdza czy dane zostały wysłane z formularza, jeśli tak, to wysyła do bazy danych zmodyfikowane zapytanie 3, z wstawionymi danymi z formularza oraz wyświetla tekst „Dane uczestnika <imię> <nazwisko> zostały dodane”, gdzie pola <> są wysłane z formularza (sposób alternatywny z atrybutem required, wstawienie rekordu sprawdzić w phpMyAdmin)

    ok. 3 min
    Polecenie z arkusza

    Wysyła do bazy danych zapytanie 3, zmodyfikowane tak, że dane wysłane z formularza są wstawione do zapytania.

    Trzy wartości z $_POST do zmiennych o znaczących nazwach, a potem zapytanie 3 z kwerendy.txt dostaje je w miejsce „Tadeusz”, „Wysocki” i 36: napisy w apostrofach wewnątrz SQL, liczba bez. INSERT nic nie zwraca do czytania, więc mysqli_query wywołujesz bez zapisywania wyniku. Podgląd pokazuje stan po wysłaniu formularza z ilustracji 5 (Magdalena Janowska, 35) - wiersz jest już w bazie, ale strona jeszcze o tym nie mówi.

    index.php PHP zmienione: 4 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'szkolenia');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Szkolenia i kursy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>SZKOLENIA</h1>
        </header>
        <main>
            <section id="left">
                <table>
                    <tr>
                        <th>Kurs</th>
                        <th>Nazwa</th>
                        <th>Cena</th>
                    </tr>
    <?php
    $courses = mysqli_query($connection, 'SELECT kod, nazwa, cena FROM kursy ORDER BY cena;');
    while ($row = mysqli_fetch_row($courses)) {
        echo '<tr>';
        echo "<td><img src='$row[0].jpg' alt='kurs'></td>";
        echo "<td>$row[1]</td><td>$row[2]</td>";
        echo '</tr>';
    }
    ?>
                </table>
            </section>
            <section id="right">
                <h2>Zapisy na kursy</h2>
                <form method="post">
                    <label for="name">Imię</label><br>
                    <input type="text" id="name" name="name"><br>
                    <label for="surname">Nazwisko</label><br>
                    <input type="text" id="surname" name="surname"><br>
                    <label for="age">Wiek</label><br>
                    <input type="number" id="age" name="age"><br>
                    <label for="course">Rodzaj kursu</label><br>
                    <select id="course" name="course">
    <?php
    $names = mysqli_query($connection, 'SELECT nazwa FROM kursy;');
    while ($row = mysqli_fetch_row($names)) {
        echo "<option>$row[0]</option>";
    }
    ?>
                    </select><br>
                    <button type="submit">Dodaj dane</button>
                </form>
    <?php
    if (!empty($_POST['name']) && !empty($_POST['surname']) && !empty($_POST['age'])) {
        $name = $_POST['name'];
        $surname = $_POST['surname'];
        $age = $_POST['age'];
        mysqli_query($connection, "INSERT INTO uczestnicy (imie, nazwisko, wiek) VALUES ('$name', '$surname', $age);");
    }
    ?>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

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

  41. Krok 41: Skrypt 3: paragraf z potwierdzeniem

    Kryteria:

    R.5.7 Skrypt 3 sprawdza czy dane zostały wysłane z formularza, jeśli tak, to wysyła do bazy danych zmodyfikowane zapytanie 3, z wstawionymi danymi z formularza oraz wyświetla tekst „Dane uczestnika <imię> <nazwisko> zostały dodane”, gdzie pola <> są wysłane z formularza (sposób alternatywny z atrybutem required, wstawienie rekordu sprawdzić w phpMyAdmin)

    ok. 1 min
    Polecenie z arkusza

    Wyświetla paragraf o treści „Dane uczestnika <imię> <nazwisko> zostały dodane”, gdzie pola <> oznaczają wartości wysłane z formularza.

    echo z paragrafem składa komunikat z imienia i nazwiska z formularza - tych samych zmiennych, które poszły do bazy. Podgląd: „Dane uczestnika Magdalena Janowska zostały dodane”, jak na ilustracji 5.

    index.php PHP zmienione: 1 linia
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'szkolenia');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Szkolenia i kursy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>SZKOLENIA</h1>
        </header>
        <main>
            <section id="left">
                <table>
                    <tr>
                        <th>Kurs</th>
                        <th>Nazwa</th>
                        <th>Cena</th>
                    </tr>
    <?php
    $courses = mysqli_query($connection, 'SELECT kod, nazwa, cena FROM kursy ORDER BY cena;');
    while ($row = mysqli_fetch_row($courses)) {
        echo '<tr>';
        echo "<td><img src='$row[0].jpg' alt='kurs'></td>";
        echo "<td>$row[1]</td><td>$row[2]</td>";
        echo '</tr>';
    }
    ?>
                </table>
            </section>
            <section id="right">
                <h2>Zapisy na kursy</h2>
                <form method="post">
                    <label for="name">Imię</label><br>
                    <input type="text" id="name" name="name"><br>
                    <label for="surname">Nazwisko</label><br>
                    <input type="text" id="surname" name="surname"><br>
                    <label for="age">Wiek</label><br>
                    <input type="number" id="age" name="age"><br>
                    <label for="course">Rodzaj kursu</label><br>
                    <select id="course" name="course">
    <?php
    $names = mysqli_query($connection, 'SELECT nazwa FROM kursy;');
    while ($row = mysqli_fetch_row($names)) {
        echo "<option>$row[0]</option>";
    }
    ?>
                    </select><br>
                    <button type="submit">Dodaj dane</button>
                </form>
    <?php
    if (!empty($_POST['name']) && !empty($_POST['surname']) && !empty($_POST['age'])) {
        $name = $_POST['name'];
        $surname = $_POST['surname'];
        $age = $_POST['age'];
        mysqli_query($connection, "INSERT INTO uczestnicy (imie, nazwisko, wiek) VALUES ('$name', '$surname', $age);");
        echo "<p>Dane uczestnika $name $surname zostały dodane</p>";
    }
    ?>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  42. Krok 42: Skrypt 3: komunikat o brakujących danych

    Kryteria:

    R.5.8 Skrypt 3 w przeciwnym do R.5.7 przypadku wyświetla tekst „Wprowadź wszystkie dane”

    ok. 1 min
    Polecenie z arkusza

    W przeciwnym wypadku wyświetla paragraf „Wprowadź wszystkie dane”.

    Gałąź else wykonuje się, gdy którekolwiek z trzech pól jest puste - a także przy pierwszym wejściu na stronę, gdy formularza nikt jeszcze nie wysłał. Dlatego na ilustracji 2 komunikat „Wprowadź wszystkie dane” widać od razu po otwarciu strony (klucz R.5.8). Podgląd wraca do tego stanu.

    index.php PHP zmienione: 2 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'szkolenia');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Szkolenia i kursy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>SZKOLENIA</h1>
        </header>
        <main>
            <section id="left">
                <table>
                    <tr>
                        <th>Kurs</th>
                        <th>Nazwa</th>
                        <th>Cena</th>
                    </tr>
    <?php
    $courses = mysqli_query($connection, 'SELECT kod, nazwa, cena FROM kursy ORDER BY cena;');
    while ($row = mysqli_fetch_row($courses)) {
        echo '<tr>';
        echo "<td><img src='$row[0].jpg' alt='kurs'></td>";
        echo "<td>$row[1]</td><td>$row[2]</td>";
        echo '</tr>';
    }
    ?>
                </table>
            </section>
            <section id="right">
                <h2>Zapisy na kursy</h2>
                <form method="post">
                    <label for="name">Imię</label><br>
                    <input type="text" id="name" name="name"><br>
                    <label for="surname">Nazwisko</label><br>
                    <input type="text" id="surname" name="surname"><br>
                    <label for="age">Wiek</label><br>
                    <input type="number" id="age" name="age"><br>
                    <label for="course">Rodzaj kursu</label><br>
                    <select id="course" name="course">
    <?php
    $names = mysqli_query($connection, 'SELECT nazwa FROM kursy;');
    while ($row = mysqli_fetch_row($names)) {
        echo "<option>$row[0]</option>";
    }
    ?>
                    </select><br>
                    <button type="submit">Dodaj dane</button>
                </form>
    <?php
    if (!empty($_POST['name']) && !empty($_POST['surname']) && !empty($_POST['age'])) {
        $name = $_POST['name'];
        $surname = $_POST['surname'];
        $age = $_POST['age'];
        mysqli_query($connection, "INSERT INTO uczestnicy (imie, nazwisko, wiek) VALUES ('$name', '$surname', $age);");
        echo "<p>Dane uczestnika $name $surname zostały dodane</p>";
    } else {
        echo '<p>Wprowadź wszystkie dane</p>';
    }
    ?>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  43. Krok 43: Zamknięcie połączenia z bazą

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

    Na końcu zamykane jest połączenie z bazą.

    mysqli_close($connection) na końcu pliku, po wszystkich trzech skryptach. Klucz R.5.2 sprawdza, że zamknięcie jest ostatnią operacją na bazie. Strona spełnia wszystkie kryteria arkusza - dalej trzy kroki „jak w prawdziwym projekcie”.

    Typowe błędy

    mysqli_close po skrypcie 1 sprawia, że skrypty 2 i 3 idą po zamkniętym połączeniu - lista kursów zostaje pusta, a zapis nie działa.

    index.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'szkolenia');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Szkolenia i kursy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>SZKOLENIA</h1>
        </header>
        <main>
            <section id="left">
                <table>
                    <tr>
                        <th>Kurs</th>
                        <th>Nazwa</th>
                        <th>Cena</th>
                    </tr>
    <?php
    $courses = mysqli_query($connection, 'SELECT kod, nazwa, cena FROM kursy ORDER BY cena;');
    while ($row = mysqli_fetch_row($courses)) {
        echo '<tr>';
        echo "<td><img src='$row[0].jpg' alt='kurs'></td>";
        echo "<td>$row[1]</td><td>$row[2]</td>";
        echo '</tr>';
    }
    ?>
                </table>
            </section>
            <section id="right">
                <h2>Zapisy na kursy</h2>
                <form method="post">
                    <label for="name">Imię</label><br>
                    <input type="text" id="name" name="name"><br>
                    <label for="surname">Nazwisko</label><br>
                    <input type="text" id="surname" name="surname"><br>
                    <label for="age">Wiek</label><br>
                    <input type="number" id="age" name="age"><br>
                    <label for="course">Rodzaj kursu</label><br>
                    <select id="course" name="course">
    <?php
    $names = mysqli_query($connection, 'SELECT nazwa FROM kursy;');
    while ($row = mysqli_fetch_row($names)) {
        echo "<option>$row[0]</option>";
    }
    ?>
                    </select><br>
                    <button type="submit">Dodaj dane</button>
                </form>
    <?php
    if (!empty($_POST['name']) && !empty($_POST['surname']) && !empty($_POST['age'])) {
        $name = $_POST['name'];
        $surname = $_POST['surname'];
        $age = $_POST['age'];
        mysqli_query($connection, "INSERT INTO uczestnicy (imie, nazwisko, wiek) VALUES ('$name', '$surname', $age);");
        echo "<p>Dane uczestnika $name $surname zostały dodane</p>";
    } else {
        echo '<p>Wprowadź wszystkie dane</p>';
    }
    ?>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    <?php
    mysqli_close($connection);
    ?>
    </body>
    </html>

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

  44. Etap 5: Dobre praktyki (poza kluczem)

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

    Krok 44: Refaktor: zapytanie przygotowane dla INSERT

    Kryteria:

    R.5.7 Skrypt 3 sprawdza czy dane zostały wysłane z formularza, jeśli tak, to wysyła do bazy danych zmodyfikowane zapytanie 3, z wstawionymi danymi z formularza oraz wyświetla tekst „Dane uczestnika <imię> <nazwisko> zostały dodane”, gdzie pola <> są wysłane z formularza (sposób alternatywny z atrybutem required, wstawienie rekordu sprawdzić w phpMyAdmin)

    ok. 4 min
    Polecenie z arkusza

    Wysyła do bazy danych zapytanie 3, zmodyfikowane tak, że dane wysłane z formularza są wstawione do zapytania.

    Imię i nazwisko z formularza szły prosto do INSERT - to SQL injection, i to w najgroźniejszej odmianie, bo zapytanie zapisuje: nazwisko Nowak'); DROP TABLE uczestnicy; -- dokleiłoby drugie polecenie. W prawdziwych projektach dane trafiają do zapytania przez zapytanie przygotowane: mysqli_prepare z trzema ?, mysqli_stmt_bind_param z typami 'ssi' (dwa napisy i liczba całkowita) i mysqli_stmt_execute. Stara linia zostaje w komentarzu. Podgląd bez zmian.

    Typowe błędy

    Liczba liter w 'ssi' musi równać się liczbie znaków ? w zapytaniu i liczbie zmiennych - inaczej mysqli_stmt_bind_param zgłosi błąd i wiersz nie trafi do bazy.

    index.php PHP zmienione: 4 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'szkolenia');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Szkolenia i kursy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>SZKOLENIA</h1>
        </header>
        <main>
            <section id="left">
                <table>
                    <tr>
                        <th>Kurs</th>
                        <th>Nazwa</th>
                        <th>Cena</th>
                    </tr>
    <?php
    $courses = mysqli_query($connection, 'SELECT kod, nazwa, cena FROM kursy ORDER BY cena;');
    while ($row = mysqli_fetch_row($courses)) {
        echo '<tr>';
        echo "<td><img src='$row[0].jpg' alt='kurs'></td>";
        echo "<td>$row[1]</td><td>$row[2]</td>";
        echo '</tr>';
    }
    ?>
                </table>
            </section>
            <section id="right">
                <h2>Zapisy na kursy</h2>
                <form method="post">
                    <label for="name">Imię</label><br>
                    <input type="text" id="name" name="name"><br>
                    <label for="surname">Nazwisko</label><br>
                    <input type="text" id="surname" name="surname"><br>
                    <label for="age">Wiek</label><br>
                    <input type="number" id="age" name="age"><br>
                    <label for="course">Rodzaj kursu</label><br>
                    <select id="course" name="course">
    <?php
    $names = mysqli_query($connection, 'SELECT nazwa FROM kursy;');
    while ($row = mysqli_fetch_row($names)) {
        echo "<option>$row[0]</option>";
    }
    ?>
                    </select><br>
                    <button type="submit">Dodaj dane</button>
                </form>
    <?php
    if (!empty($_POST['name']) && !empty($_POST['surname']) && !empty($_POST['age'])) {
        $name = $_POST['name'];
        $surname = $_POST['surname'];
        $age = $_POST['age'];
        // mysqli_query($connection, "INSERT INTO uczestnicy (imie, nazwisko, wiek) VALUES ('$name', '$surname', $age);");
        $statement = mysqli_prepare($connection, 'INSERT INTO uczestnicy (imie, nazwisko, wiek) VALUES (?, ?, ?)');
        mysqli_stmt_bind_param($statement, 'ssi', $name, $surname, $age);
        mysqli_stmt_execute($statement);
        echo "<p>Dane uczestnika $name $surname zostały dodane</p>";
    } else {
        echo '<p>Wprowadź wszystkie dane</p>';
    }
    ?>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    <?php
    mysqli_close($connection);
    ?>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  45. Krok 45: Refaktor: escapowanie danych przed wypisaniem

    Kryteria: ,

    R.5.4 Skrypt 1 dla każdego zwróconego przez bazę rekordu, wyświetla w II i III kolumnie tabeli kolejne nazwy i ceny kursów

    R.5.6 Skrypt 2 wyświetla nazwy kursów jako elementy listy rozwijanej w znaczniku <option>

    ok. 3 min
    Polecenie z arkusza

    Wyświetla paragraf o treści „Dane uczestnika <imię> <nazwisko> zostały dodane”, gdzie pola <> oznaczają wartości wysłane z formularza.

    Skrypt 3 wypisuje do HTML imię i nazwisko wprost z formularza, a skrypty 1 i 2 nazwy kursów z bazy, które kiedyś też ktoś wpisał. Napis <script>…</script> w polu „Imię” wykonałby się w przeglądarce (XSS). htmlspecialchars() zamienia <, >, ", & na encje, więc tekst wyświetla się dosłownie i nie staje się kodem. Do bazy idą dane surowe ($name), na ekran ich bezpieczne kopie ($safeName) - escapujesz na wyjściu, nie przy zapisie. Ceny zostają - liczby nie niosą znaczników. Podgląd bez zmian.

    Typowe błędy

    htmlspecialchars przed INSERT zapisałoby do bazy &lt; zamiast < - zniekształcone dane, które później trzeba odkręcać.

    index.php PHP zmienione: 7 linii
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'szkolenia');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Szkolenia i kursy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>SZKOLENIA</h1>
        </header>
        <main>
            <section id="left">
                <table>
                    <tr>
                        <th>Kurs</th>
                        <th>Nazwa</th>
                        <th>Cena</th>
                    </tr>
    <?php
    $courses = mysqli_query($connection, 'SELECT kod, nazwa, cena FROM kursy ORDER BY cena;');
    while ($row = mysqli_fetch_row($courses)) {
        echo '<tr>';
        echo "<td><img src='$row[0].jpg' alt='kurs'></td>";
        $courseName = htmlspecialchars($row[1]);
        echo "<td>$courseName</td><td>$row[2]</td>";
        echo '</tr>';
    }
    ?>
                </table>
            </section>
            <section id="right">
                <h2>Zapisy na kursy</h2>
                <form method="post">
                    <label for="name">Imię</label><br>
                    <input type="text" id="name" name="name"><br>
                    <label for="surname">Nazwisko</label><br>
                    <input type="text" id="surname" name="surname"><br>
                    <label for="age">Wiek</label><br>
                    <input type="number" id="age" name="age"><br>
                    <label for="course">Rodzaj kursu</label><br>
                    <select id="course" name="course">
    <?php
    $names = mysqli_query($connection, 'SELECT nazwa FROM kursy;');
    while ($row = mysqli_fetch_row($names)) {
        $courseName = htmlspecialchars($row[0]);
        echo "<option>$courseName</option>";
    }
    ?>
                    </select><br>
                    <button type="submit">Dodaj dane</button>
                </form>
    <?php
    if (!empty($_POST['name']) && !empty($_POST['surname']) && !empty($_POST['age'])) {
        $name = $_POST['name'];
        $surname = $_POST['surname'];
        $age = $_POST['age'];
        // mysqli_query($connection, "INSERT INTO uczestnicy (imie, nazwisko, wiek) VALUES ('$name', '$surname', $age);");
        $statement = mysqli_prepare($connection, 'INSERT INTO uczestnicy (imie, nazwisko, wiek) VALUES (?, ?, ?)');
        mysqli_stmt_bind_param($statement, 'ssi', $name, $surname, $age);
        mysqli_stmt_execute($statement);
        $safeName = htmlspecialchars($name);
        $safeSurname = htmlspecialchars($surname);
        echo "<p>Dane uczestnika $safeName $safeSurname zostały dodane</p>";
    } else {
        echo '<p>Wprowadź wszystkie dane</p>';
    }
    ?>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    <?php
    mysqli_close($connection);
    ?>
    </body>
    </html>

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

  46. Krok 46: Refaktor: oczyszczenie i rzutowanie danych

    ok. 2 min
    Polecenie z arkusza

    Skrypt 3 działa tylko wtedy, gdy imię, nazwisko i wiek zostały wprowadzone w formularzu.

    Dwie poprawki na to, co przychodzi z formularza:

    • trim() zdejmuje spacje z początku i końca imienia i nazwiska - bez tego „ Magdalena ” ze spacją trafiłaby do bazy ze spacją i nie znalazłoby jej później żadne wyszukiwanie,
    • (int) rzutuje wiek na liczbę całkowitą: type="number" to tylko wygoda w przeglądarce, a żądanie POST można wysłać bez niej. Po rzutowaniu typ 'i' w bindowaniu dostaje naprawdę liczbę.

    Na egzaminie wystarczy wersja z kroków wcześniejszych - klucz tego nie sprawdza. To już kod gotowy do prawdziwego użycia.

    Typowe błędy

    Walidacja po stronie przeglądarki nie zastępuje walidacji w PHP - formularz można wysłać z dowolnymi wartościami. Uwaga na kolejność: !empty sprawdza tu wartość przed trim(), więc pole z samymi spacjami przejdzie warunek i trafi do bazy jako pusty napis - pełna walidacja sprawdza wartość już po trim() (tak robi wersja nowoczesna).

    index.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'szkolenia');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Szkolenia i kursy</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>SZKOLENIA</h1>
        </header>
        <main>
            <section id="left">
                <table>
                    <tr>
                        <th>Kurs</th>
                        <th>Nazwa</th>
                        <th>Cena</th>
                    </tr>
    <?php
    $courses = mysqli_query($connection, 'SELECT kod, nazwa, cena FROM kursy ORDER BY cena;');
    while ($row = mysqli_fetch_row($courses)) {
        echo '<tr>';
        echo "<td><img src='$row[0].jpg' alt='kurs'></td>";
        $courseName = htmlspecialchars($row[1]);
        echo "<td>$courseName</td><td>$row[2]</td>";
        echo '</tr>';
    }
    ?>
                </table>
            </section>
            <section id="right">
                <h2>Zapisy na kursy</h2>
                <form method="post">
                    <label for="name">Imię</label><br>
                    <input type="text" id="name" name="name"><br>
                    <label for="surname">Nazwisko</label><br>
                    <input type="text" id="surname" name="surname"><br>
                    <label for="age">Wiek</label><br>
                    <input type="number" id="age" name="age"><br>
                    <label for="course">Rodzaj kursu</label><br>
                    <select id="course" name="course">
    <?php
    $names = mysqli_query($connection, 'SELECT nazwa FROM kursy;');
    while ($row = mysqli_fetch_row($names)) {
        $courseName = htmlspecialchars($row[0]);
        echo "<option>$courseName</option>";
    }
    ?>
                    </select><br>
                    <button type="submit">Dodaj dane</button>
                </form>
    <?php
    if (!empty($_POST['name']) && !empty($_POST['surname']) && !empty($_POST['age'])) {
        $name = trim($_POST['name']);
        $surname = trim($_POST['surname']);
        $age = (int) $_POST['age'];
        // mysqli_query($connection, "INSERT INTO uczestnicy (imie, nazwisko, wiek) VALUES ('$name', '$surname', $age);");
        $statement = mysqli_prepare($connection, 'INSERT INTO uczestnicy (imie, nazwisko, wiek) VALUES (?, ?, ?)');
        mysqli_stmt_bind_param($statement, 'ssi', $name, $surname, $age);
        mysqli_stmt_execute($statement);
        $safeName = htmlspecialchars($name);
        $safeSurname = htmlspecialchars($surname);
        echo "<p>Dane uczestnika $safeName $safeSurname zostały dodane</p>";
    } else {
        echo '<p>Wprowadź wszystkie dane</p>';
    }
    ?>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    <?php
    mysqli_close($connection);
    ?>
    </body>
    </html>

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

  47. Etap 6: Oddanie pracy

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

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

    ok. 5 min
    Polecenie z arkusza

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

    W przeglądarka.txt wpisujesz nazwę przeglądarki, w której sprawdzasz stronę przez http://localhost/….

    W folderze z numerem zdającego muszą być: obrazy 1.jpg … 8.jpg, import.png, index.php, kw1.jpg … kw4.jpg, kwerendy.txt, przeglądarka.txt, styl.css. Nagraj płytę, sprawdź jej odczyt i opisz numerem zdającego. Pusta lub nieczytelna płyta to zero punktów za wszystkie pięć rezultatów.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

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

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.