Przejdź do głównej treści
INF.03-01-21.06-SGCzerwiec 2021 · 35 kroków · ok. 73 min · 36 kryteriów z klucza

Rozwiązanie krok po kroku: Aplikacja internetowa restauracji (Nowoczesny kod (flex, PDO))

To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza baza z czterema zapytaniami (INSERT, dwa SELECT, UPDATE), strona restauracja.html z formularzem rezerwacji, style w styl_1.css i skrypt rezerwacja.php, który dopisuje rezerwację do bazy - i tak samo spełnia każde kryterium klucza. Inna jest droga:

  • flexbox zamiast float: panele lewy, prawy i dolny stoją we wspólnym znaczniku <main>, który ustawia je w rzędzie i zawija panel dolny do nowej linii - bez float i clear. Szerokość 60% zostaje na panelu lewym, tak jak ocenia ją klucz,
  • zmienne CSS dla kolorów z arkusza, panele nazwane po treści (#dish, #offer, #booking), wiersze formularza w blokach <div> zamiast <br>, pochylenie numeru zdającego z arkusza stylów,
  • PHP na PDO z wyjątkami: dane z formularza są sprawdzane (data w formacie rrrr-mm-dd, liczba osób co najmniej 1, numer telefonu), a do bazy trafiają zapytaniem przygotowanym,
  • bezpieczne wypisywanie (htmlspecialchars w funkcji e()), komunikaty przy błędnych danych i awarii bazy oraz wzorzec PRG - odświeżenie strony po rezerwacji nie dopisuje jej drugi raz.

Na końcu krótkie porównanie z wersją podstawową (zakładka obok). Baza i zdjęcie pochodzą z prawdziwego archiwum zad1, a podgląd skryptu to wynik prawdziwego uruchomienia kodu w PHP na bazie z arkusza, po wysłaniu formularza.

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

  1. Etap 1: Baza danych

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

    Krok 1: Baza danych baza

    Kryteria:

    R.1.1 Wykonano import tabel do bazy danych o nazwie baza , czynność udokumentowano plikiem o nazwie zad1 w formacie JPEG

    ok. 2 min
    Polecenie z arkusza

    Utwórz nową bazę danych o nazwie baza.

    Rozpakuj zad1.zip z pulpitu (hasło Wykon@jTo) do folderu z numerem zdającego: baza.sql i menu.jpg. W phpMyAdmin Nowa → baza albo zapytaniem:

    CREATE DATABASE baza CHARACTER SET utf8mb4 COLLATE utf8mb4_polish_ci;
    

    Arkusz nie podaje porównywania, więc wybór należy do Ciebie. Tabele z baza.sql mają zestaw znaków (latin1) zapisany we własnej definicji, więc ustawienie bazy ich nie zmienia.

    Typowe błędy

    Nazwa jest nietypowa i łatwo ją „poprawić” na restauracja - skrypt łączy się z bazą baza (R.5.2).

  2. Krok 2: Import tabel i zrzut zad1.jpg

    Kryteria:

    R.1.1 Wykonano import tabel do bazy danych o nazwie baza , czynność udokumentowano plikiem o nazwie zad1 w formacie JPEG

    ok. 3 min
    Polecenie z arkusza

    Zaimportuj do bazy tabele z pliku baza.sql, z wcześniej rozpakowanego archiwum. Wykonaj zrzut ekranu po imporcie. Zrzut zapisz w formacie JPEG i nazwij zad1. Nie kadruj zrzutu. Zrzut powinien obejmować cały ekran, z widocznym paskiem zadań. Na zrzucie powinny być widoczne elementy wskazujące na poprawnie wykonany import tabel.

    Import → baza.sql → Wykonaj. Plik tworzy kilka tabel; zadanie korzysta z trzech z obrazu 1: dania (typ: 1 zupy, 2 mięsne, 3 przekąski, 4 napoje), pracownicy (stanowisko: 1 kucharz, 2 pomoc, 3 kelner, 4 barman) i pustej rezerwacje. Zrzut zapisz jako zad1.jpg - w tym arkuszu tak nazywa się zrzut importu.

    Typowe błędy

    import.jpg zamiast zad1.jpg - klucz R.1.1 sprawdza nazwę pliku.

  3. Krok 3: Zapytanie 1: nowa rezerwacja (INSERT)

    Kryteria: ,

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

    R.1.3 Utworzono zapytanie 1 wstawiające do tabeli rezerwacje rekord z danymi: 1, '2017-07-04', 4, 111222333. pole id jest nadawane automatycznie. W pliku z kwerendami lub na zrzucie istnieje zapis np.: INSERT INTO rezerwacje(`nr_stolika`, `data_rez`, `liczba_osob`, `telefon`) VALUES (1, "2017-07-04", 4, "111222333"); (możliwe również INSERT INTO rezerwacje VALUES (...) lub INSERT INTO rezerwacje SET (lista_przypisań) oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczne jest poprawne zapytanie lub tabela z dodanym rekordem

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 1: wstawiające do tabeli rezerwacje rekord z danymi: numer stolika = 1, data=„2017-07-04”, liczba osób = 4, numer telefonu = „111222333”, wartość pola id nadawana jest automatycznie.

    INSERT INTO z jawną listą kolumn - po nazwie tabeli kolumny, po VALUES wartości w tej samej kolejności. Kolumny id nie ma na liście: ma AUTO_INCREMENT, więc numer nada baza. Data i telefon to teksty, więc w apostrofach; numer stolika i liczba osób - bez. Lista kolumn sprawia, że zapytanie nie zależy od kolejności kolumn w tabeli. Każda część w osobnej linii. Zrzut kw1.jpg pokazuje tabelę z nowym rekordem (id = 1), zapytanie trafia do kwerendy.txt.

    Typowe błędy

    Bez listy kolumn (INSERT INTO rezerwacje VALUES (1, ...)) pierwsza wartość trafi do id - stolik 1 stałby się identyfikatorem.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    INSERT INTO rezerwacje (nr_stolika, data_rez, liczba_osob, telefon)
    VALUES (1, '2017-07-04', 4, '111222333');
    Podgląd strony po tym kroku
    Podgląd strony po kroku 3
  4. Krok 4: Zapytanie 2: dania mięsne

    Kryteria:

    R.1.4 Utworzono zapytanie 2 wybierające z tabeli dania jedynie pola nazwa i cena dla dań mięsnych. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa, cena FROM dania WHERE typ = 2; oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczne jest dokładnie 5 rekordów: Kaczka i owoc 30, Kurczak pieczony 40, wieprzowy przysmak 35, Mintaj w panierce 30, Alle kotlet 30

    ok. 1 min
    Polecenie z arkusza

    Zapytanie 2: wybierające z tabeli dania jedynie pola nazwa i cena dla dań mięsnych.

    Dania mięsne to typ = 2. Wynik (sprawdzony na bazie z archiwum): 5 dań, od „Kaczka i owoc” do „Alle kotlet” - jak w kluczu. Zrzut kw2.jpg.

    kwerendy.txt Tekst zmienione: 5 linii
    -- Zapytanie 1
    INSERT INTO rezerwacje (nr_stolika, data_rez, liczba_osob, telefon)
    VALUES (1, '2017-07-04', 4, '111222333');
    
    -- Zapytanie 2
    SELECT nazwa, cena
    FROM dania
    WHERE typ = 2;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 3: tanie przekąski

    Kryteria:

    R.1.5 Utworzono zapytanie 3 wybierające z tabeli dania jedynie pole nazwa dla przekąsek, których cena jest mniejsza od 15 zł. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa FROM dania WHERE typ = 3 AND cena < 15; oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczne są jedynie nazwy: Orzechy i chipsy, Bukiet warzyw

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 3: wybierające z tabeli dania jedynie pole nazwa dla takich przekąsek, których cena jest mniejsza od 15 zł.

    Dwa warunki z AND: przekąska (typ = 3) i cena mniejsza od 15 (< - przekąski za równe 15 zł odpadają). Wynik: „Orzechy i chipsy” i „Bukiet warzyw”. Zrzut kw3.jpg.

    kwerendy.txt Tekst zmienione: 5 linii
    -- Zapytanie 1
    INSERT INTO rezerwacje (nr_stolika, data_rez, liczba_osob, telefon)
    VALUES (1, '2017-07-04', 4, '111222333');
    
    -- Zapytanie 2
    SELECT nazwa, cena
    FROM dania
    WHERE typ = 2;
    
    -- Zapytanie 3
    SELECT nazwa
    FROM dania
    WHERE typ = 3 AND cena < 15;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 4: kelnerzy zostają barmanami (UPDATE)

    Kryteria:

    R.1.6 Utworzono zapytanie 4 aktualizujące dane w tabeli pracownicy . Dla wszystkich pracowników, którzy zatrudnieni są jako kelner (stanowisko = 3), należy dokonać zmiany stanowiska na barman (stanowisko = 4). W pliku z kwerendami lub na zrzucie istnieje zapis np.: UPDATE pracownicy SET stanowisko = 4 WHERE stanowisko = 3; oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczne jest prawidłowe zapytanie lub rekordy ze zmienionymi danymi stanowisko = 4 zmieniono dla rekordów z danymi Anna Przybylska, Maria Kowal oraz pozostało stanowisko = 4 dla Ewy Nowackiej

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 4: aktualizujące dane w tabeli pracownicy. Dla wszystkich pracowników, którzy zatrudnieni są jako kelner, należy dokonać zmiany stanowiska na barman.

    UPDATE ... SET ... WHERE zmienia istniejące wiersze. Kelner to 3, barman to 4. Zmieniają się dwie osoby - Anna Przybylska i Maria Kowal; Ewa Nowacka już była barmanką (sprawdzone na bazie z archiwum). Zrzut kw4.jpg pokazuje tabelę po zmianie.

    Typowe błędy

    UPDATE bez WHERE zmienia stanowisko WSZYSTKIM pracownikom - phpMyAdmin wykona to bez pytania.

    kwerendy.txt Tekst zmienione: 5 linii
    -- Zapytanie 1
    INSERT INTO rezerwacje (nr_stolika, data_rez, liczba_osob, telefon)
    VALUES (1, '2017-07-04', 4, '111222333');
    
    -- Zapytanie 2
    SELECT nazwa, cena
    FROM dania
    WHERE typ = 2;
    
    -- Zapytanie 3
    SELECT nazwa
    FROM dania
    WHERE typ = 3 AND cena < 15;
    
    -- Zapytanie 4
    UPDATE pracownicy
    SET stanowisko = 4
    WHERE stanowisko = 3;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Etap 2: Strona HTML

    Kroki 7-17 · po tym etapie masz 18 z 36 kryteriów (pkt)

    Krok 7: Plik restauracja.html: HTML5, język, kodowanie

    Kryteria:

    R.2.1 Witrynę internetową zapisano w pliku restauracja.html oraz zastosowano prawidłowy standard kodowania polskich znaków lub umieszczono prolog języka HTML 5

    ok. 2 min
    Polecenie z arkusza

    Wymagania dotyczące witryny: Nazwa pliku: restauracja.html. Zastosowany język HTML 5. Zastosowany właściwy standard kodowania polskich znaków.

    <!DOCTYPE html>, <html lang="pl"> (przeglądarka i czytnik ekranu wiedzą, że treść jest po polsku) i <meta charset="UTF-8"> jako pierwszy element <head> (R.2.1). Strona jest zwykłym plikiem .html - kod PHP będzie w osobnym pliku.

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

    Kryteria: ,

    R.2.2 Nadano tytuł strony: "Restauracja Wszystkie Smaki”

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

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony widoczny na karcie przeglądarki: „Restauracja Wszystkie Smaki”. Arkusz stylów w pliku o nazwie styl_1.css prawidłowo połączony z kodem strony.

    <title> (R.2.2), <link> do styl_1.css - z podkreśleniem i cyfrą - i viewport, żeby telefon nie pomniejszał strony do szerokości ekranu komputera. Całe formatowanie będzie w arkuszu stylów, bez atrybutów style w HTML-u (R.3.4).

    Typowe błędy

    Klucz odbiera R.3.4 także wtedy, gdy choć jeden selektor nie trafia w blok (np. kropka zamiast #).

    restauracja.html HTML zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Restauracja Wszystkie Smaki</title>
        <link rel="stylesheet" href="styl_1.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (restauracja.html)
  9. Krok 9: Bloki: baner, trzy panele w main, stopka

    Kryteria:

    R.2.3 Układ strony zdefiniowano dzieląc stronę na baner, panele lewy i prawy, dolny oraz stopkę. Zastosowano znaczniki sekcji

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki: baner, panele lewy i prawy, dolny, stopka. Podział zrealizowany za pomocą znaczników sekcji tak, aby układ bloków strony po uruchomieniu w przeglądarce, był zgodny z Obrazem 2.

    Baner to <header>, stopka <footer>. Trzy panele stoją we wspólnym <main> - to główna treść strony i zarazem pojemnik, który w CSS ułoży je zgodnie z obrazem 2:

    • lewy <section id="dish"> - zdjęcie dania,
    • prawy <section id="offer"> - oferta z cenami,
    • dolny <section id="booking"> - rezerwacja stolika.

    Identyfikatory mówią, co jest w panelu, a nie gdzie stoi - po zmianie układu nazwa #left przestałaby pasować (R.2.3).

    restauracja.html HTML zmienione: 12 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Restauracja Wszystkie Smaki</title>
        <link rel="stylesheet" href="styl_1.css">
    </head>
    <body>
        <header>
        </header>
        <main>
            <section id="dish">
            </section>
            <section id="offer">
            </section>
            <section id="booking">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (restauracja.html)
  10. Krok 10: Baner

    ok. 1 min
    Polecenie z arkusza

    Zawartość banera: nagłówek pierwszego stopnia o treści: Witamy w restauracji „Wszystkie Smaki”.

    <h1> w <header>; cudzysłów wokół nazwy restauracji należy do treści i w HTML-u wpisujesz go wprost.

    restauracja.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Restauracja Wszystkie Smaki</title>
        <link rel="stylesheet" href="styl_1.css">
    </head>
    <body>
        <header>
            <h1>Witamy w restauracji "Wszystkie Smaki"</h1>
        </header>
        <main>
            <section id="dish">
            </section>
            <section id="offer">
            </section>
            <section id="booking">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (restauracja.html)
  11. Krok 11: Panel lewy: zdjęcie

    Kryteria: ,

    R.2.5 W panelu lewym umieszczono obraz menu.jpg

    R.2.6 Zdefiniowano dla obrazu tekst alternatywny "Nasze danie"

    ok. 1 min
    Polecenie z arkusza

    Zawartość panelu lewego: obraz menu.jpg z tekstem alternatywnym o treści: „Nasze danie”.

    Zdjęcie z archiwum, bez obróbki, z tekstem alternatywnym z arkusza (R.2.5, R.2.6). Wysokości nie podajesz w atrybutach HTML - ustawi ją arkusz stylów.

    restauracja.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Restauracja Wszystkie Smaki</title>
        <link rel="stylesheet" href="styl_1.css">
    </head>
    <body>
        <header>
            <h1>Witamy w restauracji "Wszystkie Smaki"</h1>
        </header>
        <main>
            <section id="dish">
                <img src="menu.jpg" alt="Nasze danie">
            </section>
            <section id="offer">
            </section>
            <section id="booking">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (restauracja.html)
  12. Krok 12: Panel prawy: nagłówek i lista numerowana

    Kryteria:

    R.2.7 W panelu prawym zapisano listę numerowaną z trzema elementami: Obiady od 40 zł; Przekąski od 10 zł; Kolacje od 20 zł, zastosowano znaczniki <ol>, <li> oraz ich zamknięcia

    ok. 1 min
    Polecenie z arkusza

    Zawartość panelu prawego: Nagłówek czwartego stopnia o treści: „U nas dobrze zjesz!”. Lista numerowana z trzema elementami: Obiady od 40 zł; Przekąski od 10 zł; Kolacje od 20 zł.

    <h4> i lista numerowana <ol> z trzema <li>, każdy z zamknięciem - klucz R.2.7 wymaga znaczników zamykających.

    restauracja.html HTML zmienione: 6 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Restauracja Wszystkie Smaki</title>
        <link rel="stylesheet" href="styl_1.css">
    </head>
    <body>
        <header>
            <h1>Witamy w restauracji "Wszystkie Smaki"</h1>
        </header>
        <main>
            <section id="dish">
                <img src="menu.jpg" alt="Nasze danie">
            </section>
            <section id="offer">
                <h4>U nas dobrze zjesz!</h4>
                <ol>
                    <li>Obiady od 40 zł</li>
                    <li>Przekąski od 10 zł</li>
                    <li>Kolacje od 20 zł</li>
                </ol>
            </section>
            <section id="booking">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (restauracja.html)
  13. Krok 13: Panel dolny: formularz wysyłany do rezerwacja.php

    Kryteria:

    R.2.4 W banerze zapisano nagłówek h1: "Witamy w restauracji "Wszystkie Smaki"”, w panelu prawym zapisano nagłówek h4: "U nas dobrze zjesz!", w panelu dolnym zapisano nagłówek h2: "Zarezerwuj stolik on-line"

    ok. 2 min
    Polecenie z arkusza

    Zawartość panelu dolnego: Nagłówek drugiego stopnia o treści: „Zarezerwuj stolik on-line”. Formularz zgodny z obrazem 2, wysyłający dane do pliku rezerwacja.php metodą bezpieczną.

    • action="rezerwacja.php" - dokąd trafią dane,
    • method="post" - „metodę bezpieczną” arkusz rozumie jako POST: dane idą w treści żądania, a nie w adresie strony (w specyfikacji HTTP słowo „bezpieczna” znaczy co innego, ale klucz R.3.3 wymaga POST).

    Ten krok domyka nagłówki z R.2.4: <h1> w banerze, <h4> w panelu prawym i <h2> tutaj.

    restauracja.html HTML zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Restauracja Wszystkie Smaki</title>
        <link rel="stylesheet" href="styl_1.css">
    </head>
    <body>
        <header>
            <h1>Witamy w restauracji "Wszystkie Smaki"</h1>
        </header>
        <main>
            <section id="dish">
                <img src="menu.jpg" alt="Nasze danie">
            </section>
            <section id="offer">
                <h4>U nas dobrze zjesz!</h4>
                <ol>
                    <li>Obiady od 40 zł</li>
                    <li>Przekąski od 10 zł</li>
                    <li>Kolacje od 20 zł</li>
                </ol>
            </section>
            <section id="booking">
                <h2>Zarezerwuj stolik on-line</h2>
                <form action="rezerwacja.php" method="post">
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (restauracja.html)
  14. Krok 14: Formularz: trzy pola z podpisami

    ok. 3 min
    Polecenie z arkusza

    Zawiera podpisy i pola: „Data (format rrrr-mm-dd):”, poniżej pole edycyjne. „Ile osób?”, poniżej pole edycyjne typu numerycznego. „Twój numer telefonu:”, poniżej pole edycyjne.

    Każda para podpis + pole stoi we własnym bloku <div class="field"> - zamiast <br> po podpisie i po polu. „Poniżej” ustawi za chwilę jedna reguła CSS.

    • <label for> wskazuje id pola: kliknięcie podpisu ustawia kursor w polu, a czytnik ekranu odczytuje podpis razem z polem,
    • name - pod tą nazwą wartość trafi do $_POST w skrypcie (date, people, phone),
    • data zostaje polem tekstowym (arkusz: pole edycyjne z podanym formatem), ale pattern z czterema, dwiema i dwiema cyframi przepuści tylko zapis rrrr-mm-dd, a placeholder podpowiada go w pustym polu,
    • liczba osób: type="number" (R.2.9) z min="1", telefon: type="tel" - wygląda jak zwykłe pole tekstowe, a na telefonie otwiera klawiaturę z cyframi,
    • required - przeglądarka nie wyśle formularza z pustym polem.

    To wygoda dla użytkownika, a nie zabezpieczenie: dane da się wysłać z pominięciem formularza, dlatego skrypt sprawdzi je jeszcze raz.

    Typowe błędy

    Pole bez name w ogóle nie zostanie wysłane - skrypt nie znajdzie wartości w $_POST.

    restauracja.html HTML zmienione: 12 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Restauracja Wszystkie Smaki</title>
        <link rel="stylesheet" href="styl_1.css">
    </head>
    <body>
        <header>
            <h1>Witamy w restauracji "Wszystkie Smaki"</h1>
        </header>
        <main>
            <section id="dish">
                <img src="menu.jpg" alt="Nasze danie">
            </section>
            <section id="offer">
                <h4>U nas dobrze zjesz!</h4>
                <ol>
                    <li>Obiady od 40 zł</li>
                    <li>Przekąski od 10 zł</li>
                    <li>Kolacje od 20 zł</li>
                </ol>
            </section>
            <section id="booking">
                <h2>Zarezerwuj stolik on-line</h2>
                <form action="rezerwacja.php" method="post">
                <div class="field">
                    <label for="date">Data (format rrrr-mm-dd):</label>
                    <input type="text" id="date" name="date" placeholder="rrrr-mm-dd" pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}" required>
                </div>
                <div class="field">
                    <label for="people">Ile osób?</label>
                    <input type="number" id="people" name="people" min="1" required>
                </div>
                <div class="field">
                    <label for="phone">Twój numer telefonu:</label>
                    <input type="tel" id="phone" name="phone" autocomplete="tel" required>
                </div>
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (restauracja.html)
  15. Krok 15: Formularz: pole wyboru zgody

    Kryteria:

    R.2.9 Na stronie umieszczono cztery pola z podpisami, kolejno: "Data (format rrrr-mm-dd):", pole edycyjne, "Ile osób?", pole numeryczne, "Twój numer telefonu:" pole edycyjne, pole wyboru (checkbox) z napisem: „Zgadzam się na przetwarzanie moich danych osobowych”

    ok. 1 min
    Polecenie z arkusza

    Pole wyboru z napisem: „Zgadzam się na przetwarzanie moich danych osobowych”.

    <input type="checkbox"> z podpisem za polem, jak na obrazie 2. Ten blok nie ma klasy field, więc podpis zostanie w tej samej linii co pole. required przy polu wyboru znaczy: musi być zaznaczone. Tu komplet czterech pól z podpisami (R.2.9).

    restauracja.html HTML zmienione: 4 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Restauracja Wszystkie Smaki</title>
        <link rel="stylesheet" href="styl_1.css">
    </head>
    <body>
        <header>
            <h1>Witamy w restauracji "Wszystkie Smaki"</h1>
        </header>
        <main>
            <section id="dish">
                <img src="menu.jpg" alt="Nasze danie">
            </section>
            <section id="offer">
                <h4>U nas dobrze zjesz!</h4>
                <ol>
                    <li>Obiady od 40 zł</li>
                    <li>Przekąski od 10 zł</li>
                    <li>Kolacje od 20 zł</li>
                </ol>
            </section>
            <section id="booking">
                <h2>Zarezerwuj stolik on-line</h2>
                <form action="rezerwacja.php" method="post">
                <div class="field">
                    <label for="date">Data (format rrrr-mm-dd):</label>
                    <input type="text" id="date" name="date" placeholder="rrrr-mm-dd" pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}" required>
                </div>
                <div class="field">
                    <label for="people">Ile osób?</label>
                    <input type="number" id="people" name="people" min="1" required>
                </div>
                <div class="field">
                    <label for="phone">Twój numer telefonu:</label>
                    <input type="tel" id="phone" name="phone" autocomplete="tel" required>
                </div>
                <div>
                    <input type="checkbox" id="consent" name="consent" required>
                    <label for="consent">Zgadzam się na przetwarzanie moich danych osobowych</label>
                </div>
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (restauracja.html)
  16. Krok 16: Formularz: przyciski WYCZYŚĆ i REZERWUJ

    Kryteria: , ,

    R.2.8 W panelu dolnym zapisano formularz zawierający dwa przyciski: WYCZYŚĆ oraz REZERWUJ, zastosowano znacznik <form> oraz jego zamknięcie

    R.3.2 Przycisk WYCZYŚĆ powoduje w formularzu usunięcie wpisanych danych

    R.3.3 Przycisk REZERWUJ wysyła dane do pliku rezerwacja.php metodą POST

    ok. 2 min
    Polecenie z arkusza

    Przycisk „WYCZYŚĆ”, po jego wciśnięciu formularz jest czyszczony. Przycisk „REZERWUJ”, po jego wciśnięciu są wysyłane dane z formularza.

    Działanie przycisku zależy od typu: type="reset" przywraca pola do wartości początkowych - czyści formularz bez jednej linii skryptu (R.3.2); type="submit" wysyła formularz pod adres z action metodą POST (R.2.8, R.3.3). Wpisz coś w podglądzie i kliknij WYCZYŚĆ.

    Typowe błędy

    Sam <button> bez type wewnątrz formularza wysyła dane - WYCZYŚĆ zamiast czyścić przeniósłby na rezerwacja.php.

    restauracja.html HTML zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Restauracja Wszystkie Smaki</title>
        <link rel="stylesheet" href="styl_1.css">
    </head>
    <body>
        <header>
            <h1>Witamy w restauracji "Wszystkie Smaki"</h1>
        </header>
        <main>
            <section id="dish">
                <img src="menu.jpg" alt="Nasze danie">
            </section>
            <section id="offer">
                <h4>U nas dobrze zjesz!</h4>
                <ol>
                    <li>Obiady od 40 zł</li>
                    <li>Przekąski od 10 zł</li>
                    <li>Kolacje od 20 zł</li>
                </ol>
            </section>
            <section id="booking">
                <h2>Zarezerwuj stolik on-line</h2>
                <form action="rezerwacja.php" method="post">
                <div class="field">
                    <label for="date">Data (format rrrr-mm-dd):</label>
                    <input type="text" id="date" name="date" placeholder="rrrr-mm-dd" pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}" required>
                </div>
                <div class="field">
                    <label for="people">Ile osób?</label>
                    <input type="number" id="people" name="people" min="1" required>
                </div>
                <div class="field">
                    <label for="phone">Twój numer telefonu:</label>
                    <input type="tel" id="phone" name="phone" autocomplete="tel" required>
                </div>
                <div>
                    <input type="checkbox" id="consent" name="consent" required>
                    <label for="consent">Zgadzam się na przetwarzanie moich danych osobowych</label>
                </div>
                <button type="reset">WYCZYŚĆ</button>
                <button type="submit">REZERWUJ</button>
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (restauracja.html)
  17. Krok 17: Stopka z numerem zdającego

    ok. 1 min
    Polecenie z arkusza

    Zawartość stopki: napis o treści: „Stronę internetową opracował: ”, dalej wstawiony Twój numer PESEL, lub w przypadku jego braku, inny numer, którym został podpisany arkusz, numer powinien być zapisany pochyloną czcionką.

    Numer stoi w <span class="author"> - pochylenie nada mu arkusz stylów, więc HTML opisuje tylko treść. Na egzaminie w miejscu nazwiska wpisujesz swój PESEL; tu stoi autor rozwiązania.

    restauracja.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Restauracja Wszystkie Smaki</title>
        <link rel="stylesheet" href="styl_1.css">
    </head>
    <body>
        <header>
            <h1>Witamy w restauracji "Wszystkie Smaki"</h1>
        </header>
        <main>
            <section id="dish">
                <img src="menu.jpg" alt="Nasze danie">
            </section>
            <section id="offer">
                <h4>U nas dobrze zjesz!</h4>
                <ol>
                    <li>Obiady od 40 zł</li>
                    <li>Przekąski od 10 zł</li>
                    <li>Kolacje od 20 zł</li>
                </ol>
            </section>
            <section id="booking">
                <h2>Zarezerwuj stolik on-line</h2>
                <form action="rezerwacja.php" method="post">
                <div class="field">
                    <label for="date">Data (format rrrr-mm-dd):</label>
                    <input type="text" id="date" name="date" placeholder="rrrr-mm-dd" pattern="[0-9]{4}-[0-9]{2}-[0-9]{2}" required>
                </div>
                <div class="field">
                    <label for="people">Ile osób?</label>
                    <input type="number" id="people" name="people" min="1" required>
                </div>
                <div class="field">
                    <label for="phone">Twój numer telefonu:</label>
                    <input type="tel" id="phone" name="phone" autocomplete="tel" required>
                </div>
                <div>
                    <input type="checkbox" id="consent" name="consent" required>
                    <label for="consent">Zgadzam się na przetwarzanie moich danych osobowych</label>
                </div>
                <button type="reset">WYCZYŚĆ</button>
                <button type="submit">REZERWUJ</button>
                </form>
            </section>
        </main>
        <footer>
            Stronę internetową opracował: <span class="author">Bartosz Bryniarski</span>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (restauracja.html)
  18. Etap 3: Styl CSS

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

    Krok 18: Plik styl_1.css: zmienne, tło i krój całej strony

    Kryteria:

    R.4.1 Ustawiono krój czcionki Verdana dla całej strony (*, body, html lub kontener wszystkich sekcji)

    ok. 2 min
    Polecenie z arkusza

    Plik CSS zawiera formatowanie: Całej strony: kolor tła #F6E5DC, krój czcionki Verdana.

    Trzy kolory z arkusza w zmiennych - ciemny brąz #2F180C pojawi się w trzech regułach (baner, stopka, przyciski), więc zmiana odcienia to jedna linia. „Cała strona” to body: tło obejmie okno, a krój odziedziczą wszystkie elementy. Po Verdanie krój zapasowy sans-serif - gdy Verdany nie ma w systemie, przeglądarka weźmie dowolny krój bezszeryfowy (R.4.1).

    styl_1.css CSS nowy plik
    :root {
        --brown: #2F180C;
        --cream: #F6E5DC;
        --sand: #D0B6A8;
    }
    
    body {
        background-color: var(--cream);
        font-family: Verdana, sans-serif;
    }
    Podgląd strony po tym kroku (restauracja.html)
  19. Krok 19: Baner i stopka

    Kryteria:

    R.4.4 Ustawiono kolor czcionki khaki dla banera i stopki

    ok. 2 min
    Polecenie z arkusza

    Banera: kolor tła #2F180C, wyrównanie tekstu do środka, kolor czcionki khaki, wysokość 60 px. Stopki: kolor tła #2F180C, kolor czcionki khaki, wysokość 60 px.

    Baner i stopka mają te same trzy właściwości, więc dostają jedną regułę z dwoma selektorami; wyśrodkowanie dotyczy tylko banera - na obrazie 2 tekst stopki stoi przy lewej krawędzi. khaki to nazwa koloru w CSS, wpisujesz ją wprost (R.4.4).

    styl_1.css CSS zmienione: 11 linii
    :root {
        --brown: #2F180C;
        --cream: #F6E5DC;
        --sand: #D0B6A8;
    }
    
    body {
        background-color: var(--cream);
        font-family: Verdana, sans-serif;
    }
    
    header,
    footer {
        background-color: var(--brown);
        color: khaki;
        height: 60px;
    }
    
    header {
        text-align: center;
    }
    Podgląd strony po tym kroku (restauracja.html)
  20. Krok 20: Panele lewy i prawy

    Kryteria: ,

    R.4.6 Ustawiono szerokość panelu lewego na 60%

    R.4.8 Ustawiono rozmiar czcionki na 150% dla panelu prawego

    ok. 1 min
    Polecenie z arkusza

    Panelu lewego: szerokość 60%. Panelu prawego: rozmiar czcionki 150%, marginesy wewnętrzne 70 px.

    Selektory identyfikatorów (#). Szerokość zostaje na samym panelu lewym, tak jak ocenia ją klucz (R.4.6); panel prawy nie ma podanej szerokości - zajmie resztę rzędu (R.4.8).

    Typowe błędy

    .dish (kropka) zamiast #dish nie trafia w panel - klucz R.3.4 wprost wymienia ten błąd.

    styl_1.css CSS zmienione: 9 linii
    :root {
        --brown: #2F180C;
        --cream: #F6E5DC;
        --sand: #D0B6A8;
    }
    
    body {
        background-color: var(--cream);
        font-family: Verdana, sans-serif;
    }
    
    header,
    footer {
        background-color: var(--brown);
        color: khaki;
        height: 60px;
    }
    
    header {
        text-align: center;
    }
    
    #dish {
        width: 60%;
    }
    
    #offer {
        font-size: 150%;
        padding: 70px;
    }
    Podgląd strony po tym kroku (restauracja.html)
  21. Krok 21: Panel dolny

    Kryteria: , ,

    R.4.2 Ustawiono kolor tła #F6E5DC dla całej strony (*, body, html lub kontener wszystkich sekcji) , #2F180C dla banera i stopki oraz #D0B6A8 dla panelu dolnego

    R.4.3 Ustawiono wyrównanie tekstu do środka dla banera i panelu dolnego

    R.4.7 Ustawiono marginesy wewnętrzne (padding) dla panelu prawego na 70px oraz dla panelu dolnego na 30px

    ok. 1 min
    Polecenie z arkusza

    Panelu dolnego: kolor tła #D0B6A8, wyrównanie tekstu do środka, marginesy wewnętrzne 30 px.

    Ta reguła domyka kolory tła (R.4.2: strona, baner, stopka i panel dolny), wyśrodkowanie (R.4.3: baner i panel dolny) i marginesy wewnętrzne (R.4.7: 70 px w prawym, 30 px w dolnym).

    styl_1.css CSS zmienione: 6 linii
    :root {
        --brown: #2F180C;
        --cream: #F6E5DC;
        --sand: #D0B6A8;
    }
    
    body {
        background-color: var(--cream);
        font-family: Verdana, sans-serif;
    }
    
    header,
    footer {
        background-color: var(--brown);
        color: khaki;
        height: 60px;
    }
    
    header {
        text-align: center;
    }
    
    #dish {
        width: 60%;
    }
    
    #offer {
        font-size: 150%;
        padding: 70px;
    }
    
    #booking {
        background-color: var(--sand);
        text-align: center;
        padding: 30px;
    }
    Podgląd strony po tym kroku (restauracja.html)
  22. Krok 22: Stopka: pochylony numer

    Kryteria:

    R.2.10 W stopce zapisano napis o treści: „Stronę internetową opracował: ”, dalej wstawiony numer PESEL (lub innym, którym został podpisany arkusz), numer jest pochylony

    ok. 1 min
    Polecenie z arkusza

    Numer powinien być zapisany pochyloną czcionką.

    font-style: italic dla klasy author. Klucz dopuszcza też pochylenie znacznikiem HTML (<i>), ale z arkusza stylów jest spójniej - całe formatowanie w jednym pliku (R.2.10).

    styl_1.css CSS zmienione: 4 linie
    :root {
        --brown: #2F180C;
        --cream: #F6E5DC;
        --sand: #D0B6A8;
    }
    
    body {
        background-color: var(--cream);
        font-family: Verdana, sans-serif;
    }
    
    header,
    footer {
        background-color: var(--brown);
        color: khaki;
        height: 60px;
    }
    
    header {
        text-align: center;
    }
    
    #dish {
        width: 60%;
    }
    
    #offer {
        font-size: 150%;
        padding: 70px;
    }
    
    #booking {
        background-color: var(--sand);
        text-align: center;
        padding: 30px;
    }
    
    .author {
        font-style: italic;
    }
    Podgląd strony po tym kroku (restauracja.html)
  23. Krok 23: Obraz: wysokość 350 px

    Kryteria:

    R.4.5 Ustawiono wysokość banera i stopki na 60px oraz obrazu na 350px

    ok. 1 min
    Polecenie z arkusza

    Obrazu: wysokość 350 px.

    Tylko wysokość - szerokość dopasuje się do proporcji zdjęcia (R.4.5: baner, stopka i obraz).

    styl_1.css CSS zmienione: 4 linie
    :root {
        --brown: #2F180C;
        --cream: #F6E5DC;
        --sand: #D0B6A8;
    }
    
    body {
        background-color: var(--cream);
        font-family: Verdana, sans-serif;
    }
    
    header,
    footer {
        background-color: var(--brown);
        color: khaki;
        height: 60px;
    }
    
    header {
        text-align: center;
    }
    
    #dish {
        width: 60%;
    }
    
    #offer {
        font-size: 150%;
        padding: 70px;
    }
    
    #booking {
        background-color: var(--sand);
        text-align: center;
        padding: 30px;
    }
    
    .author {
        font-style: italic;
    }
    
    img {
        height: 350px;
    }
    Podgląd strony po tym kroku (restauracja.html)
  24. Krok 24: Oba przyciski

    Kryteria: ,

    R.4.9 Ustawiono styl obu przycisków: marginesy zewnętrzne (margin) 10 px oraz wewnętrzne (padding) 5 px

    R.4.10 Ustawiono styl obu przycisków: kolor tła #2F180C, kolor czcionki khaki

    ok. 1 min
    Polecenie z arkusza

    Obu przycisków: marginesy zewnętrzne 10 px, wewnętrzne 5 px, kolor tła #2F180C, kolor czcionki khaki.

    Selektor znacznika button obejmuje oba przyciski naraz (R.4.9, R.4.10).

    styl_1.css CSS zmienione: 7 linii
    :root {
        --brown: #2F180C;
        --cream: #F6E5DC;
        --sand: #D0B6A8;
    }
    
    body {
        background-color: var(--cream);
        font-family: Verdana, sans-serif;
    }
    
    header,
    footer {
        background-color: var(--brown);
        color: khaki;
        height: 60px;
    }
    
    header {
        text-align: center;
    }
    
    #dish {
        width: 60%;
    }
    
    #offer {
        font-size: 150%;
        padding: 70px;
    }
    
    #booking {
        background-color: var(--sand);
        text-align: center;
        padding: 30px;
    }
    
    .author {
        font-style: italic;
    }
    
    img {
        height: 350px;
    }
    
    button {
        margin: 10px;
        padding: 5px;
        background-color: var(--brown);
        color: khaki;
    }
    Podgląd strony po tym kroku (restauracja.html)
  25. Krok 25: Formularz: podpis nad polem

    ok. 1 min
    Polecenie z arkusza

    Formularz zgodny z obrazem 2, zawiera podpisy i pola: „Data (format rrrr-mm-dd):”, poniżej pole edycyjne, „Ile osób?”, poniżej pole edycyjne typu numerycznego, „Twój numer telefonu:”, poniżej pole edycyjne.

    <label> i <input> są elementami liniowymi i stanęłyby obok siebie. display: block zamienia podpis w blok na całą szerokość - pole spada pod niego, a blok <div> zaczyna następną parę od nowej linii. Selektor .field label nie dotyka podpisu zgody, który ma zostać obok pola wyboru.

    styl_1.css CSS zmienione: 4 linie
    :root {
        --brown: #2F180C;
        --cream: #F6E5DC;
        --sand: #D0B6A8;
    }
    
    body {
        background-color: var(--cream);
        font-family: Verdana, sans-serif;
    }
    
    header,
    footer {
        background-color: var(--brown);
        color: khaki;
        height: 60px;
    }
    
    header {
        text-align: center;
    }
    
    #dish {
        width: 60%;
    }
    
    #offer {
        font-size: 150%;
        padding: 70px;
    }
    
    #booking {
        background-color: var(--sand);
        text-align: center;
        padding: 30px;
    }
    
    .author {
        font-style: italic;
    }
    
    img {
        height: 350px;
    }
    
    button {
        margin: 10px;
        padding: 5px;
        background-color: var(--brown);
        color: khaki;
    }
    
    .field label {
        display: block;
    }
    Podgląd strony po tym kroku (restauracja.html)
  26. Krok 26: Układ bloków zgodny z obrazem 2

    Kryteria:

    R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z obrazem 2 w arkuszu

    ok. 3 min
    Polecenie z arkusza

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

    • display: flex na <main> stawia panele w rzędzie, a flex-wrap: wrap pozwala przenieść to, co się nie mieści, do następnej linii,
    • #offer { flex: 1; } - panel prawy rośnie i zajmuje resztę rzędu obok 60% panelu lewego. Przy wąskim oknie zwęża się (tekst się zawija), zamiast spaść pod zdjęcie - panel z float w wersji podstawowej spada pod nie przy ekranie 1024 px, bo ma szerokość swojej treści,
    • #booking { flex-basis: 100%; } - panel dolny chce całej szerokości, więc spada pod oba panele. box-sizing: border-box wlicza jego padding 30 px w te 100% - bez tego panel byłby o 60 px szerszy od strony i pojawiłby się poziomy pasek przewijania.

    Baner i stopka są poza pojemnikiem, więc stoją nad nim i pod nim same z siebie - bez float nie ma też clear (R.3.1).

    Dlaczego nie siatka? W siatce (grid) procent szerokości elementu liczy się od jego kolumny: panel z width: 60% w kolumnie szerokiej na 60% strony zająłby 36% strony. Szerokość trzeba by przenieść do grid-template-columns, a klucz z tego arkusza ocenia ją na panelu lewym. Flex liczy procenty od pojemnika, więc wymiar z arkusza działa wprost.

    Typowe błędy

    width: 100% zamiast flex-basis przy panelu z paddingiem bez box-sizing: border-box daje poziomy pasek przewijania.

    styl_1.css CSS zmienione: 8 linii
    :root {
        --brown: #2F180C;
        --cream: #F6E5DC;
        --sand: #D0B6A8;
    }
    
    body {
        background-color: var(--cream);
        font-family: Verdana, sans-serif;
    }
    
    header,
    footer {
        background-color: var(--brown);
        color: khaki;
        height: 60px;
    }
    
    header {
        text-align: center;
    }
    
    main {
        display: flex;
        flex-wrap: wrap;
    }
    
    #dish {
        width: 60%;
    }
    
    #offer {
        flex: 1;
        font-size: 150%;
        padding: 70px;
    }
    
    #booking {
        flex-basis: 100%;
        box-sizing: border-box;
        background-color: var(--sand);
        text-align: center;
        padding: 30px;
    }
    
    .author {
        font-style: italic;
    }
    
    img {
        height: 350px;
    }
    
    button {
        margin: 10px;
        padding: 5px;
        background-color: var(--brown);
        color: khaki;
    }
    
    .field label {
        display: block;
    }
    Podgląd strony po tym kroku (restauracja.html)
  27. Etap 4: Skrypt PHP

    Kroki 27-33 · po tym etapie masz 36 z 36 kryteriów (pkt)

    Krok 27: Plik rezerwacja.php i komunikat

    Kryteria: ,

    R.5.1 Skrypt napisano w języku PHP, zawiera przynajmniej jedną poprawnie zapisaną instrukcję języka

    R.5.4 Skrypt wyświetla tekst: "Dodano rezerwację do bazy"

    ok. 3 min
    Polecenie z arkusza

    Wymagania dotyczące skryptu: Napisany w języku PHP, w pliku o nazwie rezerwacja.php. Jest uruchamiany po wypełnieniu formularza i wciśnięciu przycisku REZERWUJ. Nie jest wymagane sprawdzenie poprawności danych wpisanych w formularz. Wypisuje tekst „Dodano rezerwację do bazy”.

    Utwórz rezerwacja.php w tym samym folderze co strona - tam wysyła formularz (action). Plik ma dwie części:

    • na górze blok PHP, który przygotuje komunikat - tu za chwilę dojdą dane, ich sprawdzenie i zapis do bazy,
    • pod nim zwykły dokument HTML5 z kodowaniem, tytułem i tym samym arkuszem stylów co strona; komunikat stoi w bloku #booking, więc dostaje tło i wyśrodkowanie panelu rezerwacji. Wypisuje go skrót <?= ... ?> (to samo co <?php echo ... ?>), a odnośnik prowadzi z powrotem do restauracji.

    Logika jest w jednym miejscu, a w HTML-u zostaje tylko wypisywanie (R.5.1, R.5.4). Podgląd obok to wynik prawdziwego uruchomienia pliku po wysłaniu formularza.

    rezerwacja.php PHP nowy plik
    <?php
    $message = 'Dodano rezerwację do bazy';
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Rezerwacja stolika</title>
        <link rel="stylesheet" href="styl_1.css">
    </head>
    <body>
        <section id="booking">
            <h2>Rezerwacja stolika</h2>
            <p><?= $message ?></p>
            <p><a href="restauracja.html">Wróć do strony restauracji</a></p>
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (rezerwacja.php)
  28. Krok 28: Połączenie z bazą w bloku try

    Kryteria:

    R.5.2 Skrypt wykonuje połączenie z serwerem bazy danych i wybór bazy o nazwie baza

    ok. 3 min
    Polecenie z arkusza

    Łączy się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie baza.

    • new PDO(...) - adres w jednym napisie: serwer localhost, baza baza, kodowanie utf8mb4; dalej użytkownik root i puste hasło (R.5.2),
    • ERRMODE_EXCEPTION - błąd bazy rzuca wyjątek, zamiast zwracać false, którego łatwo nie zauważyć,
    • try / catch (PDOException ...) - gdy baza nie działa, użytkownik zobaczy zwykłe zdanie zamiast komunikatu PHP ze szczegółami serwera. Komunikat o sukcesie ustawia się w try, czyli dopiero wtedy, gdy wszystko wcześniej się udało.

    PDO zamiast mysqli z tabeli 1 - klucz przyjmuje obie biblioteki (uwaga przy R.5). Klucz każe sprawdzać skrypt w XAMPP 7.4.9, dlatego kod nie korzysta ze składni dodanej dopiero w PHP 8 (np. catch bez zmiennej).

    rezerwacja.php PHP zmienione: 10 linii
    <?php
    $message = '';
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=baza;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        ]);
    
        $message = 'Dodano rezerwację do bazy';
    } catch (PDOException $exception) {
        $message = 'Nie udało się zapisać rezerwacji. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Rezerwacja stolika</title>
        <link rel="stylesheet" href="styl_1.css">
    </head>
    <body>
        <section id="booking">
            <h2>Rezerwacja stolika</h2>
            <p><?= $message ?></p>
            <p><a href="restauracja.html">Wróć do strony restauracji</a></p>
        </section>
    </body>
    </html>

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

  29. Krok 29: Pobranie danych z formularza i ich sprawdzenie

    Kryteria:

    R.5.5 Skrypt prawidłowo pobiera dane z tablicy $_POST np. $telefon = $_POST['tel']; lub bezpośrednio do zapytania

    ok. 4 min
    Polecenie z arkusza

    Pobiera dane z formularza.

    Dane wysłane metodą POST są w tablicy $_POST, pod nazwami z atrybutów name formularza (R.5.5). Arkusz nie wymaga ich sprawdzania, ale w prawdziwej rezerwacji to podstawa:

    • ?? '' - gdy pola nie ma (np. plik otwarty wprost z paska adresu), zmienna dostaje pusty napis zamiast ostrzeżenia „Undefined array key”; trim obcina spacje z brzegów,
    • data: DateTime::createFromFormat('!Y-m-d', ...) czyta napis w formacie rrrr-mm-dd, a porównanie z powrotem z tym samym formatem odrzuca daty, których nie ma - 2026-02-31 PHP po cichu zamieniłby na 3 marca,
    • liczba osób: filter_var z FILTER_VALIDATE_INT i min_range 1 zwraca liczbę całkowitą albo false (dla 0, -2, 2.5, abc),
    • telefon: wyrażenie regularne przepuszcza 9-15 cyfr, spacji, plusów i myślników.

    Każdy błąd trafia do listy $errors. Połączenie z bazą powstaje teraz tylko dla poprawnych danych (if ($errors === [])), a przy błędach pod napisem „Rezerwacja nie została zapisana.” pojawia się lista zdań dla użytkownika. Sprawdzone na prawdziwym PHP: 17.10.2026, 0 osób i telefon abc dają trzy komunikaty.

    Typowe błędy

    $_POST['data'] przy polu name="date" daje pustą wartość - klucz w $_POST to dokładnie name pola. Klucz R.5.5 wymaga odczytu z tablicy $_POST, dlatego nie ma tu filter_input.

    rezerwacja.php PHP zmienione: 30 linii
    <?php
    // dane z formularza i ich sprawdzenie
    $date = trim($_POST['date'] ?? '');
    $people = filter_var($_POST['people'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
    $phone = trim($_POST['phone'] ?? '');
    
    $errors = [];
    $parsedDate = DateTime::createFromFormat('!Y-m-d', $date);
    if ($parsedDate === false || $parsedDate->format('Y-m-d') !== $date) {
        $errors[] = 'Podaj datę w formacie rrrr-mm-dd.';
    }
    if ($people === false) {
        $errors[] = 'Podaj liczbę osób - co najmniej 1.';
    }
    if (preg_match('/^[0-9 +-]{9,15}$/', $phone) !== 1) {
        $errors[] = 'Podaj numer telefonu.';
    }
    
    $message = 'Rezerwacja nie została zapisana.';
    if ($errors === []) {
        try {
            $pdo = new PDO('mysql:host=localhost;dbname=baza;charset=utf8mb4', 'root', '', [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            ]);
    
            $message = 'Dodano rezerwację do bazy';
        } catch (PDOException $exception) {
            $message = 'Nie udało się zapisać rezerwacji. Spróbuj ponownie za chwilę.';
        }
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Rezerwacja stolika</title>
        <link rel="stylesheet" href="styl_1.css">
    </head>
    <body>
        <section id="booking">
            <h2>Rezerwacja stolika</h2>
            <p><?= $message ?></p>
    <?php foreach ($errors as $error): ?>
            <p role="alert"><?= $error ?></p>
    <?php endforeach; ?>
            <p><a href="restauracja.html">Wróć do strony restauracji</a></p>
        </section>
    </body>
    </html>

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

  30. Krok 30: Zapytanie przygotowane wstawiające rezerwację

    Kryteria:

    R.5.6 Skrypt wysyła do bazy zapytanie wstawiające rekord w tabeli rezerwacje o wartościach podanych w formularzu (bez numeru stolika). Należy sprawdzić czy rekord został dodany do bazy w phpMyAdmin

    ok. 4 min
    Polecenie z arkusza

    Wysyła do bazy danych zapytanie wstawiające rekord do tabeli rezerwacje z danymi pobranymi z formularza bez wypełnionego numeru stolika (zmodyfikowane zapytanie 1, patrz punkt „Operacje na bazie danych”).

    Zapytanie 1 bez numeru stolika: na liście kolumn tylko trzy pola, a w VALUES parametry :date, :people, :phone zamiast wartości. Numer stolika zostaje pusty (NULL), a id nada baza (R.5.6).

    • prepare wysyła do bazy samą treść zapytania, execute([...]) - osobno dane. Napis z formularza nigdy nie stanie się częścią kodu SQL, nawet gdyby zawierał apostrof albo średnik,
    • nie ma apostrofów wokół tekstów ani sklejania napisów - o typy i cudzysłowy dba PDO.

    Sprawdzone na prawdziwej bazie: po wysłaniu formularza w tabeli rezerwacje jest nowy wiersz z datą 2026-10-17, 2 osobami i NULL w numerze stolika.

    rezerwacja.php PHP zmienione: 4 linie
    <?php
    // dane z formularza i ich sprawdzenie
    $date = trim($_POST['date'] ?? '');
    $people = filter_var($_POST['people'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
    $phone = trim($_POST['phone'] ?? '');
    
    $errors = [];
    $parsedDate = DateTime::createFromFormat('!Y-m-d', $date);
    if ($parsedDate === false || $parsedDate->format('Y-m-d') !== $date) {
        $errors[] = 'Podaj datę w formacie rrrr-mm-dd.';
    }
    if ($people === false) {
        $errors[] = 'Podaj liczbę osób - co najmniej 1.';
    }
    if (preg_match('/^[0-9 +-]{9,15}$/', $phone) !== 1) {
        $errors[] = 'Podaj numer telefonu.';
    }
    
    $message = 'Rezerwacja nie została zapisana.';
    if ($errors === []) {
        try {
            $pdo = new PDO('mysql:host=localhost;dbname=baza;charset=utf8mb4', 'root', '', [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            ]);
    
            $statement = $pdo->prepare(
                'INSERT INTO rezerwacje (data_rez, liczba_osob, telefon) VALUES (:date, :people, :phone)'
            );
            $statement->execute(['date' => $date, 'people' => $people, 'phone' => $phone]);
            $message = 'Dodano rezerwację do bazy';
        } catch (PDOException $exception) {
            $message = 'Nie udało się zapisać rezerwacji. Spróbuj ponownie za chwilę.';
        }
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Rezerwacja stolika</title>
        <link rel="stylesheet" href="styl_1.css">
    </head>
    <body>
        <section id="booking">
            <h2>Rezerwacja stolika</h2>
            <p><?= $message ?></p>
    <?php foreach ($errors as $error): ?>
            <p role="alert"><?= $error ?></p>
    <?php endforeach; ?>
            <p><a href="restauracja.html">Wróć do strony restauracji</a></p>
        </section>
    </body>
    </html>

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

  31. Krok 31: Zamknięcie połączenia i sprawdzenie w phpMyAdmin

    Kryteria:

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

    ok. 2 min
    Polecenie z arkusza

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

    PDO nie ma funkcji „zamknij”: połączenie zamyka się, gdy znika ostatnie odwołanie do obiektu. Dlatego najpierw $statement = null (przygotowane zapytanie też trzyma połączenie), a potem $pdo = null - zaraz po zapisie, więc to ostatnia operacja na bazie (R.5.3).

    Sprawdź całość: otwórz http://localhost/…/restauracja.html, wypełnij formularz i kliknij REZERWUJ - przeglądarka pokazuje „Dodano rezerwację do bazy”, a w phpMyAdmin w tabeli rezerwacje jest nowy wiersz (obok: stan tabeli z prawdziwej bazy po jednej rezerwacji z formularza i zapytaniu 1).

    rezerwacja.php PHP zmienione: 2 linie
    <?php
    // dane z formularza i ich sprawdzenie
    $date = trim($_POST['date'] ?? '');
    $people = filter_var($_POST['people'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
    $phone = trim($_POST['phone'] ?? '');
    
    $errors = [];
    $parsedDate = DateTime::createFromFormat('!Y-m-d', $date);
    if ($parsedDate === false || $parsedDate->format('Y-m-d') !== $date) {
        $errors[] = 'Podaj datę w formacie rrrr-mm-dd.';
    }
    if ($people === false) {
        $errors[] = 'Podaj liczbę osób - co najmniej 1.';
    }
    if (preg_match('/^[0-9 +-]{9,15}$/', $phone) !== 1) {
        $errors[] = 'Podaj numer telefonu.';
    }
    
    $message = 'Rezerwacja nie została zapisana.';
    if ($errors === []) {
        try {
            $pdo = new PDO('mysql:host=localhost;dbname=baza;charset=utf8mb4', 'root', '', [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            ]);
    
            $statement = $pdo->prepare(
                'INSERT INTO rezerwacje (data_rez, liczba_osob, telefon) VALUES (:date, :people, :phone)'
            );
            $statement->execute(['date' => $date, 'people' => $people, 'phone' => $phone]);
            $statement = null;
            $pdo = null;    // zamknięcie połączenia - ostatnia operacja na bazie
            $message = 'Dodano rezerwację do bazy';
        } catch (PDOException $exception) {
            $message = 'Nie udało się zapisać rezerwacji. Spróbuj ponownie za chwilę.';
        }
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Rezerwacja stolika</title>
        <link rel="stylesheet" href="styl_1.css">
    </head>
    <body>
        <section id="booking">
            <h2>Rezerwacja stolika</h2>
            <p><?= $message ?></p>
    <?php foreach ($errors as $error): ?>
            <p role="alert"><?= $error ?></p>
    <?php endforeach; ?>
            <p><a href="restauracja.html">Wróć do strony restauracji</a></p>
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (rezerwacja.php)
    Podgląd strony po kroku 31
  32. Krok 32: Podsumowanie rezerwacji i bezpieczne wypisywanie

    ok. 2 min

    Dodatek spoza arkusza - klucz go nie ocenia. Pod komunikatem z arkusza skrypt powtarza, co zapisał: liczbę osób, datę i telefon (zmienna $saved mówi, że zapis się udał). To dane od użytkownika, więc przechodzą przez funkcję e() z góry pliku - htmlspecialchars z ENT_QUOTES zamienia <, >, & i cudzysłowy na encje. Znacznik wpisany w pole nie wykona się na stronie, tylko pokaże jako tekst.

    Przez e() idą też komunikat i błędy - zasada „wszystko, co wypisujesz, przechodzi przez e()” jest prostsza do pilnowania niż rozstrzyganie przy każdej zmiennej, skąd pochodzi.

    rezerwacja.php PHP zmienione: 12 linii
    <?php
    function e($value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES);
    }
    
    // dane z formularza i ich sprawdzenie
    $date = trim($_POST['date'] ?? '');
    $people = filter_var($_POST['people'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
    $phone = trim($_POST['phone'] ?? '');
    
    $errors = [];
    $parsedDate = DateTime::createFromFormat('!Y-m-d', $date);
    if ($parsedDate === false || $parsedDate->format('Y-m-d') !== $date) {
        $errors[] = 'Podaj datę w formacie rrrr-mm-dd.';
    }
    if ($people === false) {
        $errors[] = 'Podaj liczbę osób - co najmniej 1.';
    }
    if (preg_match('/^[0-9 +-]{9,15}$/', $phone) !== 1) {
        $errors[] = 'Podaj numer telefonu.';
    }
    
    $message = 'Rezerwacja nie została zapisana.';
    $saved = false;
    if ($errors === []) {
        try {
            $pdo = new PDO('mysql:host=localhost;dbname=baza;charset=utf8mb4', 'root', '', [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            ]);
    
            $statement = $pdo->prepare(
                'INSERT INTO rezerwacje (data_rez, liczba_osob, telefon) VALUES (:date, :people, :phone)'
            );
            $statement->execute(['date' => $date, 'people' => $people, 'phone' => $phone]);
            $statement = null;
            $pdo = null;    // zamknięcie połączenia - ostatnia operacja na bazie
            $saved = true;
            $message = 'Dodano rezerwację do bazy';
        } catch (PDOException $exception) {
            $message = 'Nie udało się zapisać rezerwacji. Spróbuj ponownie za chwilę.';
        }
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Rezerwacja stolika</title>
        <link rel="stylesheet" href="styl_1.css">
    </head>
    <body>
        <section id="booking">
            <h2>Rezerwacja stolika</h2>
            <p><?= e($message) ?></p>
    <?php if ($saved): ?>
            <p>Rezerwacja dla <?= e($people) ?> os. na dzień <?= e($date) ?>, telefon <?= e($phone) ?>.</p>
    <?php endif; ?>
    <?php foreach ($errors as $error): ?>
            <p role="alert"><?= e($error) ?></p>
    <?php endforeach; ?>
            <p><a href="restauracja.html">Wróć do strony restauracji</a></p>
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (rezerwacja.php)
  33. Krok 33: Odświeżenie nie dodaje rezerwacji drugi raz (PRG)

    ok. 4 min

    Dodatek spoza arkusza - klucz go nie ocenia. Strona z komunikatem jest wynikiem żądania POST: odświeżenie (F5) wysyła formularz jeszcze raz i w bazie są dwie takie same rezerwacje. Wzorzec PRG (Post/Redirect/Get) to usuwa, a napis z arkusza zostaje:

    • session_start() na samym początku pliku - sesja przechowa rezerwację między dwoma żądaniami,
    • cała obsługa formularza stoi w if ($_SERVER['REQUEST_METHOD'] === 'POST'). Po udanym zapisie skrypt wkłada rezerwację do $_SESSION i wysyła header('Location: rezerwacja.php'), a potem exit - header tylko ustawia nagłówek odpowiedzi i nie zatrzymuje skryptu,
    • przeglądarka wczytuje plik jeszcze raz, zwykłym żądaniem GET. Gałąź elseif odczytuje rezerwację z sesji, od razu ją usuwa (unset) i wypisuje „Dodano rezerwację do bazy” z podsumowaniem,
    • kolejne odświeżenie nie ma już czego pokazać - zostaje zdanie z odnośnikiem do formularza, bez drugiego zapisu.

    Zmienne dostają wartości początkowe na górze pliku, więc każda gałąź ma z czego wypisać. Sprawdzone na prawdziwym PHP: wysłanie formularza (przekierowanie 302), komunikat po przekierowaniu, odświeżenie bez drugiego wiersza w tabeli, data 2026-13-01, telefon ze znacznikiem <b> i złe hasło do bazy - za każdym razem bez ostrzeżeń PHP.

    Typowe błędy

    header('Location: ...') po wypisaniu choćby jednego znaku (także pustej linii przed <?php) nie zadziała - nagłówki muszą wyjść przed treścią strony.

    rezerwacja.php PHP zmienione: 49 linii
    <?php
    session_start();
    
    function e($value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES);
    }
    
    $date = '';
    $people = '';
    $phone = '';
    $errors = [];
    $message = 'Formularz rezerwacji jest na stronie restauracji.';
    $saved = false;
    
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        // dane z formularza i ich sprawdzenie
        $date = trim($_POST['date'] ?? '');
        $people = filter_var($_POST['people'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);
        $phone = trim($_POST['phone'] ?? '');
    
        $parsedDate = DateTime::createFromFormat('!Y-m-d', $date);
        if ($parsedDate === false || $parsedDate->format('Y-m-d') !== $date) {
            $errors[] = 'Podaj datę w formacie rrrr-mm-dd.';
        }
        if ($people === false) {
            $errors[] = 'Podaj liczbę osób - co najmniej 1.';
        }
        if (preg_match('/^[0-9 +-]{9,15}$/', $phone) !== 1) {
            $errors[] = 'Podaj numer telefonu.';
        }
    
        $message = 'Rezerwacja nie została zapisana.';
        if ($errors === []) {
            try {
                $pdo = new PDO('mysql:host=localhost;dbname=baza;charset=utf8mb4', 'root', '', [
                    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                ]);
                $statement = $pdo->prepare(
                    'INSERT INTO rezerwacje (data_rez, liczba_osob, telefon) VALUES (:date, :people, :phone)'
                );
                $statement->execute(['date' => $date, 'people' => $people, 'phone' => $phone]);
                $statement = null;
                $pdo = null;    // zamknięcie połączenia - ostatnia operacja na bazie
    
                // PRG: rezerwacja do sesji i przekierowanie - odświeżenie nie zapisze jej drugi raz
                $_SESSION['booking'] = ['date' => $date, 'people' => $people, 'phone' => $phone];
                header('Location: rezerwacja.php');
                exit;
            } catch (PDOException $exception) {
                $message = 'Nie udało się zapisać rezerwacji. Spróbuj ponownie za chwilę.';
            }
        }
    } elseif (isset($_SESSION['booking'])) {
        // żądanie GET po przekierowaniu: rezerwacja z sesji, od razu usunięta
        ['date' => $date, 'people' => $people, 'phone' => $phone] = $_SESSION['booking'];
        unset($_SESSION['booking']);
        $saved = true;
        $message = 'Dodano rezerwację do bazy';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Rezerwacja stolika</title>
        <link rel="stylesheet" href="styl_1.css">
    </head>
    <body>
        <section id="booking">
            <h2>Rezerwacja stolika</h2>
            <p><?= e($message) ?></p>
    <?php if ($saved): ?>
            <p>Rezerwacja dla <?= e($people) ?> os. na dzień <?= e($date) ?>, telefon <?= e($phone) ?>.</p>
    <?php endif; ?>
    <?php foreach ($errors as $error): ?>
            <p role="alert"><?= e($error) ?></p>
    <?php endforeach; ?>
            <p><a href="restauracja.html">Wróć do strony restauracji</a></p>
        </section>
    </body>
    </html>

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

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

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

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

    ok. 3 min

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

    wersja podstawowawersja nowoczesna
    układfloat: left paneli i clear panelu dolnego<main> z display: flex i flex-wrap
    panele#left, #right, #bottom#dish, #offer, #booking w <main>
    kolorywpisane w każdą regułęzmienne CSS
    formularz<br> po podpisach, pola bez ograniczeńbloki <div>, pattern, min, required
    pochylenie numeruznacznik <i>klasa i font-style w CSS
    bibliotekamysqli (tabela 1 arkusza)PDO z wyjątkami
    dane z formularzawklejone do zapytaniasprawdzone i podane jako parametry
    wyniksam tekstdokument HTML z podsumowaniem przez e()
    odświeżenie po zapisiedodaje rezerwację drugi razPRG - przekierowanie, napis z sesji

    W prawdziwym projekcie dane logowania do bazy trzyma się zwykle w osobnym pliku (np. config.php), a formularz chroni się tokenem CSRF - klucz tego nie wymaga.

    Na egzaminie wersja podstawowa trzyma się funkcji z tabeli 1 arkusza i łatwiej ją napisać bez dokumentacji pod ręką. Nowoczesna jest odporna na błędne i podmienione dane z formularza. Obie biblioteki klucz przyjmuje.

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

  35. Etap 6: Oddanie pracy

    Krok 35 · po tym etapie masz 36 z 36 kryteriów (pkt)

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

    ok. 5 min
    Polecenie z arkusza

    Po zakończeniu pracy utwórz plik tekstowy. Zapisz w nim nazwę przeglądarki internetowej, w której weryfikowałeś poprawność działania witryny. Plik zapisz w folderze z Twoim numerem PESEL (lub innym, którym został podpisany arkusz) jako przeglądarka.txt. Nagraj płytę z rezultatami pracy. W folderze z numerem PESEL (lub innym, którym został podpisany arkusz) powinny się znajdować pliki: kw1.jpg, kw2.jpg, kw3.jpg, kw4.jpg, kwerendy.txt, menu.jpg, przeglądarka.txt, restauracja.html, rezerwacja.php, styl_1.css, zad1.jpg, ewentualnie inne przygotowane pliki.

    W przeglądarka.txt nazwa przeglądarki, w której sprawdzana była strona przez http://localhost/.... Sprawdź listę plików (zrzut importu nazywa się tu zad1.jpg), nagraj płytę, sprawdź jej odczyt i opisz ją numerem zdającego.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

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

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.