Przejdź do głównej treści
EE.09-03-20.06-SGCzerwiec 2020 · 36 kroków · ok. 75 min · 34 kryteria z klucza

Rozwiązanie krok po kroku: Kalendarz z wpisami na lipiec (Nowoczesny kod (flex, grid, PDO))

To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza egzamin5 z tabelą zadania i czterema zapytaniami, logo o wysokości 150 px oraz strona kalendarz.php z dwoma blokami banera, blokami dni lipca i formularzem w stopce - i tak samo spełnia każde kryterium klucza. Inna jest droga:

  • flexbox i siatka zamiast float: dwa bloki banera stoją w znaczniku <header> ułożonym we flexie, a bloki dni - w <main> z siatką, która sama mieści w rzędzie tyle kafelków, ile pozwala szerokość okna; stopka nie potrzebuje clear,
  • zmienne CSS dla dwóch kolorów z arkusza, etykieta <label for> przy polu i przycisk <button>,
  • PHP na PDO na górze pliku kalendarz.php, przed HTML-em - w banerze i bloku głównym zostają tylko pętla i wypisywanie danych,
  • zapytanie przygotowane do zapisu wpisu, pusty formularz nic nie kasuje, a dane z bazy wypisywane przez htmlspecialchars,
  • po zapisie przekierowanie - odświeżenie strony nie wyśle formularza drugi raz.

To arkusz EE.09 z czerwca 2020 - kwalifikacji formuły 2017, którą zastąpiła INF.03. Baza i grafiki pochodzą z oryginalnego archiwum CKE egzamin5.zip (załącznik na stronie arkusza), zapytania i zrzuty są policzone na MariaDB 10.4, a podgląd od kroków z PHP to wynik prawdziwego uruchomienia kodu w PHP 8.2.

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

  1. Etap 1: Baza danych

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

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

    ok. 2 min
    Polecenie z arkusza

    Na pulpicie znajdziesz archiwum ZIP o nazwie egzamin5.zip zabezpieczone hasłem: kaL(end)arz% 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 w jego miejscu stoi Bartosz Bryniarski), do niego archiwum egzamin5.zip rozpakowane z hasłem kaL(end)arz%. W środku: baza5.sql, logo1.png i logo2.png - w tym zadaniu potrzebne jest tylko logo1.png. Do tego folderu trafiają wszystkie rezultaty pracy.

  2. Krok 2: XAMPP i pusta baza egzamin5

    ok. 2 min
    Polecenie z arkusza

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

    Start przy Apache i MySQL, potem http://localhost/phpmyadmin, Nowa, nazwa egzamin5.

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

    Kryteria:

    R.1.1 Wykonano import tabel do bazy danych egzamin5 , czynność udokumentowano plikiem o nazwie import w formacie PNG. Zrzut zawiera cały obszar ekranu monitora z widocznym paskiem zadań

    ok. 3 min
    Polecenie z arkusza

    Do bazy egzamin5 zaimportuj tabele z pliku baza5.sql z rozpakowanego archiwum. Wykonaj zrzut ekranu po imporcie. Zrzut zapisz w formacie PNG pod nazwą import w folderze opisanym numerem PESEL. Nie kadruj zrzutu. Zrzut powinien obejmować cały ekran monitora, z widocznym paskiem zadań. Na zrzucie powinny być widoczne elementy wskazujące na poprawnie wykonany import tabel

    Baza egzamin5, karta Import, plik baza5.sql, Wykonaj. Tabela zadania ma 63 wiersze: wszystkie dni lipca i sierpnia 2020 oraz jeden dzień listopada. Miesiące są zapisane bez polskich liter (lipiec, sierpien, listopad). Zrzut całego ekranu z paskiem zadań: import.png.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 3
  4. Krok 4: Zapytanie 1: miesiąc i rok dla 1 lipca

    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 miesiac i rok z tabeli zadania dla zadania z datą 2020-07-01. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT miesiac, rok FROM zadania WHERE dataZadania = '2020-07- 01'; oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczny jest dokładnie jeden rekord z danymi: lipiec 2020

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 1: wybierające jedynie pola miesiac i rok z tabeli zadania dla zadania z datą 2020-07-01

    Data w formacie RRRR-MM-DD, w apostrofach. Wynik: jeden wiersz - lipiec, 2020. Zapytanie trafia do kwerendy.txt, zrzut - do kw1.jpg. Każda klauzula w osobnej linii: przy dłuższych zapytaniach łatwiej znaleźć błąd. To zapytanie zasili nagłówek w banerze.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT miesiac, rok
    FROM zadania
    WHERE dataZadania = '2020-07-01';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: zadania z lipca

    Kryteria:

    R.1.4 Utworzono zapytanie 2 wybierające jedynie pola dataZadania i wpis z tabeli zadania dla zadań, których miesiąc to lipiec. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT dataZadania, wpis FROM zadania WHERE miesiac = 'lipiec'; oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczne są jedynie pola dataZadania i wpis. Rekordy mają daty jedynie z lipca przy czym jest widoczna albo poprawna kwerenda albo dokładnie 31 rekordów (daty od 1-31 lipca)

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 2: wybierające jedynie pola dataZadania i wpis z tabeli zadania dla zadań, których miesiąc to lipiec

    Dwie kolumny i warunek miesiac = 'lipiec'. Wynik: 31 wierszy (1-31 lipca). Zrzut: kw2.jpg. Z tego zapytania powstaną bloki dni.

    kwerendy.txt Tekst zmienione: 5 linii
    -- Zapytanie 1
    SELECT miesiac, rok
    FROM zadania
    WHERE dataZadania = '2020-07-01';
    
    -- Zapytanie 2
    SELECT dataZadania, wpis
    FROM zadania
    WHERE miesiac = 'lipiec';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: miesiące bez powtórzeń zaczynające się na „l”

    Kryteria:

    R.1.5 Utworzono zapytanie 3 wybierające jedynie pole miesiac i wyświetlające je bez powtórzeń, z tabeli zadania dla miesięcy zaczynających się na literę „l”. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT DISTINCT miesiac FROM zadania WHERE miesiac LIKE "l%"; oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczne są jedynie dwa rekordy, jedynie dane: lipiec i listopad

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie pole miesiąc i wyświetlające je bez powtórzeń, z tabeli zadania dla miesięcy zaczynających się na literę „l” (l jak lew, lizak)

    DISTINCT usuwa powtórzenia, LIKE 'l%' - litera l na początku i dowolny ciąg po niej. Kolumna nazywa się miesiac, bez „ą”. Wynik: lipiec i listopad. Zrzut: kw3.jpg.

    kwerendy.txt Tekst zmienione: 5 linii
    -- Zapytanie 1
    SELECT miesiac, rok
    FROM zadania
    WHERE dataZadania = '2020-07-01';
    
    -- Zapytanie 2
    SELECT dataZadania, wpis
    FROM zadania
    WHERE miesiac = 'lipiec';
    
    -- Zapytanie 3
    SELECT DISTINCT miesiac
    FROM zadania
    WHERE miesiac LIKE 'l%';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: nowy wpis dla 13 lipca

    Kryteria:

    R.1.6 Utworzono zapytanie 4 zmieniające pole wpis w tabeli zadania dla zadania z datą 2020- 07-13, nowy wpis to „Wycieczka: jezioro”. W pliku z kwerendami lub na zrzucie istnieje zapis np.: UPDATE zadania SET wpis='Wycieczka: jezioro' WHERE dataZadania='2020-07-13'; oraz wynik działania zapytania udokumentowano zrzutem wskazującym na poprawne wykonanie zapytania

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 4: zmieniające pole wpis w tabeli zadania dla zadania z datą 2020-07-13, nowy wpis to „Wycieczka: jezioro”

    UPDATE z warunkiem na datę - phpMyAdmin odpowiada „1 wiersz zmieniony”. Zrzut: kw4.jpg. Skrypt użyje tego zapytania z parametrem w miejscu nowego wpisu.

    Typowe błędy

    UPDATE bez WHERE zmienia wszystkie 63 wiersze.

    kwerendy.txt Tekst zmienione: 5 linii
    -- Zapytanie 1
    SELECT miesiac, rok
    FROM zadania
    WHERE dataZadania = '2020-07-01';
    
    -- Zapytanie 2
    SELECT dataZadania, wpis
    FROM zadania
    WHERE miesiac = 'lipiec';
    
    -- Zapytanie 3
    SELECT DISTINCT miesiac
    FROM zadania
    WHERE miesiac LIKE 'l%';
    
    -- Zapytanie 4
    UPDATE zadania
    SET wpis = 'Wycieczka: jezioro'
    WHERE dataZadania = '2020-07-13';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Grafika i strona HTML

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

    Krok 8: Grafika: logo1.png o wysokości 150 px

    Kryteria:

    R.2.1 Plik logo1.png przeskalowano z zachowaniem proporcji do wysokości 150 px, odpowiadająca temu szerokość wynosi 188 px (± 2 px)

    ok. 2 min
    Polecenie z arkusza

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

    Oryginał ma 350 × 280 px. Przy zachowanych proporcjach szerokość to 350 × 150 / 280 = 187,5, czyli 188 px (klucz przyjmuje 188 ± 2 px). GIMP: Obraz → Skaluj obraz z zamkniętym łańcuchem, wysokość 150, potem Plik → Nadpisz logo1.png (R.2.1).

    Podgląd strony po tym kroku
    Podgląd strony po kroku 8
  9. Krok 9: Plik kalendarz.php i kodowanie znaków

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

    Składa się ze strony o nazwie kalendarz.php. Zastosowany właściwy standard kodowania polskich znaków

    Szkielet HTML5 w pliku kalendarz.php: lang="pl", <meta charset="UTF-8"> jako pierwszy element <head> (plik zapisany w UTF-8) i <meta name="viewport"> - bez niego telefon pokazałby stronę pomniejszoną jak na monitorze (R.2.2).

    kalendarz.php PHP nowy plik
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (kalendarz.php)
  10. Krok 10: Tytuł strony

    Kryteria:

    R.2.3 Nadano tytuł strony: "Mój kalendarz”

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony widoczny na karcie przeglądarki: „Mój kalendarz”

    <title>Mój kalendarz</title> - napis na karcie przeglądarki (R.2.3).

    kalendarz.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Mój kalendarz</title>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (kalendarz.php)
  11. Krok 11: Podpięcie arkusza stylów styl5.css

    Kryteria:

    R.3.4 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl5.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 zamaist #)

    ok. 1 min
    Polecenie z arkusza

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

    <link rel="stylesheet" href="styl5.css">. Całe formatowanie pochodzi z tego pliku - bez atrybutów style w HTML-u (R.3.4).

    kalendarz.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Mój kalendarz</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (kalendarz.php)
  12. Krok 12: Bloki: dwa banery, blok główny, stopka

    Kryteria:

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

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki: dwa bloki banera, poniżej blok główny, w którym znajdują się bloki wyświetlające dane kolejnych dni, na dole blok stopki.

    Znaczniki mówią, czym jest każda część strony:

    • <header> - nagłówek strony, w nim dwa bloki banera jako <section> z identyfikatorami banner-logo i banner-title,
    • <main> - główna treść; bloki dni to też znaczniki <section>, wygeneruje je skrypt,
    • <footer> - stopka z formularzem.

    Klucz wymaga znaczników sekcji (R.2.4): są nimi bloki banera i bloki dni. Znacznik <header> nie zmienia wyglądu, ale pozwoli ustawić dwa bloki w rzędzie jedną regułą CSS.

    kalendarz.php PHP zmienione: 10 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Mój kalendarz</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <header>
            <section id="banner-logo">
            </section>
            <section id="banner-title">
            </section>
        </header>
        <main>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (kalendarz.php)
  13. Krok 13: Pierwszy blok banera: logo z tekstem alternatywnym

    Kryteria:

    R.2.6 W pierwszym bloku banera umieszczono obraz logo1.png wraz z tekstem alternatywnym "Mój kalendarz"

    ok. 1 min
    Polecenie z arkusza

    Zawartość pierwszego bloku banera: logo1.png z tekstem alternatywnym o treści: „Mój kalendarz”

    alt="Mój kalendarz" (R.2.6). Atrybuty width i height równe wymiarom pliku nie zmieniają wyglądu - przeglądarka rezerwuje miejsce na obraz, zanim go pobierze, więc strona nie „skacze” przy wczytywaniu.

    kalendarz.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Mój kalendarz</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <header>
            <section id="banner-logo">
                <img src="logo1.png" alt="Mój kalendarz" width="188" height="150">
            </section>
            <section id="banner-title">
            </section>
        </header>
        <main>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (kalendarz.php)
  14. Krok 14: Drugi blok banera: nagłówek „KALENDARZ”

    ok. 1 min
    Polecenie z arkusza

    Zawartość drugiego bloku banera: Nagłówek pierwszego stopnia o treści: „KALENDARZ”

    <h1> - pod nim stanie nagłówek trzeciego stopnia z miesiącem i rokiem z bazy.

    kalendarz.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Mój kalendarz</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <header>
            <section id="banner-logo">
                <img src="logo1.png" alt="Mój kalendarz" width="188" height="150">
            </section>
            <section id="banner-title">
                <h1>KALENDARZ</h1>
            </section>
        </header>
        <main>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (kalendarz.php)
  15. Krok 15: Stopka: formularz metodą POST

    Kryteria: ,

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

    R.3.3 Zdefiniowano dla formularza metodę POST

    ok. 1 min
    Polecenie z arkusza

    Zawartość stopki: Formularz wysyłający dane metodą POST do tej samej strony

    <form method="post"> bez action - dane idą do adresu, z którego strona została wczytana, czyli do kalendarz.php (R.2.7, R.3.3).

    kalendarz.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Mój kalendarz</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <header>
            <section id="banner-logo">
                <img src="logo1.png" alt="Mój kalendarz" width="188" height="150">
            </section>
            <section id="banner-title">
                <h1>KALENDARZ</h1>
            </section>
        </header>
        <main>
        </main>
        <footer>
            <form method="post">
            </form>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (kalendarz.php)
  16. Krok 16: Pole edycyjne z etykietą „dodaj wpis: ”

    ok. 2 min
    Polecenie z arkusza

    Pole edycyjne podpisane: „dodaj wpis: ”

    • <label for="entry"> wiąże podpis z polem o id="entry" - kliknięcie napisu ustawia kursor w polu, a czytnik ekranu odczyta podpis przy polu,
    • name="wpis" - klucz, pod którym tekst trafi do PHP ($_POST['wpis']),
    • required - przeglądarka nie wyśle pustego formularza. To tylko wygoda: skrypt i tak sprawdzi wpis po swojej stronie, bo żądanie można wysłać z pominięciem formularza.
    kalendarz.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Mój kalendarz</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <header>
            <section id="banner-logo">
                <img src="logo1.png" alt="Mój kalendarz" width="188" height="150">
            </section>
            <section id="banner-title">
                <h1>KALENDARZ</h1>
            </section>
        </header>
        <main>
        </main>
        <footer>
            <form method="post">
                <label for="entry">dodaj wpis: </label>
                <input type="text" id="entry" name="wpis" required>
            </form>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (kalendarz.php)
  17. Krok 17: Przycisk „DODAJ”

    Kryteria: ,

    R.2.8 W formularzu umieszczono pole do wprowadzania danych oraz przycisk "DODAJ"

    R.3.2 Zastosowano przycisk do wysyłania danych z formularza

    ok. 1 min
    Polecenie z arkusza

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

    <button type="submit"> - przycisk wysyłający, z napisem między znacznikami (w <button> może stać też ikona albo pogrubienie, czego <input> nie potrafi). R.2.8 i R.3.2.

    kalendarz.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Mój kalendarz</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <header>
            <section id="banner-logo">
                <img src="logo1.png" alt="Mój kalendarz" width="188" height="150">
            </section>
            <section id="banner-title">
                <h1>KALENDARZ</h1>
            </section>
        </header>
        <main>
        </main>
        <footer>
            <form method="post">
                <label for="entry">dodaj wpis: </label>
                <input type="text" id="entry" name="wpis" required>
                <button type="submit">DODAJ</button>
            </form>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (kalendarz.php)
  18. Krok 18: Stopka: paragraf z autorem strony

    ok. 1 min
    Polecenie z arkusza

    Paragraf (akapit) o treści: „Stronę wykonał: ”, dalej wstawiony numer PESEL zdającego

    Na egzaminie numer PESEL, tu w jego miejscu Bartosz Bryniarski - pod formularzem, jak na obrazie 2.

    kalendarz.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Mój kalendarz</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <header>
            <section id="banner-logo">
                <img src="logo1.png" alt="Mój kalendarz" width="188" height="150">
            </section>
            <section id="banner-title">
                <h1>KALENDARZ</h1>
            </section>
        </header>
        <main>
        </main>
        <footer>
            <form method="post">
                <label for="entry">dodaj wpis: </label>
                <input type="text" id="entry" name="wpis" required>
                <button type="submit">DODAJ</button>
            </form>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (kalendarz.php)
  19. Etap 3: Styl CSS

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

    Krok 19: Plik styl5.css: kolory z arkusza w zmiennych

    ok. 2 min

    Arkusz używa koloru #483D8B w trzech miejscach (dwa bloki banera i stopka) i turkusowego #AFEEEE dla dni. Zmienne CSS w :root dają kolorom nazwy: var(--navy) czyta się łatwiej niż kod szesnastkowy, a zmiana odcienia to jedna linia. Klucz sprawdza wartość koloru - przez zmienną dociera ona do elementu tak samo jak wpisana wprost.

    styl5.css CSS nowy plik
    :root {
        --navy: #483D8B;
        --turquoise: #AFEEEE;
    }
    Podgląd strony po tym kroku (kalendarz.php)
  20. Krok 20: Krój Arial dla całej strony

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

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

    Selektor * obejmuje też pole tekstowe i przycisk, które kroju po body nie dziedziczą (R.4.1).

    styl5.css CSS zmienione: 3 linie
    :root {
        --navy: #483D8B;
        --turquoise: #AFEEEE;
    }
    * {
        font-family: Arial;
    }
    Podgląd strony po tym kroku (kalendarz.php)
  21. Krok 21: Pierwszy blok banera we flexie

    ok. 2 min
    Polecenie z arkusza

    Dla pierwszego bloku banera: kolor tła #483D8B, wysokość 150 px, szerokość 30%

    display: flex na <header> ustawia jego dzieci - dwa bloki banera - w jednym rzędzie, bez float i bez clear pod spodem. Szerokość 30% zostaje na bloku: klucz z 2020 roku sprawdza szerokość ustawioną dla bloku, a nie podział kolumn rodzica.

    styl5.css CSS zmienione: 8 linii
    :root {
        --navy: #483D8B;
        --turquoise: #AFEEEE;
    }
    * {
        font-family: Arial;
    }
    header {
        display: flex;
    }
    #banner-logo {
        background-color: var(--navy);
        height: 150px;
        width: 30%;
    }
    Podgląd strony po tym kroku (kalendarz.php)
  22. Krok 22: Drugi blok banera

    Kryteria: ,

    R.4.4 Ustawiono wysokość dla wszystkich bloków banera na 150px

    R.4.5 Ustawiono szerokość pierwszego bloku banera na 30%, drugiego bloku banera na 70%

    ok. 1 min
    Polecenie z arkusza

    Dla drugiego bloku banera: kolor tła #483D8B, biały kolor czcionki, wysokość 150 px, szerokość 70%

    30% + 70% = 100% szerokości nagłówka. Wysokość obu bloków banera (R.4.4) i ich szerokości (R.4.5) są ustawione.

    styl5.css CSS zmienione: 6 linii
    :root {
        --navy: #483D8B;
        --turquoise: #AFEEEE;
    }
    * {
        font-family: Arial;
    }
    header {
        display: flex;
    }
    #banner-logo {
        background-color: var(--navy);
        height: 150px;
        width: 30%;
    }
    #banner-title {
        background-color: var(--navy);
        color: white;
        height: 150px;
        width: 70%;
    }
    Podgląd strony po tym kroku (kalendarz.php)
  23. Krok 23: Blok jednego dnia na siatce

    Kryteria:

    R.4.8 Ustawiono dla bloku z danymi z kalendarza: kolor tła #AFEEEE, wysokość 100px, szerokość 150px, marginesy zewnętrzne (margin) 3px, obramowanie o szerokości 1px linią ciągłą

    ok. 3 min
    Polecenie z arkusza

    Dla bloku wyświetlającego dane jednego dnia: kolor tła #AFEEEE, szerokość 150 px, wysokość 100 px, marginesy zewnętrzne 3 px, obramowanie o szerokości 1 px, linią ciągłą

    • .day - klasa, bo bloków dni będzie 31; wymiary, margines i obramowanie dokładnie z arkusza (R.4.8). Arkusz nie podaje koloru linii, więc obramowanie bierze kolor tekstu,
    • main z siatką: repeat(auto-fill, 158px) tworzy tyle kolumn po 158 px, ile zmieści się w szerokości okna. 158 = 150 (blok) + 2 (obramowanie z obu stron) + 6 (margines z obu stron), więc każdy kafelek wypełnia swoją kolumnę co do piksela, a przy zmianie szerokości okna kafelki same przechodzą do kolejnego rzędu.
    styl5.css CSS zmienione: 11 linii
    :root {
        --navy: #483D8B;
        --turquoise: #AFEEEE;
    }
    * {
        font-family: Arial;
    }
    header {
        display: flex;
    }
    #banner-logo {
        background-color: var(--navy);
        height: 150px;
        width: 30%;
    }
    #banner-title {
        background-color: var(--navy);
        color: white;
        height: 150px;
        width: 70%;
    }
    main {
        display: grid;
        grid-template-columns: repeat(auto-fill, 158px);
    }
    .day {
        background-color: var(--turquoise);
        width: 150px;
        height: 100px;
        margin: 3px;
        border: 1px solid;
    }
    Podgląd strony po tym kroku (kalendarz.php)
  24. Krok 24: Stopka

    Kryteria: , ,

    R.4.2 Ustawiono kolor tła #483D8B dla obu bloków banera i stopki

    R.4.3 Ustawiono biały kolor czcionki dla drugiego bloku banera i dla stopki

    R.4.7 Ustawiono marginesy wewnętrzne (padding) na 10px dla stopki

    ok. 1 min
    Polecenie z arkusza

    Dla stopki: kolor tła #483D8B, biały kolor czcionki, marginesy wewnętrzne 10 px

    Tło obu bloków banera i stopki (R.4.2), biały tekst w drugim bloku banera i stopce (R.4.3), padding: 10px (R.4.7). Stopka stoi pod siatką bez żadnego clear - siatka, inaczej niż pływające bloki, ma swoją wysokość.

    styl5.css CSS zmienione: 5 linii
    :root {
        --navy: #483D8B;
        --turquoise: #AFEEEE;
    }
    * {
        font-family: Arial;
    }
    header {
        display: flex;
    }
    #banner-logo {
        background-color: var(--navy);
        height: 150px;
        width: 30%;
    }
    #banner-title {
        background-color: var(--navy);
        color: white;
        height: 150px;
        width: 70%;
    }
    main {
        display: grid;
        grid-template-columns: repeat(auto-fill, 158px);
    }
    .day {
        background-color: var(--turquoise);
        width: 150px;
        height: 100px;
        margin: 3px;
        border: 1px solid;
    }
    footer {
        background-color: var(--navy);
        color: white;
        padding: 10px;
    }
    Podgląd strony po tym kroku (kalendarz.php)
  25. Krok 25: Nagłówek piątego stopnia do środka

    Kryteria:

    R.4.6 Ustawiono wyrównanie tekstu do środka dla znacznika h5

    ok. 1 min
    Polecenie z arkusza

    Dla znacznika nagłówka piątego stopnia: wyrównanie tekstu do środka

    Selektor znacznika h5 - data w każdym bloku dnia wyśrodkowana jak na obrazie 3 (R.4.6).

    styl5.css CSS zmienione: 3 linie
    :root {
        --navy: #483D8B;
        --turquoise: #AFEEEE;
    }
    * {
        font-family: Arial;
    }
    header {
        display: flex;
    }
    #banner-logo {
        background-color: var(--navy);
        height: 150px;
        width: 30%;
    }
    #banner-title {
        background-color: var(--navy);
        color: white;
        height: 150px;
        width: 70%;
    }
    main {
        display: grid;
        grid-template-columns: repeat(auto-fill, 158px);
    }
    .day {
        background-color: var(--turquoise);
        width: 150px;
        height: 100px;
        margin: 3px;
        border: 1px solid;
    }
    footer {
        background-color: var(--navy);
        color: white;
        padding: 10px;
    }
    h5 {
        text-align: center;
    }
    Podgląd strony po tym kroku (kalendarz.php)
  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 w pełni zgodny z Obrazem 2 w arkuszu egzaminacyjnym (uwaga! Blok główny nie musi zawierać bloków kalendarza)

    ok. 2 min
    Polecenie z arkusza

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

    Układ jest już kompletny: flex w <header> stawia dwa bloki banera obok siebie, pod nimi <main> z siatką kafelków dni, na dole stopka. Ani jednego float, ani jednego clear - każda reguła układu stoi przy rodzicu, którego dzieci ustawia (R.3.1). Bloki dni wygeneruje skrypt; klucz przy R.3.1 zaznacza, że nie muszą być widoczne.

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

  27. Etap 4: Skrypt PHP

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

    Krok 27: Połączenie z bazą egzamin5 przez PDO

    Kryteria:

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

    ok. 4 min
    Polecenie z arkusza

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

    Cała logika na górze pliku, przed <!DOCTYPE html> - HTML niżej tylko wypisuje gotowe dane:

    • funkcja e() - skrót do htmlspecialchars, przyda się przy każdym wypisaniu danych z bazy,
    • new PDO(...) - serwer localhost, baza egzamin5 i kodowanie w jednym napisie (DSN), użytkownik root, puste hasło (R.5.1),
    • ERRMODE_EXCEPTION - każdy błąd bazy rzuca wyjątek, który łapie catch; uczeń zobaczy zrozumiały komunikat zamiast ostrzeżeń PHP,
    • FETCH_ASSOC - wiersze jako tablice z nazwami kolumn ($day['wpis'] zamiast $row[1]),
    • finally wykonuje się zawsze po try - tam połączenie jest zamykane.

    Tablice $days i $period wypełnią zapytania w kolejnych krokach. Od tego kroku podgląd to wynik prawdziwego uruchomienia w PHP 8.2.

    kalendarz.php PHP zmienione: 20 linii
    <?php
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
    }
    
    $days = [];
    $period = null;
    $pdo = null;
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=egzamin5;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $exception) {
        $error = 'nie udało się połączyć z bazą danych, spróbuj ponownie za chwilę';
    } finally {
        $pdo = null;
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Mój kalendarz</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <header>
            <section id="banner-logo">
                <img src="logo1.png" alt="Mój kalendarz" width="188" height="150">
            </section>
            <section id="banner-title">
                <h1>KALENDARZ</h1>
            </section>
        </header>
        <main>
        </main>
        <footer>
            <form method="post">
                <label for="entry">dodaj wpis: </label>
                <input type="text" id="entry" name="wpis" required>
                <button type="submit">DODAJ</button>
            </form>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (kalendarz.php)
  28. Krok 28: Skrypt 1: miesiąc i rok w nagłówku trzeciego stopnia

    Kryteria: , ,

    R.5.3 Skrypt 1 wysyła do bazy danych zapytanie 1 lub Skrypt 2 wysyła do bazy zapytanie 2

    R.5.4 Skrypt 1 wyświetla tekst: „miesiąc: <nazwa_miesiąca>, rok: <rok>”, gdzie pola w nawiasach <> oznaczają dane pobrane skryptem

    R.2.5 W drugim bloku banera zapisano nagłówek h1: "KALENDARZ" i nagłówek h3 (w skrypcie lub HTML) oraz zapisano przynajmniej jeden paragraf (znacznik <p>)

    ok. 3 min
    Polecenie z arkusza

    Działanie skryptu 1: Wysyła do bazy danych zapytanie 1. Wyświetla w nagłówku trzeciego stopnia tekst: „miesiąc: <nazwa_miesiąca>, rok: <rok>”, gdzie pola w nawiasach <…> oznaczają dane pobrane skryptem

    Dwie części w dwóch miejscach pliku:

    • na górze zapytanie 1 (R.5.3); zwraca jeden wiersz, więc fetch() zamiast fetchAll() - wynikiem jest jedna tablica albo false, gdy wiersza nie ma. Zapytanie nie ma danych od użytkownika, więc query wystarczy,
    • w drugim bloku banera if ($period): - nagłówek pojawi się tylko wtedy, gdy dane naprawdę przyszły. <?= ... ?> to skrót od <?php echo ... ?>, a każda wartość przechodzi przez e().

    Na stronie: „miesiąc: lipiec, rok: 2020” (R.5.4). Drugi blok banera ma teraz <h1> i <h3>, a w stopce jest paragraf (R.2.5).

    kalendarz.php PHP zmienione: 4 linie
    <?php
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
    }
    
    $days = [];
    $period = null;
    $pdo = null;
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=egzamin5;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $period = $pdo->query("SELECT miesiac, rok FROM zadania WHERE dataZadania = '2020-07-01';")->fetch();
    } catch (PDOException $exception) {
        $error = 'nie udało się połączyć z bazą danych, spróbuj ponownie za chwilę';
    } finally {
        $pdo = null;
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Mój kalendarz</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <header>
            <section id="banner-logo">
                <img src="logo1.png" alt="Mój kalendarz" width="188" height="150">
            </section>
            <section id="banner-title">
                <h1>KALENDARZ</h1>
                <?php if ($period): ?>
                    <h3>miesiąc: <?= e($period['miesiac']) ?>, rok: <?= e($period['rok']) ?></h3>
                <?php endif; ?>
            </section>
        </header>
        <main>
        </main>
        <footer>
            <form method="post">
                <label for="entry">dodaj wpis: </label>
                <input type="text" id="entry" name="wpis" required>
                <button type="submit">DODAJ</button>
            </form>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (kalendarz.php)
  29. Krok 29: Skrypt 2: zapytanie 2 i blok dla każdego wiersza

    Kryteria:

    R.5.5 Skrypt 2 definiuje w bloku głównym blok (za pomocą znaczników sekcji), a w nim wyświetla dane przesłane skryptem. Liczba wygenerowanych bloków odpowiada liczbie wierszy zwróconych zapytaniem

    ok. 4 min
    Polecenie z arkusza

    Działanie skryptu 2: Wysyła do bazy danych zapytanie 2. Definiuje blok (za pomocą znacznika sekcji), a w nim wyświetla dane przesłane skryptem, dotyczące jednego wiersza. Liczba wygenerowanych bloków odpowiada liczbie wierszy zwróconych zapytaniem

    Na górze fetchAll() - wszystkie 31 wierszy zapytania 2 trafia do tablicy $days. W <main> pętla foreach ($days as $day): z zamknięciem endforeach; - ta składnia z dwukropkiem jest czytelniejsza niż klamry, gdy między fragmentami PHP stoi HTML.

    W każdym obrocie powstaje <section class="day"> - 31 bloków, tyle ile wierszy (R.5.5). Na razie puste, ale już widać siatkę kafelków.

    kalendarz.php PHP zmienione: 5 linii
    <?php
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
    }
    
    $days = [];
    $period = null;
    $pdo = null;
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=egzamin5;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $period = $pdo->query("SELECT miesiac, rok FROM zadania WHERE dataZadania = '2020-07-01';")->fetch();
        $days = $pdo->query("SELECT dataZadania, wpis FROM zadania WHERE miesiac = 'lipiec';")->fetchAll();
    } catch (PDOException $exception) {
        $error = 'nie udało się połączyć z bazą danych, spróbuj ponownie za chwilę';
    } finally {
        $pdo = null;
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Mój kalendarz</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <header>
            <section id="banner-logo">
                <img src="logo1.png" alt="Mój kalendarz" width="188" height="150">
            </section>
            <section id="banner-title">
                <h1>KALENDARZ</h1>
                <?php if ($period): ?>
                    <h3>miesiąc: <?= e($period['miesiac']) ?>, rok: <?= e($period['rok']) ?></h3>
                <?php endif; ?>
            </section>
        </header>
        <main>
            <?php foreach ($days as $day): ?>
                <section class="day">
                </section>
            <?php endforeach; ?>
        </main>
        <footer>
            <form method="post">
                <label for="entry">dodaj wpis: </label>
                <input type="text" id="entry" name="wpis" required>
                <button type="submit">DODAJ</button>
            </form>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (kalendarz.php)
  30. Krok 30: Skrypt 2: data w h5, wpis w paragrafie

    Kryteria:

    R.5.6 Data zadania jest zapisana w nagłówku h5 oraz pole wpis jest zapisane w paragrafie

    ok. 2 min
    Polecenie z arkusza

    Blok jest zgodny z Obrazem 3 oraz jest w nim wyświetlone: Data zadania w nagłówku piątego stopnia. Pole wpis w paragrafie (akapicie)

    Klucze tablicy to nazwy kolumn z zapytania: data w <h5>, wpis w <p> (R.5.6). Każda wartość przechodzi przez e(), więc znak < we wpisie wyświetli się jako znak, a nie jako początek znacznika. Blok 13 lipca pokazuje „Wycieczka: jezioro” z zapytania 4.

    kalendarz.php PHP zmienione: 2 linie
    <?php
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
    }
    
    $days = [];
    $period = null;
    $pdo = null;
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=egzamin5;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $period = $pdo->query("SELECT miesiac, rok FROM zadania WHERE dataZadania = '2020-07-01';")->fetch();
        $days = $pdo->query("SELECT dataZadania, wpis FROM zadania WHERE miesiac = 'lipiec';")->fetchAll();
    } catch (PDOException $exception) {
        $error = 'nie udało się połączyć z bazą danych, spróbuj ponownie za chwilę';
    } finally {
        $pdo = null;
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Mój kalendarz</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <header>
            <section id="banner-logo">
                <img src="logo1.png" alt="Mój kalendarz" width="188" height="150">
            </section>
            <section id="banner-title">
                <h1>KALENDARZ</h1>
                <?php if ($period): ?>
                    <h3>miesiąc: <?= e($period['miesiac']) ?>, rok: <?= e($period['rok']) ?></h3>
                <?php endif; ?>
            </section>
        </header>
        <main>
            <?php foreach ($days as $day): ?>
                <section class="day">
                    <h5><?= e($day['dataZadania']) ?></h5>
                    <p><?= e($day['wpis']) ?></p>
                </section>
            <?php endforeach; ?>
        </main>
        <footer>
            <form method="post">
                <label for="entry">dodaj wpis: </label>
                <input type="text" id="entry" name="wpis" required>
                <button type="submit">DODAJ</button>
            </form>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (kalendarz.php)
  31. Krok 31: Odczyt wpisu z formularza

    Kryteria:

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

    ok. 2 min
    Polecenie z arkusza

    Działanie skryptu, który odbiera dane przesłane z formularza. Skrypt pobiera dane z pola edycyjnego

    $_POST['wpis'] ?? '' - wpis z formularza, a gdy formularza nie wysłano (zwykłe wejście na stronę), pusty napis zamiast ostrzeżenia o brakującym kluczu. Klucz wymaga tablicy $_POST (R.5.7), dlatego nie filter_input. trim zdejmuje spacje z brzegów - wpis z samych spacji stanie się pustym napisem. Podgląd: wysłane „ Kajaki ” - skrypt je odczytał, jeszcze nic nie zapisał.

    kalendarz.php PHP zmienione: 1 linia
    <?php
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
    }
    
    $days = [];
    $period = null;
    $pdo = null;
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=egzamin5;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $entry = trim($_POST['wpis'] ?? '');
        $period = $pdo->query("SELECT miesiac, rok FROM zadania WHERE dataZadania = '2020-07-01';")->fetch();
        $days = $pdo->query("SELECT dataZadania, wpis FROM zadania WHERE miesiac = 'lipiec';")->fetchAll();
    } catch (PDOException $exception) {
        $error = 'nie udało się połączyć z bazą danych, spróbuj ponownie za chwilę';
    } finally {
        $pdo = null;
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Mój kalendarz</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <header>
            <section id="banner-logo">
                <img src="logo1.png" alt="Mój kalendarz" width="188" height="150">
            </section>
            <section id="banner-title">
                <h1>KALENDARZ</h1>
                <?php if ($period): ?>
                    <h3>miesiąc: <?= e($period['miesiac']) ?>, rok: <?= e($period['rok']) ?></h3>
                <?php endif; ?>
            </section>
        </header>
        <main>
            <?php foreach ($days as $day): ?>
                <section class="day">
                    <h5><?= e($day['dataZadania']) ?></h5>
                    <p><?= e($day['wpis']) ?></p>
                </section>
            <?php endforeach; ?>
        </main>
        <footer>
            <form method="post">
                <label for="entry">dodaj wpis: </label>
                <input type="text" id="entry" name="wpis" required>
                <button type="submit">DODAJ</button>
            </form>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

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

  32. Krok 32: Zapis wpisu: zapytanie 4 jako zapytanie przygotowane

    Kryteria:

    R.5.8 Skrypt obsługujący formularz uaktualnia dane w bazie danych. Wpis pobrany z formularza jest wpisany do rekordu o dacie 2020-07-13. Należy sprawdzić w bazie danych czy po wciśnięciu przycisku baza została uaktualniona, nie jest wymagane uaktualnienie na stronie

    ok. 3 min
    Polecenie z arkusza

    Następnie uaktualnia dane w bazie danych. Aby uprościć zadanie skrypt wprowadza wpis zawsze do rekordu o dacie 2020-07-13. Do uaktualnienia danych wykorzystaj zapytanie 4 modyfikując w nim wartość pola wpis

    Zapytanie 4, w którym nowy wpis zastępuje parametr :wpis:

    • $pdo->prepare(...) - baza dostaje samą treść zapytania,
    • execute(['wpis' => $entry]) - osobno przychodzi wartość; nigdy nie stanie się częścią kodu SQL, więc wpis z apostrofem (Kino 'Helios') zapisze się poprawnie, a próba wstrzyknięcia SQL zapisze się jako zwykły tekst,
    • if ($entry !== '') - pusty formularz nie kasuje wpisu.

    Zapis stoi przed odczytem dni, więc nowy wpis widać od razu. Sprawdzenie, jak każe klucz (R.5.8): wiersz z datą 2020-07-13 ma wpis Kajaki.

    Typowe błędy

    Wpis z literami „ś” czy „ż” kończy się błędem bazy: tabela z archiwum ma kodowanie latin1. Wersja nowoczesna złapie ten wyjątek i pokaże komunikat (następny krok), ale zapisu nie będzie - klucz sprawdza import pliku bez zmian, więc tabeli nie przerabiaj.

    kalendarz.php PHP zmienione: 4 linie
    <?php
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
    }
    
    $days = [];
    $period = null;
    $pdo = null;
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=egzamin5;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $entry = trim($_POST['wpis'] ?? '');
        if ($entry !== '') {
            $statement = $pdo->prepare("UPDATE zadania SET wpis = :wpis WHERE dataZadania = '2020-07-13';");
            $statement->execute(['wpis' => $entry]);
        }
        $period = $pdo->query("SELECT miesiac, rok FROM zadania WHERE dataZadania = '2020-07-01';")->fetch();
        $days = $pdo->query("SELECT dataZadania, wpis FROM zadania WHERE miesiac = 'lipiec';")->fetchAll();
    } catch (PDOException $exception) {
        $error = 'nie udało się połączyć z bazą danych, spróbuj ponownie za chwilę';
    } finally {
        $pdo = null;
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Mój kalendarz</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <header>
            <section id="banner-logo">
                <img src="logo1.png" alt="Mój kalendarz" width="188" height="150">
            </section>
            <section id="banner-title">
                <h1>KALENDARZ</h1>
                <?php if ($period): ?>
                    <h3>miesiąc: <?= e($period['miesiac']) ?>, rok: <?= e($period['rok']) ?></h3>
                <?php endif; ?>
            </section>
        </header>
        <main>
            <?php foreach ($days as $day): ?>
                <section class="day">
                    <h5><?= e($day['dataZadania']) ?></h5>
                    <p><?= e($day['wpis']) ?></p>
                </section>
            <?php endforeach; ?>
        </main>
        <footer>
            <form method="post">
                <label for="entry">dodaj wpis: </label>
                <input type="text" id="entry" name="wpis" required>
                <button type="submit">DODAJ</button>
            </form>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (kalendarz.php)
  33. Krok 33: Zamknięcie połączenia i komunikat o błędzie

    Kryteria:

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

    ok. 3 min
    Polecenie z arkusza

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

    PDO nie ma funkcji close - połączenie zamyka się, gdy znika ostatnie odwołanie do obiektu. Dlatego w bloku finally stoją $statement = null i $pdo = null. finally wykonuje się po try zawsze - po udanym zapisie i odczycie, ale też po wyjątku - więc zamknięcie jest ostatnią operacją na bazie w każdej gałęzi (R.5.2).

    Gdy cokolwiek się nie uda, catch zapisuje komunikat w $error, a blok główny pokazuje go w paragrafie na całą szerokość siatki (grid-column: 1 / -1). Treść wyjątku zostaje na serwerze: komunikat bazy zdradzałby nazwy tabel i kolumn. Podgląd: strona po zamknięciu połączenia - dane były już w tablicach, więc wszystko się wyświetla.

    kalendarz.php PHP zmienione: 5 linii
    <?php
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
    }
    
    $days = [];
    $period = null;
    $pdo = null;
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=egzamin5;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $entry = trim($_POST['wpis'] ?? '');
        if ($entry !== '') {
            $statement = $pdo->prepare("UPDATE zadania SET wpis = :wpis WHERE dataZadania = '2020-07-13';");
            $statement->execute(['wpis' => $entry]);
        }
        $period = $pdo->query("SELECT miesiac, rok FROM zadania WHERE dataZadania = '2020-07-01';")->fetch();
        $days = $pdo->query("SELECT dataZadania, wpis FROM zadania WHERE miesiac = 'lipiec';")->fetchAll();
    } catch (PDOException $exception) {
        $error = 'nie udało się odczytać lub zapisać danych, spróbuj ponownie za chwilę';
    } finally {
        $statement = null;
        $pdo = null;
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Mój kalendarz</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <header>
            <section id="banner-logo">
                <img src="logo1.png" alt="Mój kalendarz" width="188" height="150">
            </section>
            <section id="banner-title">
                <h1>KALENDARZ</h1>
                <?php if ($period): ?>
                    <h3>miesiąc: <?= e($period['miesiac']) ?>, rok: <?= e($period['rok']) ?></h3>
                <?php endif; ?>
            </section>
        </header>
        <main>
            <?php if (isset($error)): ?>
                <p class="error"><?= e($error) ?></p>
            <?php endif; ?>
            <?php foreach ($days as $day): ?>
                <section class="day">
                    <h5><?= e($day['dataZadania']) ?></h5>
                    <p><?= e($day['wpis']) ?></p>
                </section>
            <?php endforeach; ?>
        </main>
        <footer>
            <form method="post">
                <label for="entry">dodaj wpis: </label>
                <input type="text" id="entry" name="wpis" required>
                <button type="submit">DODAJ</button>
            </form>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    styl5.css CSS zmienione: 3 linie
    :root {
        --navy: #483D8B;
        --turquoise: #AFEEEE;
    }
    * {
        font-family: Arial;
    }
    header {
        display: flex;
    }
    #banner-logo {
        background-color: var(--navy);
        height: 150px;
        width: 30%;
    }
    #banner-title {
        background-color: var(--navy);
        color: white;
        height: 150px;
        width: 70%;
    }
    main {
        display: grid;
        grid-template-columns: repeat(auto-fill, 158px);
    }
    .day {
        background-color: var(--turquoise);
        width: 150px;
        height: 100px;
        margin: 3px;
        border: 1px solid;
    }
    footer {
        background-color: var(--navy);
        color: white;
        padding: 10px;
    }
    h5 {
        text-align: center;
    }
    .error {
        grid-column: 1 / -1;
    }
    Podgląd strony po tym kroku (kalendarz.php)
  34. Krok 34: Odświeżenie po zapisie nie wysyła formularza drugi raz

    ok. 3 min

    Dodatek spoza arkusza - klucz go nie ocenia. Strona po wysłaniu formularza jest wynikiem żądania POST, więc odświeżenie (F5) pyta, czy wysłać dane ponownie. Wzorzec PRG (Post/Redirect/Get):

    • po udanym execute skrypt zapamiętuje $saved = true,
    • po bloku finally (połączenie jest już zamknięte) header('Location: kalendarz.php') każe przeglądarce wczytać stronę jeszcze raz zwykłym żądaniem GET,
    • exit jest konieczne - header tylko ustawia nagłówek i nie zatrzymuje skryptu.

    Sprawdzone na prawdziwym PHP: wysłanie formularza daje odpowiedź 302, a strona wczytana po przekierowaniu pokazuje „Kajaki” w bloku 13 lipca. Kolejne odświeżenie nie wysyła już niczego.

    kalendarz.php PHP zmienione: 5 linii
    <?php
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
    }
    
    $days = [];
    $period = null;
    $pdo = null;
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=egzamin5;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $entry = trim($_POST['wpis'] ?? '');
        if ($entry !== '') {
            $statement = $pdo->prepare("UPDATE zadania SET wpis = :wpis WHERE dataZadania = '2020-07-13';");
            $statement->execute(['wpis' => $entry]);
            $saved = true;
        }
        $period = $pdo->query("SELECT miesiac, rok FROM zadania WHERE dataZadania = '2020-07-01';")->fetch();
        $days = $pdo->query("SELECT dataZadania, wpis FROM zadania WHERE miesiac = 'lipiec';")->fetchAll();
    } catch (PDOException $exception) {
        $error = 'nie udało się odczytać lub zapisać danych, spróbuj ponownie za chwilę';
    } finally {
        $statement = null;
        $pdo = null;
    }
    if (isset($saved)) {
        header('Location: kalendarz.php');
        exit;
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Mój kalendarz</title>
        <link rel="stylesheet" href="styl5.css">
    </head>
    <body>
        <header>
            <section id="banner-logo">
                <img src="logo1.png" alt="Mój kalendarz" width="188" height="150">
            </section>
            <section id="banner-title">
                <h1>KALENDARZ</h1>
                <?php if ($period): ?>
                    <h3>miesiąc: <?= e($period['miesiac']) ?>, rok: <?= e($period['rok']) ?></h3>
                <?php endif; ?>
            </section>
        </header>
        <main>
            <?php if (isset($error)): ?>
                <p class="error"><?= e($error) ?></p>
            <?php endif; ?>
            <?php foreach ($days as $day): ?>
                <section class="day">
                    <h5><?= e($day['dataZadania']) ?></h5>
                    <p><?= e($day['wpis']) ?></p>
                </section>
            <?php endforeach; ?>
        </main>
        <footer>
            <form method="post">
                <label for="entry">dodaj wpis: </label>
                <input type="text" id="entry" name="wpis" required>
                <button type="submit">DODAJ</button>
            </form>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (kalendarz.php)
  35. Etap 5: Porównanie z wersją podstawową

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

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

    ok. 3 min

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

    wersja podstawowawersja nowoczesna
    układ banerafloat: left dwóch sekcji<header> z display: flex
    bloki dnifloat: left, clear przy stopce<main> z siatką repeat(auto-fill, 158px)
    kolorywpisane w każdej regulezmienne --navy, --turquoise
    formularznapis, <input type="submit">label for, required, <button>
    bibliotekamysqli (tabela 1 arkusza)PDO z wyjątkami
    gdzie jest skryptpołączenie i zapis na górze, zapytania w banerze i bloku głównymwszystko na górze, w HTML-u tylko foreach i <?= ?>
    zapis wpisudane wklejone w treść UPDATE, potem zapytanie przygotowaneparametr :wpis od początku, pusty wpis pominięty
    wypisywanie danychhtmlspecialchars dopisane na końcue() od pierwszej linii
    zamknięcie bazymysqli_close po bloku głównym$pdo = null w finally
    po zapisieF5 wysyła formularz ponownieprzekierowanie (PRG)

    Na egzaminie wersja podstawowa trzyma się funkcji z tabeli 1 i łatwiej ją napisać bez dokumentacji. Nowoczesna nie wywraca się na apostrofie, nie kasuje wpisu pustym formularzem i lepiej układa kafelki na wąskim ekranie - a klucz przyjmuje obie.

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

  36. Etap 6: Oddanie pracy

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

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

    W przeglądarka.txt nazwa przeglądarki, w której sprawdzana była strona. W folderze z numerem zdającego: import.png, kalendarz.php, kw1.jpg - kw4.jpg, kwerendy.txt, logo1.png, przeglądarka.txt i styl5.css. 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 34.

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