Przejdź do głównej treści
EE.09-03-21.01-SGStyczeń 2021 · 28 kroków · ok. 63 min · 40 kryteriów z klucza

Rozwiązanie krok po kroku: Kalkulator BMI z zapisem wyników (Nowoczesny kod (flex, PDO))

To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza egzamin z czterema zapytaniami, przeskalowana ilustracja z przezroczystością i strona bmi.php z tabelą przedziałów oraz formularzem, który liczy BMI i zapisuje pomiar - i tak samo spełnia każde kryterium klucza. Inna jest droga:

  • znaczniki <header>, <main> i <footer>: logo i baner to sekcje w nagłówku strony, bloki lewy i prawy stoją w kontenerze, a w rzędy ustawia je display: flex zamiast float i clear,
  • zmienne CSS dla kolorów z arkusza, etykiety <label> przy polach formularza,
  • PHP na PDO na górze pliku, przed HTML-em: odczyt danych z formularza z walidacją liczb, obliczenie, odczyt i zapis w bazie w jednym miejscu, a w HTML-u zostaje tylko wypisanie,
  • przedział BMI odczytany z tabeli bmi zapytaniem przygotowanym zamiast granic wpisanych w kod,
  • funkcja e() (htmlspecialchars) przy każdej wypisywanej wartości, try/catch/finally z zamknięciem połączenia w każdym przypadku.

To arkusz EE.09 ze stycznia 2021 - kwalifikacji formuły 2017, którą zastąpiła INF.03. Baza i grafika pochodzą z oryginalnego archiwum CKE z3.zip (załącznik na stronie arkusza, hasło w treści zadania). Zapytania i zrzuty są policzone na MariaDB 10.4 - tej samej, która jest w XAMPP. Podgląd od kroków z PHP to wynik prawdziwego uruchomienia kodu w PHP 8.2, także po wysłaniu formularza danymi z obrazu 2 (waga 60, wzrost 170).

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

  1. Etap 1: Baza danych

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

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

    ok. 2 min
    Polecenie z arkusza

    Aby wykonać zadanie, zaloguj się na konto Egzamin bez hasła. Na pulpicie znajdziesz archiwum ZIP o nazwie z3.zip zabezpieczone hasłem, nawiasy są częścią hasła: (Moj3BmI) Archiwum należy rozpakować. Na pulpicie konta Egzamin utwórz folder. Jako nazwy folderu użyj swojego numeru PESEL. Umieść w tym folderze rozpakowane pliki.

    Folder na pulpicie nazwany numerem PESEL - w tym rozwiązaniu Bartosz Bryniarski. Archiwum z3.zip rozpakuj do niego, hasło (Moj3BmI) razem z nawiasami. W środku: egzamin.sql, rys1.png i wzor.png.

  2. Krok 2: XAMPP, baza egzamin i import egzamin.sql

    ok. 2 min
    Polecenie z arkusza

    Do wykonania zadania należy użyć tabel: bmi oraz wynik przedstawionych na obrazie 1. Uruchom usługi MySQL i Apache za pomocą XAMPP Control Panel. Za pomocą narzędzia phpMyAdmin wykonaj podane operacje na bazie danych: Utwórz bazę danych o nazwie egzamin. Do bazy egzamin zaimportuj plik egzamin.sql z rozpakowanego archiwum

    W XAMPP Control Panel Start przy Apache i MySQL, w phpMyAdmin (http://localhost/phpmyadmin) Nowa → nazwa egzamin → Utwórz, potem karta Import, plik egzamin.sql, Wykonaj. Plik zakłada sześć tabel, ale zadanie korzysta tylko z dwóch z obrazu 1: bmi (cztery przedziały wskaźnika: od - do i opis) i wynik (zapisane pomiary; pole bmi_id wskazuje przedział z tabeli bmi).

  3. Krok 3: Zrzut import.png

    Kryteria:

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

    ok. 2 min
    Polecenie z arkusza

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

    Karta Struktura bazy egzamin pokazuje listę tabel z liczbą rekordów - to „elementy wskazujące na poprawnie wykonany import”. Zrzut całego ekranu z paskiem zadań zapisujesz jako import.png.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 3
  4. Krok 4: Zapytanie 1: przedziały BMI

    Kryteria: ,

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

    R.1.3 Utworzono zapytanie 1 wybierające jedynie pola informacja, wart_min i wart_max z tabeli bmi . W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT informacja, wart_min, wart_max FROM bmi; oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczne są dokładnie 4 rekordy, jedynie pola informacja, wart_min, wart_max dla wartości: niedowaga, waga prawidlowa, nadwaga, otylosc

    ok. 2 min
    Polecenie z arkusza

    Zapisz i wykonaj zapytania SQL działające na bazie egzamin. Zapytania zapisz w pliku kwerendy.txt, w folderze z numerem PESEL. Wykonaj zrzuty ekranu przedstawiające wyniki działania kwerend. Zrzuty zapisz w formacie JPG i nadaj im nazwy kw1, kw2, kw3, kw4. Zrzuty powinny obejmować cały ekran monitora z widocznym paskiem zadań. Zapytanie 1: wybierające jedynie pola informacja, wart_min i wart_max z tabeli bmi

    Trzy pola w kolejności kolumn tabeli na stronie. Wynik: 4 przedziały (niedowaga 0-18, waga prawidłowa 19-25, nadwaga 26-30, otyłość 31-100). Zapytanie do kwerendy.txt, zrzut kw1.jpg - to samo zapytanie wyśle skrypt 1.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT informacja, wart_min, wart_max FROM bmi;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: nowy pomiar w tabeli wynik

    Kryteria:

    R.1.4 Utworzono zapytanie 2 wstawiające do tabeli wynik rekord, o danych: klucz główny nadawany automatycznie, bmi_id = 3, data_pomiaru = 2020-05-10, wynik = 27. W pliku z kwerendami lub na zrzucie istnieje zapis np.: INSERT INTO wynik(bmi_id, data_pomiaru, wynik) VALUES (3, "2020-05- 10", 27); (możliwe również bez pierwszego nawiasu lub przez listę przypisań ...SET bmi_id=3...) oraz wynik działania zapytania udokumentowano zrzutem wskazującym na poprawne wykonanie kwerendy 2

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 2: wstawiające do tabeli wynik rekord z danymi: klucz główny nadawany automatycznie, bmi_id = 3, data_pomiaru = 2020-05-10, wynik = 27

    • INSERT INTO wynik (bmi_id, data_pomiaru, wynik) - lista pól bez id: klucz główny ma AUTO_INCREMENT, więc baza nada go sama (tu: 2),
    • VALUES (3, '2020-05-10', 27) - wartości w kolejności pól; data jako tekst w apostrofach, w formacie RRRR-MM-DD.

    phpMyAdmin odpowiada „1 wiersz wstawiony” - zrzut kw2.jpg. Zapytanie wykonaj raz: każde kolejne uruchomienie dopisze następny rekord.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT informacja, wart_min, wart_max FROM bmi;
    
    -- Zapytanie 2
    INSERT INTO wynik (bmi_id, data_pomiaru, wynik) VALUES (3, '2020-05-10', 27);
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: relacja wynik - bmi

    Kryteria:

    R.1.5 Utworzono zapytanie 3 wybierające jedynie pola wynik i data_pomiaru z tabeli wynik oraz informacja z tabeli bmi wykorzystano relację 1..n. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT wynik, data_pomiaru, bmi.informacja FROM wynik JOIN bmi ON wynik.bmi_id = bmi.id; możliwe też INNER JOIN lub bez JOIN (porównanie kluczy w warunku) oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczny jest co najmniej jeden wiersz jedynie z danymi: 20, 2020-05-24, waga prawidłowa. większa liczba wierszy wynika z wykonania kwerendy 2 lub wstawienia rekordów przez stronę ale nie z powodu nieprawidłowego przypisania kluczy w zapytaniu

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie pola wynik i data_pomiaru z tabeli wynik oraz informacja z tabeli bmi; zapytanie wykorzystuje relację

    • SELECT wynik, data_pomiaru, informacja - dwa pola z tabeli wynik, opis przedziału z tabeli bmi,
    • FROM wynik JOIN bmi ON wynik.bmi_id = bmi.id - relacja z obrazu 1: bmi_id pomiaru wskazuje id przedziału.

    Wynik po zapytaniu 2: dwa wiersze - 20, 2020-05-24, waga prawidlowa (rekord z importu) oraz 27, 2020-05-10, nadwaga. Zrzut: kw3.jpg.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT informacja, wart_min, wart_max FROM bmi;
    
    -- Zapytanie 2
    INSERT INTO wynik (bmi_id, data_pomiaru, wynik) VALUES (3, '2020-05-10', 27);
    
    -- Zapytanie 3
    SELECT wynik, data_pomiaru, informacja FROM wynik JOIN bmi ON wynik.bmi_id = bmi.id;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: granice nadwagi

    Kryteria:

    R.1.6 Utworzono zapytanie 4 wybierające jedynie pola wart_min i wart_max z tabeli bmi dla nadwagi. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT wart_min, wart_max FROM bmi WHERE informacja="nadwaga"; oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczny jest jeden wiersz jedynie z danymi 26, 30

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 4: wybierające jedynie pola wart_min i wart_max z tabeli bmi dla nadwagi (w polu informacja)

    SELECT wart_min, wart_max FROM bmi z warunkiem WHERE informacja = 'nadwaga' - tekst w apostrofach, tak jak jest zapisany w bazie. Wynik: jeden wiersz 26, 30. Zrzut: kw4.jpg.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT informacja, wart_min, wart_max FROM bmi;
    
    -- Zapytanie 2
    INSERT INTO wynik (bmi_id, data_pomiaru, wynik) VALUES (3, '2020-05-10', 27);
    
    -- Zapytanie 3
    SELECT wynik, data_pomiaru, informacja FROM wynik JOIN bmi ON wynik.bmi_id = bmi.id;
    
    -- Zapytanie 4
    SELECT wart_min, wart_max FROM bmi WHERE informacja = 'nadwaga';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Grafika i strona HTML

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

    Krok 8: Grafika: wysokość 400 px z przezroczystością

    Kryteria:

    R.2.1 Plik rys1.png przeskalowano z zachowaniem proporcji do wysokości 400 px, odpowiadająca temu szerokość wynosi 330 px (± 5 px); przezroczystość została zachowana

    ok. 2 min
    Polecenie z arkusza

    Plik rys1.png, wypakowany z archiwum, należy przeskalować z zachowaniem proporcji tak, aby jego wysokość wynosiła dokładnie 400 px. Należy zachować przezroczystość obrazu

    Oryginał ma 400 × 485 px i przezroczyste tło. W GIMP-ie Obraz → Skaluj obraz z zamkniętym łańcuchem proporcji, wysokość 400 px - szerokość: 400 × 400 / 485 = 329,9, czyli 330 px (klucz: 330 ±5). Zapis jako PNG - ten format przechowuje przezroczystość.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 8
  9. Krok 9: Plik bmi.php: nagłówek dokumentu

    Kryteria: , ,

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

    R.2.3 Nadano tytuł strony: "Twoje BMI”

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

    ok. 2 min
    Polecenie z arkusza

    Cechy witryny: Składa się ze strony o nazwie bmi.php. Zastosowany właściwy standard kodowania polskich znaków. Tytuł strony widoczny na karcie przeglądarki: „Twoje BMI”. Arkusz stylów w pliku o nazwie styl3.css prawidłowo połączony z kodem strony

    Cała część <head> w jednym kroku:

    • <!DOCTYPE html> - HTML5, lang="pl" - język treści,
    • <meta charset="UTF-8"> jako pierwszy element <head> (R.2.2 - razem z nazwą pliku bmi.php),
    • <title>Twoje BMI</title> (R.2.3),
    • <meta name="viewport"> - telefon pokazuje stronę w swojej szerokości (arkusz tego nie wymaga),
    • <link rel="stylesheet" href="styl3.css"> (R.3.5 - punkt przyznawany razem z poprawnymi selektorami).
    bmi.php PHP nowy plik
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Twoje BMI</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (bmi.php)
  10. Krok 10: Bloki: nagłówek z logo i banerem, część główna, kolumny, stopka

    Kryteria:

    R.2.4 Wygląd strony zdefiniowano dzieląc ją na bloki: logo, baner, blok główny, lewy i prawy oraz stopka. Zastosowano znaczniki sekcji

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki: obok siebie logo i baner, poniżej blok główny, poniżej bloki lewy i prawy oraz stopka. Podział zrealizowany za pomocą znaczników sekcji, zgodnie z obrazem 2

    <header> z sekcjami logo i banner, <main> - blok główny (jeden na stronie, więc bez identyfikatora), kontener <div class="columns"> z sekcjami left i right oraz <footer> - stopka. Znaczniki HTML5 mówią, czym jest każda część strony; <header> i kontener kolumn dostaną w CSS display: flex.

    bmi.php PHP zmienione: 16 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Twoje BMI</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <section id="logo">
            </section>
            <section id="banner">
            </section>
        </header>
        <main>
        </main>
        <div class="columns">
            <section id="left">
            </section>
            <section id="right">
            </section>
        </div>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (bmi.php)
  11. Krok 11: Logo i baner

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku logo: obraz wzor.png z tekstem alternatywnym „wzór BMI”. Zawartość bloku banera: nagłówek pierwszego stopnia o treści „Oblicz swoje BMI”

    Obraz wzoru z tekstem alternatywnym i nagłówek <h1> - poziom narzucony przez arkusz.

    bmi.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Twoje BMI</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <section id="logo">
                <img src="wzor.png" alt="wzór BMI">
            </section>
            <section id="banner">
                <h1>Oblicz swoje BMI</h1>
            </section>
        </header>
        <main>
        </main>
        <div class="columns">
            <section id="left">
            </section>
            <section id="right">
            </section>
        </div>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (bmi.php)
  12. Krok 12: Blok główny: tabela

    Kryteria:

    R.2.10 Pierwszy wiersz tabeli jest wierszem nagłówkowym, zastosowano znacznik <th> (opcjonalnie <thead> i <td>) oraz jego zamknięcie

    ok. 2 min
    Polecenie z arkusza

    Zawartość bloku głównego: Tabela z trzema kolumnami. Komórki nagłówkowe w pierwszym wierszu: „Interpretacja BMI”, „Wartość minimalna”, „Wartość maksymalna”

    Tabela z wierszem nagłówkowym w <thead> (klucz wprost dopuszcza <thead>, R.2.10) i pustym <tbody>, w którym skrypt wypisze przedziały.

    bmi.php PHP zmienione: 11 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Twoje BMI</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <section id="logo">
                <img src="wzor.png" alt="wzór BMI">
            </section>
            <section id="banner">
                <h1>Oblicz swoje BMI</h1>
            </section>
        </header>
        <main>
            <table>
                <thead>
                    <tr>
                        <th>Interpretacja BMI</th>
                        <th>Wartość minimalna</th>
                        <th>Wartość maksymalna</th>
                    </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
        </main>
        <div class="columns">
            <section id="left">
            </section>
            <section id="right">
            </section>
        </div>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (bmi.php)
  13. Krok 13: Blok lewy: nagłówek i formularz

    Kryteria: , , , ,

    R.2.5 W bloku banera zapisano nagłówek h1: "Oblicz swoje BMI", w bloku lewym nagłówek h2: "Podaj wagę i wzrost"

    R.2.7 W bloku lewym umieszczono formularz, zastosowano znacznik <form> oraz jego zamknięcie

    R.2.8 W formularzu umieszczono dwa pola edycyjne oraz przycisk "Oblicz i zapamiętaj wynik"

    R.3.2 Przycisk wysyła dane z formularza oraz przynajmniej jedno pole do wprowadzania danych jest typu liczbowego

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

    ok. 3 min
    Polecenie z arkusza

    Zawartość bloku lewego: Nagłówek drugiego stopnia o treści: „Podaj wagę i wzrost”. Formularz wysyłający dane metodą POST do tej samej strony, zawierający: Napis „Waga:”, następnie pole edycyjne typu numerycznego o wartości minimalnej 1. Napis „Wzrost w cm:”, następnie pole edycyjne typu numerycznego o wartości minimalnej 1. Przycisk wysyłający zawartość formularza z opisem „Oblicz i zapamiętaj wynik”

    • <h2> - razem z <h1> banera nagłówki są gotowe (R.2.5),
    • <form method="post"> bez action wysyła dane do bieżącego adresu, czyli do tej samej strony (R.2.7, R.3.3),
    • napisy w <label for="…">: kliknięcie napisu ustawia kursor w polu, a czytnik ekranu odczyta go razem z polem,
    • dwa pola type="number" z min="1" i nazwami waga, wzrost oraz <button type="submit"> z opisem z arkusza (R.2.8, R.3.2).
    bmi.php PHP zmienione: 8 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Twoje BMI</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <section id="logo">
                <img src="wzor.png" alt="wzór BMI">
            </section>
            <section id="banner">
                <h1>Oblicz swoje BMI</h1>
            </section>
        </header>
        <main>
            <table>
                <thead>
                    <tr>
                        <th>Interpretacja BMI</th>
                        <th>Wartość minimalna</th>
                        <th>Wartość maksymalna</th>
                    </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
        </main>
        <div class="columns">
            <section id="left">
                <h2>Podaj wagę i wzrost</h2>
                <form method="post">
                    <label for="weight">Waga:</label>
                    <input type="number" id="weight" name="waga" min="1"><br>
                    <label for="height">Wzrost w cm:</label>
                    <input type="number" id="height" name="wzrost" min="1"><br>
                    <button type="submit">Oblicz i zapamiętaj wynik</button>
                </form>
            </section>
            <section id="right">
            </section>
        </div>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (bmi.php)
  14. Krok 14: Blok prawy i stopka

    Kryteria: ,

    R.2.6 W bloku logo umieszczono obraz wzor.png oraz w bloku prawym umieszczono obraz rys1.png. Przynajmniej jeden z obrazów ma tekst alternatywny: "wzór BMI" (dla wzor.png ) lub "ćwiczenia" (dla rys1.png ), gdy obraz nie istnieje powinien wyświetlać się tekst alternatywny

    R.3.4 Kliknięcie odsyłacza otwiera plik tekstowy zawierający kwerendy

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku prawego: obraz rys1.png z tekstem alternatywnym o treści: „ćwiczenia”. Zawartość stopki: Tekst: „Autor: ”, dalej wstawiony numer PESEL zdającego. Odsyłacz do pliku kwerendy.txt o treści „Zobacz kwerendy”

    Ilustracja 330 × 400 px z tekstem alternatywnym (R.2.6). W stopce po „Autor: ” na egzaminie stoi numer PESEL, tu Bartosz Bryniarski, i odsyłacz do kwerendy.txt (R.3.4).

    bmi.php PHP zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Twoje BMI</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <section id="logo">
                <img src="wzor.png" alt="wzór BMI">
            </section>
            <section id="banner">
                <h1>Oblicz swoje BMI</h1>
            </section>
        </header>
        <main>
            <table>
                <thead>
                    <tr>
                        <th>Interpretacja BMI</th>
                        <th>Wartość minimalna</th>
                        <th>Wartość maksymalna</th>
                    </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
        </main>
        <div class="columns">
            <section id="left">
                <h2>Podaj wagę i wzrost</h2>
                <form method="post">
                    <label for="weight">Waga:</label>
                    <input type="number" id="weight" name="waga" min="1"><br>
                    <label for="height">Wzrost w cm:</label>
                    <input type="number" id="height" name="wzrost" min="1"><br>
                    <button type="submit">Oblicz i zapamiętaj wynik</button>
                </form>
            </section>
            <section id="right">
                <img src="rys1.png" alt="ćwiczenia">
            </section>
        </div>
        <footer>
            Autor: Bartosz Bryniarski
            <a href="kwerendy.txt">Zobacz kwerendy</a>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (bmi.php)
  15. Etap 3: Styl CSS

    Kroki 15-21 · po tym etapie masz 30 z 40 kryteriów (pkt)

    Krok 15: Zmienne kolorów i selektor body

    Kryteria:

    R.4.1 Ustawiono krój czcionki Georgia dla znacznika body lub kontenera całej strony

    ok. 2 min
    Polecenie z arkusza

    Cechy formatowania CSS: Dla znacznika body: krój czcionki Georgia, kolor tła #FFFAFA

    Reguła :root z zmiennymi CSS - kolory z arkusza dostają nazwy, a reguły odwołują się do nich przez var(--coral). Reguła body: Georgia z zapasowym krojem szeryfowym (R.4.1) i jasne tło.

    styl3.css CSS nowy plik
    :root {
        --coral: #FF7F50;
        --snow: #FFFAFA;
        --light-gray: #D3D3D3;
    }
    
    body {
        font-family: Georgia, serif;
        background-color: var(--snow);
    }
    Podgląd strony po tym kroku (bmi.php)
  16. Krok 16: Układ: dwa rzędy na flex

    Kryteria:

    R.3.1 Wygląd układu bloków po uruchomieniu strony w przeglądarce jest w pełni zgodny z obrazem 2 w arkuszu egzaminacyjnym

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki: obok siebie logo i baner, poniżej blok główny, poniżej bloki lewy i prawy oraz stopka. Podział zrealizowany za pomocą znaczników sekcji, zgodnie z obrazem 2

    display: flex na <header> i kontenerze .columns ustawia ich sekcje w rzędzie. <main> i <footer> stoją między nimi zwykłymi blokami na całą szerokość - bez float i clear. Klucz ocenia układ w przeglądarce (R.3.1), a szerokości 25/75 i 40/60 stoją na samych blokach, jak oczekuje R.4.5.

    styl3.css CSS zmienione: 5 linii
    :root {
        --coral: #FF7F50;
        --snow: #FFFAFA;
        --light-gray: #D3D3D3;
    }
    
    body {
        font-family: Georgia, serif;
        background-color: var(--snow);
    }
    
    header,
    .columns {
        display: flex;
    }
    Podgląd strony po tym kroku (bmi.php)
  17. Krok 17: Logo i baner

    Kryteria:

    R.4.10 Ustawiono rozmiar czcionki 130% dla banera

    ok. 1 min
    Polecenie z arkusza

    Dla bloku logo: tło koloru #FF7F50, szerokość 25%, wysokość 100 px. Dla banera: tło koloru #FF7F50, wyrównanie tekstu do środka, szerokość 75%, wysokość 100 px, rozmiar czcionki 130%

    Oba bloki 100 px wysokości, szerokości 25 + 75 = 100%, rozmiar czcionki banera 130% (R.4.10).

    styl3.css CSS zmienione: 14 linii
    :root {
        --coral: #FF7F50;
        --snow: #FFFAFA;
        --light-gray: #D3D3D3;
    }
    
    body {
        font-family: Georgia, serif;
        background-color: var(--snow);
    }
    
    header,
    .columns {
        display: flex;
    }
    
    #logo {
        background-color: var(--coral);
        width: 25%;
        height: 100px;
    }
    
    #banner {
        background-color: var(--coral);
        text-align: center;
        width: 75%;
        height: 100px;
        font-size: 130%;
    }
    Podgląd strony po tym kroku (bmi.php)
  18. Krok 18: Blok główny i stopka

    Kryteria:

    R.4.2 Ustawiono kolor tła #FFFAFA dla znacznika body lub kontenera całej strony, oraz kolor tła #FF7F50 dla bloków logo, banera, głównego i stopki

    ok. 1 min
    Polecenie z arkusza

    Dla bloku głównego: tło koloru #FF7F50, marginesy wewnętrzne 70 px. Dla stopki: tło koloru #FF7F50, marginesy wewnętrzne 20 px

    Selektory znaczników main i footer - koralowe tło z tej samej zmiennej co logo i baner (R.4.2 razem z tłem strony).

    styl3.css CSS zmienione: 10 linii
    :root {
        --coral: #FF7F50;
        --snow: #FFFAFA;
        --light-gray: #D3D3D3;
    }
    
    body {
        font-family: Georgia, serif;
        background-color: var(--snow);
    }
    
    header,
    .columns {
        display: flex;
    }
    
    #logo {
        background-color: var(--coral);
        width: 25%;
        height: 100px;
    }
    
    #banner {
        background-color: var(--coral);
        text-align: center;
        width: 75%;
        height: 100px;
        font-size: 130%;
    }
    
    main {
        background-color: var(--coral);
        padding: 70px;
    }
    
    footer {
        background-color: var(--coral);
        padding: 20px;
    }
    Podgląd strony po tym kroku (bmi.php)
  19. Krok 19: Bloki lewy i prawy

    Kryteria:

    R.4.4 Ustawiono wysokość bloku logo i banera na 100px, bloku lewego i prawego: 400px

    ok. 1 min
    Polecenie z arkusza

    Dla bloku lewego: szerokość 40%, wysokość 400 px. Dla bloku prawego: szerokość 60%, wysokość 400 px, wyrównanie tekstu do prawej strony

    Rząd 40 + 60%, oba bloki po 400 px wysokości (R.4.4); ilustracja przy prawej krawędzi.

    styl3.css CSS zmienione: 11 linii
    :root {
        --coral: #FF7F50;
        --snow: #FFFAFA;
        --light-gray: #D3D3D3;
    }
    
    body {
        font-family: Georgia, serif;
        background-color: var(--snow);
    }
    
    header,
    .columns {
        display: flex;
    }
    
    #logo {
        background-color: var(--coral);
        width: 25%;
        height: 100px;
    }
    
    #banner {
        background-color: var(--coral);
        text-align: center;
        width: 75%;
        height: 100px;
        font-size: 130%;
    }
    
    main {
        background-color: var(--coral);
        padding: 70px;
    }
    
    footer {
        background-color: var(--coral);
        padding: 20px;
    }
    
    #left {
        width: 40%;
        height: 400px;
    }
    
    #right {
        width: 60%;
        height: 400px;
        text-align: right;
    }
    Podgląd strony po tym kroku (bmi.php)
  20. Krok 20: Formularz

    Kryteria:

    R.4.7 Ustawiono marginesy wewnętrzne (padding) 70px dla bloku głównego, 20px dla stopki oraz marginesy zewnętrzne (margin) 80px dla znacznika form

    ok. 1 min
    Polecenie z arkusza

    Dla znacznika formularza: marginesy zewnętrzne 80 px

    Margines zewnętrzny formularza; razem z marginesami wewnętrznymi bloku głównego i stopki - R.4.7.

    styl3.css CSS zmienione: 4 linie
    :root {
        --coral: #FF7F50;
        --snow: #FFFAFA;
        --light-gray: #D3D3D3;
    }
    
    body {
        font-family: Georgia, serif;
        background-color: var(--snow);
    }
    
    header,
    .columns {
        display: flex;
    }
    
    #logo {
        background-color: var(--coral);
        width: 25%;
        height: 100px;
    }
    
    #banner {
        background-color: var(--coral);
        text-align: center;
        width: 75%;
        height: 100px;
        font-size: 130%;
    }
    
    main {
        background-color: var(--coral);
        padding: 70px;
    }
    
    footer {
        background-color: var(--coral);
        padding: 20px;
    }
    
    #left {
        width: 40%;
        height: 400px;
    }
    
    #right {
        width: 60%;
        height: 400px;
        text-align: right;
    }
    
    form {
        margin: 80px;
    }
    Podgląd strony po tym kroku (bmi.php)
  21. Krok 21: Tabela i wiersz pod kursorem

    Kryteria: , , , ,

    R.4.3 Ustawiono biały kolor czcionki dla znacznika tabeli

    R.4.5 Ustawiono szerokość bloku logo na 25%, banera na 75%, bloku lewego na 40%, prawego na 60%, znacznika table 80%

    R.4.6 Ustawiono wyrównanie tekstu do środka dla banera, znacznika tabeli oraz wyrównanie tekstu do prawej strony dla bloku prawego

    R.4.8 Ustawiono obramowanie o szerokości 1px linią kropkowaną, białą dla znacznika tabeli

    R.4.9 W momencie najechania kursorem na pojedynczy wiersz tabeli kolor tła wiersza zmienia się na #D3D3D3, a czcionki na czarny (np. za pomocą pseudoklasy hover)

    ok. 2 min
    Polecenie z arkusza

    Dla znacznika tabeli: biały kolor czcionki, wyrównanie tekstu do środka, szerokość 80%, obramowanie 1px, kropkowane, białe. W momencie najechania kursorem na pojedynczy wiersz tabeli, kolor tła wiersza zmienia się na #D3D3D3 a czcionki na czarny

    Biały tekst (R.4.3), wyrównanie do środka (R.4.6 razem z banerem i blokiem prawym), szerokość 80% (R.4.5), kropkowane białe obramowanie (R.4.8) i pseudoklasa tr:hover (R.4.9).

    styl3.css CSS zmienione: 12 linii
    :root {
        --coral: #FF7F50;
        --snow: #FFFAFA;
        --light-gray: #D3D3D3;
    }
    
    body {
        font-family: Georgia, serif;
        background-color: var(--snow);
    }
    
    header,
    .columns {
        display: flex;
    }
    
    #logo {
        background-color: var(--coral);
        width: 25%;
        height: 100px;
    }
    
    #banner {
        background-color: var(--coral);
        text-align: center;
        width: 75%;
        height: 100px;
        font-size: 130%;
    }
    
    main {
        background-color: var(--coral);
        padding: 70px;
    }
    
    footer {
        background-color: var(--coral);
        padding: 20px;
    }
    
    #left {
        width: 40%;
        height: 400px;
    }
    
    #right {
        width: 60%;
        height: 400px;
        text-align: right;
    }
    
    form {
        margin: 80px;
    }
    
    table {
        color: white;
        text-align: center;
        width: 80%;
        border: 1px dotted white;
    }
    
    tr:hover {
        background-color: var(--light-gray);
        color: black;
    }
    Podgląd strony po tym kroku (bmi.php)
  22. Etap 4: Skrypt PHP

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

    Krok 22: PDO: połączenie, zapytanie 1 i zamknięcie

    Kryteria: , , ,

    R.5.1 Skrypt napisano w języku PHP i zawiera on co najmniej jedną instrukcję poprawną składniowo

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

    R.5.3 Po wykonaniu operacji na bazie zastosowano funkcję zamknięcia bazy

    R.5.5 Skrypt 1 wysyła do bazy danych zapytanie 1 lub Skrypt 2 wysyła do bazy zmodyfikowane zapytanie 2 tak, że dane wstawiane do tabeli są wyliczone z danych formularza lub zwrócone funkcją PHP np. date('Y-m-d')

    ok. 4 min
    Polecenie z arkusza

    Napisany w języku PHP, w pliku bmi.php. Skrypt łączy się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie egzamin. Działanie skryptu 1: Wysyła do bazy danych zapytanie 1. Po wykonaniu operacji na bazie skrypt zamyka połączenie z serwerem

    Logika PHP stoi na górze pliku, przed <!DOCTYPE html> - najpierw dane, potem HTML, który je wypisuje.

    • e() zamienia wartość na tekst i przepuszcza go przez htmlspecialchars, więc znaki <, >, & i cudzysłowy pokażą się jako tekst,
    • new PDO(...) - localhost, baza egzamin w adresie DSN, root, puste hasło (R.5.1, R.5.2), PDO::ERRMODE_EXCEPTION - błąd bazy rzuca wyjątek,
    • $pdo->query(...)->fetchAll(PDO::FETCH_ASSOC) - zapytanie 1 (R.5.5) i cztery przedziały w tablicy $ranges, z polami dostępnymi po nazwach,
    • catch zapisuje neutralny komunikat, a finally wykonuje się po sukcesie i po błędzie: $pdo = null zamyka połączenie (R.5.3).
    bmi.php PHP zmienione: 20 linii
    <?php
    function e($value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
    }
    
    $ranges = [];
    $error = null;
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=egzamin;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        ]);
        $ranges = $pdo->query('SELECT informacja, wart_min, wart_max FROM bmi;')->fetchAll(PDO::FETCH_ASSOC);
    } catch (PDOException $exception) {
        $error = 'Baza danych jest chwilowo niedostępna';
    } finally {
        $pdo = null;
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Twoje BMI</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <section id="logo">
                <img src="wzor.png" alt="wzór BMI">
            </section>
            <section id="banner">
                <h1>Oblicz swoje BMI</h1>
            </section>
        </header>
        <main>
            <table>
                <thead>
                    <tr>
                        <th>Interpretacja BMI</th>
                        <th>Wartość minimalna</th>
                        <th>Wartość maksymalna</th>
                    </tr>
                </thead>
                <tbody>
                </tbody>
            </table>
        </main>
        <div class="columns">
            <section id="left">
                <h2>Podaj wagę i wzrost</h2>
                <form method="post">
                    <label for="weight">Waga:</label>
                    <input type="number" id="weight" name="waga" min="1"><br>
                    <label for="height">Wzrost w cm:</label>
                    <input type="number" id="height" name="wzrost" min="1"><br>
                    <button type="submit">Oblicz i zapamiętaj wynik</button>
                </form>
            </section>
            <section id="right">
                <img src="rys1.png" alt="ćwiczenia">
            </section>
        </div>
        <footer>
            Autor: Bartosz Bryniarski
            <a href="kwerendy.txt">Zobacz kwerendy</a>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (bmi.php)
  23. Krok 23: Wiersze tabeli: foreach w tbody

    Kryteria: ,

    R.2.9 W bloku głównym umieszczono tabelę o trzech kolumnach i przynajmniej jednym wierszem danych, użyto znaczniki <table>, <tr>, <td> i ich zamknięcia; generowana skryptem lub HTML

    R.5.6 Skrypt 1 wyświetla otrzymane dane w komórkach tabeli zgodnie z obrazem 2 arkusza egzaminacyjnego

    ok. 3 min
    Polecenie z arkusza

    Dane otrzymane z bazy są umieszczane w kolejnych wierszach tabeli, w odpowiednich komórkach, tak jak na Obrazie 2

    Pętla w składni alternatywnej foreach (...): / endforeach;, między nimi zwykły HTML wiersza, a w komórkach skrócone <?= ... ?> z e(). Trzy komórki w kolejności nagłówków (R.2.9, R.5.6); przy awarii bazy - jeden wiersz z komunikatem. Podgląd: cztery przedziały, jak na obrazie 2.

    bmi.php PHP zmienione: 12 linii
    <?php
    function e($value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
    }
    
    $ranges = [];
    $error = null;
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=egzamin;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        ]);
        $ranges = $pdo->query('SELECT informacja, wart_min, wart_max FROM bmi;')->fetchAll(PDO::FETCH_ASSOC);
    } catch (PDOException $exception) {
        $error = 'Baza danych jest chwilowo niedostępna';
    } finally {
        $pdo = null;
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Twoje BMI</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <section id="logo">
                <img src="wzor.png" alt="wzór BMI">
            </section>
            <section id="banner">
                <h1>Oblicz swoje BMI</h1>
            </section>
        </header>
        <main>
            <table>
                <thead>
                    <tr>
                        <th>Interpretacja BMI</th>
                        <th>Wartość minimalna</th>
                        <th>Wartość maksymalna</th>
                    </tr>
                </thead>
                <tbody>
    <?php foreach ($ranges as $range): ?>
                    <tr>
                        <td><?= e($range['informacja']) ?></td>
                        <td><?= e($range['wart_min']) ?></td>
                        <td><?= e($range['wart_max']) ?></td>
                    </tr>
    <?php endforeach; ?>
    <?php if ($error !== null): ?>
                    <tr>
                        <td colspan="3"><?= e($error) ?></td>
                    </tr>
    <?php endif; ?>
                </tbody>
            </table>
        </main>
        <div class="columns">
            <section id="left">
                <h2>Podaj wagę i wzrost</h2>
                <form method="post">
                    <label for="weight">Waga:</label>
                    <input type="number" id="weight" name="waga" min="1"><br>
                    <label for="height">Wzrost w cm:</label>
                    <input type="number" id="height" name="wzrost" min="1"><br>
                    <button type="submit">Oblicz i zapamiętaj wynik</button>
                </form>
            </section>
            <section id="right">
                <img src="rys1.png" alt="ćwiczenia">
            </section>
        </div>
        <footer>
            Autor: Bartosz Bryniarski
            <a href="kwerendy.txt">Zobacz kwerendy</a>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (bmi.php)
  24. Etap 5: Skrypt PHP: obliczenie i zapis BMI

    Kroki 24-26 · po tym etapie masz 40 z 40 kryteriów (pkt)

    Krok 24: Dane z formularza, obliczenie i wypisanie wyniku

    Kryteria: , ,

    R.5.4 W skrypcie działającym na danych przesłanych przez formularz zastosowano tablicę $_POST, zapis musi być poprawny składniowo i logicznie

    R.5.7 Skrypt 2 liczy wskaźnik BMI na podstawie danych z formularza, wzór np. $waga / ($wzrost * $wzrost) * 10000;

    R.5.8 Skrypt 2 wyświetla dane i obliczenia według wzoru: „Twoja waga: <podana_waga>; Twój wzrost: <podany_wzrost> BMI wynosi: <obliczona_wartość>”

    ok. 4 min
    Polecenie z arkusza

    Działanie skryptu 2, który odbiera dane przesłane z formularza: Skrypt odbiera dane z dwóch pól edycyjnych. Jeśli przynajmniej jedno pole edycyjne jest puste, nie jest wykonywana żadna akcja. Jeżeli wpisano dane, skrypt liczy wskaźnik BMI zgodnie ze wzorem znajdującym się na obrazie wzor.png (otrzymaną wartość należy pomnożyć przez 10000, gdyż wzrost podany jest w centymetrach). Dane i obliczenia są wyświetlone według wzoru: „Twoja waga: <podana_waga>; Twój wzrost: <podany_wzrost> BMI wynosi: <obliczona_wartość>”, łamanie linii przed „BMI”

    • $_POST['waga'] ?? null - wartość pola albo null, gdy formularza nie wysłano (R.5.4),
    • filter_var(..., FILTER_VALIDATE_FLOAT, ['options' => ['min_range' => 1]]) zwraca liczbę albo false - dla pustego pola, tekstu, zera i liczby mniejszej od 1. Warunek !== false dla obu pól daje zachowanie z arkusza: puste pole - brak akcji, a przy okazji żaden tekst ani zero nie trafi do dzielenia,
    • wzór z wzor.png z mnożeniem przez 10 000, bo wzrost jest w centymetrach (R.5.7),
    • w bloku lewym, pod formularzem, if (...): / endif; wypisuje paragraf z danymi i wynikiem, z <br> przed „BMI” (R.5.8).

    Podgląd po wysłaniu 60 i 170: „BMI wynosi: 20.76124567474”, jak na obrazie 2. Sprawdzone też: pierwsze wejście, puste pole, tekst zamiast liczby i wzrost 0 - strona nie wypisuje wyniku i nie zgłasza błędu.

    bmi.php PHP zmienione: 10 linii
    <?php
    function e($value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
    }
    
    $ranges = [];
    $error = null;
    $saveError = null;
    $weight = filter_var($_POST['waga'] ?? null, FILTER_VALIDATE_FLOAT, ['options' => ['min_range' => 1]]);
    $height = filter_var($_POST['wzrost'] ?? null, FILTER_VALIDATE_FLOAT, ['options' => ['min_range' => 1]]);
    $bmi = null;
    if ($weight !== false && $height !== false) {
        $bmi = $weight / ($height * $height) * 10000;
    }
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=egzamin;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        ]);
        $ranges = $pdo->query('SELECT informacja, wart_min, wart_max FROM bmi;')->fetchAll(PDO::FETCH_ASSOC);
    } catch (PDOException $exception) {
        $error = 'Baza danych jest chwilowo niedostępna';
    } finally {
        $pdo = null;
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Twoje BMI</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <section id="logo">
                <img src="wzor.png" alt="wzór BMI">
            </section>
            <section id="banner">
                <h1>Oblicz swoje BMI</h1>
            </section>
        </header>
        <main>
            <table>
                <thead>
                    <tr>
                        <th>Interpretacja BMI</th>
                        <th>Wartość minimalna</th>
                        <th>Wartość maksymalna</th>
                    </tr>
                </thead>
                <tbody>
    <?php foreach ($ranges as $range): ?>
                    <tr>
                        <td><?= e($range['informacja']) ?></td>
                        <td><?= e($range['wart_min']) ?></td>
                        <td><?= e($range['wart_max']) ?></td>
                    </tr>
    <?php endforeach; ?>
    <?php if ($error !== null): ?>
                    <tr>
                        <td colspan="3"><?= e($error) ?></td>
                    </tr>
    <?php endif; ?>
                </tbody>
            </table>
        </main>
        <div class="columns">
            <section id="left">
                <h2>Podaj wagę i wzrost</h2>
                <form method="post">
                    <label for="weight">Waga:</label>
                    <input type="number" id="weight" name="waga" min="1"><br>
                    <label for="height">Wzrost w cm:</label>
                    <input type="number" id="height" name="wzrost" min="1"><br>
                    <button type="submit">Oblicz i zapamiętaj wynik</button>
                </form>
    <?php if ($bmi !== null): ?>
                <p>Twoja waga: <?= e($weight) ?>; Twój wzrost: <?= e($height) ?><br>BMI wynosi: <?= e($bmi) ?></p>
    <?php endif; ?>
            </section>
            <section id="right">
                <img src="rys1.png" alt="ćwiczenia">
            </section>
        </div>
        <footer>
            Autor: Bartosz Bryniarski
            <a href="kwerendy.txt">Zobacz kwerendy</a>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (bmi.php)
  25. Krok 25: Przedział BMI z tabeli bmi

    Kryteria:

    R.5.9 Skrypt 2 wyznacza przedział BMI, zgodnie z tabelą bmi w bazie danych i przypisuje wartość 1 dla niedowagi, 2 dla wagi prawidłowej, 3 dla nadwagi i 4 dla otyłości (sprawdzić dla danych, które mieszczą się w przedziałach z tabeli bmi widocznej na obrazie 2 arkusza egzaminacyjnego)

    ok. 3 min
    Polecenie z arkusza

    Następnie skrypt wyznacza przedział BMI, zgodnie z wartościami, jakie są umieszczone w tabeli i przypisuje wartość 1 dla niedowagi, 2 dla wagi prawidłowej, 3 dla nadwagi i 4 dla otyłości

    Zamiast przepisywać granice do kodu, skrypt pyta o nie tabelę bmi: SELECT id FROM bmi WHERE wart_min <= ? ORDER BY wart_min DESC LIMIT 1 - spośród przedziałów, które zaczynają się nie wyżej niż obliczone BMI, bierze ten o największym początku. Identyfikatory w tabeli to dokładnie numery z arkusza: 1 niedowaga, 2 waga prawidłowa, 3 nadwaga, 4 otyłość (R.5.9).

    Porównanie z samym początkiem przedziału zamyka dziury między granicami całkowitymi (BMI 18,6 trafia do niedowagi, 25,5 - do wagi prawidłowej), a gdyby ktoś zmienił granice w bazie, skrypt od razu je uwzględni. BMI pochodzi z obliczeń, ale trafia do zapytania jako parametr ? zapytania przygotowanego (prepare + execute) - dane nigdy nie są doklejane do treści SQL.

    bmi.php PHP zmienione: 6 linii
    <?php
    function e($value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
    }
    
    $ranges = [];
    $error = null;
    $saveError = null;
    $weight = filter_var($_POST['waga'] ?? null, FILTER_VALIDATE_FLOAT, ['options' => ['min_range' => 1]]);
    $height = filter_var($_POST['wzrost'] ?? null, FILTER_VALIDATE_FLOAT, ['options' => ['min_range' => 1]]);
    $bmi = null;
    if ($weight !== false && $height !== false) {
        $bmi = $weight / ($height * $height) * 10000;
    }
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=egzamin;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        ]);
        $ranges = $pdo->query('SELECT informacja, wart_min, wart_max FROM bmi;')->fetchAll(PDO::FETCH_ASSOC);
    
        if ($bmi !== null) {
            $find = $pdo->prepare('SELECT id FROM bmi WHERE wart_min <= ? ORDER BY wart_min DESC LIMIT 1;');
            $find->execute([$bmi]);
            $rangeId = (int) $find->fetchColumn();
        }
    } catch (PDOException $exception) {
        $error = 'Baza danych jest chwilowo niedostępna';
    } finally {
        $pdo = null;
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Twoje BMI</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <section id="logo">
                <img src="wzor.png" alt="wzór BMI">
            </section>
            <section id="banner">
                <h1>Oblicz swoje BMI</h1>
            </section>
        </header>
        <main>
            <table>
                <thead>
                    <tr>
                        <th>Interpretacja BMI</th>
                        <th>Wartość minimalna</th>
                        <th>Wartość maksymalna</th>
                    </tr>
                </thead>
                <tbody>
    <?php foreach ($ranges as $range): ?>
                    <tr>
                        <td><?= e($range['informacja']) ?></td>
                        <td><?= e($range['wart_min']) ?></td>
                        <td><?= e($range['wart_max']) ?></td>
                    </tr>
    <?php endforeach; ?>
    <?php if ($error !== null): ?>
                    <tr>
                        <td colspan="3"><?= e($error) ?></td>
                    </tr>
    <?php endif; ?>
                </tbody>
            </table>
        </main>
        <div class="columns">
            <section id="left">
                <h2>Podaj wagę i wzrost</h2>
                <form method="post">
                    <label for="weight">Waga:</label>
                    <input type="number" id="weight" name="waga" min="1"><br>
                    <label for="height">Wzrost w cm:</label>
                    <input type="number" id="height" name="wzrost" min="1"><br>
                    <button type="submit">Oblicz i zapamiętaj wynik</button>
                </form>
    <?php if ($bmi !== null): ?>
                <p>Twoja waga: <?= e($weight) ?>; Twój wzrost: <?= e($height) ?><br>BMI wynosi: <?= e($bmi) ?></p>
    <?php endif; ?>
            </section>
            <section id="right">
                <img src="rys1.png" alt="ćwiczenia">
            </section>
        </div>
        <footer>
            Autor: Bartosz Bryniarski
            <a href="kwerendy.txt">Zobacz kwerendy</a>
        </footer>
    </body>
    </html>

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

  26. Krok 26: Zapis pomiaru: zapytanie 2 przygotowane

    ok. 3 min
    Polecenie z arkusza

    Skrypt wysyła do bazy danych zmodyfikowane zapytanie 2 wstawiające dane do bazy: bmi_id – wyznaczony przedział np. 3, jeśli obliczone BMI to nadwaga; data_pomiaru – aktualna data wyznaczona funkcją PHP, w formacie Y-m-d; wynik – obliczony wskaźnik BMI

    Zapytanie 2 z trzema parametrami ?, a w execute ich wartości w tej samej kolejności: przedział, date('Y-m-d') (dzisiejsza data w formacie bazy) i obliczone BMI. Nie trzeba pilnować apostrofów wokół daty - zajmuje się tym PDO.

    Zapis ma własny try/catch: gdy się nie powiedzie, pod wynikiem pojawi się „Nie udało się zapisać wyniku”, a BMI i tak jest wyświetlone. Sprawdzone w bazie po wysłaniu 60 i 170: rekord z bmi_id = 2, dzisiejszą datą (2026-11-10) i wynikiem 21 - kolumna wynik jest całkowita, więc baza zapisuje wartość bez części ułamkowej. Dla 54, 74, 80 i 95 kg przy 170 cm zapisały się przedziały 1, 2, 3 i 4.

    Typowe błędy

    Formularz wysyłany ponownie (F5 po wysłaniu) dopisuje kolejny rekord. Typowa odpowiedź na to - przekierowanie po zapisie (wzorzec PRG) - wymagałaby przechowania wyniku do wyświetlenia (np. w sesji), a arkusz każe pokazać wynik w odpowiedzi na wysłanie formularza.

    bmi.php PHP zmienione: 10 linii
    <?php
    function e($value): string
    {
        return htmlspecialchars((string) $value, ENT_QUOTES, 'UTF-8');
    }
    
    $ranges = [];
    $error = null;
    $saveError = null;
    $weight = filter_var($_POST['waga'] ?? null, FILTER_VALIDATE_FLOAT, ['options' => ['min_range' => 1]]);
    $height = filter_var($_POST['wzrost'] ?? null, FILTER_VALIDATE_FLOAT, ['options' => ['min_range' => 1]]);
    $bmi = null;
    if ($weight !== false && $height !== false) {
        $bmi = $weight / ($height * $height) * 10000;
    }
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=egzamin;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        ]);
        $ranges = $pdo->query('SELECT informacja, wart_min, wart_max FROM bmi;')->fetchAll(PDO::FETCH_ASSOC);
    
        if ($bmi !== null) {
            $find = $pdo->prepare('SELECT id FROM bmi WHERE wart_min <= ? ORDER BY wart_min DESC LIMIT 1;');
            $find->execute([$bmi]);
            $rangeId = (int) $find->fetchColumn();
    
            try {
                $insert = $pdo->prepare('INSERT INTO wynik (bmi_id, data_pomiaru, wynik) VALUES (?, ?, ?);');
                $insert->execute([$rangeId, date('Y-m-d'), $bmi]);
            } catch (PDOException $exception) {
                $saveError = 'Nie udało się zapisać wyniku';
            }
        }
    } catch (PDOException $exception) {
        $error = 'Baza danych jest chwilowo niedostępna';
    } finally {
        $pdo = null;
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Twoje BMI</title>
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <section id="logo">
                <img src="wzor.png" alt="wzór BMI">
            </section>
            <section id="banner">
                <h1>Oblicz swoje BMI</h1>
            </section>
        </header>
        <main>
            <table>
                <thead>
                    <tr>
                        <th>Interpretacja BMI</th>
                        <th>Wartość minimalna</th>
                        <th>Wartość maksymalna</th>
                    </tr>
                </thead>
                <tbody>
    <?php foreach ($ranges as $range): ?>
                    <tr>
                        <td><?= e($range['informacja']) ?></td>
                        <td><?= e($range['wart_min']) ?></td>
                        <td><?= e($range['wart_max']) ?></td>
                    </tr>
    <?php endforeach; ?>
    <?php if ($error !== null): ?>
                    <tr>
                        <td colspan="3"><?= e($error) ?></td>
                    </tr>
    <?php endif; ?>
                </tbody>
            </table>
        </main>
        <div class="columns">
            <section id="left">
                <h2>Podaj wagę i wzrost</h2>
                <form method="post">
                    <label for="weight">Waga:</label>
                    <input type="number" id="weight" name="waga" min="1"><br>
                    <label for="height">Wzrost w cm:</label>
                    <input type="number" id="height" name="wzrost" min="1"><br>
                    <button type="submit">Oblicz i zapamiętaj wynik</button>
                </form>
    <?php if ($bmi !== null): ?>
                <p>Twoja waga: <?= e($weight) ?>; Twój wzrost: <?= e($height) ?><br>BMI wynosi: <?= e($bmi) ?></p>
    <?php endif; ?>
    <?php if ($saveError !== null): ?>
                <p><?= e($saveError) ?></p>
    <?php endif; ?>
            </section>
            <section id="right">
                <img src="rys1.png" alt="ćwiczenia">
            </section>
        </div>
        <footer>
            Autor: Bartosz Bryniarski
            <a href="kwerendy.txt">Zobacz kwerendy</a>
        </footer>
    </body>
    </html>

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

  27. Etap 6: Porównanie z wersją podstawową

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

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

    ok. 3 min

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

    wersja podstawowawersja nowoczesna
    blokisześć <section> jedna pod drugą<header>, <main>, kontener kolumn i <footer>
    układfloat: left i clear: bothdisplay: flex na <header> i .columns
    kolorywpisane w każdej regulezmienne --coral, --snow, --light-gray
    formularznapisy przed polami, <input type="submit"><label for> i <button>
    bibliotekamysqli (tabela 1 arkusza), dwa połączeniaPDO z wyjątkami, jedno połączenie
    dane z formularzaempty (w Dobrych praktykach is_numeric)filter_var z FILTER_VALIDATE_FLOAT
    przedział BMIdrabinka if z granicami w kodziezapytanie do tabeli bmi
    zapisINSERT z wklejonymi zmiennymizapytanie przygotowane z ?
    wypisywaniewprost (zabezpieczone w Dobrych praktykach)zawsze przez e()
    błąd bazy„Fatal error” ze ścieżką plikucatch i komunikat, wynik BMI dalej widoczny

    Sprawdzone na prawdziwym PHP: opis przedziału ze znacznikiem <s> ta wersja pokazuje dosłownie, a bez bazy egzamin strona wyświetla tabelę z komunikatem „Baza danych jest chwilowo niedostępna”, liczy BMI i informuje, że wyniku nie zapisano.

    Na egzaminie wersja podstawowa trzyma się funkcji z tabeli 1 arkusza i łatwiej ją napisać bez dokumentacji; nowoczesna oddziela logikę od wyglądu i nie wywraca się na złych danych ani przy awarii bazy - klucz przyjmuje obie.

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

  28. Etap 7: Oddanie pracy

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

    Krok 28: 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, zapisz go w folderze z numerem PESEL, jako przeglądarka.txt. Nagraj płytę z rezultatami pracy.

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

    przeglądarka.txt Tekst nowy plik
    Google Chrome

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

Podgląd

To jedno z możliwych rozwiązań. Egzaminator ocenia spełnienie kryteriów z klucza odpowiedzi, a nie zgodność z tym kodem - inna, poprawna droga też daje punkty.

Autor rozwiązania: . Licencja CC BY-NC 4.0 - kopiowanie i przeróbki do celów niekomercyjnych, z podaniem autora i źródła (link do tej strony) (zasady wykorzystania). Treść arkusza i klucza oceniania pochodzi z CKE.

Potwierdzenie

Informacja