Serwis używa plików cookies do analizy ruchu i poprawy działania. Szczegóły
EE.09-02-21.06-SGCzerwiec 2021 · 44 kroki · ok. 86 min · 37 kryteriów z klucza
Rozwiązanie krok po kroku: Sklep internetowy z eko-warzywami (Wersja podstawowa)
Internetowy sklep z eko-warzywami: baza dane2 (import pliku dane.sql - pięć tabel, w zadaniu potrzebne są produkty, producenci i rodzaje), cztery zapytania - owoce i warzywa, produkty z niskim stanem połączone z producentem (JOIN), nowy użytkownik bazy (CREATE USER) i nowy produkt (INSERT). Do tego grafika arbuz.jpg przycięta do proporcji 3:2 i zmniejszona do 300 × 200 px, strona sklep.php z dwoma banerami, blokiem produktów z paskiem przewijania i formularzem w stopce, style w styl2.css oraz skrypt PHP, który z bazy buduje bloki produktów i dopisuje produkt z formularza.
To arkusz EE.09 z czerwca 2021 - kwalifikacji formuły 2017, którą zastąpiła INF.03. Kroki idą zdanie po zdaniu za treścią arkusza: przy każdym jest polecenie z arkusza, a kod pokazuje dokładnie te linie, które je spełniają. Skrypt jest najpierw napisany najprościej, funkcjami mysqli z tabeli 1 arkusza, a na końcu sprawdzony na cenie z przecinkiem, na prawdziwym wstrzyknięciu SQL i zabezpieczony.
To jest wersja podstawowa. W zakładce „Nowoczesny kod (flex, PDO)” jest drugi sposób rozwiązania tego samego arkusza: semantyczne znaczniki, układ bez float, PDO z zapytaniem przygotowanym na górze pliku i przekierowanie po zapisie - obie wersje są zgodne z kluczem.
Baza i zdjęcia pochodzą z oryginalnego archiwum CKE zad2.zip (załącznik do pobrania na stronie arkusza, hasło w treści zadania). Zapytania i zrzuty są policzone na MariaDB 10.4 - tej samej, która jest w XAMPP, 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 (bez treści), 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; egzaminator ocenia spełnienie kryteriów z klucza, nie zgodność z tym kodem.
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 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.
Na pulpicie tworzysz folder nazwany numerem PESEL - w tym rozwiązaniu w jego miejscu stoi imię i nazwisko autora, Bartosz Bryniarski. Archiwum zad2.zip rozpakowujesz do niego, podając hasło #Warzywni@czeK - wielkość liter i znaki # oraz @ mają znaczenie. W środku jest plik dane.sql i dziewięć zdjęć produktów (arbuz.jpg, gruszka.jpg, koper.jpg, marchew.jpg, morela.jpg, owoce.jpg, papaja.jpg, papryka.jpg, ziemniak.jpg).
Arkusz podkreśla, że strona i zdjęcia muszą leżeć w tym samym folderze - skrypt poda w znaczniku obrazu samą nazwę pliku z bazy.
Typowe błędy
Pliki zapisane poza folderem zdającego (np. tylko w htdocs) nie trafią na płytę - a ocenia się wyłącznie to, co jest na płycie.
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
W XAMPP Control Panel klikasz Start przy Apache i MySQL, potem http://localhost/phpmyadmin. Nowa w lewym panelu, nazwa dane2, Utwórz.
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
Zaznacz bazę dane2, karta Import, Wybierz plik → dane.sql, Wykonaj. Powstaje pięć tabel - poza produkty, producenci i rodzaje także hobby i osoby z innego zadania. Po imporcie lista tabel w karcie Struktura - zrzut całego ekranu z paskiem zadań: egzamin.jpg.
W tabeli produkty są nie tylko owoce i warzywa, ale też filmy (rodzaje 7 - 13) - dlatego zapytania filtrują po Rodzaje_id.
Typowe błędy
W tym arkuszu formaty są odwrotne niż zwykle: zrzut importu w JPEG (egzamin.jpg), zrzuty kwerend w PNG (kw1.png - kw4.png). Zły format albo wykadrowany zrzut nie jest oceniany.
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
Karta SQL bazy dane2:
SELECT nazwa, ilosc, opis, cena, zdjecie - pięć kolumn w kolejności z arkusza,
FROM produkty,
WHERE Rodzaje_id = 1 OR Rodzaje_id = 2 - jedna z dwóch wartości: 1 to owoce, 2 to warzywa. To samo da WHERE Rodzaje_id IN (1, 2).
Wynik: 8 wierszy, od gruszki do papryki - dokładnie jak w kluczu. Zapytanie zapisujesz w pliku kwerendy.txt, a cały ekran - jako kw1.png. Z tego zapytania skrypt zbuduje bloki produktów.
Typowe błędy
WHERE Rodzaje_id = 1 AND Rodzaje_id = 2 daje pusty wynik - pole nie może mieć dwóch wartości naraz.
kwerendy.txtTekstnowy plik
-- Zapytanie 1SELECT nazwa, ilosc, opis, cena, zdjecie FROM produkty WHERE Rodzaje_id = 1 OR Rodzaje_id = 2;
Podgląd strony po tym kroku
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
SELECT produkty.nazwa, producenci.nazwa - obie tabele mają kolumnę nazwa, więc przed nazwą kolumny stoi nazwa tabeli,
FROM produkty JOIN producenci ON produkty.Producenci_id = producenci.id - relacja: każdy produkt łączy się z producentem, którego id zapisano w polu Producenci_id,
WHERE produkty.ilosc < 10 AND produkty.Rodzaje_id = 1 - oba warunki naraz.
Wynik: 2 wiersze - arbuz (FruitFarm) i papaja (Owoce-rolnik), zgodnie z kluczem. Koper też ma 2 sztuki, ale jest warzywem. Zrzut: kw2.png.
Typowe błędy
Samo SELECT nazwa, nazwa przy dwóch tabelach kończy się błędem „Column 'nazwa' in field list is ambiguous” - baza nie wie, o którą tabelę chodzi.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT nazwa, ilosc, opis, cena, zdjecie FROM produkty WHERE Rodzaje_id = 1 OR Rodzaje_id = 2;-- Zapytanie 2SELECT produkty.nazwa, producenci.nazwa FROM produkty JOIN producenci ON produkty.Producenci_id = producenci.id WHERE produkty.ilosc < 10 AND produkty.Rodzaje_id = 1;
Podgląd strony po tym kroku
Krok 6: Zapytanie 3: użytkownik anna
Kryteria:
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 'anna'@'localhost' - konto bazy danych: nazwa i host, z którego wolno się logować (tu: ten sam komputer),
IDENTIFIED BY '@nna23' - hasło w apostrofach.
phpMyAdmin odpowiada „MySQL zwrócił pusty wynik” - zrzut kw3.png z widocznym zapytaniem. Klucz wymaga widocznego hasła, więc konto trzeba założyć zapytaniem w karcie SQL, a nie kreatorem Konta użytkowników.
Typowe błędy
Drugie uruchomienie tego samego zapytania kończy się błędem „Operation CREATE USER failed” - konto już istnieje. Na zrzut wystarczy pierwsze, udane wykonanie.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT nazwa, ilosc, opis, cena, zdjecie FROM produkty WHERE Rodzaje_id = 1 OR Rodzaje_id = 2;-- Zapytanie 2SELECT produkty.nazwa, producenci.nazwa FROM produkty JOIN producenci ON produkty.Producenci_id = producenci.id WHERE 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
Najpierw dwie wartości z innych tabel: w rodzaje owoce mają id1, a w producenci warzywa-rolnik ma id4 (karta Przeglądaj obu tabel). Potem:
INSERT INTO produkty (Rodzaje_id, Producenci_id, nazwa, ilosc, opis, cena, zdjecie) - lista kolumn bez id: klucz główny ma AUTO_INCREMENT, więc baza nada go sama,
VALUES (1, 4, 'owoc1', 10, '', 9.99, 'owoce.jpg') - wartości w tej samej kolejności; puste pole opisu to '', a cena z kropką: w SQL przecinek oddziela wartości.
phpMyAdmin odpowiada „1 wiersz wstawiony” - zrzut kw4.png. Nowy produkt dostał id 43 i jako owoc pojawi się na stronie.
Typowe błędy
9,99 zamiast 9.99 to dla SQL dwie wartości (9 i 99) - baza zgłosi „Column count doesn't match value count”.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT nazwa, ilosc, opis, cena, zdjecie FROM produkty WHERE Rodzaje_id = 1 OR Rodzaje_id = 2;-- Zapytanie 2SELECT produkty.nazwa, producenci.nazwa FROM produkty JOIN producenci ON produkty.Producenci_id = producenci.id WHERE 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');
Podgląd strony po tym kroku
Etap 2: Grafika i strona HTML
Kroki 8-21 · po tym etapie masz 18 z 37 kryteriów (pkt)
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 - jest szerszy niż 3:2. Wysokość zostaje, a szerokość dla proporcji 3:2 to 297 × 3 / 2 = 445,5, czyli 446 px. W GIMP-ie:
narzędzie Kadrowanie z zaznaczoną opcją Stałe: Proporcje i wartością 3:2, zaznaczenie na całą wysokość, przesunięte na środek (z każdej strony odpada ok. 27 px),
Obraz → Skaluj obraz, z zamkniętym łańcuchem szerokość 300 - wysokość sama przeliczy się na 200,
Plik → Nadpisz arbuz.jpg.
Pozostałe zdjęcia mają już 300 × 200 px i zostają bez zmian (R.2.1).
Typowe błędy
Samo skalowanie do 300 × 200 px bez kadrowania spłaszcza arbuz (500 × 297 to nie 3:2), a skalowanie z łańcuchem bez kadrowania daje 300 × 178 px - klucz przyjmuje 300 × 200 ± 2 px.
Podgląd strony po tym kroku
Krok 9: Plik sklep.php
Kryteria:
R.2.2 Witrynę internetową zapisano w pliku PHP o nazwie sklep
ok. 1 min
Polecenie z arkusza
Składa się ze strony o nazwie sklep.php
Plik sklep.php w folderze zdającego, obok zdjęć. Rozszerzenie .php, bo dojdzie skrypt - na razie szkielet HTML5: <!DOCTYPE html> i lang="pl" (R.2.2).
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
Arkusz stylów w pliku o nazwie styl2.css prawidłowo połączony z kodem strony
<link rel="stylesheet" href="styl2.css"> w <head>. Plik stylów powstanie po HTML-u (R.3.5).
Typowe błędy
Klucz uznaje R.3.5 tylko wtedy, gdy całe formatowanie pochodzi z tego pliku i każdy selektor trafia w swój blok (kropka zamiast # odbiera punkt).
R.2.5 Wygląd strony zdefiniowano dzieląc ją na bloki: dwa bloki banera, główny i stopka. Zastosowano znaczniki sekcji
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
Cztery bloki w kolejności z obrazu 2: dwa banery i blok główny jako <section> z identyfikatorami banner-left, banner-right i main (po angielsku - arkusz nazw nie narzuca), na dole <footer>. Bloki produktów wygeneruje skrypt w bloku głównym. Obok siebie banery ustawi CSS (R.2.5).
Zawartość stopki: Formularz wysyłający dane do tego samego pliku metodą POST
<form method="post">bez atrybutu action wysyła dane do adresu, z którego został wczytany - czyli do sklep.php. Metoda POST przesyła pola w treści żądania; w PHP odczytasz je z tablicy $_POST (R.3.3).
Drugie pole, name="cena". Typ text, jak w arkuszu - uczeń może więc wpisać cenę z przecinkiem; skrypt będzie musiał sobie z tym poradzić (krok w dobrych praktykach).
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. 1 min
Polecenie z arkusza
Przycisk „Dodaj produkt” wysyłający dane do skryptu 2
<input type="submit" value="Dodaj produkt"> - przycisk typu submit wysyła formularz (R.3.4). Formularz ma teraz dwa pola edycyjne i przycisk, z zamknięciem </form> (R.2.9).
Typowe błędy
<input type="button"> wygląda tak samo, ale niczego nie wysyła - brak punktu R.3.4.
Napis o treści „Stronę wykonał: ”, dalej wstawiony numer PESEL zdającego lub w przypadku jego braku inny numer, którym został podpisany arkusz
Na egzaminie po dwukropku wpisujesz swój numer PESEL; w tym rozwiązaniu w jego miejscu stoi Bartosz Bryniarski. Arkusz mówi o napisie, nie o akapicie, więc tekst stoi wprost w stopce - formularz jest blokiem, więc napis i tak zaczyna się w nowej linii, jak na obrazie 2.
Dla banera prawego: kolor tła #00600F, biały kolor czcionki, szerokość 25%, wysokość 90 px
75% + 25% = 100%, więc oba banery zmieszczą się w jednym rzędzie - bez obramowań i padding, które by je poszerzyły (R.4.5). Biały jest tekst listy; odnośnik „soki” zostaje w kolorze odnośnika, jak na obrazie 2.
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)
Blok ma stałą wysokość 600 px, a produktów jest więcej, niż się w niej zmieści. overflow: scroll dodaje paski przewijania - produkty przewija się wewnątrz bloku, a stopka zostaje na swoim miejscu (R.4.7). Wysokość banerów i bloku głównego jest ustawiona (R.4.4).
Typowe błędy
overflow: hidden ucina produkty bez możliwości przewinięcia - klucz przyjmuje scroll albo auto.
R.4.10 Ustawiono styl bloku produktu: marginesy zewnętrzne 10px (margin), obramowanie linia ciągła 1px w kolorze #00600F, szerokość 300px
ok. 1 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
Bloków produktów będzie kilka, więc selektor klasy.product. Szerokość 300 px równa się szerokości zdjęć (R.4.10). Bloki pojawią się dopiero ze skryptem.
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. 3 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
oba banery dostają float: left i stają obok siebie (75% + 25%),
blok główny z clear: both zaczyna się pod nimi,
bloki produktów też pływają w lewo: każdy zajmuje 300 + 2 + 20 = 322 px, więc na ekranie szerokości ok. 1150 px mieszczą się po trzy w rzędzie, jak na obrazie 2,
stopka nie potrzebuje clear: blok główny ma overflow, więc pływające produkty zostają w jego wnętrzu.
Klucz przy R.3.1 nie uwzględnia bloków produktów - liczy się układ banerów, bloku głównego i stopki.
Kroki 31-40 · po tym etapie masz 36 z 37 kryteriów (pkt)
Krok 31: Połączenie z bazą dane2
Kryteria:
R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy dane2
ok. 3 min
Polecenie z arkusza
Skrypt łączy się z serwerem bazy danych na localhost, użytkownik root bez hasła, baza danych o nazwie dane2
Na samej górze sklep.php, przed <!DOCTYPE html>, pierwszy blok PHP. Funkcja mysqli_connect z tabeli 1 arkusza: serwer localhost, użytkownik root, puste hasło '' i nazwa bazy dane2 - czwarty argument od razu wybiera bazę (R.5.1). Połączenie trafia do zmiennej $connection.
Dlaczego na górze: formularz stoi w stopce, czyli po bloku produktów. Gdyby zapis produktu był przy formularzu, nowy produkt pojawiłby się na liście dopiero po odświeżeniu strony. W tym bloku stanie więc skrypt 2. Od tego kroku podgląd to wynik prawdziwego uruchomienia w PHP 8.2; stronę sprawdzasz przez http://localhost/….
Krok 32: 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
Blok PHP w <section id="main">:
mysqli_query z zapytaniem 1 z kwerendy.txt (R.5.3),
while ($row = mysqli_fetch_row($result)) - mysqli_fetch_row zwraca kolejny wiersz jako tablicę numerowaną od zera, a gdy wierszy zabraknie - false, i pętla się kończy (R.5.5),
w każdym obrocie echo wypisuje znacznik sekcji z klasą product - tą samą, którą ma reguła w CSS.
W bazie po zapytaniu 4 jest dziewięć owoców i warzyw (osiem z importu i owoc1), więc na stronie jest 9 bloków. Na razie puste - widać same ramki.
R.2.8 Przynajmniej jeden obraz ma tekst alternatywny "warzywniak" (umieszczony skryptem lub HTML)
ok. 2 min
Polecenie z arkusza
Obraz o nazwie pobranej z bazy danych i tekście alternatywnym „warzywniak”
Kolumna zdjecie jest piąta w zapytaniu, więc to $row[4]; trafia do src. Tekst alternatywny jest stały: warzywniak (R.2.8). Cudzysłowy atrybutów wewnątrz napisu w cudzysłowie trzeba poprzedzić ukośnikiem: \". Zdjęcia leżą obok strony, więc sama nazwa pliku wystarczy jako ścieżka.
Krok 37: Skrypt 1: cena w nagłówku drugiego stopnia
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
ok. 2 min
Polecenie z arkusza
Nagłówek drugiego stopnia o treści „<cena> zł”, gdzie pola umieszczone w nawiasach < > oznaczają dane pobrane z bazy danych
$row[3] - kolumna cena, za nią spacja i „zł”. Blok produktu ma komplet: zdjęcie, nazwę w <h5>, opis i stan w osobnych akapitach i cenę w <h2> - wszystko z bazy (R.5.4). Pierwszy blok: gruszka, „opis: Klaps”, „na stanie: 20”, „2.56 zł”, jak na obrazie 2.
Działanie skryptu 2: Jeśli wypełniono formularz, pobiera z niego dane
W bloku na górze pliku, pod połączeniem:
!empty($_POST['nazwa']) && !empty($_POST['cena']) - prawdziwe tylko wtedy, gdy oba pola wysłano i nie są puste. Przy zwykłym wejściu na stronę klucza w $_POST nie ma, a empty nie zgłasza wtedy ostrzeżenia,
$nazwa i $cena - wartości pól; kluczem w tablicy $_POST jest atrybut name pola.
Podgląd: formularz wysłany z nazwą „jablko” i ceną „3.50” - skrypt odczytał dane, ale jeszcze nigdzie ich nie zapisał.
Krok 39: Skrypt 2: zapis produktu zmodyfikowanym zapytaniem 4
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 z kwerendy.txt, w którym w miejsce 'owoc1' stoi '$nazwa' (napis, więc w apostrofach), a w miejsce 9.99 - $cena (liczba, bez apostrofów). Reszta bez zmian: owoce (1), warzywa-rolnik (4), ilość 10, pusty opis, zdjęcie owoce.jpg. Całe zapytanie jest w cudzysłowie, więc PHP podstawia wartości zmiennych.
Sprawdzenie, jak każe klucz (R.5.6): po wpisaniu „jablko” i „3.50” w tabeli produkty przybył wiersz z tą nazwą i ceną. Zapis stoi na górze pliku, przed skryptem 1, więc nowy produkt widać od razu - na końcu listy.
Typowe błędy
Nazwa z literami „ł”, „ś” itp. (np. „jabłko”) kończy się błędem „Incorrect string value”: tabele z archiwum mają kodowanie latin1, które tych liter nie zna, a MariaDB z XAMPP działa w trybie ścisłym. To cecha bazy z archiwum - klucz sprawdza import pliku bez zmian, więc tabeli nie przerabiaj, a zapis sprawdź nazwą bez polskich liter.
R.5.2 Ostatnią operacją na bazie jest jej zamknięcie (zgodne ze składnią)
ok. 1 min
Polecenie z arkusza
Po wykonaniu operacji na bazie danych skrypt zamyka połączenie z serwerem.
mysqli_close($connection) po pętli skryptu 1. Skrypt 2 wykonał się wcześniej, na górze pliku, a formularz w stopce nie korzysta już z bazy - więc zamknięcie jest ostatnią operacją na bazie (R.5.2). Strona spełnia wszystkie kryteria arkusza. Dalej trzy kroki o zabezpieczeniach - klucz ich nie wymaga.
Typowe błędy
mysqli_close w górnym bloku, zaraz po zapisie, sprawia, że skrypt 1 dostaje zamknięte połączenie i PHP kończy się błędem.
Kroki 41-43 · po tym etapie masz 36 z 37 kryteriów (pkt)
Krok 41: Zabezpieczenie: cena z przecinkiem i walidacja liczby
ok. 3 min
Pole ceny jest zwykłym polem tekstowym, a po polsku cenę pisze się z przecinkiem. Sprawdzone na wersji z poprzednich kroków: cena 4,50 zamienia zapytanie w VALUES (1, 4, 'jablko', 10, '', 4,50, 'owoce.jpg') - dla SQL to osiem wartości zamiast siedmiu („Column count doesn't match value count”). W PHP 8.1 i nowszych kończy się to wyjątkiem mysqli_sql_exception i zamiast strony widać „Fatal error”, a w PHP 7.4 z XAMPP produkt po prostu się nie zapisuje - bez żadnego komunikatu.
Dwie linie naprawiają ten przypadek:
str_replace(',', '.', ...) zamienia przecinek na kropkę, a trim zdejmuje spacje z brzegów,
is_numeric($cena) przepuszcza do zapytania tylko liczbę - napis w rodzaju „tanio” albo fragment kodu SQL w polu ceny nie dotrze już do bazy.
Stara linia zostaje w komentarzu. Podgląd: cena „4,50” zapisała się jako 4.5.
Krok 42: Zabezpieczenie: zapytanie przygotowane dla INSERT
ok. 4 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
Walidacja ceny nie chroni nazwy - ta nadal trafia do zapytania wprost (SQL injection). Sprawdzone na wersji z poprzedniego kroku:
nazwa x', 10, '', 5, 'owoce.jpg'), (1, 4, 'gratis z ceną 0.00 zamyka napis i dokleja do VALUESdrugi wiersz - w sklepie pojawia się dodatkowy produkt „gratis” za 0 zł, którego nikt nie dodawał,
zwykła nazwa z apostrofem, np. Jablko 'Ligol', rozbija składnię zapytania - PHP 8.1 i nowsze zgłaszają wyjątek mysqli_sql_exception i strona się nie wyświetla.
Lekarstwo to zapytanie przygotowane: mysqli_prepare ze znakami ? w miejscu nazwy i ceny, mysqli_stmt_bind_param z typami 'sd' (napis i liczba zmiennoprzecinkowa) i mysqli_stmt_execute. Baza dostaje treść zapytania i dane osobno, więc dane nigdy nie staną się kodem SQL. Podgląd: ten sam „atak” zapisał się jako jeden produkt o dziwnej nazwie - bez „gratisu”. Stara linia zostaje w komentarzu.
Krok 43: Zabezpieczenie: htmlspecialchars przy wyświetlaniu nazwy i opisu
ok. 3 min
Polecenie z arkusza
Nagłówek piątego stopnia z nazwą pobraną z bazy
Zapytanie przygotowane chroni bazę, ale nazwa z formularza wraca potem na stronę - i każdy, kto otworzy sklep, zobaczy ją jako kod HTML. Sprawdzone: produkt o nazwie <b>PROMOCJA</b> wyświetlał się jako pogrubiony napis. Tak samo przeszedłby znacznik <script> - kod JavaScript uruchamiałby się u każdego klienta sklepu (stored XSS).
Funkcja htmlspecialchars zamienia <, >, & i cudzysłowy na encje (np. < na <), więc przeglądarka pokazuje znaki dosłownie. Stosuje się ją przy wypisywaniu tekstów, które mogły przyjść od użytkownika - tutaj nazwy i opisu. Ilość i cena to liczby z kolumn typu double, więc kodu HTML w nich nie ma. Podgląd: nazwę widać dosłownie, ze znacznikami.
Krok 44 · po tym etapie masz 36 z 37 kryteriów (pkt)
Krok 44: 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 wpisujesz nazwę przeglądarki, w której sprawdzasz stronę przez http://localhost/….
W folderze z numerem zdającego muszą być: arbuz.jpg, egzamin.jpg, gruszka.jpg, koper.jpg, kw1.png - kw4.png, kwerendy.txt, marchew.jpg, morela.jpg, owoce.jpg, papaja.jpg, papryka.jpg, przeglądarka.txt, sklep.php, styl2.css i ziemniak.jpg. Nagraj płytę, sprawdź jej odczyt, opisz ją numerem PESEL i zostaw na stanowisku.
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.