INF.03-01-22.06-SGCzerwiec 2022 · 32 kroki · ok. 65 min · 35 kryteriów z klucza
Rozwiązanie krok po kroku: Portal dla wędkarzy (Nowoczesny kod (grid, PDO))
To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza wedkowanie z czterema zapytaniami, zdjęcie przeskalowane do 400 px i strona wedkuj.php z listą ryb rzecznych i tabelą ryb drapieżnych - i tak samo spełnia każde kryterium klucza. Inna jest droga:
siatka CSS zamiast float: bloki stoją w znaczniku <main> z kolumnami 55% i 45%, a blok prawy zajmuje dwa wiersze siatki. Klucz wprost dopuszcza szerokości w grid-template-columns, więc bloki mogą stać w kodzie w naturalnej kolejności - lewe, potem prawy,
zmienne CSS dla kolorów z arkusza, tabela z częściami <thead> i <tbody>, odnośnik do kwerend w akapicie zamiast po <br>,
PHP na PDO: oba zapytania strona wykonuje na górze pliku wedkuj.php, przed HTML-em, w bloku try, a w HTML-u zostają tylko pętle i wypisywanie przez funkcję e() (htmlspecialchars),
komunikat przy awarii bazy zamiast ostrzeżeń PHP, zapytanie 3 z aliasami tabel.
Na końcu krótkie porównanie z wersją podstawową (zakładka obok). Baza i zdjęcie pochodzą z prawdziwego archiwum pliki1, a podgląd od kroków z PHP to wynik prawdziwego uruchomienia kodu w PHP na bazie z arkusza.
Jak liczą się punkty. Klucz ma 35 pozycji (R.1.1 - R.5.6), każda za 1 punkt; pozycja R.2.10 jest w opublikowanym kluczu pusta (bez treści kryterium). Próg zdania to 75%, czyli 27 punktów. Przy każdym kroku jest napisane, które kryteria spełnia.
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 35 kryteriów (pkt)
Krok 1: Baza danych wedkowanie
Kryteria:
R.1.1 Wykonano import tabel do bazy danych wedkowanie czynność udokumentowano plikiem o nazwie import w formacie PNG
ok. 2 min
Polecenie z arkusza
Utwórz bazę danych o nazwie wedkowanie.
Rozpakuj pliki1.zip z pulpitu (hasło WedkoWaniE&) do folderu zdającego: baza.sql i ryba1.jpg. W phpMyAdmin Nowa → wedkowanie (bez „ę”) albo zapytaniem:
CREATE DATABASE wedkowanie CHARACTER SET utf8mb4 COLLATE utf8mb4_polish_ci;
Arkusz nie podaje porównywania, więc wybór należy do Ciebie. Tabele z baza.sql mają zestaw znaków (latin1) zapisany we własnej definicji, więc ustawienie bazy ich nie zmienia.
Typowe błędy
Skrypt łączy się z bazą po nazwie - wędkowanie z polską literą to inna baza i R.5.1 przepada.
Krok 2: Import tabel z pliku baza.sql
Kryteria:
R.1.1 Wykonano import tabel do bazy danych wedkowanie czynność udokumentowano plikiem o nazwie import w formacie PNG
ok. 2 min
Polecenie z arkusza
Do bazy wedkowanie zaimportuj tabele z pliku baza.sql z rozpakowanego archiwum.
Import → baza.sql → Wykonaj. Zadanie korzysta z trzech tabel z obrazu 1: ryby (styl_zycia: 1 - drapieżnik, 2 - ryba spokojnego żeru), okres_ochronny (wymiar_ochronny w cm) i lowisko (rodzaj: 1 morze, 2 jezioro, 3 rzeka, 4 zalew, 5 staw). Obie mniejsze tabele wskazują rybę kluczem obcym Ryby_id. Plik zawiera też tabele samochody i typy - w zadaniu ich nie używasz.
Krok 3: Zrzut ekranu import.png
Kryteria:
R.1.1 Wykonano import tabel do bazy danych wedkowanie czynność udokumentowano plikiem o nazwie import w formacie PNG
ok. 2 min
Polecenie z arkusza
Wykonaj zrzut ekranu po imporcie. Zrzut zapisz w formacie PNG i nazwij import. 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.
Print Screen, cały ekran z paskiem zadań, zapis jako import.png - w PNG, a zrzuty zapytań będą w JPEG.
Typowe błędy
Wykadrowany zrzut albo import.jpg zamiast import.png - R.1.1 przepada.
Krok 4: Zapytanie 1: ryby drapieżne
Kryteria: ,
R.1.2 Zapisano plik o nazwie kwerendy.txt zawierający co najmniej jedno zapytanie SQL, wynikające z treści zadania
R.1.3 Utworzono zapytanie 1 wybierające jedynie pola id, nazwa i wystepowanie z tabeli Ryby dla ryb drapieżnych. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT id, nazwa, wystepowanie FROM ryby WHERE styl_zycia=1; oraz wynik działania udokumentowano zrzutem, na którym jest widocznych dokładnie 5 wierszy dla id = 1, 3, 4, 5, 6, widoczne jedynie kolumny id, nazwa, wystepowanie
ok. 2 min
Polecenie z arkusza
Zapytanie 1: wybierające jedynie pola id, nazwa i wystepowanie z tabeli Ryby dla ryb drapieżnych.
„Drapieżne” to w bazie styl_zycia = 1 (opis w treści zadania). Każda klauzula w osobnej linii - łatwiej przeczytać i poprawić. Wynik: 5 ryb o identyfikatorach 1, 3, 4, 5, 6 - jak w kluczu. Zrzut kw1.jpg, zapytanie do kwerendy.txt. To zapytanie wyśle Skrypt 2.
Krok 5: Zapytanie 2: wymiar ochronny poniżej 30 cm
Kryteria:
R.1.4 Utworzono zapytanie 2 wybierające jedynie pola Ryby_id oraz wymiar_ochronny z tabeli Okres_ochronny dla ryb, których wymiar ochronny jest mniejszy niż 30 cm. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT Ryby_id, wymiar_ochronny FROM okres_ochronny WHERE wymiar_ochronny < 30; oraz wynik działania udokumentowano zrzutem, na którym są widoczne dokładnie 4 wiersze: 4 15; 6 0; 7 0; 8 25
ok. 1 min
Polecenie z arkusza
Zapytanie 2: wybierające jedynie pola Ryby_id oraz wymiar_ochronny z tabeli Okres_ochronny dla ryb, których wymiar ochronny jest mniejszy niż 30 cm.
„Mniejszy niż” to < (mniejszy lub równy byłby <=). Wynik: 4 wiersze - 4/15, 6/0, 7/0, 8/25 - jak w kluczu. Zrzut kw2.jpg.
R.1.5 Utworzono zapytanie 3 wybierające jedynie pole nazwa z tabeli Ryby oraz odpowiadające tej nazwie pola akwen i wojewodztwo z tabeli Lowisko dla łowisk, które są rzekami. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa, akwen, wojewodztwo FROM ryby JOIN lowisko ON ryby.id = lowisko.Ryby_id WHERE rodzaj = 3; (możliwe również INNER JOIN lub porównanie kluczy po WHERE) oraz wynik działania udokumentowano zrzutem, na którym są widoczne dokładnie 2 rekordy z danymi: Szczupak Warta-Obrzycko Wielkopolskie; Leszcz Przemsza k.Okradzinowa Slaskie
ok. 3 min
Polecenie z arkusza
Zapytanie 3: wybierające jedynie pole nazwa z tabeli Ryby oraz odpowiadające tej nazwie pola akwen i wojewodztwo z tabeli Lowisko dla łowisk, które są rzekami. Zapytanie wykorzystuje relację.
aliasy tabelr i l - krótki przedrostek mówi, z której tabeli pochodzi każde pole, także w warunku,
JOIN ... ON l.Ryby_id = r.id - relacja z obrazu 1: łowisko wskazuje swoją rybę,
WHERE l.rodzaj = 3 - tylko rzeki.
Wynik: 2 wiersze - Szczupak (Warta-Obrzycko, Wielkopolskie) i Leszcz (Przemsza k. Okradzinowa, Slaskie). Zrzut kw3.jpg. To zapytanie wyśle Skrypt 1.
kwerendy.txtTekstzmienione: 6 linii
-- Zapytanie 1SELECT id, nazwa, wystepowanieFROM rybyWHERE styl_zycia = 1;-- Zapytanie 2SELECT Ryby_id, wymiar_ochronnyFROM okres_ochronnyWHERE wymiar_ochronny < 30;-- Zapytanie 3SELECT r.nazwa, l.akwen, l.wojewodztwoFROM ryby AS rJOIN lowisko AS l ON l.Ryby_id = r.idWHERE l.rodzaj = 3;
Podgląd strony po tym kroku
Krok 7: Zapytanie 4: kolumna dobowy_limit 0-255
Kryteria:
R.1.6 Utworzono zapytanie 4 dodające do tabeli Ryby kolumnę dobowy_limit typu numerycznego, o rozmiarze pozwalającym na wpisanie jedynie liczb naturalnych z przedziału <0, 255> (tinyint unsigned). W pliku z kwerendami lub na zrzucie istnieje zapis np.: ALTER TABLE ryby ADD dobowy_limit TINYINT UNSIGNED; (jedynie typ TINYINT jest poprawny, błędem jest zapis UNSIGNED przed typem) oraz wynik działania udokumentowano zrzutem, na którym jest widoczna wykonana kwerenda i komunikat lub struktura tabeli z nazwą nowego pola
ok. 2 min
Polecenie z arkusza
Zapytanie 4: dodające do tabeli Ryby kolumnę dobowy_limit typu numerycznego, o rozmiarze pozwalającym na wpisanie jedynie liczb z przedziału <0, 255>.
Typ dobierasz do zakresu: TINYINT zajmuje 1 bajt, czyli 256 wartości; bez znaku (UNSIGNED) to dokładnie 0-255. Klucz przyjmuje tylko ten typ i tę kolejność słów (sprawdzone na bazie z archiwum: phpMyAdmin pokazuje tinyint(3) UNSIGNED). Zapytanie wykonaj raz - drugie zgłosi, że kolumna już istnieje. Zrzut kw4.jpg: kwerenda z komunikatem albo struktura tabeli z nowym polem.
Typowe błędy
UNSIGNED TINYINT (odwrotna kolejność) to błąd składni; INT pozwala na dużo większe liczby, a TINYINT bez UNSIGNED na zakres -128..127 - klucz nie uzna żadnego z nich.
Kroki 8-17 · po tym etapie masz 15 z 35 kryteriów (pkt)
Krok 8: ryba1.jpg: szerokość 400 px
Kryteria:
R.2.1 Grafikę w pliku o nazwie ryba1.jpg przeskalowano z zachowaniem proporcji do szerokości 400 px, odpowiadająca temu wysokość wynosi 320 px (± 2 px)
ok. 2 min
Polecenie z arkusza
Przygotowanie grafiki: Plik ryba1.jpg, wypakowany z archiwum, należy przeskalować z zachowaniem proporcji tak, aby jego szerokość wynosiła dokładnie 400 px.
Grafika jest taka sama jak w wersji podstawowej. Zdjęcie ma 600 × 480 px. W GIMP-ie Obraz → Skaluj obraz, łańcuch proporcji zamknięty, szerokość 400 - wysokość wyjdzie 320 px (klucz R.2.1: 320 ± 2). Eksport pod tą samą nazwą.
Podgląd strony po tym kroku
Krok 9: Plik wedkuj.php: HTML5, język, kodowanie
Kryteria:
R.2.2 Witrynę internetową zapisano w pliku wedkuj.php oraz zapisano jawnie standard kodowania polskich znaków i deklarację HTML5 <!DOCTYPE HTML> oraz zadeklarowano dla witryny język polski
ok. 2 min
Polecenie z arkusza
Cechy witryny: Składa się ze strony o nazwie wedkuj.php zapisanej w języku HTML5. Zadeklarowany język dla witryny: polski. Jawnie zastosowany właściwy standard kodowania polskich znaków.
<!DOCTYPE html>, <html lang="pl"> i <meta charset="UTF-8"> jako pierwszy element <head> - razem R.2.2. Plik ma rozszerzenie .php, bo dojdzie do niego kod PHP; na egzaminie leży w htdocs XAMPP-a i otwierasz go przez http://localhost/....
R.3.3 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl_1.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 styl_1.css prawidłowo połączony z kodem strony.
Nazwa z podkreśleniem i cyfrą: styl_1.css. Całe formatowanie pochodzi z tego pliku - bez atrybutów style w HTML-u (R.3.3).
Typowe błędy
styl.css zamiast styl_1.css odcina wszystkie style (R.3.3 i cały rezultat R.4); to samo przy jednym selektorze, który nie trafia w blok (np. kropka zamiast #).
Krok 12: Bloki: baner, dwa lewe, prawy w main, stopka
Kryteria:
R.2.4 Układ strony zdefiniowano dzieląc ją na bloki: banera, dwa bloki lewe, prawy i stopkę. Zastosowano znaczniki sekcji
ok. 2 min
Polecenie z arkusza
Podział strony na bloki: na górze blok banera, poniżej po lewej stronie dwa bloki lewe, obok jeden blok prawy, na dole blok stopki. 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.
Baner to <header>, stopka <footer>. Trzy bloki stoją we wspólnym <main> - to główna treść strony i zarazem pojemnik siatki:
pierwszy lewy <section id="rivers"> - ryby rzeczne,
drugi lewy <section id="predators"> - ryby drapieżne,
prawy <aside id="photo"> - zdjęcie i odnośnik, czyli treść dodatkowa obok głównej.
Bloki stoją w kodzie w kolejności czytania: najpierw lewe, potem prawy. W wersji z float blok prawy musiał stać przed lewymi, żeby zaczynał się od góry - siatka ustawi go na swoim miejscu niezależnie od kolejności w kodzie (R.2.4).
Zawartość pierwszego bloku lewego: Nagłówek trzeciego stopnia o treści: „Ryby zamieszkujące rzeki”. Lista numerowana (uporządkowana) wypełniona przez skrypt 1.
<h3> i pusta <ol> - elementy listy wypisze Skrypt 1.
Krok 15: Drugi blok lewy: tabela z wierszem nagłówkowym
Kryteria:
R.2.8 Pierwszy wiersz tabeli zawiera komórki nagłówkowe o treści, kolejno: L.p., Gatunek, Występowanie, zastosowano znacznik <th>
ok. 2 min
Polecenie z arkusza
Zawartość drugiego bloku lewego: Nagłówek trzeciego stopnia o treści: „Ryby drapieżne naszych wód”. Tabela o trzech kolumnach wypełniona danymi. Pierwszy wiersz zawiera komórki nagłówkowe o treści, kolejno: „L.p.”, „Gatunek”, „Występowanie”.
<thead> z pierwszym wierszem: trzy komórki nagłówkowe <th> (R.2.8). scope="col" mówi czytnikowi ekranu, że nagłówek opisuje kolumnę - przy każdej komórce danych odczyta jej nazwę,
<tbody> - pusta część na wiersze z danymi, które wypisze Skrypt 2.
Podział na <thead> i <tbody> nie zmienia wyglądu, a oddziela w kodzie nagłówek od danych.
R.2.9 W bloku prawym umieszczono obraz ryba1.jpg wraz z tekstem alternatywnym "Sum"
R.3.2 Po wybraniu odnośnika "Pobierz kwerendy" otwiera się lub pobiera plik kwerendy.txt
ok. 2 min
Polecenie z arkusza
Zawartość bloku prawego: Obraz ryba1.jpg z tekstem alternatywnym o treści: „Sum”. W kolejnej linii odnośnik do pliku kwerendy.txt z treścią: „Pobierz kwerendy”.
Obraz z tekstem alternatywnym z arkusza (R.2.9). „W kolejnej linii” daje tu akapit wokół odnośnika - blok zawsze zaczyna nową linię, więc <br> nie jest potrzebny. Odnośnik może prowadzić do dowolnego pliku: href="kwerendy.txt" - przeglądarka otworzy plik tekstowy albo go pobierze (R.3.2).
Typowe błędy
kwerendy.txt musi leżeć w tym samym folderze co strona - inaczej odnośnik prowadzi donikąd (R.3.2).
R.2.5 W banerze zapisano nagłówek h1: "Portal dla wędkarzy" oraz w blokach lewych przynajmniej jeden nagłówek h3: "Ryby zamieszkujące rzeki" lub "Ryby drapieżne naszych wód" oraz w stopce paragraf o treści: "Stronę wykonał:" z numerem zdającego
ok. 1 min
Polecenie z arkusza
Zawartość stopki: akapit (paragraf) o treści: „Stronę wykonał: ”, oraz zawierający numer, którym został podpisany arkusz.
Akapit w <footer>. Ten krok domyka R.2.5: <h1> w banerze, <h3> w blokach lewych i akapit w stopce. Na egzaminie wpisujesz numer, którym podpisany jest arkusz (PESEL) - tu stoi autor rozwiązania.
Kroki 18-25 · po tym etapie masz 26 z 35 kryteriów (pkt)
Krok 18: Plik styl_1.css: zmienne i krój czcionki
Kryteria:
R.4.1 Ustawiono domyślne formatowanie wszystkich selektorów (dla *): krój czcionki Helvetica (w przypadku wymienionych kilku czcionek Helvetica jest pierwsza)
ok. 2 min
Polecenie z arkusza
Styl CSS zdefiniowany w całości w zewnętrznym pliku o nazwie styl_1.css. Domyślne formatowanie wszystkich selektorów: krój czcionki Helvetica.
Trzy kolory z arkusza w zmiennych - #4FB3BF pojawi się w trzech regułach (blok prawy, obramowania tabeli, podświetlenie wiersza), więc zmiana odcienia to jedna linia. Selektor * z Helvetiką na pierwszym miejscu (tak wymaga R.4.1) i krojami zapasowymi: Helvetiki zwykle nie ma w Windows, więc przeglądarka weźmie Arial, a w ostateczności dowolny krój bezszeryfowy.
Dla obu bloków lewych: kolor tła #EFEFEF, szerokość 55%. Dodatkowo dla pierwszego bloku lewego wysokość 150 px oraz dla drugiego – 300 px.
Wspólna reguła z tłem i dwie osobne z wysokościami: razem 150 + 300 = 450 px - tyle, ile blok prawy. Szerokości 55% tu nie ma: w siatce procent przy bloku liczyłby się od jego kolumny (55% z 55% strony), więc trafi do definicji kolumn siatki - klucz R.4.5 wprost to dopuszcza.
R.4.2 Ustawiono kolor tła #00838F dla banera i stopki, #EFEFEF dla obu bloków lewych oraz #4FB3BF dla bloku prawego
R.4.4 Ustawiono wysokość pierwszego bloku lewego 150px, drugiego lewego 300px oraz prawego 450px
R.4.6 Ustawiono wyrównanie tekstu do środka dla banera, stopki i bloku prawego
ok. 1 min
Polecenie z arkusza
Dla bloku prawego: kolor tła #4FB3BF, szerokość 45%, wysokość 450 px, wyrównanie tekstu do środka.
Tło, wysokość i wyśrodkowanie; szerokość 45% - jak przy blokach lewych - w kolumnach siatki. Ta reguła domyka kolory tła (R.4.2), wysokości (R.4.4) i wyśrodkowanie banera, stopki i bloku prawego (R.4.6).
Krok 23: Tabela: obramowania, wyśrodkowanie, marginesy komórek
Kryteria: ,
R.4.7 Ustawiono marginesy wewnętrzne (padding) dla banera i stopki 5px, dla selektorów td i th 4px oraz marginesy zewnętrzne (margin) 20px dla selektora img i auto dla tabeli
R.4.9 Ustawiono dla tabeli i komórki tabeli obramowanie o cechach 1px solid #4FB3BF, obramowanie połączone border-collapse: collapse; lub border-spacing: 0px; (styl dla th nie jest wymagany)
ok. 2 min
Polecenie z arkusza
Dla selektora tabeli, komórek tabeli, komórek nagłówkowych: obramowanie linią ciągłą o szerokości 1 px i kolorze #4FB3BF, obramowanie tabeli i komórek połączone. Dodatkowo dla selektora tabeli: marginesy zewnętrzne wyliczane automatycznie przez przeglądarkę, szerokość 80%. Dodatkowo dla selektora komórek tabeli i nagłówkowych: marginesy wewnętrzne 4 px.
Trzy reguły w kolejności arkusza, każda z selektorami znaczników. border-collapse: collapse łączy ramki tabeli i komórek w jedną linię (R.4.9); margin: auto przy szerokości 80% stawia tabelę na środku bloku. Ten krok domyka marginesy (R.4.7: 5 px banera i stopki, 4 px komórek, 20 px obrazu, auto tabeli).
Krok 25: Układ bloków: siatka z kolumnami 55% i 45%
Kryteria: ,
R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z obrazem 2 w arkuszu (prawidłowo zastosowano właściwości CSS układające bloki na stronie, np. float i clear albo display flex albo grid)
R.4.5 Ustawiono szerokość obu bloków lewych 55%, bloku prawego 45% (width lub grid- template-columns lub flex) oraz selektora table 80%
ok. 3 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.
display: grid na <main> i grid-template-columns: 55% 45% - dwie kolumny o szerokościach z arkusza (R.4.5 razem z 80% tabeli),
grid-template-areas rysuje układ z obrazu 2 słowami: w lewej kolumnie rivers nad predators, w prawej dwa razy photo - blok prawy zajmuje oba wiersze, czyli 150 + 300 = 450 px,
grid-area przypisuje każdy blok do jego obszaru.
Baner i stopka są poza siatką, więc stoją nad nią i pod nią same z siebie - bez float nie ma clear (R.3.1). Wiersze siatki dopasowują się do wysokości bloków, więc wysokości 150, 300 i 450 px z arkusza działają wprost.
Typowe błędy
width: 55% przy bloku w siatce liczy się od kolumny: blok miałby 55% z 55% strony, czyli 30% - szerokości podaje się w grid-template-columns.
Kroki 26-30 · po tym etapie masz 34 z 35 kryteriów (pkt)
Krok 26: Połączenie z bazą na górze pliku i zamknięcie
Kryteria: ,
R.5.1 Skrypt zawiera działające połączenie z serwerem bazy danych i wybór bazy wedkowanie
R.5.2 Ostatnią operacją na bazie jest jej zamknięcie (zgodne ze składnią)
ok. 4 min
Polecenie z arkusza
Wymagania dotyczące skryptów: Napisane w języku PHP. Skrypty łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie wedkowanie. Na końcu jest zamykane połączenie z serwerem.
Oba skrypty pobiorą dane przed HTML-em, w jednym bloku PHP na samej górze wedkuj.php - kod rozmawiający z bazą jest w jednym miejscu, a połączenie zamyka się, zanim przeglądarka dostanie pierwszy znacznik.
new PDO(...) - serwer localhost, baza wedkowanie, kodowanie utf8mb4, użytkownik root bez hasła (R.5.1). ERRMODE_EXCEPTION - błąd bazy rzuca wyjątek, zamiast zwracać false, którego łatwo nie zauważyć; FETCH_ASSOC - wiersze z nazwami kolumn,
$pdo = null - PDO nie ma funkcji „zamknij”: połączenie zamyka się, gdy znika ostatnie odwołanie do obiektu. Stoi na końcu bloku, więc pozostanie ostatnią operacją na bazie (R.5.2),
try / catch (PDOException ...) - gdy baza nie działa, uczeń zobaczy zwykłe zdanie, a nie komunikat PHP ze szczegółami serwera.
PDO zamiast mysqli z tabeli 1 - klucz przyjmuje obie biblioteki (uwaga przy R.5). Od tego kroku podgląd to wynik prawdziwego uruchomienia strony.
wedkuj.phpPHPzmienione: 12 linii
<?phptry {$pdo = new PDO('mysql:host=localhost;dbname=wedkowanie;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$pdo = null; // zamknięcie połączenia - ostatnia operacja na bazie} catch (PDOException $exception) {$databaseError = 'Nie udało się pobrać danych z bazy. Spróbuj odświeżyć stronę za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Wędkowanie</title> <linkrel="stylesheet"href="styl_1.css"></head><body> <header> <h1>Portal dla wędkarzy</h1> </header> <main> <sectionid="rivers"> <h3>Ryby zamieszkujące rzeki</h3> <ol> </ol> </section> <sectionid="predators"> <h3>Ryby drapieżne naszych wód</h3> <table> <thead> <tr> <thscope="col">L.p.</th> <thscope="col">Gatunek</th> <thscope="col">Występowanie</th> </tr> </thead> <tbody> </tbody> </table> </section> <asideid="photo"> <imgsrc="ryba1.jpg"alt="Sum"> <p><ahref="kwerendy.txt">Pobierz kwerendy</a></p> </aside> </main> <footer> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (wedkuj.php)
Krok 27: Skrypt 1: zapytanie 3
Kryteria:
R.5.3 Skrypt 1 wysyła do bazy danych zapytanie 3 lub skrypt 2 wysyła do bazy zapytanie 1
ok. 2 min
Polecenie z arkusza
Skrypt 1: wysyła do bazy danych zapytanie 3.
Zapytanie bez danych od użytkownika, więc wystarczy query. fetchAll() zwraca od razu wszystkie wiersze jako listę tablic z nazwami kolumn: ["nazwa" => "Szczupak", "akwen" => "Warta-Obrzycko", ...]. W catch lista dostaje wartość pustą, żeby HTML niżej nie trafił na niezdefiniowaną zmienną (R.5.3). Na stronie jeszcze nic nie widać.
wedkuj.phpPHPzmienione: 6 linii
<?phptry {$pdo = new PDO('mysql:host=localhost;dbname=wedkowanie;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);// Skrypt 1: zapytanie 3 - ryby rzeczne z łowiskiem$riverFish = $pdo->query('SELECT r.nazwa, l.akwen, l.wojewodztwo FROM ryby AS r JOIN lowisko AS l ON l.Ryby_id = r.id WHERE l.rodzaj = 3' )->fetchAll();$pdo = null; // zamknięcie połączenia - ostatnia operacja na bazie} catch (PDOException $exception) {$riverFish = [];$databaseError = 'Nie udało się pobrać danych z bazy. Spróbuj odświeżyć stronę za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Wędkowanie</title> <linkrel="stylesheet"href="styl_1.css"></head><body> <header> <h1>Portal dla wędkarzy</h1> </header> <main> <sectionid="rivers"> <h3>Ryby zamieszkujące rzeki</h3> <ol> </ol> </section> <sectionid="predators"> <h3>Ryby drapieżne naszych wód</h3> <table> <thead> <tr> <thscope="col">L.p.</th> <thscope="col">Gatunek</th> <thscope="col">Występowanie</th> </tr> </thead> <tbody> </tbody> </table> </section> <asideid="photo"> <imgsrc="ryba1.jpg"alt="Sum"> <p><ahref="kwerendy.txt">Pobierz kwerendy</a></p> </aside> </main> <footer> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Krok 28: Skrypt 1: zdanie o każdej rybie jako element listy
Kryteria: , ,
R.5.4 Skrypt 1 wyświetla każdy zwrócony zapytaniem wiersz według wzoru: "<nazwa_gatunku> pływa w rzece <nazwa_akwenu>, <województwo>”, gdzie nawiasy <> oznaczają wartości pobrane z bazy danych
R.5.5 Skrypt 1 wyświetla każdy zwrócony zapytaniem wiersz jako element listy
R.2.6 W pierwszym bloku lewym umieszczono listę numerowaną z dwoma elementami, zastosowano znaczniki <ol>, <li> (wygenerowaną skryptem lub w HTML)
ok. 3 min
Polecenie z arkusza
Każdy zwrócony zapytaniem wiersz jest wyświetlany w osobnym elemencie listy, według wzoru: ”<nazwa_gatunku> pływa w rzece <nazwa_akwenu>, <wojewodztwo>”, gdzie nawiasy <> oznaczają wartości pobrane z bazy danych.
foreach z dwukropkiem i endforeach - w HTML-u czytelniejsze niż klamry rozrzucone między znacznikami; jeden <li> na każdy wiersz wyniku,
kolumny po nazwach: $fish['nazwa'], $fish['akwen'], $fish['wojewodztwo'] - zmiana kolejności w SELECT niczego nie psuje,
<?= ... ?> to skrót od <?php echo ... ?>, a funkcja e() (na górze pliku) przepuszcza każdy tekst z bazy przez htmlspecialchars - znak < w nazwie łowiska nie rozbije strony ani nie wstrzyknie do niej kodu.
W pierwszym bloku: „Szczupak pływa w rzece Warta-Obrzycko, Wielkopolskie” i „Leszcz pływa w rzece Przemsza k. Okradzinowa, Slaskie” - jak na obrazie 2 (R.5.4, R.5.5, R.2.6).
wedkuj.phpPHPzmienione: 8 linii
<?phpfunctione(string|int|float|null$value): string{returnhtmlspecialchars((string) $value, ENT_QUOTES);}try {$pdo = new PDO('mysql:host=localhost;dbname=wedkowanie;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);// Skrypt 1: zapytanie 3 - ryby rzeczne z łowiskiem$riverFish = $pdo->query('SELECT r.nazwa, l.akwen, l.wojewodztwo FROM ryby AS r JOIN lowisko AS l ON l.Ryby_id = r.id WHERE l.rodzaj = 3' )->fetchAll();$pdo = null; // zamknięcie połączenia - ostatnia operacja na bazie} catch (PDOException $exception) {$riverFish = [];$databaseError = 'Nie udało się pobrać danych z bazy. Spróbuj odświeżyć stronę za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Wędkowanie</title> <linkrel="stylesheet"href="styl_1.css"></head><body> <header> <h1>Portal dla wędkarzy</h1> </header> <main> <sectionid="rivers"> <h3>Ryby zamieszkujące rzeki</h3> <ol><?phpforeach ($riverFishas$fish): ?> <li><?= e($fish['nazwa']) ?> pływa w rzece <?= e($fish['akwen']) ?>, <?= e($fish['wojewodztwo']) ?></li><?phpendforeach; ?> </ol> </section> <sectionid="predators"> <h3>Ryby drapieżne naszych wód</h3> <table> <thead> <tr> <thscope="col">L.p.</th> <thscope="col">Gatunek</th> <thscope="col">Występowanie</th> </tr> </thead> <tbody> </tbody> </table> </section> <asideid="photo"> <imgsrc="ryba1.jpg"alt="Sum"> <p><ahref="kwerendy.txt">Pobierz kwerendy</a></p> </aside> </main> <footer> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (wedkuj.php)
Krok 29: Skrypt 2: wiersze tabeli ryb drapieżnych
Kryteria: ,
R.5.6 Skrypt 2 wyświetla każdy zwrócony zapytaniem wiersz jako osobny wiersz tabeli zgodnie z Obrazem 2 w arkuszu egzaminacyjnym
R.2.7 W drugim bloku lewym umieszczono tabelę o trzech kolumnach, zastosowano znaczniki <table>, <tr>, <td> (wygenerowaną skryptem lub w HTML)
ok. 3 min
Polecenie z arkusza
Skrypt 2: wysyła do bazy danych zapytanie 1. Każdy zwrócony zapytaniem wiersz jest wyświetlany w osobnym wierszu tabeli.
Zapytanie 1 w tym samym bloku na górze pliku, przed zamknięciem połączenia, a w <tbody> pętla: każdy wiersz wyniku to <tr> z trzema komórkami. W kolumnie „L.p.” stoi identyfikator ryby, jak na obrazie 2 (1, 3, 4, 5, 6). Najedź na wiersz - podświetla się (R.5.6, R.2.7).
wedkuj.phpPHPzmienione: 11 linii
<?phpfunctione(string|int|float|null$value): string{returnhtmlspecialchars((string) $value, ENT_QUOTES);}try {$pdo = new PDO('mysql:host=localhost;dbname=wedkowanie;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);// Skrypt 1: zapytanie 3 - ryby rzeczne z łowiskiem$riverFish = $pdo->query('SELECT r.nazwa, l.akwen, l.wojewodztwo FROM ryby AS r JOIN lowisko AS l ON l.Ryby_id = r.id WHERE l.rodzaj = 3' )->fetchAll();// Skrypt 2: zapytanie 1 - ryby drapieżne$predators = $pdo->query('SELECT id, nazwa, wystepowanie FROM ryby WHERE styl_zycia = 1')->fetchAll();$pdo = null; // zamknięcie połączenia - ostatnia operacja na bazie} catch (PDOException $exception) {$riverFish = [];$predators = [];$databaseError = 'Nie udało się pobrać danych z bazy. Spróbuj odświeżyć stronę za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Wędkowanie</title> <linkrel="stylesheet"href="styl_1.css"></head><body> <header> <h1>Portal dla wędkarzy</h1> </header> <main> <sectionid="rivers"> <h3>Ryby zamieszkujące rzeki</h3> <ol><?phpforeach ($riverFishas$fish): ?> <li><?= e($fish['nazwa']) ?> pływa w rzece <?= e($fish['akwen']) ?>, <?= e($fish['wojewodztwo']) ?></li><?phpendforeach; ?> </ol> </section> <sectionid="predators"> <h3>Ryby drapieżne naszych wód</h3> <table> <thead> <tr> <thscope="col">L.p.</th> <thscope="col">Gatunek</th> <thscope="col">Występowanie</th> </tr> </thead> <tbody><?phpforeach ($predatorsas$fish): ?> <tr> <td><?= e($fish['id']) ?></td> <td><?= e($fish['nazwa']) ?></td> <td><?= e($fish['wystepowanie']) ?></td> </tr><?phpendforeach; ?> </tbody> </table> </section> <asideid="photo"> <imgsrc="ryba1.jpg"alt="Sum"> <p><ahref="kwerendy.txt">Pobierz kwerendy</a></p> </aside> </main> <footer> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (wedkuj.php)
Krok 30: Awaria bazy: komunikat zamiast ostrzeżeń
ok. 3 min
Dodatek spoza arkusza - klucz go nie ocenia. Gdy baza nie odpowiada (wyłączony MySQL, złe hasło), blok catch ustawia $databaseError, a nad blokami pojawia się zdanie z role="alert" - czytnik ekranu przeczyta je od razu. Lista i tabela zostają puste, bo zmienne mają w catch wartości puste, a baner, zdjęcie i stopka działają dalej.
W CSS komunikat dostaje grid-column: 1 / -1 - zajmuje całą szerokość siatki, od pierwszej do ostatniej linii kolumn, zamiast wcisnąć się w obszar jednego bloku.
Sprawdzone na prawdziwym PHP ze złym hasłem do bazy: jedno zdanie, bez ostrzeżeń i bez szczegółów serwera.
<?phpfunctione(string|int|float|null$value): string{returnhtmlspecialchars((string) $value, ENT_QUOTES);}try {$pdo = new PDO('mysql:host=localhost;dbname=wedkowanie;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);// Skrypt 1: zapytanie 3 - ryby rzeczne z łowiskiem$riverFish = $pdo->query('SELECT r.nazwa, l.akwen, l.wojewodztwo FROM ryby AS r JOIN lowisko AS l ON l.Ryby_id = r.id WHERE l.rodzaj = 3' )->fetchAll();// Skrypt 2: zapytanie 1 - ryby drapieżne$predators = $pdo->query('SELECT id, nazwa, wystepowanie FROM ryby WHERE styl_zycia = 1')->fetchAll();$pdo = null; // zamknięcie połączenia - ostatnia operacja na bazie} catch (PDOException $exception) {$riverFish = [];$predators = [];$databaseError = 'Nie udało się pobrać danych z bazy. Spróbuj odświeżyć stronę za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Wędkowanie</title> <linkrel="stylesheet"href="styl_1.css"></head><body> <header> <h1>Portal dla wędkarzy</h1> </header> <main><?phpif (isset($databaseError)): ?> <prole="alert"id="db-error"><?= e($databaseError) ?></p><?phpendif; ?> <sectionid="rivers"> <h3>Ryby zamieszkujące rzeki</h3> <ol><?phpforeach ($riverFishas$fish): ?> <li><?= e($fish['nazwa']) ?> pływa w rzece <?= e($fish['akwen']) ?>, <?= e($fish['wojewodztwo']) ?></li><?phpendforeach; ?> </ol> </section> <sectionid="predators"> <h3>Ryby drapieżne naszych wód</h3> <table> <thead> <tr> <thscope="col">L.p.</th> <thscope="col">Gatunek</th> <thscope="col">Występowanie</th> </tr> </thead> <tbody><?phpforeach ($predatorsas$fish): ?> <tr> <td><?= e($fish['id']) ?></td> <td><?= e($fish['nazwa']) ?></td> <td><?= e($fish['wystepowanie']) ?></td> </tr><?phpendforeach; ?> </tbody> </table> </section> <asideid="photo"> <imgsrc="ryba1.jpg"alt="Sum"> <p><ahref="kwerendy.txt">Pobierz kwerendy</a></p> </aside> </main> <footer> <p>Stronę wykonał: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (wedkuj.php)
Etap 5: Porównanie z wersją podstawową
Krok 31 · po tym etapie masz 34 z 35 kryteriów (pkt)
Krok 31: 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
układ
float: right bloku prawego, float: left lewych, clear stopki
<main> z siatką 55% 45% i obszarami
kolejność w kodzie
blok prawy przed lewymi (wymóg float)
kolejność czytania: lewe, potem prawy
szerokości
width przy blokach
grid-template-columns
kolory
wpisane w każdą regułę
zmienne CSS
tabela
wiersze wprost w <table>
<thead>, <tbody>, scope
odnośnik
po <br>
w akapicie
biblioteka
mysqli (tabela 1 arkusza)
PDO z wyjątkami
gdzie są zapytania
skrypty w środku HTML-u
jeden blok na górze pliku
odczyt wiersza
mysqli_fetch_row, $row[1]
fetchAll, $fish['akwen']
wypisywanie
dane z bazy wprost
e() - htmlspecialchars
awaria bazy
ostrzeżenia PHP
komunikat
Na egzaminie wersja podstawowa trzyma się funkcji z tabeli 1 arkusza i łatwiej ją napisać bez dokumentacji pod ręką. Nowoczesna oszczędza clear i pilnowania kolejności bloków w kodzie, a siatkę klucz wprost dopuszcza. Obie biblioteki klucz przyjmuje.
Krok 32 · po tym etapie masz 34 z 35 kryteriów (pkt)
Krok 32: Plik przeglądarka.txt i nagranie płyty
ok. 5 min
Polecenie z arkusza
Po zakończeniu pracy utwórz plik tekstowy o nazwie przeglądarka.txt. Zapisz w nim nazwę przeglądarki internetowej, w której weryfikowałeś poprawność działania witryny. Umieść go w folderze z numerem, którym został podpisany arkusz. Nagraj płytę z rezultatami pracy. W folderze z numerem, którym został podpisany arkusz, powinny znajdować się pliki: import.png, kw1.jpg, kw2.jpg, kw3.jpg, kw4.jpg, kwerendy.txt, przeglądarka.txt, ryba1.jpg, styl_1.css, wedkuj.php, ewentualnie inne przygotowane pliki.
W przeglądarka.txt nazwa przeglądarki, w której sprawdzana była strona przez http://localhost/.... Sprawdź listę plików (kwerendy.txt musi leżeć obok strony - prowadzi do niego odnośnik), nagraj płytę, sprawdź jej odczyt i opisz ją numerem zdającego.
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.