Serwis używa plików cookies do analizy ruchu i poprawy działania. Szczegóły
EE.09-02-21.06-SGCzerwiec 2021 · 30 kroków · ok. 79 min · 37 kryteriów z klucza
Rozwiązanie krok po kroku: Sklep internetowy z eko-warzywami (Nowoczesny kod (flex, PDO))
To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza dane2 z czterema zapytaniami, przycięta grafika arbuz.jpg oraz strona sklep.php z dwoma banerami, przewijanym blokiem produktów i formularzem w stopce - i tak samo spełnia każde kryterium klucza. Inna jest droga:
flexbox zamiast float: dwa banery stoją w <header> ułożonym we flexie, a produkty zawijają się w rzędy w bloku głównym <main>,
semantyczne znaczniki: każdy produkt w <article>, formularz z etykietami <label for> i przyciskiem <button>,
zmienne CSS dla dwóch zieleni z arkusza (ciemna zieleń wraca w banerach, stopce, nagłówkach produktów i ramkach),
PHP na PDO na górze pliku sklep.php, przed HTML-em: najpierw sprawdzenie danych z formularza, potem zapis zapytaniem z parametrami, potem odczyt - w HTML-u zostaje tylko pętla i wypisywanie przez htmlspecialchars,
cena z przecinkiem („4,50”) zapisuje się poprawnie, a błędne dane i błąd bazy kończą się zrozumiałym komunikatem, nie ostrzeżeniem PHP.
To arkusz EE.09 z czerwca 2021 - kwalifikacji formuły 2017, którą zastąpiła INF.03. Baza i zdjęcia pochodzą z oryginalnego archiwum CKE zad2.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 37 pozycji (R.1.1 - R.5.6), każda za 1 punkt; pozycja R.2.10 jest w kluczu CKE pusta, więc w praktyce ocenianych jest 36 kryteriów. Próg zdania części praktycznej to 75%, czyli 28 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-8 · po tym etapie masz 7 z 37 kryteriów (pkt)
Krok 1: Folder zdającego i archiwum zad2.zip
ok. 2 min
Polecenie z arkusza
Na pulpicie znajduje się archiwum ZIP o nazwie zad2.zip zabezpieczone hasłem: #Warzywni@czeK Należy utworzyć folder o nazwie odpowiadającej numerowi PESEL zdającego lub w przypadku jego braku innemu numerowi, którym został podpisany arkusz. Archiwum należy rozpakować do tego folderu.
Folder na pulpicie nazwany numerem PESEL (w tym rozwiązaniu w jego miejscu stoi Bartosz Bryniarski), do niego archiwum zad2.zip rozpakowane z hasłem #Warzywni@czeK. W środku: dane.sql i dziewięć zdjęć produktów. Strona i zdjęcia muszą leżeć w tym samym folderze - skrypt poda w znaczniku obrazu samą nazwę pliku z bazy.
Krok 2: XAMPP i pusta baza dane2
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 dane2
Start przy Apache i MySQL, potem http://localhost/phpmyadmin, Nowa, nazwa dane2.
Krok 3: Import pliku dane.sql i zrzut egzamin.jpg
Kryteria:
R.1.1 Wykonano import tabel do bazy danych dane2 , czynność udokumentowano plikiem o nazwie egzamin w formacie JPEG
ok. 3 min
Polecenie z arkusza
Do bazy dane2 zaimportuj plik dane.sql z rozpakowanego archiwum. Wykonaj zrzut ekranu po imporcie. Zrzut zapisz w folderze z numerem PESEL lub w przypadku jego braku w folderze z innym numerem, którym został podpisany arkusz, w formacie JPEG i nazwij egzamin. Nie kadruj zrzutu. Powinien on obejmować cały ekran monitora, z widocznym paskiem zadań. Na zrzucie powinny być widoczne elementy wskazujące na poprawnie wykonany import tabel
Baza dane2, karta Import, plik dane.sql, Wykonaj. Powstaje pięć tabel - w zadaniu potrzebne są produkty, producenci i rodzaje. Pola Rodzaje_id i Producenci_id w produktach wskazują id w tamtych tabelach. Zrzut całego ekranu z paskiem zadań: egzamin.jpg - w tym arkuszu zrzut importu jest w JPEG, a zrzuty kwerend w PNG.
Podgląd strony po tym kroku
Krok 4: Zapytanie 1: owoce i warzywa
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 nazwa, ilosc, opis, cena, zdjecie z tabeli produkty , dla produktów, których pole Rodzaje_id przyjmuje wartości: 1, 2. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa, ilosc, opis, cena, zdjecie FROM produkty WHERE Rodzaje_id = 1 OR Rodzaje_id = 2; oraz wynik działania zapytania udokumentowano zrzutem, na którym widocznych jest dokładnie 8 wierszy, jedynie pola nazwa, ilosc, opis, cena, zdjecie
ok. 3 min
Polecenie z arkusza
Zapytanie 1: wybierające jedynie pola nazwa, ilosc, opis, cena, zdjecie z tabeli Produkty, dla produktów, których pole Rodzaje_id przyjmuje jedną z wartości: 1, 2
Pięć kolumn w kolejności z arkusza i warunek na jedną z dwóch wartości (to samo da Rodzaje_id IN (1, 2)). Wynik: 8 wierszy, od gruszki do papryki. Zapytanie trafia do kwerendy.txt, zrzut - do kw1.png. Każda klauzula w osobnej linii: przy dłuższych zapytaniach łatwiej znaleźć błąd.
Krok 5: Zapytanie 2: owoce z niskim stanem i ich producenci
Kryteria:
R.1.4 Utworzono zapytanie 2 wybierające jedynie nazwę produktu i odpowiadającą mu nazwę producenta dla produktów, dla których ilość jest mniejsza od 10 oraz pole Rodzaje_id przyjmuje wartość 1. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT produkty.nazwa, producenci.nazwa FROM produkty JOIN producenci ON produkty.Producenci_id = producenci.id WHERE produkty.ilosc < 10 AND Rodzaje_id = 1; możliwe też INNER JOIN lub bez JOIN (porównanie kluczy w warunku) oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczne są dokładnie 2 rekordy: arbuz FruitFarm, papaja Owoce-rolnik
ok. 3 min
Polecenie z arkusza
Zapytanie 2: wybierające jedynie nazwę produktu z tabeli Produkty i odpowiadającą mu nazwę producenta z tabeli Producenci dla produktów, dla których ilość jest mniejsza od 10 oraz pole Rodzaje_id przyjmuje wartość 1
JOIN ... ON produkty.Producenci_id = producenci.id łączy każdy produkt z jego producentem. Obie tabele mają kolumnę nazwa, więc przed nazwą kolumny stoi nazwa tabeli. Wynik: 2 wiersze - arbuz (FruitFarm) i papaja (Owoce-rolnik). Zrzut: kw2.png.
R.1.5 Utworzono zapytanie 3 tworzące użytkownika anna z hasłem @nna23. W pliku z kwerendami lub na zrzucie istnieje zapis np.: CREATE USER 'anna'@'localhost' IDENTIFIED BY '@nna23'; oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczny jest efekt poprawnie wykonanego zapytania. Nie uznajemy za spełnione, jeśli brak widocznego hasła (np. wygenerowano z kreatora)
ok. 2 min
Polecenie z arkusza
Zapytanie 3: tworzące użytkownika anna z hasłem @nna23
CREATE USER z nazwą konta, hostem localhost i hasłem w IDENTIFIED BY. Klucz wymaga widocznego hasła, więc konto zakładasz zapytaniem w karcie SQL, nie kreatorem. Zrzut: kw3.png.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT nazwa, ilosc, opis, cena, zdjecieFROM produktyWHERE Rodzaje_id = 1 OR Rodzaje_id = 2;-- Zapytanie 2SELECT produkty.nazwa, producenci.nazwaFROM produktyJOIN producenci ON produkty.Producenci_id = producenci.idWHERE produkty.ilosc < 10 AND produkty.Rodzaje_id = 1;-- Zapytanie 3CREATE USER 'anna'@'localhost' IDENTIFIED BY '@nna23';
Podgląd strony po tym kroku
Krok 7: Zapytanie 4: nowy produkt owoc1
Kryteria:
R.1.6 Utworzono zapytanie 4 wstawiające wiersz do tabeli produkty, klucz główny nadany automatycznie, dane: Rodzaje_id: 1, Producenci_id: 4, nazwa: owoc1, ilość: 10, opis pusty, cena: 9,99, zdjecie: owoce.jpg. W pliku z kwerendami lub na zrzucie istnieje zapis np.: INSERT INTO produkty(`Rodzaje_id`, `Producenci_id`, `nazwa`, `ilosc`, `opis`, `cena`, `zdjecie`) VALUES (1, 4, "owoc1", 10,"",9.99,"owoce.jpg") (możliwe również bez wymieniania pól w nawiasie, a także przez SET + lista przypisań) oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczny jest komunikat o poprawnie wykonanym zapytaniu lub wstawiony rekord
ok. 3 min
Polecenie z arkusza
Zapytanie 4: wstawiające wiersz do tabeli Produkty według specyfikacji: klucz główny nadany automatycznie; Rodzaje_id – id wiersza z nazwą owoce w tabeli Rodzaje; Producenci_id – id wiersza z nazwą warzywa-rolnik w tabeli Producenci; pozostałe dane: nazwa: owoc1, ilość: 10, opis: puste pole, cena: 9,99, zdjecie: owoce.jpg
W rodzaje owoce mają id1, w producenci warzywa-rolnik ma id4. Lista kolumn bez id (nada je AUTO_INCREMENT), pusty opis jako '', cena z kropką - przecinek w SQL oddziela wartości. Zrzut: kw4.png. To zapytanie skrypt powtórzy z nazwą i ceną z formularza.
kwerendy.txtTekstzmienione: 4 linie
-- Zapytanie 1SELECT nazwa, ilosc, opis, cena, zdjecieFROM produktyWHERE Rodzaje_id = 1 OR Rodzaje_id = 2;-- Zapytanie 2SELECT produkty.nazwa, producenci.nazwaFROM produktyJOIN producenci ON produkty.Producenci_id = producenci.idWHERE produkty.ilosc < 10 AND produkty.Rodzaje_id = 1;-- Zapytanie 3CREATE USER 'anna'@'localhost' IDENTIFIED BY '@nna23';-- Zapytanie 4INSERT INTO produkty (Rodzaje_id, Producenci_id, nazwa, ilosc, opis, cena, zdjecie)VALUES (1, 4, 'owoc1', 10, '', 9.99, 'owoce.jpg');
R.2.1 Plik arbuz.jpg przeskalowano do rozmiaru 300 px na 200 px (± 2 px)
ok. 4 min
Polecenie z arkusza
Plik arbuz.jpg, wypakowany z archiwum, należy skadrować do proporcji 3:2, a następnie przeskalować z zachowaniem proporcji do rozmiaru 300 px na 200 px. Pozostałe pliki graficzne są przygotowane i nie należy ich zmieniać
Oryginał ma 500 × 297 px. W GIMP-ie Kadrowanie ze stałymi proporcjami 3:2 na całą wysokość (446 × 297 px, wycentrowane), potem Obraz → Skaluj obraz do szerokości 300 - wysokość sama wyjdzie 200 (R.2.1). Pozostałe zdjęcia mają już 300 × 200 px.
Podgląd strony po tym kroku
Etap 2: Strona HTML
Kroki 9-14 · po tym etapie masz 18 z 37 kryteriów (pkt)
Krok 9: Plik sklep.php i kodowanie znaków
Kryteria: ,
R.2.2 Witrynę internetową zapisano w pliku PHP o nazwie sklep
R.2.3 Zastosowano właściwy standard kodowania polskich znaków dla witryny
ok. 1 min
Polecenie z arkusza
Składa się ze strony o nazwie sklep.php. Zastosowany właściwy standard kodowania polskich znaków
Szkielet HTML5 w pliku sklep.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.2.3).
R.3.5 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl2.css , formatowanie pochodzi jedynie z tego arkusza Uwaga: kryterium to nie jest spełnione również, gdy źle przypisano co najmniej jeden selektor do bloku (np. kropka zamiast #)
ok. 1 min
Polecenie z arkusza
Tytuł strony widoczny na karcie przeglądarki: „Warzywniak”. Arkusz stylów w pliku o nazwie styl2.css prawidłowo połączony z kodem strony
<title> z napisem na karcie przeglądarki (R.2.4) i <link rel="stylesheet" href="styl2.css"> - całe formatowanie pochodzi z tego pliku, bez atrybutów style w HTML-u (R.3.5).
R.2.5 Wygląd strony zdefiniowano dzieląc ją na bloki: dwa bloki banera, główny i stopka. Zastosowano znaczniki sekcji
R.2.6 W bloku banera lewego zapisano nagłówek h1: "Internetowy sklep z eko-warzywami"
ok. 2 min
Polecenie z arkusza
Podział strony na bloki: dwa bloki banera obok siebie, blok główny i stopka. W bloku głównym znajdują się bloki produktów generowane przez skrypt. Podział zrealizowany za pomocą znaczników sekcji tak, aby wygląd układu bloków strony po uruchomieniu w przeglądarce, był zgodny z Obrazem 2. Zawartość banera lewego: nagłówek pierwszego stopnia o treści „Internetowy sklep z eko-warzywami”
Znaczniki mówią, czym jest każda część strony:
<header> - nagłówek strony, a w nim dwa bloki banera jako <section> z identyfikatorami banner-left i banner-right; lewy ma jedyne na stronie <h1> (R.2.6),
<main> - blok główny, do którego skrypt wstawi produkty,
<footer> - stopka.
Bloki banera, główny i stopka są na miejscu (R.2.5). Znacznik <header> nie zmienia wyglądu, ale pozwoli ustawić oba banery w rzędzie jedną regułą CSS.
Krok 12: Baner prawy: lista numerowana z odnośnikiem
Kryteria: ,
R.2.7 W bloku banera prawego umieszczono listę numerowaną z trzema elementami, zastosowano znaczniki <ol>, <li> oraz ich zamknięcia
R.3.2 Umieszczono odnośnik, którego kliknięcie inicjuje otwarcie w nowym oknie strony https://terapiasokami.pl/
ok. 2 min
Polecenie z arkusza
Zawartość banera prawego: Lista numerowana (uporządkowana) z elementami: warzywa, owoce, soki. Element soki jest odnośnikiem prowadzącym do strony o adresie „https://terapiasokami.pl/”, odnośnik otwiera się w osobnym oknie
<ol> - lista numerowana, każdy element w <li> z zamknięciem (R.2.7),
target="_blank" otwiera stronę w nowej karcie lub oknie (R.3.2),
rel="noopener" odcina otwartą stronę od karty sklepu - obca strona nie może wtedy przez window.opener podmienić adresu karty, z której przyszedł użytkownik. Nowe przeglądarki robią to same, ale atrybut zabezpiecza też starsze.
R.3.4 Wciśnięcie przycisku wysyła dane do skryptu (np. typ submit dla input lub button albo button bez typu)
R.2.9 W stopce umieszczono formularz z dwoma polami edycyjnymi i przyciskiem, zastosowano znacznik <form> oraz jego zamknięcie
ok. 3 min
Polecenie z arkusza
Zawartość stopki: Formularz wysyłający dane do tego samego pliku metodą POST. Pole edycyjne poprzedzone wpisem „Nazwa:”. Pole edycyjne poprzedzone wpisem „Cena:”. Przycisk „Dodaj produkt” wysyłający dane do skryptu 2
<form method="post"> bez action wysyła dane do tego samego pliku (R.3.3),
napisy „Nazwa:” i „Cena:” w <label for> - kliknięcie napisu ustawia kursor w polu, a czytnik ekranu odczyta, czego pole dotyczy,
maxlength="100" - tyle znaków mieści kolumna nazwa (varchar(100)), required - przeglądarka nie wyśle pustego pola,
pole ceny zostaje tekstowe, ale inputmode="decimal" pokazuje na telefonie klawiaturę z cyframi i przecinkiem,
<button type="submit"> wysyła formularz (R.3.4).
Formularz ma dwa pola edycyjne i przycisk, z zamknięciem </form> (R.2.9). Atrybuty w przeglądarce to wygoda, nie zabezpieczenie - każde z nich da się ominąć, więc skrypt i tak sprawdzi dane.
Kroki 15-21 · po tym etapie masz 29 z 37 kryteriów (pkt)
Krok 15: Plik styl2.css: kolory w zmiennych i krój Garamond
Kryteria:
R.4.1 Ustawiono domyślny krój czcionki dla całej strony na Garamond (dla selektora * lub body lub html lub kontenera całej strony)
ok. 2 min
Polecenie z arkusza
Domyślne dla całej strony: krój czcionki Garamond
zmienne CSS w :root nadają nazwy dwóm kolorom z arkusza; ciemna zieleń #00600F wróci sześć razy (banery, stopka, nagłówek produktu, ramka), więc zmiana odcienia to teraz jedna linia,
selektor * z krojem Garamond dla każdego elementu, także pól formularza (R.4.1); serif to krój zapasowy na komputerze bez Garamonda.
R.4.4 Ustawiono wysokość obu bloków banera: 90px, bloku głównego: 600px
R.4.7 Ustawiono przynajmniej jeden pasek przewijania (przesuwania) dla bloku głównego, np. overflow: scroll; (lub auto)
ok. 2 min
Polecenie z arkusza
Dla bloku głównego: kolor tła #C8E6C9, wysokość 600 px, paski przesuwania (scroll)
Wysokość banerów i bloku głównego jest ustawiona (R.4.4). overflow: auto pokazuje pasek przewijania wtedy, gdy produkty nie mieszczą się w 600 px - czyli tu zawsze w pionie, a w poziomie wcale; scroll rysowałby oba paski na stałe, także pusty poziomy. Klucz przyjmuje obie wartości (R.4.7).
R.4.8 Ustawiono styl selektora h5: kolor tła #00600F, biały kolor czcionki, rozmiar czcionki 200%, wyrównanie tekstu do środka, marginesy zewnętrzne (margin) 0px (0 również poprawne bez jednostki)
R.4.9 Ustawiono styl selektora h2: wyrównanie tekstu do prawej
ok. 2 min
Polecenie z arkusza
Dla selektora nagłówka piątego stopnia: kolor tła #00600F, biały kolor czcionki, rozmiar czcionki 200%, wyrównanie tekstu do środka, marginesy zewnętrzne 0 px. Dla selektora nagłówka drugiego stopnia: wyrównanie tekstu do prawej
Klucz wymaga stylu selektora znacznikah5 i h2, nie klasy (R.4.8, R.4.9). margin: 0 - przy zerze jednostka nie jest potrzebna.
R.4.10 Ustawiono styl bloku produktu: marginesy zewnętrzne 10px (margin), obramowanie linia ciągła 1px w kolorze #00600F, szerokość 300px
ok. 2 min
Polecenie z arkusza
Dla bloku produktu generowanego przez skrypt 1: marginesy zewnętrzne 10 px, obramowanie linia ciągła 1 px w kolorze #00600F, szerokość 300 px
Klasa .product z marginesem, ramką i szerokością równą szerokości zdjęć (R.4.10). Dodatek: display: block dla obrazu w bloku produktu. Obraz jest domyślnie elementem w linii tekstu i pod nim zostaje kilka pikseli na „ogonki” liter - zielony pasek nazwy przylegał do zdjęcia ze szparą. Jako blok obraz styka się z nagłówkiem.
R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z Obrazem 2 w arkuszu egzaminacyjnym (nie uwzględnia się bloków zawierających produkty)
ok. 2 min
Polecenie z arkusza
Podział zrealizowany za pomocą znaczników sekcji tak, aby wygląd układu bloków strony po uruchomieniu w przeglądarce, był zgodny z Obrazem 2
W bloku głównym:
display: flex z flex-wrap: wrap - produkty stają obok siebie, a gdy brakuje miejsca, zawijają się do nowego rzędu; na ekranie szerokości ok. 1150 px mieszczą się po trzy, jak na obrazie 2,
align-content: flex-start - rzędy przylegają do góry bloku, zamiast rozkładać się równo na 600 px wysokości.
Szerokość bloku produktu zostaje na nim (300 px), więc kryterium R.4.10 nadal jest spełnione. Układ jest kompletny: banery we flexie, przewijany blok główny i stopka pod nim - bez jednego float (R.3.1).
Kroki 22-28 · po tym etapie masz 36 z 37 kryteriów (pkt)
Krok 22: Połączenie z bazą dane2 przez PDO
Kryteria:
R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy dane2
ok. 4 min
Polecenie z arkusza
Skrypt łączy się z serwerem bazy danych na localhost, użytkownik root bez hasła, baza danych o nazwie dane2
Cała logika na górze pliku, przed <!DOCTYPE html> - HTML niżej tylko wypisuje gotowe dane. To ważne właśnie w tym arkuszu: formularz stoi w stopce, po liście produktów, więc zapis musi się wykonać, zanim lista zostanie odczytana.
funkcja e() - skrót do htmlspecialchars, przyda się przy każdym wypisaniu danych z bazy,
new PDO(...) - serwer localhost, baza dane2 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,
FETCH_ASSOC - wiersze jako tablice z nazwami kolumn ($product['cena'] zamiast $row[3]).
Od tego kroku podgląd to wynik prawdziwego uruchomienia w PHP 8.2.
Krok 23: Skrypt 1: zapytanie 1 i blok dla każdego produktu
Kryteria: ,
R.5.3 Skrypt 1 wysyła do bazy zapytanie 1 lub skrypt 2 - zapytanie 4
R.5.5 Skrypt 1 wyświetla wszystkie zwrócone zapytaniem bloki zawierające pobrane z bazy produkty przy zastosowaniu pętli
ok. 4 min
Polecenie z arkusza
Działanie skryptu 1: Wysyła do bazy zapytanie 1. Dla każdego zwróconego zapytaniem wiersza tworzy blok produktu
Dwie części w dwóch miejscach pliku:
na górze $pdo->query(...)->fetchAll() - zapytanie 1 (R.5.3); wszystkie wiersze trafiają do tablicy $products. Zapytanie nie ma danych od użytkownika, więc query wystarczy,
w <main> pętla foreach ($products as $product): z zamknięciem endforeach; - ta składnia z dwukropkiem jest czytelniejsza niż klamry, gdy między fragmentami PHP stoi HTML (R.5.5).
Każdy produkt dostaje <article> - znacznik na samodzielną całość, którą można by wyjąć ze strony (tu: karta produktu). Po zapytaniu 4 w bazie jest dziewięć owoców i warzyw, więc na stronie jest 9 pustych ramek.
sklep.phpPHPzmienione: 5 linii
<?phpfunctione(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');}$products = [];$pdo = null;try {$pdo = new PDO('mysql:host=localhost;dbname=dane2;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$products = $pdo->query('SELECT nazwa, ilosc, opis, cena, zdjecie FROM produkty WHERE Rodzaje_id = 1 OR Rodzaje_id = 2;')->fetchAll();} catch (PDOException $exception) {$error = 'nie udało się odczytać lub zapisać danych, spróbuj ponownie za chwilę';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Warzywniak</title> <linkrel="stylesheet"href="styl2.css"></head><body> <header> <sectionid="banner-left"> <h1>Internetowy sklep z eko-warzywami</h1> </section> <sectionid="banner-right"> <ol> <li>warzywa</li> <li>owoce</li> <li><ahref="https://terapiasokami.pl/"target="_blank"rel="noopener">soki</a></li> </ol> </section> </header> <main><?phpforeach ($productsas$product): ?> <articleclass="product"> </article><?phpendforeach; ?> </main> <footer> <formmethod="post"> <labelfor="name">Nazwa:</label> <inputtype="text"id="name"name="nazwa"maxlength="100"required> <labelfor="price">Cena:</label> <inputtype="text"id="price"name="cena"inputmode="decimal"required> <buttontype="submit">Dodaj produkt</button> </form> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (sklep.php)
Krok 24: Skrypt 1: zawartość bloku produktu
Kryteria: ,
R.5.4 Skrypt 1 tworzy blok, w którym znajdują się elementy: obraz, nazwa w nagłówku h5, treść "opis: <opis>" i "na stanie: <ilosc>" w osobnych paragrafach oraz "<cena> zł" w nagłówku h2. Wartości wszystkich elementów zostały pobrane z bazy
R.2.8 Przynajmniej jeden obraz ma tekst alternatywny "warzywniak" (umieszczony skryptem lub HTML)
ok. 4 min
Polecenie z arkusza
Blok produktu zawiera: Obraz o nazwie pobranej z bazy danych i tekście alternatywnym „warzywniak”. Nagłówek piątego stopnia z nazwą pobraną z bazy. Paragraf (akapit) o treści „opis: <opis>”. Paragraf o treści „na stanie: <ilosc>”. Nagłówek drugiego stopnia o treści „<cena> zł”, gdzie pola umieszczone w nawiasach < > oznaczają dane pobrane z bazy danych
Blok jest zwykłym HTML-em, a dane wchodzą w miejsca oznaczone <?= ... ?> (skrót od <?php echo ... ?>):
src z kolumny zdjecie, stały tekst alternatywny warzywniak (R.2.8),
nazwa w <h5>, opis i stan w osobnych akapitach, cena z dopiskiem „zł” w <h2> (R.5.4).
Nie trzeba ukośników przed cudzysłowami jak w echo, a każda wartość przechodzi przez e() - nazwa z formularza nie stanie się kodem HTML. Pierwszy blok: gruszka, „opis: Klaps”, „na stanie: 20”, „2.56 zł”, jak na obrazie 2.
Krok 26: Skrypt 2: zapis produktu zapytaniem 4 z parametrami
Kryteria:
R.5.6 Skrypt 2 wysyła do bazy zmodyfikowane zapytanie 4, pola nazwa i cena są pobrane z formularza. Należy sprawdzić w bazie danych czy wiersz został dodany do bazy (uwaga: dodany rekord może być widoczny na 2 stronie w phpMyAdmin)
ok. 3 min
Polecenie z arkusza
Wysyła zmodyfikowane zapytanie 4 do bazy danych. Dane nazwa i cena są pobrane z formularza, reszta danych zapytania 4 nie ulega zmianie
Zapytanie 4, w którym nazwę i cenę zastępują parametry :name i :price; reszta bez zmian - owoce (1), warzywa-rolnik (4), ilość 10, pusty opis, owoce.jpg:
$pdo->prepare(...) - baza dostaje samą treść zapytania,
execute([...]) - osobno przychodzą wartości; nigdy nie staną się częścią kodu SQL, więc nazwa z apostrofem zapisze się poprawnie, a próba wstrzyknięcia SQL zapisze się jako zwykły tekst.
Zapis stoi przed odczytem produktów, więc nowy produkt widać od razu, na końcu listy. Sprawdzenie, jak każe klucz (R.5.6): po wysłaniu „ jablko ” i „4,50” w tabeli produkty jest wiersz jablko z ceną 4.5.
Typowe błędy
Nazwa z literami „ł” czy „ś” kończy się błędem bazy: tabele z archiwum mają 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.
Krok 27: Zamknięcie połączenia i komunikat o błędzie bazy
Kryteria:
R.5.2 Ostatnią operacją na bazie jest jej zamknięcie (zgodne ze składnią)
ok. 3 min
Polecenie z arkusza
Po wykonaniu operacji na bazie danych 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). Produkty są już w tablicy, więc pętla w <main> działa bez połączenia.
Gdy baza nie odpowie albo odrzuci zapis, catch zapisuje komunikat w $error, a stopka go pokazuje. Treść wyjątku zostaje na serwerze: komunikat bazy zdradzałby nazwy tabel i kolumn. Sprawdzone na prawdziwym PHP: przy złym haśle i przy nazwie „jabłko”, której tabela latin1 nie przyjmuje, strona wyświetla banery, komunikat i formularz.
Krok 28: Odświeżenie po zapisie nie dodaje produktu 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) wysyła dane ponownie - i w sklepie pojawia się drugi taki sam produkt. Wzorzec PRG (Post/Redirect/Get):
po udanym execute skrypt zapamiętuje $saved = true,
po bloku finally (połączenie jest już zamknięte) header('Location: sklep.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 nowy produkt; w bazie jest dokładnie jeden taki wiersz.
Krok 29 · po tym etapie masz 36 z 37 kryteriów (pkt)
Krok 29: Co jest inaczej i którą wersję wybrać na egzaminie
ok. 3 min
Obie wersje dają komplet punktów. Różnice:
wersja podstawowa
wersja nowoczesna
banery
float: left dwóch sekcji, clear pod nimi
<header> z display: flex
produkty
<section> z float: left
<article> w <main> z flex-wrap
kolory
wpisane w każdej regule
zmienne --green, --mint
formularz
napisy, <input type="submit">
label for, required, maxlength, <button>
biblioteka
mysqli (tabela 1 arkusza)
PDO z wyjątkami
gdzie jest skrypt
połączenie i zapis na górze, zapytanie 1 w bloku głównym
wszystko na górze, w HTML-u tylko foreach i <?= ?>
cena „4,50”
błąd zapytania, poprawione w dobrych praktykach
przecinek zamieniany, FILTER_VALIDATE_FLOAT
zapis produktu
dane wklejone w treść INSERT, potem zapytanie przygotowane
parametry :name, :price od początku
wypisywanie danych
htmlspecialchars dopisane na końcu
e() od pierwszej linii
zamknięcie bazy
mysqli_close po liście produktów
$pdo = null w finally
po zapisie
F5 dodaje produkt ponownie
przekierowanie (PRG)
Na egzaminie wersja podstawowa trzyma się funkcji z tabeli 1 i łatwiej ją napisać bez dokumentacji. Nowoczesna nie wywraca się na cenie z przecinkiem ani na apostrofie i nie dubluje produktów przy odświeżeniu - a klucz przyjmuje obie.
Krok 30 · po tym etapie masz 36 z 37 kryteriów (pkt)
Krok 30: Plik przeglądarka.txt i nagranie płyty
ok. 5 min
Polecenie z arkusza
Po zakończeniu pracy utwórz w folderze z numerem PESEL lub w przypadku jego braku w folderze z innym numerem, którym został podpisany arkusz plik tekstowy o nazwie przeglądarka.txt. Zapisz w nim nazwę przeglądarki internetowej, w której weryfikowałeś poprawność działania witryny. 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: egzamin.jpg, kw1.png - kw4.png, kwerendy.txt, przeglądarka.txt, sklep.php, styl2.css i dziewięć zdjęć z archiwum (z przyciętym arbuz.jpg). 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.