Serwis używa plików cookies do analizy ruchu i poprawy działania. Szczegóły
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.
Suwak i strzałki obok niego - albo ←→ na klawiaturze - przechodzą krok po kroku; wyjaśnienie, pliki i podgląd zmieniają się razem z krokiem.
Lista „Etap” przenosi od razu do wybranego etapu.
„Rozwiązuję samodzielnie” chowa rozwiązanie kroku - najpierw własna próba, potem odsłonięcie.
„Tryb prezentacji” pokazuje same kroki, bez menu strony - na rzutnik.
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.
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.
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
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.
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.
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.
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.
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
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).
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).
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.
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.
<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.
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.
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.cssCSSnowy plik
:root {--navy: #483D8B;--turquoise: #AFEEEE;}
Podgląd strony po tym kroku (kalendarz.php)
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).
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.
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.
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ść.
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.
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.phpPHPzmienione: 4 linie
<?phpfunctione(?string $text): string{returnhtmlspecialchars($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><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Mój kalendarz</title> <linkrel="stylesheet"href="styl5.css"></head><body> <header> <sectionid="banner-logo"> <imgsrc="logo1.png"alt="Mój kalendarz"width="188"height="150"> </section> <sectionid="banner-title"> <h1>KALENDARZ</h1><?phpif ($period): ?> <h3>miesiąc: <?= e($period['miesiac']) ?>, rok: <?= e($period['rok']) ?></h3><?phpendif; ?> </section> </header> <main> </main> <footer> <formmethod="post"> <labelfor="entry">dodaj wpis: </label> <inputtype="text"id="entry"name="wpis"required> <buttontype="submit">DODAJ</button> </form> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (kalendarz.php)
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.phpPHPzmienione: 5 linii
<?phpfunctione(?string $text): string{returnhtmlspecialchars($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><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Mój kalendarz</title> <linkrel="stylesheet"href="styl5.css"></head><body> <header> <sectionid="banner-logo"> <imgsrc="logo1.png"alt="Mój kalendarz"width="188"height="150"> </section> <sectionid="banner-title"> <h1>KALENDARZ</h1><?phpif ($period): ?> <h3>miesiąc: <?= e($period['miesiac']) ?>, rok: <?= e($period['rok']) ?></h3><?phpendif; ?> </section> </header> <main><?phpforeach ($daysas$day): ?> <sectionclass="day"> </section><?phpendforeach; ?> </main> <footer> <formmethod="post"> <labelfor="entry">dodaj wpis: </label> <inputtype="text"id="entry"name="wpis"required> <buttontype="submit">DODAJ</button> </form> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (kalendarz.php)
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.phpPHPzmienione: 2 linie
<?phpfunctione(?string $text): string{returnhtmlspecialchars($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><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Mój kalendarz</title> <linkrel="stylesheet"href="styl5.css"></head><body> <header> <sectionid="banner-logo"> <imgsrc="logo1.png"alt="Mój kalendarz"width="188"height="150"> </section> <sectionid="banner-title"> <h1>KALENDARZ</h1><?phpif ($period): ?> <h3>miesiąc: <?= e($period['miesiac']) ?>, rok: <?= e($period['rok']) ?></h3><?phpendif; ?> </section> </header> <main><?phpforeach ($daysas$day): ?> <sectionclass="day"> <h5><?= e($day['dataZadania']) ?></h5> <p><?= e($day['wpis']) ?></p> </section><?phpendforeach; ?> </main> <footer> <formmethod="post"> <labelfor="entry">dodaj wpis: </label> <inputtype="text"id="entry"name="wpis"required> <buttontype="submit">DODAJ</button> </form> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (kalendarz.php)
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.phpPHPzmienione: 1 linia
<?phpfunctione(?string $text): string{returnhtmlspecialchars($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><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Mój kalendarz</title> <linkrel="stylesheet"href="styl5.css"></head><body> <header> <sectionid="banner-logo"> <imgsrc="logo1.png"alt="Mój kalendarz"width="188"height="150"> </section> <sectionid="banner-title"> <h1>KALENDARZ</h1><?phpif ($period): ?> <h3>miesiąc: <?= e($period['miesiac']) ?>, rok: <?= e($period['rok']) ?></h3><?phpendif; ?> </section> </header> <main><?phpforeach ($daysas$day): ?> <sectionclass="day"> <h5><?= e($day['dataZadania']) ?></h5> <p><?= e($day['wpis']) ?></p> </section><?phpendforeach; ?> </main> <footer> <formmethod="post"> <labelfor="entry">dodaj wpis: </label> <inputtype="text"id="entry"name="wpis"required> <buttontype="submit">DODAJ</button> </form> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
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.phpPHPzmienione: 4 linie
<?phpfunctione(?string $text): string{returnhtmlspecialchars($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><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Mój kalendarz</title> <linkrel="stylesheet"href="styl5.css"></head><body> <header> <sectionid="banner-logo"> <imgsrc="logo1.png"alt="Mój kalendarz"width="188"height="150"> </section> <sectionid="banner-title"> <h1>KALENDARZ</h1><?phpif ($period): ?> <h3>miesiąc: <?= e($period['miesiac']) ?>, rok: <?= e($period['rok']) ?></h3><?phpendif; ?> </section> </header> <main><?phpforeach ($daysas$day): ?> <sectionclass="day"> <h5><?= e($day['dataZadania']) ?></h5> <p><?= e($day['wpis']) ?></p> </section><?phpendforeach; ?> </main> <footer> <formmethod="post"> <labelfor="entry">dodaj wpis: </label> <inputtype="text"id="entry"name="wpis"required> <buttontype="submit">DODAJ</button> </form> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (kalendarz.php)
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.phpPHPzmienione: 5 linii
<?phpfunctione(?string $text): string{returnhtmlspecialchars($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><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Mój kalendarz</title> <linkrel="stylesheet"href="styl5.css"></head><body> <header> <sectionid="banner-logo"> <imgsrc="logo1.png"alt="Mój kalendarz"width="188"height="150"> </section> <sectionid="banner-title"> <h1>KALENDARZ</h1><?phpif ($period): ?> <h3>miesiąc: <?= e($period['miesiac']) ?>, rok: <?= e($period['rok']) ?></h3><?phpendif; ?> </section> </header> <main><?phpif (isset($error)): ?> <pclass="error"><?= e($error) ?></p><?phpendif; ?><?phpforeach ($daysas$day): ?> <sectionclass="day"> <h5><?= e($day['dataZadania']) ?></h5> <p><?= e($day['wpis']) ?></p> </section><?phpendforeach; ?> </main> <footer> <formmethod="post"> <labelfor="entry">dodaj wpis: </label> <inputtype="text"id="entry"name="wpis"required> <buttontype="submit">DODAJ</button> </form> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
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.phpPHPzmienione: 5 linii
<?phpfunctione(?string $text): string{returnhtmlspecialchars($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><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Mój kalendarz</title> <linkrel="stylesheet"href="styl5.css"></head><body> <header> <sectionid="banner-logo"> <imgsrc="logo1.png"alt="Mój kalendarz"width="188"height="150"> </section> <sectionid="banner-title"> <h1>KALENDARZ</h1><?phpif ($period): ?> <h3>miesiąc: <?= e($period['miesiac']) ?>, rok: <?= e($period['rok']) ?></h3><?phpendif; ?> </section> </header> <main><?phpif (isset($error)): ?> <pclass="error"><?= e($error) ?></p><?phpendif; ?><?phpforeach ($daysas$day): ?> <sectionclass="day"> <h5><?= e($day['dataZadania']) ?></h5> <p><?= e($day['wpis']) ?></p> </section><?phpendforeach; ?> </main> <footer> <formmethod="post"> <labelfor="entry">dodaj wpis: </label> <inputtype="text"id="entry"name="wpis"required> <buttontype="submit">DODAJ</button> </form> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (kalendarz.php)
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 podstawowa
wersja nowoczesna
układ banera
float: left dwóch sekcji
<header> z display: flex
bloki dni
float: left, clear przy stopce
<main> z siatką repeat(auto-fill, 158px)
kolory
wpisane w każdej regule
zmienne --navy, --turquoise
formularz
napis, <input type="submit">
label for, required, <button>
biblioteka
mysqli (tabela 1 arkusza)
PDO z wyjątkami
gdzie jest skrypt
połączenie i zapis na górze, zapytania w banerze i bloku głównym
wszystko na górze, w HTML-u tylko foreach i <?= ?>
zapis wpisu
dane wklejone w treść UPDATE, potem zapytanie przygotowane
parametr :wpis od początku, pusty wpis pominięty
wypisywanie danych
htmlspecialchars dopisane na końcu
e() od pierwszej linii
zamknięcie bazy
mysqli_close po bloku głównym
$pdo = null w finally
po zapisie
F5 wysyła formularz ponownie
przekierowanie (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.
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.
Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.
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: Bartosz Bryniarski. 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.