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

Rozwiązanie krok po kroku: Organizer z wpisami zadań (Nowoczesny kod (flex, grid, PDO))

To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza egzamin6 z tabelą zadania i czterema zapytaniami, logo 100 × 100 px oraz strona organizer.php z trzema blokami banera, blokami dni sierpnia i formularzem zapisu - i tak samo spełnia każde kryterium klucza. Inna jest droga:

  • flexbox i siatka zamiast float: trzy 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 organizer.php, przed HTML-em - w bloku głównym i w stopce zostają tylko pętla i jedna linijka wypisująca dane,
  • 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 logo2.png. Do tego folderu trafiają wszystkie rezultaty pracy.

  2. Krok 2: XAMPP i pusta baza egzamin6

    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 egzamin6

    Start przy Apache i MySQL, potem http://localhost/phpmyadmin, Nowa, nazwa egzamin6 - z cyfrą 6, choć archiwum ma w nazwie 5.

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

    Kryteria:

    R.1.1 Wykonano import tabel do bazy danych egzamin6 , 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 egzamin6 zaimportuj tabele z pliku baza5.sql z rozpakowanego archiwum. Wykonaj zrzut ekranu po imporcie. Zrzut zapisz w formacie PNG pod nazwą import w folderze z 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 egzamin6, 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 - sierpien. 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: zadania z sierpnia

    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 dataZadania, miesiac i wpis z tabeli zadania dla zadań, których miesiąc to sierpień. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT dataZadania, miesiac, wpis FROM zadania WHERE miesiac = 'sierpien'; oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczne są jedynie pola dataZadania, miesiac i wpis. Rekordy mają daty jedynie z sierpnia przy czym jest widoczna albo poprawna kwerenda albo dokładnie 31 rekordów (daty od 1-31 sierpnia)

    ok. 3 min
    Polecenie z arkusza

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

    Trzy kolumny i warunek miesiac = 'sierpien' - napis porównywany z tym, co jest w tabeli, czyli bez „ń”. Wynik: 31 wierszy (1-31 sierpnia). Zapytanie trafia do kwerendy.txt, zrzut - do kw1.jpg. Każda klauzula w osobnej linii: przy dłuższych zapytaniach łatwiej znaleźć błąd.

    Typowe błędy

    'sierpień' z „ń” daje pusty wynik, bo w tabeli jest sierpien.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT dataZadania, miesiac, wpis
    FROM zadania
    WHERE miesiac = 'sierpien';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: miesiąc i rok dla 1 sierpnia

    Kryteria:

    R.1.4 Utworzono zapytanie 2 wybierające jedynie pola miesiac i rok z tabeli zadania dla zadania z datą 2020-08-01. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT miesiac, rok FROM zadania WHERE dataZadania = '2020-08- 01'; oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczny jest dokładnie jeden rekord z danymi: sierpien 2020

    ok. 2 min
    Polecenie z arkusza

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

    Data w formacie RRRR-MM-DD, w apostrofach. Wynik: jeden wiersz - sierpien, 2020. Zrzut: kw2.jpg. To zapytanie zasili podpis w stopce.

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

    Kryteria:

    R.1.5 Utworzono zapytanie 3 wybierające jedynie pole wpis i wyświetlające je bez powtórzeń, z tabeli zadania dla wpisów kończących się literą „o”. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT DISTINCT wpis FROM zadania WHERE wpis LIKE "%o"; oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczny jest jedynie jeden rekord: Mielno

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie pole wpis i wyświetlające je bez powtórzeń, z tabeli zadania dla wpisów kończących się literą „o”

    DISTINCT usuwa powtórzenia, LIKE '%o' - dowolny początek i litera o na końcu. Wynik: jeden wiersz, Mielno (w tabeli stoi przy sześciu dniach lipca). Zrzut: kw3.jpg.

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

    Kryteria:

    R.1.6 Utworzono zapytanie 4 zmieniające pole wpis w tabeli zadania dla zadania z datą 2020- 08-27, nowy wpis to „Wycieczka: Karkonosze”. W pliku z kwerendami lub na zrzucie istnieje zapis np.: UPDATE zadania SET wpis='Wycieczka: Karkonosze' WHERE dataZadania='2020-08-27'; 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-08-27, nowy wpis to „Wycieczka: Karkonosze”

    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 dataZadania, miesiac, wpis
    FROM zadania
    WHERE miesiac = 'sierpien';
    
    -- Zapytanie 2
    SELECT miesiac, rok
    FROM zadania
    WHERE dataZadania = '2020-08-01';
    
    -- Zapytanie 3
    SELECT DISTINCT wpis
    FROM zadania
    WHERE wpis LIKE '%o';
    
    -- Zapytanie 4
    UPDATE zadania
    SET wpis = 'Wycieczka: Karkonosze'
    WHERE dataZadania = '2020-08-27';
    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: logo2.png 100 × 100 px

    Kryteria:

    R.2.1 Plik logo2.png przeskalowano do wymiarów 100 px na 100 px (± 2 px)

    ok. 2 min
    Polecenie z arkusza

    Plik logo2.png, wypakowany z archiwum, należy przeskalować dokładnie do wymiarów 100 px na 100 px

    Oryginał ma 300 × 300 px - kwadrat, więc zmniejszenie do 100 × 100 px zachowuje proporcje. GIMP: Obraz → Skaluj obraz, potem Plik → Nadpisz logo2.png (PNG zachowa przezroczyste tło).

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

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

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

    Szkielet HTML5 w pliku organizer.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).

    organizer.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 (organizer.php)
  10. Krok 10: Tytuł strony

    Kryteria:

    R.2.3 Nadano tytuł strony: "Organizer"

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony widoczny na karcie przeglądarki: „Organizer”

    <title>Organizer</title> - napis na karcie przeglądarki (R.2.3).

    organizer.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>Organizer</title>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (organizer.php)
  11. Krok 11: Podpięcie arkusza stylów styl6.css

    Kryteria:

    R.3.4 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl6.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. 1 min
    Polecenie z arkusza

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

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

    organizer.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>Organizer</title>
        <link rel="stylesheet" href="styl6.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (organizer.php)
  12. Krok 12: Bloki: trzy banery, blok główny, stopka

    Kryteria:

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

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki: trzy 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 trzy bloki banera jako <section> z identyfikatorami banner-title, banner-form, banner-logo,
    • <main> - główna treść; bloki dni to też znaczniki <section>, wygeneruje je skrypt,
    • <footer> - stopka.

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

    organizer.php PHP zmienione: 12 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Organizer</title>
        <link rel="stylesheet" href="styl6.css">
    </head>
    <body>
        <header>
            <section id="banner-title">
            </section>
            <section id="banner-form">
            </section>
            <section id="banner-logo">
            </section>
        </header>
        <main>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (organizer.php)
  13. Krok 13: Pierwszy blok banera: nagłówek „MÓJ ORGANIZER”

    ok. 1 min
    Polecenie z arkusza

    Zawartość pierwszego bloku banera: nagłówek drugiego stopnia o treści „MÓJ ORGANIZER”

    <h2> - nagłówek pierwszego stopnia arkusz przeznacza na podpis miesiąca w stopce.

    organizer.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>Organizer</title>
        <link rel="stylesheet" href="styl6.css">
    </head>
    <body>
        <header>
            <section id="banner-title">
                <h2>MÓJ ORGANIZER</h2>
            </section>
            <section id="banner-form">
            </section>
            <section id="banner-logo">
            </section>
        </header>
        <main>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (organizer.php)
  14. Krok 14: Drugi blok banera: formularz metodą POST

    Kryteria: ,

    R.2.6 W drugim bloku banera umieszczono formularz, zastosowano znacznik <form> i jego zamknięcie

    R.3.3 Zdefinowano dla formularza metodę POST

    ok. 1 min
    Polecenie z arkusza

    Zawartość drugiego bloku banera: 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 organizer.php (R.2.6, R.3.3).

    organizer.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>Organizer</title>
        <link rel="stylesheet" href="styl6.css">
    </head>
    <body>
        <header>
            <section id="banner-title">
                <h2>MÓJ ORGANIZER</h2>
            </section>
            <section id="banner-form">
                <form method="post">
                </form>
            </section>
            <section id="banner-logo">
            </section>
        </header>
        <main>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (organizer.php)
  15. Krok 15: Pole edycyjne z etykietą „Wpis wydarzenia: ”

    ok. 2 min
    Polecenie z arkusza

    Pole edycyjne podpisane: „Wpis wydarzenia: ”

    • <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.
    organizer.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>Organizer</title>
        <link rel="stylesheet" href="styl6.css">
    </head>
    <body>
        <header>
            <section id="banner-title">
                <h2>MÓJ ORGANIZER</h2>
            </section>
            <section id="banner-form">
                <form method="post">
                    <label for="entry">Wpis wydarzenia: </label>
                    <input type="text" id="entry" name="wpis" required>
                </form>
            </section>
            <section id="banner-logo">
            </section>
        </header>
        <main>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (organizer.php)
  16. Krok 16: Przycisk „ZAPISZ”

    Kryteria: ,

    R.2.7 W formularzu umieszczono pole do wprowadzania danych oraz przycisk "ZAPISZ"

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

    ok. 1 min
    Polecenie z arkusza

    Przycisk wysyłający zawartość formularza z etykietą „ZAPISZ”

    <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.7 i R.3.2.

    organizer.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>Organizer</title>
        <link rel="stylesheet" href="styl6.css">
    </head>
    <body>
        <header>
            <section id="banner-title">
                <h2>MÓJ ORGANIZER</h2>
            </section>
            <section id="banner-form">
                <form method="post">
                    <label for="entry">Wpis wydarzenia: </label>
                    <input type="text" id="entry" name="wpis" required>
                    <button type="submit">ZAPISZ</button>
                </form>
            </section>
            <section id="banner-logo">
            </section>
        </header>
        <main>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (organizer.php)
  17. Krok 17: Trzeci blok banera: logo z tekstem alternatywnym

    Kryteria:

    R.2.8 W trzecim bloku banera umieszczono obraz logo2.png wraz z tekstem alternatywnym "Mój organizer"

    ok. 1 min
    Polecenie z arkusza

    Zawartość trzeciego bloku banera: logo2.png z tekstem alternatywnym o treści: „Mój organizer”

    alt="Mój organizer" (R.2.8). 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.

    organizer.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>Organizer</title>
        <link rel="stylesheet" href="styl6.css">
    </head>
    <body>
        <header>
            <section id="banner-title">
                <h2>MÓJ ORGANIZER</h2>
            </section>
            <section id="banner-form">
                <form method="post">
                    <label for="entry">Wpis wydarzenia: </label>
                    <input type="text" id="entry" name="wpis" required>
                    <button type="submit">ZAPISZ</button>
                </form>
            </section>
            <section id="banner-logo">
                <img src="logo2.png" alt="Mój organizer" width="100" height="100">
            </section>
        </header>
        <main>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (organizer.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. Nad paragrafem stanie nagłówek z miesiącem i rokiem.

    organizer.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>Organizer</title>
        <link rel="stylesheet" href="styl6.css">
    </head>
    <body>
        <header>
            <section id="banner-title">
                <h2>MÓJ ORGANIZER</h2>
            </section>
            <section id="banner-form">
                <form method="post">
                    <label for="entry">Wpis wydarzenia: </label>
                    <input type="text" id="entry" name="wpis" required>
                    <button type="submit">ZAPISZ</button>
                </form>
            </section>
            <section id="banner-logo">
                <img src="logo2.png" alt="Mój organizer" width="100" height="100">
            </section>
        </header>
        <main>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (organizer.php)
  19. Etap 3: Styl CSS

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

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

    ok. 2 min

    Arkusz używa zieleni #3CB371 w pięciu miejscach (trzy bloki banera, stopka, obramowanie dnia) i brzoskwiniowego #FFDAB9 dla dni. Zmienne CSS w :root dają kolorom nazwy: var(--green) 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.

    styl6.css CSS nowy plik
    :root {
        --green: #3CB371;
        --peach: #FFDAB9;
    }
    Podgląd strony po tym kroku (organizer.php)
  20. Krok 20: Krój Georgia dla całej strony

    Kryteria:

    R.4.1 Ustawiono domyślny krój czcionki dla całej strony na Georgia (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 Georgia

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

    styl6.css CSS zmienione: 3 linie
    :root {
        --green: #3CB371;
        --peach: #FFDAB9;
    }
    * {
        font-family: Georgia;
    }
    Podgląd strony po tym kroku (organizer.php)
  21. Krok 21: Pierwszy i drugi blok banera w rzędzie

    ok. 2 min
    Polecenie z arkusza

    Dla pierwszego i drugiego bloku banera: kolor tła #3CB371, biały kolor czcionki, wysokość 100 px, szerokość 40%

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

    styl6.css CSS zmienione: 9 linii
    :root {
        --green: #3CB371;
        --peach: #FFDAB9;
    }
    * {
        font-family: Georgia;
    }
    header {
        display: flex;
    }
    #banner-title, #banner-form {
        background-color: var(--green);
        color: white;
        height: 100px;
        width: 40%;
    }
    Podgląd strony po tym kroku (organizer.php)
  22. Krok 22: Trzeci blok banera

    Kryteria: ,

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

    R.4.5 Ustawiono szerokość pierwszego i drugiego bloku banera na 40%, trzeciego bloku banera na 20%

    ok. 1 min
    Polecenie z arkusza

    Dla trzeciego bloku banera: kolor tła #3CB371, wysokość 100 px, szerokość 20%

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

    styl6.css CSS zmienione: 5 linii
    :root {
        --green: #3CB371;
        --peach: #FFDAB9;
    }
    * {
        font-family: Georgia;
    }
    header {
        display: flex;
    }
    #banner-title, #banner-form {
        background-color: var(--green);
        color: white;
        height: 100px;
        width: 40%;
    }
    #banner-logo {
        background-color: var(--green);
        height: 100px;
        width: 20%;
    }
    Podgląd strony po tym kroku (organizer.php)
  23. Krok 23: Blok jednego dnia na siatce

    Kryteria:

    R.4.8 Ustawiono dla bloku z danymi z kalendarza: kolor tła #FFDAB9, szerokość 170px, wyskość 90px, marginesy zewnętrzne 5px, obramowanie o szerokości 1px linią ciągłą o kolorze #3CB371

    ok. 3 min
    Polecenie z arkusza

    Dla bloku wyświetlającego dane jednego dnia: kolor tła #FFDAB9, szerokość 170 px, wysokość 90 px, marginesy zewnętrzne 5 px, obramowanie o szerokości 1 px, linią ciągłą o kolorze #3CB371

    • .day - klasa, bo bloków dni będzie 31; wymiary, margines i obramowanie dokładnie z arkusza (R.4.8),
    • main z siatką: repeat(auto-fill, 182px) tworzy tyle kolumn po 182 px, ile zmieści się w szerokości okna. 182 = 170 (blok) + 2 (obramowanie z obu stron) + 10 (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.
    styl6.css CSS zmienione: 11 linii
    :root {
        --green: #3CB371;
        --peach: #FFDAB9;
    }
    * {
        font-family: Georgia;
    }
    header {
        display: flex;
    }
    #banner-title, #banner-form {
        background-color: var(--green);
        color: white;
        height: 100px;
        width: 40%;
    }
    #banner-logo {
        background-color: var(--green);
        height: 100px;
        width: 20%;
    }
    main {
        display: grid;
        grid-template-columns: repeat(auto-fill, 182px);
    }
    .day {
        background-color: var(--peach);
        width: 170px;
        height: 90px;
        margin: 5px;
        border: 1px solid var(--green);
    }
    Podgląd strony po tym kroku (organizer.php)
  24. Krok 24: Stopka: tło i kolor tekstu

    Kryteria: ,

    R.4.2 Ustawiono kolor tła #3CB371 dla trzech bloków banera i stopki

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

    ok. 1 min
    Polecenie z arkusza

    Dla stopki: kolor tła #3CB371, biały kolor czcionki

    Tło trzech bloków banera i stopki (R.4.2), biały tekst w dwóch pierwszych blokach banera i stopce (R.4.3). Stopka stoi pod siatką bez żadnego clear - siatka, inaczej niż pływające bloki, ma swoją wysokość.

    styl6.css CSS zmienione: 4 linie
    :root {
        --green: #3CB371;
        --peach: #FFDAB9;
    }
    * {
        font-family: Georgia;
    }
    header {
        display: flex;
    }
    #banner-title, #banner-form {
        background-color: var(--green);
        color: white;
        height: 100px;
        width: 40%;
    }
    #banner-logo {
        background-color: var(--green);
        height: 100px;
        width: 20%;
    }
    main {
        display: grid;
        grid-template-columns: repeat(auto-fill, 182px);
    }
    .day {
        background-color: var(--peach);
        width: 170px;
        height: 90px;
        margin: 5px;
        border: 1px solid var(--green);
    }
    footer {
        background-color: var(--green);
        color: white;
    }
    Podgląd strony po tym kroku (organizer.php)
  25. Krok 25: Nagłówek szóstego stopnia do prawej

    Kryteria:

    R.4.6 Ustawiono wyrównanie tekstu do prawej strony dla znacznika h6

    ok. 1 min
    Polecenie z arkusza

    Dla znacznika nagłówka szóstego stopnia: wyrównanie tekstu do prawej strony

    Selektor znacznika h6 - data i miesiąc w każdym bloku dnia do prawej (R.4.6).

    styl6.css CSS zmienione: 3 linie
    :root {
        --green: #3CB371;
        --peach: #FFDAB9;
    }
    * {
        font-family: Georgia;
    }
    header {
        display: flex;
    }
    #banner-title, #banner-form {
        background-color: var(--green);
        color: white;
        height: 100px;
        width: 40%;
    }
    #banner-logo {
        background-color: var(--green);
        height: 100px;
        width: 20%;
    }
    main {
        display: grid;
        grid-template-columns: repeat(auto-fill, 182px);
    }
    .day {
        background-color: var(--peach);
        width: 170px;
        height: 90px;
        margin: 5px;
        border: 1px solid var(--green);
    }
    footer {
        background-color: var(--green);
        color: white;
    }
    h6 {
        text-align: right;
    }
    Podgląd strony po tym kroku (organizer.php)
  26. Krok 26: Formularz: marginesy wewnętrzne 30 px

    Kryteria:

    R.4.7 Ustawiono marginesy wewnętrzne (padding) na 30px dla znacznika formularza

    ok. 1 min
    Polecenie z arkusza

    Dla znacznika formularza: marginesy wewnętrzne 30 px

    padding: 30px (R.4.7).

    styl6.css CSS zmienione: 3 linie
    :root {
        --green: #3CB371;
        --peach: #FFDAB9;
    }
    * {
        font-family: Georgia;
    }
    header {
        display: flex;
    }
    #banner-title, #banner-form {
        background-color: var(--green);
        color: white;
        height: 100px;
        width: 40%;
    }
    #banner-logo {
        background-color: var(--green);
        height: 100px;
        width: 20%;
    }
    main {
        display: grid;
        grid-template-columns: repeat(auto-fill, 182px);
    }
    .day {
        background-color: var(--peach);
        width: 170px;
        height: 90px;
        margin: 5px;
        border: 1px solid var(--green);
    }
    footer {
        background-color: var(--green);
        color: white;
    }
    h6 {
        text-align: right;
    }
    form {
        padding: 30px;
    }
    Podgląd strony po tym kroku (organizer.php)
  27. Krok 27: 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 trzy 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 26.

  28. Etap 4: Skrypt PHP

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

    Krok 28: Połączenie z bazą egzamin6 przez PDO

    Kryteria:

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

    ok. 4 min
    Polecenie z arkusza

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

    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 egzamin6 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[2]),
    • 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.

    organizer.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=egzamin6;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>Organizer</title>
        <link rel="stylesheet" href="styl6.css">
    </head>
    <body>
        <header>
            <section id="banner-title">
                <h2>MÓJ ORGANIZER</h2>
            </section>
            <section id="banner-form">
                <form method="post">
                    <label for="entry">Wpis wydarzenia: </label>
                    <input type="text" id="entry" name="wpis" required>
                    <button type="submit">ZAPISZ</button>
                </form>
            </section>
            <section id="banner-logo">
                <img src="logo2.png" alt="Mój organizer" width="100" height="100">
            </section>
        </header>
        <main>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (organizer.php)
  29. Krok 29: Skrypt 1: zapytanie 1 i blok dla każdego wiersza

    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 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 1: Wysyła do bazy danych zapytanie 1. Definiuje blok (za pomocą znacznika sekcji), a w nim wyświetla dane przesłane skryptem dotyczące jednego wiersza.

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

    • na górze $pdo->query(...)->fetchAll() - zapytanie 1 bez zmian (R.5.3), wszystkie 31 wierszy trafia do tablicy $days. Zapytanie nie ma danych od użytkownika, więc query wystarczy,
    • 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.4). Na razie puste, ale już widać siatkę kafelków.

    organizer.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=egzamin6;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $days = $pdo->query("SELECT dataZadania, miesiac, wpis FROM zadania WHERE miesiac = 'sierpien';")->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>Organizer</title>
        <link rel="stylesheet" href="styl6.css">
    </head>
    <body>
        <header>
            <section id="banner-title">
                <h2>MÓJ ORGANIZER</h2>
            </section>
            <section id="banner-form">
                <form method="post">
                    <label for="entry">Wpis wydarzenia: </label>
                    <input type="text" id="entry" name="wpis" required>
                    <button type="submit">ZAPISZ</button>
                </form>
            </section>
            <section id="banner-logo">
                <img src="logo2.png" alt="Mój organizer" width="100" height="100">
            </section>
        </header>
        <main>
            <?php foreach ($days as $day): ?>
                <section class="day">
                </section>
            <?php endforeach; ?>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (organizer.php)
  30. Krok 30: Skrypt 1: data i miesiąc w h6, wpis w paragrafie

    Kryteria:

    R.5.5 Data zadania i miesiac są zapisana w nagłówku h6 oraz pole wpis jest zapisane w paragrafie

    ok. 2 min
    Polecenie z arkusza

    W nagłówku szóstego stopnia data zadania i miesiąc oddzielone przecinkiem. Pole wpis w paragrafie (akapicie)

    <?= ... ?> to skrót od <?php echo ... ?>. Każda wartość przechodzi przez e(), więc znak < we wpisie wyświetli się jako znak, a nie jako początek znacznika. Klucze tablicy to nazwy kolumn z zapytania: dataZadania, miesiac, wpis (R.5.5). Blok 27 sierpnia pokazuje „Wycieczka: Karkonosze” z zapytania 4.

    organizer.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=egzamin6;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $days = $pdo->query("SELECT dataZadania, miesiac, wpis FROM zadania WHERE miesiac = 'sierpien';")->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>Organizer</title>
        <link rel="stylesheet" href="styl6.css">
    </head>
    <body>
        <header>
            <section id="banner-title">
                <h2>MÓJ ORGANIZER</h2>
            </section>
            <section id="banner-form">
                <form method="post">
                    <label for="entry">Wpis wydarzenia: </label>
                    <input type="text" id="entry" name="wpis" required>
                    <button type="submit">ZAPISZ</button>
                </form>
            </section>
            <section id="banner-logo">
                <img src="logo2.png" alt="Mój organizer" width="100" height="100">
            </section>
        </header>
        <main>
            <?php foreach ($days as $day): ?>
                <section class="day">
                    <h6><?= e($day['dataZadania']) ?>, <?= e($day['miesiac']) ?></h6>
                    <p><?= e($day['wpis']) ?></p>
                </section>
            <?php endforeach; ?>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (organizer.php)
  31. Krok 31: Skrypt 2: miesiąc i rok w nagłówku pierwszego stopnia

    Kryteria: ,

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

    R.2.5 W pierwszym bloku banera zapisano nagłówek h2: "MÓJ OGRANIZER" oraz w stopce nagłówek h1 (w skrypcie lub HTML) oraz zapisano przynajmniej jeden paragraf (znacznik <p>)

    ok. 3 min
    Polecenie z arkusza

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

    Zapytanie 2 zwraca jeden wiersz, więc fetch() zamiast fetchAll() - wynikiem jest jedna tablica albo false, gdy wiersza nie ma. W stopce if ($period): - nagłówek pojawi się tylko wtedy, gdy dane naprawdę przyszły. Na stronie: „miesiąc: sierpien, rok: 2020” (R.5.6), a <h2> w banerze, <h1> w stopce i paragraf dają R.2.5.

    organizer.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=egzamin6;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $days = $pdo->query("SELECT dataZadania, miesiac, wpis FROM zadania WHERE miesiac = 'sierpien';")->fetchAll();
        $period = $pdo->query("SELECT miesiac, rok FROM zadania WHERE dataZadania = '2020-08-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>Organizer</title>
        <link rel="stylesheet" href="styl6.css">
    </head>
    <body>
        <header>
            <section id="banner-title">
                <h2>MÓJ ORGANIZER</h2>
            </section>
            <section id="banner-form">
                <form method="post">
                    <label for="entry">Wpis wydarzenia: </label>
                    <input type="text" id="entry" name="wpis" required>
                    <button type="submit">ZAPISZ</button>
                </form>
            </section>
            <section id="banner-logo">
                <img src="logo2.png" alt="Mój organizer" width="100" height="100">
            </section>
        </header>
        <main>
            <?php foreach ($days as $day): ?>
                <section class="day">
                    <h6><?= e($day['dataZadania']) ?>, <?= e($day['miesiac']) ?></h6>
                    <p><?= e($day['wpis']) ?></p>
                </section>
            <?php endforeach; ?>
        </main>
        <footer>
            <?php if ($period): ?>
                <h1>miesiąc: <?= e($period['miesiac']) ?>, rok: <?= e($period['rok']) ?></h1>
            <?php endif; ?>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (organizer.php)
  32. Krok 32: Odczyt wpisu z formularza

    Kryteria:

    R.5.7 W skrypcie działającym na danych przesłanych przez formularz zastosowano tablicę $_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 „ Basen ” - skrypt je odczytał, jeszcze nic nie zapisał.

    organizer.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=egzamin6;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $entry = trim($_POST['wpis'] ?? '');
        $days = $pdo->query("SELECT dataZadania, miesiac, wpis FROM zadania WHERE miesiac = 'sierpien';")->fetchAll();
        $period = $pdo->query("SELECT miesiac, rok FROM zadania WHERE dataZadania = '2020-08-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>Organizer</title>
        <link rel="stylesheet" href="styl6.css">
    </head>
    <body>
        <header>
            <section id="banner-title">
                <h2>MÓJ ORGANIZER</h2>
            </section>
            <section id="banner-form">
                <form method="post">
                    <label for="entry">Wpis wydarzenia: </label>
                    <input type="text" id="entry" name="wpis" required>
                    <button type="submit">ZAPISZ</button>
                </form>
            </section>
            <section id="banner-logo">
                <img src="logo2.png" alt="Mój organizer" width="100" height="100">
            </section>
        </header>
        <main>
            <?php foreach ($days as $day): ?>
                <section class="day">
                    <h6><?= e($day['dataZadania']) ?>, <?= e($day['miesiac']) ?></h6>
                    <p><?= e($day['wpis']) ?></p>
                </section>
            <?php endforeach; ?>
        </main>
        <footer>
            <?php if ($period): ?>
                <h1>miesiąc: <?= e($period['miesiac']) ?>, rok: <?= e($period['rok']) ?></h1>
            <?php endif; ?>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

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

  33. Krok 33: 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-08-27. 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-08-27. 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-08-27 ma wpis Basen.

    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.

    organizer.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=egzamin6;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-08-27';");
            $statement->execute(['wpis' => $entry]);
        }
        $days = $pdo->query("SELECT dataZadania, miesiac, wpis FROM zadania WHERE miesiac = 'sierpien';")->fetchAll();
        $period = $pdo->query("SELECT miesiac, rok FROM zadania WHERE dataZadania = '2020-08-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>Organizer</title>
        <link rel="stylesheet" href="styl6.css">
    </head>
    <body>
        <header>
            <section id="banner-title">
                <h2>MÓJ ORGANIZER</h2>
            </section>
            <section id="banner-form">
                <form method="post">
                    <label for="entry">Wpis wydarzenia: </label>
                    <input type="text" id="entry" name="wpis" required>
                    <button type="submit">ZAPISZ</button>
                </form>
            </section>
            <section id="banner-logo">
                <img src="logo2.png" alt="Mój organizer" width="100" height="100">
            </section>
        </header>
        <main>
            <?php foreach ($days as $day): ?>
                <section class="day">
                    <h6><?= e($day['dataZadania']) ?>, <?= e($day['miesiac']) ?></h6>
                    <p><?= e($day['wpis']) ?></p>
                </section>
            <?php endforeach; ?>
        </main>
        <footer>
            <?php if ($period): ?>
                <h1>miesiąc: <?= e($period['miesiac']) ?>, rok: <?= e($period['rok']) ?></h1>
            <?php endif; ?>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (organizer.php)
  34. Krok 34: Zamknięcie połączenia i komunikat o błędzie

    Kryteria:

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

    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.

    organizer.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=egzamin6;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-08-27';");
            $statement->execute(['wpis' => $entry]);
        }
        $days = $pdo->query("SELECT dataZadania, miesiac, wpis FROM zadania WHERE miesiac = 'sierpien';")->fetchAll();
        $period = $pdo->query("SELECT miesiac, rok FROM zadania WHERE dataZadania = '2020-08-01';")->fetch();
    } 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>Organizer</title>
        <link rel="stylesheet" href="styl6.css">
    </head>
    <body>
        <header>
            <section id="banner-title">
                <h2>MÓJ ORGANIZER</h2>
            </section>
            <section id="banner-form">
                <form method="post">
                    <label for="entry">Wpis wydarzenia: </label>
                    <input type="text" id="entry" name="wpis" required>
                    <button type="submit">ZAPISZ</button>
                </form>
            </section>
            <section id="banner-logo">
                <img src="logo2.png" alt="Mój organizer" width="100" height="100">
            </section>
        </header>
        <main>
            <?php if (isset($error)): ?>
                <p class="error"><?= e($error) ?></p>
            <?php endif; ?>
            <?php foreach ($days as $day): ?>
                <section class="day">
                    <h6><?= e($day['dataZadania']) ?>, <?= e($day['miesiac']) ?></h6>
                    <p><?= e($day['wpis']) ?></p>
                </section>
            <?php endforeach; ?>
        </main>
        <footer>
            <?php if ($period): ?>
                <h1>miesiąc: <?= e($period['miesiac']) ?>, rok: <?= e($period['rok']) ?></h1>
            <?php endif; ?>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    styl6.css CSS zmienione: 3 linie
    :root {
        --green: #3CB371;
        --peach: #FFDAB9;
    }
    * {
        font-family: Georgia;
    }
    header {
        display: flex;
    }
    #banner-title, #banner-form {
        background-color: var(--green);
        color: white;
        height: 100px;
        width: 40%;
    }
    #banner-logo {
        background-color: var(--green);
        height: 100px;
        width: 20%;
    }
    main {
        display: grid;
        grid-template-columns: repeat(auto-fill, 182px);
    }
    .day {
        background-color: var(--peach);
        width: 170px;
        height: 90px;
        margin: 5px;
        border: 1px solid var(--green);
    }
    footer {
        background-color: var(--green);
        color: white;
    }
    h6 {
        text-align: right;
    }
    form {
        padding: 30px;
    }
    .error {
        grid-column: 1 / -1;
    }
    Podgląd strony po tym kroku (organizer.php)
  35. Krok 35: 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: organizer.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 „Basen” w bloku 27 sierpnia. Kolejne odświeżenie nie wysyła już niczego.

    organizer.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=egzamin6;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-08-27';");
            $statement->execute(['wpis' => $entry]);
            $saved = true;
        }
        $days = $pdo->query("SELECT dataZadania, miesiac, wpis FROM zadania WHERE miesiac = 'sierpien';")->fetchAll();
        $period = $pdo->query("SELECT miesiac, rok FROM zadania WHERE dataZadania = '2020-08-01';")->fetch();
    } 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: organizer.php');
        exit;
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Organizer</title>
        <link rel="stylesheet" href="styl6.css">
    </head>
    <body>
        <header>
            <section id="banner-title">
                <h2>MÓJ ORGANIZER</h2>
            </section>
            <section id="banner-form">
                <form method="post">
                    <label for="entry">Wpis wydarzenia: </label>
                    <input type="text" id="entry" name="wpis" required>
                    <button type="submit">ZAPISZ</button>
                </form>
            </section>
            <section id="banner-logo">
                <img src="logo2.png" alt="Mój organizer" width="100" height="100">
            </section>
        </header>
        <main>
            <?php if (isset($error)): ?>
                <p class="error"><?= e($error) ?></p>
            <?php endif; ?>
            <?php foreach ($days as $day): ?>
                <section class="day">
                    <h6><?= e($day['dataZadania']) ?>, <?= e($day['miesiac']) ?></h6>
                    <p><?= e($day['wpis']) ?></p>
                </section>
            <?php endforeach; ?>
        </main>
        <footer>
            <?php if ($period): ?>
                <h1>miesiąc: <?= e($period['miesiac']) ?>, rok: <?= e($period['rok']) ?></h1>
            <?php endif; ?>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (organizer.php)
  36. Etap 5: Porównanie z wersją podstawową

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

    Krok 36: 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 trzech sekcji<header> z display: flex
    bloki dnifloat: left, clear przy stopce<main> z siatką repeat(auto-fill, 182px)
    kolorywpisane w każdej regulezmienne --green, --peach
    formularznapis, <input type="submit">label for, required, <button>
    bibliotekamysqli (tabela 1 arkusza)PDO z wyjątkami
    gdzie jest skryptpołączenie na górze, zapytania w bloku głównym i stopcewszystko na górze, w HTML-u tylko foreach i <?= ?>
    zapis wpisudane wklejone w treść UPDATEparametr :wpis, pusty wpis pominięty
    wypisywanie danychwprostprzez e() (htmlspecialchars)
    zamknięcie bazymysqli_close w stopce$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 35.

  37. Etap 6: Oddanie pracy

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

    Krok 37: 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, kw1.jpg - kw4.jpg, kwerendy.txt, logo2.png, organizer.php, przeglądarka.txt i styl6.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 35.

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