Serwis używa plików cookies do analizy ruchu i poprawy działania. Szczegóły
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.
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 logo2.png. Do tego folderu trafiają wszystkie rezultaty pracy.
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.
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
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.
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.
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.
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.
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
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).
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).
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.
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.
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.
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.
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.cssCSSnowy plik
:root {--green: #3CB371;--peach: #FFDAB9;}
Podgląd strony po tym kroku (organizer.php)
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).
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.
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.
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ść.
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.
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.
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.
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.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=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><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Organizer</title> <linkrel="stylesheet"href="styl6.css"></head><body> <header> <sectionid="banner-title"> <h2>MÓJ ORGANIZER</h2> </section> <sectionid="banner-form"> <formmethod="post"> <labelfor="entry">Wpis wydarzenia: </label> <inputtype="text"id="entry"name="wpis"required> <buttontype="submit">ZAPISZ</button> </form> </section> <sectionid="banner-logo"> <imgsrc="logo2.png"alt="Mój organizer"width="100"height="100"> </section> </header> <main><?phpforeach ($daysas$day): ?> <sectionclass="day"> <h6><?= e($day['dataZadania']) ?>, <?= e($day['miesiac']) ?></h6> <p><?= e($day['wpis']) ?></p> </section><?phpendforeach; ?> </main> <footer><?phpif ($period): ?> <h1>miesiąc: <?= e($period['miesiac']) ?>, rok: <?= e($period['rok']) ?></h1><?phpendif; ?> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (organizer.php)
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.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=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><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Organizer</title> <linkrel="stylesheet"href="styl6.css"></head><body> <header> <sectionid="banner-title"> <h2>MÓJ ORGANIZER</h2> </section> <sectionid="banner-form"> <formmethod="post"> <labelfor="entry">Wpis wydarzenia: </label> <inputtype="text"id="entry"name="wpis"required> <buttontype="submit">ZAPISZ</button> </form> </section> <sectionid="banner-logo"> <imgsrc="logo2.png"alt="Mój organizer"width="100"height="100"> </section> </header> <main><?phpforeach ($daysas$day): ?> <sectionclass="day"> <h6><?= e($day['dataZadania']) ?>, <?= e($day['miesiac']) ?></h6> <p><?= e($day['wpis']) ?></p> </section><?phpendforeach; ?> </main> <footer><?phpif ($period): ?> <h1>miesiąc: <?= e($period['miesiac']) ?>, rok: <?= e($period['rok']) ?></h1><?phpendif; ?> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
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.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=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><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Organizer</title> <linkrel="stylesheet"href="styl6.css"></head><body> <header> <sectionid="banner-title"> <h2>MÓJ ORGANIZER</h2> </section> <sectionid="banner-form"> <formmethod="post"> <labelfor="entry">Wpis wydarzenia: </label> <inputtype="text"id="entry"name="wpis"required> <buttontype="submit">ZAPISZ</button> </form> </section> <sectionid="banner-logo"> <imgsrc="logo2.png"alt="Mój organizer"width="100"height="100"> </section> </header> <main><?phpforeach ($daysas$day): ?> <sectionclass="day"> <h6><?= e($day['dataZadania']) ?>, <?= e($day['miesiac']) ?></h6> <p><?= e($day['wpis']) ?></p> </section><?phpendforeach; ?> </main> <footer><?phpif ($period): ?> <h1>miesiąc: <?= e($period['miesiac']) ?>, rok: <?= e($period['rok']) ?></h1><?phpendif; ?> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (organizer.php)
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.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=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><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Organizer</title> <linkrel="stylesheet"href="styl6.css"></head><body> <header> <sectionid="banner-title"> <h2>MÓJ ORGANIZER</h2> </section> <sectionid="banner-form"> <formmethod="post"> <labelfor="entry">Wpis wydarzenia: </label> <inputtype="text"id="entry"name="wpis"required> <buttontype="submit">ZAPISZ</button> </form> </section> <sectionid="banner-logo"> <imgsrc="logo2.png"alt="Mój organizer"width="100"height="100"> </section> </header> <main><?phpif (isset($error)): ?> <pclass="error"><?= e($error) ?></p><?phpendif; ?><?phpforeach ($daysas$day): ?> <sectionclass="day"> <h6><?= e($day['dataZadania']) ?>, <?= e($day['miesiac']) ?></h6> <p><?= e($day['wpis']) ?></p> </section><?phpendforeach; ?> </main> <footer><?phpif ($period): ?> <h1>miesiąc: <?= e($period['miesiac']) ?>, rok: <?= e($period['rok']) ?></h1><?phpendif; ?> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
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.
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 podstawowa
wersja nowoczesna
układ banera
float: left trzech sekcji
<header> z display: flex
bloki dni
float: left, clear przy stopce
<main> z siatką repeat(auto-fill, 182px)
kolory
wpisane w każdej regule
zmienne --green, --peach
formularz
napis, <input type="submit">
label for, required, <button>
biblioteka
mysqli (tabela 1 arkusza)
PDO z wyjątkami
gdzie jest skrypt
połączenie na górze, zapytania w bloku głównym i stopce
wszystko na górze, w HTML-u tylko foreach i <?= ?>
zapis wpisu
dane wklejone w treść UPDATE
parametr :wpis, pusty wpis pominięty
wypisywanie danych
wprost
przez e() (htmlspecialchars)
zamknięcie bazy
mysqli_close w stopce
$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 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.
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.