INF.03-04-24.06-SGCzerwiec 2024 · 39 kroków · ok. 78 min · 37 kryteriów z klucza
Rozwiązanie krok po kroku: Galeria zdjęć z bazą (Nowoczesny kod (grid, PDO))
To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza galeria z czterema zapytaniami, dzięcioł przeskalowany do 520 × 346 px i strona galeria.php, na której zdjęcia z bazy pokazują tytuł, autora i odnośnik „Pobierz” po najechaniu kursorem - i tak samo spełnia każde kryterium klucza. Inna jest droga:
siatka CSS na całej stronie: grid-template-areas z kolumnami 15% 70% 15% (klucz R.4.4 przyjmuje je wprost) zamiast float i clear trzech bloków; bloki zdjęć zostają opływane (float), bo tak każe arkusz,
kolory w zmiennych CSS, przejście tylko dla przezroczystości (transition: opacity), opis zdjęcia widoczny także przy przechodzeniu klawiszem Tab (:focus-within),
karta zdjęcia jako <figure> z podpisem <figcaption> - znaczniki mówią, że tytuł, autor i odnośnik opisują to zdjęcie,
PHP na PDO nad HTML-em: oba zapytania wykonane i połączenie zamknięte, zanim zacznie się strona; szablon tylko wypisuje dane, kolumny po nazwach,
bezpieczeństwo i odporność: e() przy każdej wartości z bazy, rawurlencode() w adresie pliku, Skrypt 2 nie psuje strony, gdy żadne zdjęcie nie ma stu polubień, komunikat przy awarii bazy.
Na końcu krótkie porównanie z wersją podstawową (zakładka obok). Baza i zdjęcia pochodzą z prawdziwego archiwum pliki4, zapytania zostały wykonane na tej bazie w MariaDB z XAMPP-a, a podgląd strony od etapu PHP to wynik prawdziwego uruchomienia kodu z danego kroku w PHP 8.2.
Jak liczą się punkty. Klucz ma 37 kryteriów (R.1.1 - R.5.9), każde za 1 punkt. Próg zdania to 75%, czyli 28 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 37 kryteriów (pkt)
Krok 1: Baza danych galeria
ok. 2 min
Polecenie z arkusza
Utwórz bazę danych o nazwie galeria, z zestawem polskich znaków (np. utf8_unicode_ci)
Archiwum pliki4.zip rozpakuj hasłem (G@LeriA) - razem z nawiasami okrągłymi - do folderu z numerem zdającego; arkusz zaleca, żeby zdjęcia leżały obok pliku strony. W phpMyAdmin Nowa → galeria, porównywanie utf8_unicode_ci, albo zapytaniem:
CREATE DATABASE galeria CHARACTER SET utf8 COLLATE utf8_unicode_ci;
Typowe błędy
Nazwa bazy musi być dokładnie galeria - skrypty łączą się z nią po nazwie (R.5.1).
Krok 2: Import tabel z pliku baza.sql
ok. 2 min
Polecenie z arkusza
Do bazy zaimportuj tabele z pliku baza.sql z rozpakowanego archiwum
Import → baza.sql. Dwie tabele z obrazu 1: autorzy (11 osób) i zdjecia (12 zdjęć z tytułem, nazwą pliku, liczbą polubień i kluczem obcym autorzy_id) - jeden autor, wiele zdjęć, czyli relacja 1..n.
Krok 3: Zrzut ekranu import.png
Kryteria:
R.1.1 Wykonano import tabel do bazy danych galeria 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.
Cały ekran z paskiem zadań, zapis jako import.png. Uwaga na formaty: import w PNG, zrzuty zapytań w JPEG (kw1.jpg - kw4.jpg).
Typowe błędy
Wykadrowany zrzut albo zrzut bez paska zadań nie jest oceniany (uwaga w kluczu przy R.1).
Krok 4: Zapytanie 1: zdjęcia z co najmniej 100 polubieniami
Kryteria: ,
R.1.2 Zapisano plik kwerendy.txt zawierający co najmniej jedno zapytanie SQL, wynikające z treści zadania
R.1.3 Wykonano zapytanie 1 wybierające jedynie pola tytul i plik z tabeli zdjecia dla zdjęć z polubieniami większymi lub równymi 100. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT tytul, plik FROM zdjecia WHERE polubienia >= 100; oraz wynik działania udokumentowano zrzutem, na którym są widoczne dane: Slava Ukraini! kiev.jpg
ok. 1 min
Polecenie z arkusza
Zapytanie 1: wybierające jedynie pola tytul i plik z tabeli zdjecia dla zdjęć z polubieniami większymi lub równymi 100
„Większymi lub równymi” to >=. Wynik: jeden wiersz - Slava Ukraini!kiev.jpg, jak w kluczu. To zapytanie wyśle Skrypt 2. Zapytania trafiają do kwerendy.txt, zrzut do kw1.jpg.
Krok 5: Zapytanie 2: zdjęcia z autorami według nazwiska
Kryteria:
R.1.4 Wykonano zapytanie 2 wybierające jedynie pola plik, tytul, polubienia z tabeli zdjecia oraz odpowiadające im pola imie i nazwisko z tabeli autorzy posortowane rosnąco według nazwiska. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT plik, tytul, polubienia, imie, nazwisko FROM zdjecia JOIN autorzy ON autorzy_id = autorzy.id ORDER BY nazwisko; (możliwe również INNER JOIN lub bez JOIN i porównanie kluczy po WHERE, opcjonalnie ASC przy sortowaniu) oraz wynik działania udokumentowano zrzutem, na którym jest widocznych dokładnie 12 rekordów, jedynie pola plik, tytul, polubienia, imie, nazwisko, wyniki są posortowane wg. nazwiska
ok. 3 min
Polecenie z arkusza
Zapytanie 2: wybierające jedynie pola plik, tytul, polubienia z tabeli zdjecia oraz odpowiadające im pola imie i nazwisko z tabeli autorzy posortowane rosnąco według nazwiska. Należy posłużyć się relacją
aliasy tabelz (zdjęcia) i a (autorzy) - przy każdej kolumnie widać, skąd pochodzi,
JOIN autorzy AS a ON a.id = z.autorzy_id - relacja z obrazu 1: klucz obcy zdjęcia wskazuje autora,
ORDER BY a.nazwisko - rosnąco, bo to domyślny kierunek.
Wynik: 12 wierszy, od trzech zdjęć Jolanty Białej. Kolejność zdjęć tej samej autorki nie jest ustalona - gwarantuje ją tylko ORDER BY; drugi klucz sortowania (np. , z.tytul) by ją ustalił, ale zmieniłby układ galerii z obrazu 2. Zrzut: kw2.jpg. To zapytanie wyśle Skrypt 1.
kwerendy.txtTekstzmienione: 6 linii
-- Zapytanie 1SELECT tytul, plikFROM zdjeciaWHERE polubienia >= 100;-- Zapytanie 2SELECT z.plik, z.tytul, z.polubienia, a.imie, a.nazwiskoFROM zdjecia AS zJOIN autorzy AS a ON a.id = z.autorzy_idORDER BY a.nazwisko;
Podgląd strony po tym kroku
Krok 6: Zapytanie 3: liczba zdjęć dla każdego imienia
Kryteria:
R.1.5 Wykonano zapytanie 3 wybierające jedynie pole imie oraz liczące ile jest zdjęć dla tego imienia. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT imie, COUNT(*) FROM zdjecia JOIN autorzy ON autorzy_id = autorzy.id GROUP BY imie; (lub dowolne pole zamiast *, możliwe również INNER JOIN lub bez JOIN i porównanie kluczy po WHERE) oraz wynik działania udokumentowano zrzutem, na którym są widoczne dane: Ewa 1; Ewelina 2; Jadwiga 4; Jan 2; Jolanta 3
ok. 2 min
Polecenie z arkusza
Zapytanie 3: wybierające jedynie pole imie oraz liczące ile jest zdjęć dla tego imienia. Należy posłużyć się relacją
Imię jest w tabeli autorów, zdjęcia w drugiej - stąd złączenie. GROUP BY a.imie tworzy grupę na imię, a COUNT(z.id) liczy zdjęcia w grupie (klucz dopuszcza dowolne pole zamiast *); alias liczba_zdjec nazywa kolumnę wyniku. Wynik: Ewa 1, Ewelina 2, Jadwiga 4, Jan 2, Jolanta 3 - autorzy bez zdjęć nie mają pary w złączeniu i nie trafiają do wyniku. Zrzut: kw3.jpg.
kwerendy.txtTekstzmienione: 6 linii
-- Zapytanie 1SELECT tytul, plikFROM zdjeciaWHERE polubienia >= 100;-- Zapytanie 2SELECT z.plik, z.tytul, z.polubienia, a.imie, a.nazwiskoFROM zdjecia AS zJOIN autorzy AS a ON a.id = z.autorzy_idORDER BY a.nazwisko;-- Zapytanie 3SELECT a.imie, COUNT(z.id) AS liczba_zdjecFROM autorzy AS aJOIN zdjecia AS z ON z.autorzy_id = a.idGROUP BY a.imie;
Podgląd strony po tym kroku
Krok 7: Zapytanie 4: nowa kolumna rozmiarPliku
Kryteria:
R.1.6 Wykonano zapytanie 4 modyfikujące strukturę tabeli zdjecia. Dodana jest kolumna rozmiarPliku typu całkowitego. W pliku z kwerendami lub na zrzucie istnieje zapis np.: ALTER TABLE zdjecia ADD COLUMN rozmiarPliku INT; (słowo COLUMN jest opcjonalne, dowolny typ całkowity) oraz wynik działania udokumentowano zrzutem, na którym jest widoczny komunikat o poprawnym wykonaniu zapytania lub struktura tabeli z dodanym polem rozmiarPliku typu całkowitego
ok. 2 min
Polecenie z arkusza
Zapytanie 4: modyfikujące strukturę tabeli zdjecia. Dodana jest kolumna rozmiarPliku typu całkowitego
Strukturę zmienia ALTER TABLE ... ADD COLUMN. Typ INT UNSIGNED - całkowity bez liczb ujemnych, bo rozmiar pliku nie może być ujemny (klucz przyjmuje dowolny typ całkowity). W istniejących wierszach nowa kolumna ma NULL. Zrzut kw4.jpg: komunikat o wykonaniu albo struktura tabeli z rozmiarPliku int(10) unsigned. Skrypty wybierają konkretne kolumny, więc nowe pole im nie przeszkadza.
Typowe błędy
Drugie wykonanie kończy się błędem „Duplicate column name” - kolumna już istnieje.
kwerendy.txtTekstzmienione: 4 linie
-- Zapytanie 1SELECT tytul, plikFROM zdjeciaWHERE polubienia >= 100;-- Zapytanie 2SELECT z.plik, z.tytul, z.polubienia, a.imie, a.nazwiskoFROM zdjecia AS zJOIN autorzy AS a ON a.id = z.autorzy_idORDER BY a.nazwisko;-- Zapytanie 3SELECT a.imie, COUNT(z.id) AS liczba_zdjecFROM autorzy AS aJOIN zdjecia AS z ON z.autorzy_id = a.idGROUP BY a.imie;-- Zapytanie 4ALTER TABLE zdjeciaADD COLUMN rozmiarPliku INT UNSIGNED;
Podgląd strony po tym kroku
Etap 2: Grafika i strona HTML
Kroki 8-16 · po tym etapie masz 14 z 37 kryteriów (pkt)
Krok 8: woodpecker.jpg: dokładnie 520 × 346 px
Kryteria:
R.2.1 Grafikę woodpecker.jpg przeskalowano do dokładnych wymiarów: szerokość 520 px i wysokość 346 px
ok. 2 min
Polecenie z arkusza
Plik woodpecker.jpg, wypakowany z archiwum, należy przeskalować do dokładnych wymiarów szerokość 520 px i wysokość 346 px.
Zdjęcie ma 640 × 428 px, pozostałe zdjęcia z archiwum - 520 × 346. W GIMP-ie Obraz → Skaluj obraz z rozłączonym łańcuchem proporcji i obiema wartościami: 520 i 346. Zamknięty łańcuch wyliczyłby wysokość 348 (inne proporcje), a arkusz wymaga dokładnych wymiarów. Eksport: Plik → Nadpisz woodpecker.jpg.
Typowe błędy
Zdjęcie 520 × 348 (z zamkniętym łańcuchem) nie spełnia warunku „dokładnych wymiarów”.
Podgląd strony po tym kroku
Krok 9: Plik galeria.php: HTML5, język, kodowanie
Kryteria:
R.2.2 Witrynę internetową zapisano w pliku galeria.php oraz zapisano jawnie standard kodowania polskich znaków i deklarację HTML5 <!DOCTYPE HTML> i zadeklarowano język witryny polski np.<HTML lang="pl">. Strona zawiera sekcje head i body
ok. 2 min
Polecenie z arkusza
Składa się ze strony o nazwie galeria.php zapisanej w języku HTML5. Ustawiony język zawartości strony na polski. Jawnie zastosowany właściwy standard kodowania polskich znaków
Plik galeria.php: <!DOCTYPE html>, <html lang="pl">, <meta charset="UTF-8"> jako pierwszy element <head> i sekcja <body> (R.2.2).
R.3.3 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl.css , formatowanie CSS 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.css prawidłowo połączony z kodem strony
Nazwa styl.css, nie style.css. R.3.3 przepada też wtedy, gdy któryś selektor jest źle zapisany, np. kropka zamiast # przy identyfikatorze.
R.2.4 Układ strony zdefiniowano dzieląc ją na bloki: baner, lewy, środkowy, prawy oraz stopkę. Zastosowano semantyczne znaczniki sekcji HTML5 zgodnie z ich znaczeniem, w tym do części nagłówkowej - header, stopki - footer, dopuszcza się zagnieżdżenie bloków div w znacznikach semantycznych
ok. 2 min
Polecenie z arkusza
Podział strony na bloki: baner, poniżej trzy bloki: lewy, środkowy i prawy, na dole blok stopki. Podział zrealizowany wyłącznie za pomocą semantycznych znaczników sekcji języka HTML5 tak, aby po uruchomieniu w przeglądarce wygląd układu bloków był zgodny z obrazem 2
Baner <header>, stopka <footer> (klucz R.2.4 wymienia je wprost). Galeria to główna treść - <main>. Bloki boczne niosą treść dodatkową, więc oba to <aside>, a identyfikatory nazywają je od treści, nie od położenia: topics (tematy) i favourite (najbardziej lubiane) - po przestawieniu bloków nazwy nadal będą prawdziwe.
R.2.6 W bloku lewym umieszczono jedną listę numerowaną z pięcioma elementami, zastosowano znaczniki <ol> i <li>
ok. 2 min
Polecenie z arkusza
Zawartość bloku lewego: Nagłówek drugiego stopnia o treści: „Tematy zdjęć”. Lista numerowana (uporządkowana) z elementami: Zwierzęta, Krajobrazy, Miasta, Przyroda, Samochody
<h2> i lista uporządkowana <ol> z pięcioma <li> - numery doda przeglądarka (R.2.6).
Krok 15: Blok prawy: nagłówek i napis o dużym znaczeniu
Kryteria:
R.2.9 W bloku prawym umieszczono napis o treści „Zobacz wszystkie nasze zdjęcia” zapisany w znaczniku semantycznym pisanym czcionką pogrubioną i oznaczającym tekst o dużym znaczeniu - znacznik <strong>
ok. 2 min
Polecenie z arkusza
Zawartość bloku prawego: Nagłówek drugiego stopnia o treści: „Najbardziej lubiane”. Efekt działania skryptu 2. Napis o treści „Zobacz wszystkie nasze zdjęcia” zapisany w znaczniku semantycznym pisanym czcionką pogrubioną i oznaczającym tekst o dużym znaczeniu
Tekst o dużym znaczeniu to <strong> (<b> też pogrubia, ale nic nie znaczy) - R.2.9. Między nagłówkiem a napisem stanie zdjęcie ze Skryptu 2. Blok środkowy w całości wypełni Skrypt 1.
R.2.5 W banerze zapisano nagłówek h1: "Zdjęcia" , w bloku lewym h2: "Tematy zdjęć" , w bloku prawym h2: "Najbardziej lubiane" , w stopce h5 z numerem zdającego
ok. 1 min
Polecenie z arkusza
Zawartość stopki: nagłówek piątego stopnia o treści „Stronę wykonał: ”, dalej wstawiony numer zdającego
Nagłówek <h5>. Na egzaminie po „Stronę wykonał:” wpisujesz swój numer zdającego (PESEL); tu stoi autor rozwiązania, Bartosz Bryniarski. Strona ma już wszystkie nagłówki z klucza (R.2.5).
Domyślne formatowanie wszystkich selektorów: krój czcionki Helvetica
Sienna występuje w banerze, stopce i odnośnikach, NavajoWhite w dwóch blokach - zapisane raz jako zmienne CSS na :root. Selektor * z Helvetiką (R.4.1); na Windows zwykle jej nie ma, więc dalej stoi Arial (taki krój widać na obrazie 2) i ogólny krój bezszeryfowy.
R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z obrazem 2 w arkuszu. Nie należy brać pod uwagę bloków generowanych skryptem. (prawidłowo zastosowano właściwości CSS decydujące o układzie strony np. float i clear albo display flex albo grid)
ok. 3 min
Polecenie z arkusza
Podział strony na bloki: baner, poniżej trzy bloki: lewy, środkowy i prawy, na dole blok stopki. Podział zrealizowany wyłącznie za pomocą semantycznych znaczników sekcji języka HTML5 tak, aby po uruchomieniu w przeglądarce wygląd układu bloków był zgodny z obrazem 2
<body> staje się siatką. grid-template-columns: 15% 70% 15% to szerokości bloku lewego, środkowego i prawego z arkusza (klucz R.4.4 przyjmuje je w tej postaci), a grid-template-areas rysuje obraz 2 słowami: baner przez trzy kolumny, trzy bloki obok siebie, stopka przez trzy kolumny (R.3.1). Każdy blok trafia w swoje pole przez grid-area.
Bez float stopka nie potrzebuje clear, a bloki nie „spadają” pod siebie, gdy suma szerokości z marginesami przekroczy 100%.
Wspólne dla bloku lewego i prawego: kolor tła NavajoWhite, szerokość 15%, wysokość 700 px
Oba bloki boczne to <aside>, więc wspólne cechy łapie jeden selektor znacznika. Szerokość 15% dają kolumny siatki - width: 15% liczyłby się od kolumny i zwęził blok do 15% z 15%.
Typowe błędy
Procentowa szerokość elementu siatki liczy się od jego pola, nie od strony - width: 15% przy kolumnie 15% daje ok. 2% szerokości okna.
Krok 21: Blok środkowy: wysokość i paski przewijania
Kryteria: ,
R.4.3 Ustawiono wysokość bloków lewego, środkowego i prawego na 700px
R.4.5 Ustawiono wyrównanie tekstu do środka dla banera i stopki oraz zawsze widoczne paski przewijania (overflow: scroll;) dla bloku środkowego
ok. 2 min
Polecenie z arkusza
Dla bloku środkowego: szerokość 70%, wysokość 700 px, zawsze widoczne paski przewijania
„Zawsze widoczne” paski to overflow: scroll - rysowane od początku, nawet gdy treść się mieści (auto pokazałoby je tylko w razie potrzeby). Dwanaście zdjęć i tak nie zmieści się w 700 px. Wysokości trzech bloków są komplet (R.4.3), wyśrodkowanie banera i stopki razem z paskami tego bloku to R.4.5. Szerokość 70% - środkowa kolumna siatki.
R.4.4 Ustawiono szerokość bloku lewego i prawego 15%, środkowego 70% (width lub grid- template-columns lub flex), selektora img 100%
ok. 1 min
Polecenie z arkusza
Dla selektora obrazu: szerokość 100%
Zgodnie z uwagą arkusza selektor to sam znacznik img (klucz nie uzna klasy). Obraz zajmie całą szerokość rodzica: karty zdjęcia w galerii albo bloku prawego. Szerokości są komplet: kolumny 15% 70% 15% i img 100% (R.4.4).
Krok 23: Karta zdjęcia: obok siebie, szerokość, marginesy, pozycja względna
Kryteria:
R.4.7 Ustawiono dla bloku generowanego skryptem 1: bloki ustawione obok siebie, szerokość 46%, marginesy zewnętrzne (margin) 2% (np. dla klasy, id jest błędem )
ok. 2 min
Polecenie z arkusza
Dla bloku generowanego skryptem 1 i zawierającego zdjęcie i opis: bloki ustawione obok siebie (opływanie), szerokość 46%, marginesy zewnętrzne 2%, pozycjonowanie względne
Karty zdjęć wypisze Skrypt 1 z klasą photo (klucz: klasa, identyfikator byłby błędem - kart jest dwanaście).
Tu zostaje float: left: arkusz mówi wprost „opływanie”. Siatka w <main> dałaby ten sam wygląd, ale nie jest opływaniem, a punkt R.4.7 ma pierwszeństwo,
46% + 2 × 2% marginesu = 50% - dwie karty w rzędzie; margin: 2% zastępuje też domyślny margines znacznika <figure> (40 px z boków),
position: relative nie przesuwa karty, ale robi z niej punkt odniesienia dla opisu pozycjonowanego absolute (R.4.7).
Krok 24: Opis zdjęcia: niewidoczny i pozycjonowany na zdjęciu
Kryteria:
R.4.9 Ustawiono pełną przezroczystość dla nagłówka trzeciego stopnia, paragrafu i odnośnika (opacity: 0;)
ok. 2 min
Polecenie z arkusza
Wspólne dla nagłówka trzeciego stopnia, paragrafu, odnośnika: pełna przezroczystość (0), pozycjonowanie pozwalające przesuwać element względem rodzica
:is(h3, p, a) zbiera trzy selektory w jeden: .photo :is(h3, p, a) znaczy to samo co .photo h3, .photo p, .photo a. Przedrostek .photo ogranicza styl do kart - <strong> i nagłówki bloków bocznych zostają widoczne. opacity: 0 - element jest w pełni przezroczysty, ale istnieje i reaguje na kliknięcie (R.4.9). Przesuwanie „względem rodzica” to position: absolute z tabeli 2 arkusza - liczy się od najbliższego przodka z pozycją, czyli od karty .photo.
R.4.2 Ustawiono kolor tła Sienna i biały kolor czcionki dla banera i stopki, kolor tła: Navaj oWhite dla bloku lewego i prawego oraz kolor tła Sienna dla odnośnika
R.4.6 Ustawiono marginesy wewnętrzne (padding) 10px dla banera i stopki, 15px dla odnośnika
R.4.8 Ustawiono cechy pozycjonowania (position): relative dla bloku generowanego skryptem 1, absolute dla nagłówka trzeciego stopnia, paragrafu i odnośnika oraz odległości od górnej krawędzi (top) 5% dla nagłówka trzeciego stopnia, 30% dla paragrafu, 70% dla odnośnika oraz od lewej krawędzi (left) 70% dla odnośnika (kryterium należy uznać za spełnione, gdy poprawnie zdefiniowano formatowanie dla co najmniej 4 z 6 elementów)
ok. 2 min
Polecenie z arkusza
Dodatkowo dla odnośnika: odległość od górnej krawędzi rodzica 70%, od lewej krawędzi 70%, kolor tła Sienna, marginesy wewnętrzne 15 px
top i left stawiają odnośnik w prawym dolnym rogu zdjęcia, a tło Sienna z odstępem 15 px robi z niego przycisk (obraz 3). Pozycje opisu są komplet (R.4.8), tak samo kolory tła (R.4.2) i marginesy wewnętrzne (R.4.6).
Krok 28: Karta pod kursorem: zdjęcie blednie, opis się pojawia
Kryteria:
R.4.10 W momencie, gdy kursor myszy znajdzie się na bloku generowanym skryptem 1: przezroczystość dla zdjęcia w tym bloku zmienia się na 0.3, przezroczystość dla nagłówka trzeciego stopnia, paragrafu i odnośnika zmienia się na 1, dla zdjęcia zdefiniowano płynne przejście transition: 0.5s ease
ok. 3 min
Polecenie z arkusza
W momencie, gdy kursor myszy znajdzie się na bloku generowanym skryptem 1: Zdjęcia z bloku środkowego przyjmują wartość przezroczystości 0.3 i jest ona zmieniana płynnie przez 0.5 sekundy efektem ease. Nagłówka trzeciego stopnia: brak przezroczystości (1). Paragrafu: brak przezroczystości. Odnośnika: brak przezroczystości
.photo:hover img - obraz w karcie, nad którą stoi kursor: przezroczystość 0.3; opis w tej karcie dostaje 1 (R.4.10),
transition: opacity 0.5s ease stoi w zwykłej regule obrazu, więc zdjęcie blednie przez pół sekundy i tak samo wraca. Nazwa właściwości w transition mówi, co ma się zmieniać płynnie - bez niej przejście dostałaby każda zmiana obrazu, także szerokość,
:focus-within - ten sam efekt, gdy fokus jest wewnątrz karty: kto przechodzi po stronie klawiszem Tab, po dojściu do „Pobierz” widzi tytuł, autora i odnośnik. Arkusz tego nie wymaga, a bez tego odnośnik byłby niewidoczny dla osoby bez myszy.
R.5.3 Skrypt 1 zawiera instrukcje wysyłające do bazy danych zapytanie 2 lub skrypt 2: zapytanie 1
R.5.9 Zastosowano znaczące nazewnictwo dla wszystkich zmiennych lub funkcji w języku polskim lub angielskim. Nazwy odzwierciedlają cel zastosowania
ok. 2 min
Polecenie z arkusza
Skrypt 1: Wysyła do bazy danych zapytanie 2
$pdo->query(...) wysyła zapytanie 2 - te same linie co w kwerendy.txt - a ->fetchAll() odbiera 12 wierszy do tablicy $photos (R.5.3). W zapytaniu nie ma wartości od użytkownika, więc query wystarcza. Nazwy zmiennych po angielsku mówią, co przechowują (R.5.9).
galeria.phpPHPzmienione: 8 linii
<?phpfunctione(string $text): string{returnhtmlspecialchars($text, ENT_QUOTES);}$photos = [];$dbError = '';try {$pdo = new PDO('mysql:host=localhost;dbname=galeria;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);// Skrypt 1: zapytanie 2$photos = $pdo->query('SELECT z.plik, z.tytul, z.polubienia, a.imie, a.nazwisko FROM zdjecia AS z JOIN autorzy AS a ON a.id = z.autorzy_id ORDER BY a.nazwisko' )->fetchAll();$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać zdjęć. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Galeria</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Zdjęcia</h1> </header> <asideid="topics"> <h2>Tematy zdjęć</h2> <ol> <li>Zwierzęta</li> <li>Krajobrazy</li> <li>Miasta</li> <li>Przyroda</li> <li>Samochody</li> </ol> </aside> <main> </main> <asideid="favourite"> <h2>Najbardziej lubiane</h2> <strong>Zobacz wszystkie nasze zdjęcia</strong> </aside> <footer> <h5>Stronę wykonał: Bartosz Bryniarski</h5> </footer></body></html>
R.5.4 Skrypt 1 tworzy element blokowy dla każdego zwróconego z bazy wiersza
ok. 2 min
Polecenie z arkusza
Każdy zwrócony zapytaniem wiersz jest wyświetlany w osobnym bloku (obraz 3)
W <main> pętla foreach ($photos as $photo): w składni alternatywnej - HTML karty zostaje zwykłym HTML-em, bez echo i ukośników przed cudzysłowami. Karta to <figure> - element blokowy przeznaczony na ilustrację z podpisem (R.5.4). Na razie dwanaście pustych kart.
galeria.phpPHPzmienione: 4 linie
<?phpfunctione(string $text): string{returnhtmlspecialchars($text, ENT_QUOTES);}$photos = [];$dbError = '';try {$pdo = new PDO('mysql:host=localhost;dbname=galeria;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);// Skrypt 1: zapytanie 2$photos = $pdo->query('SELECT z.plik, z.tytul, z.polubienia, a.imie, a.nazwisko FROM zdjecia AS z JOIN autorzy AS a ON a.id = z.autorzy_id ORDER BY a.nazwisko' )->fetchAll();$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać zdjęć. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Galeria</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Zdjęcia</h1> </header> <asideid="topics"> <h2>Tematy zdjęć</h2> <ol> <li>Zwierzęta</li> <li>Krajobrazy</li> <li>Miasta</li> <li>Przyroda</li> <li>Samochody</li> </ol> </aside> <main><?phpforeach ($photosas$photo): ?> <figureclass="photo"> </figure><?phpendforeach; ?> </main> <asideid="favourite"> <h2>Najbardziej lubiane</h2> <strong>Zobacz wszystkie nasze zdjęcia</strong> </aside> <footer> <h5>Stronę wykonał: Bartosz Bryniarski</h5> </footer></body></html>
Podgląd strony po tym kroku (galeria.php)
Krok 32: Skrypt 1: zdjęcie z tekstem alternatywnym
Kryteria: ,
R.2.7 W bloku środkowym umieszczono przynajmniej jeden obraz z tekstem alternatywnym "zdjęcie" (wygenerowany skryptem lub w HTML)
R.5.5 W każdym bloku wyświetlono zdjęcie, którego źródłem jest pole <plik> z bazy
ok. 2 min
Polecenie z arkusza
Każdy blok zawiera: Obraz, którego źródło zostało zwrócone zapytaniem, z tekstem alternatywnym „zdjęcie”
Źródło z kolumny plik ($photo['plik'] - po nazwie, nie po numerze), tekst alternatywny - stałe słowo z arkusza. Podgląd wygląda już jak obraz 2: zdjęcia po dwa w rzędzie, z paskiem przewijania bloku środkowego (R.5.5, R.2.7).
galeria.phpPHPzmienione: 1 linia
<?phpfunctione(string $text): string{returnhtmlspecialchars($text, ENT_QUOTES);}$photos = [];$dbError = '';try {$pdo = new PDO('mysql:host=localhost;dbname=galeria;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);// Skrypt 1: zapytanie 2$photos = $pdo->query('SELECT z.plik, z.tytul, z.polubienia, a.imie, a.nazwisko FROM zdjecia AS z JOIN autorzy AS a ON a.id = z.autorzy_id ORDER BY a.nazwisko' )->fetchAll();$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać zdjęć. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Galeria</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Zdjęcia</h1> </header> <asideid="topics"> <h2>Tematy zdjęć</h2> <ol> <li>Zwierzęta</li> <li>Krajobrazy</li> <li>Miasta</li> <li>Przyroda</li> <li>Samochody</li> </ol> </aside> <main><?phpforeach ($photosas$photo): ?> <figureclass="photo"> <imgsrc="<?= e($photo['plik']) ?>"alt="zdjęcie"> </figure><?phpendforeach; ?> </main> <asideid="favourite"> <h2>Najbardziej lubiane</h2> <strong>Zobacz wszystkie nasze zdjęcia</strong> </aside> <footer> <h5>Stronę wykonał: Bartosz Bryniarski</h5> </footer></body></html>
Podgląd strony po tym kroku (galeria.php)
Krok 33: Skrypt 1: tytuł w nagłówku trzeciego stopnia
ok. 2 min
Polecenie z arkusza
Nagłówek trzeciego stopnia z tytułem zwróconym zapytaniem
Opis zdjęcia trafia do podpisu <figcaption> - znacznik mówi, że tytuł, autor i odnośnik dotyczą tego zdjęcia. Pierwszy jest <h3> z tytułem. W podglądzie go nie widać: styl ukrywa opis do czasu najechania kursorem (opacity: 0). Pozycjonowanie się nie zmienia - absolute liczy się od najbliższego przodka z pozycją, czyli nadal od karty.
galeria.phpPHPzmienione: 3 linie
<?phpfunctione(string $text): string{returnhtmlspecialchars($text, ENT_QUOTES);}$photos = [];$dbError = '';try {$pdo = new PDO('mysql:host=localhost;dbname=galeria;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);// Skrypt 1: zapytanie 2$photos = $pdo->query('SELECT z.plik, z.tytul, z.polubienia, a.imie, a.nazwisko FROM zdjecia AS z JOIN autorzy AS a ON a.id = z.autorzy_id ORDER BY a.nazwisko' )->fetchAll();$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać zdjęć. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Galeria</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Zdjęcia</h1> </header> <asideid="topics"> <h2>Tematy zdjęć</h2> <ol> <li>Zwierzęta</li> <li>Krajobrazy</li> <li>Miasta</li> <li>Przyroda</li> <li>Samochody</li> </ol> </aside> <main><?phpforeach ($photosas$photo): ?> <figureclass="photo"> <imgsrc="<?= e($photo['plik']) ?>"alt="zdjęcie"> <figcaption> <h3><?= e($photo['tytul']) ?></h3> </figcaption> </figure><?phpendforeach; ?> </main> <asideid="favourite"> <h2>Najbardziej lubiane</h2> <strong>Zobacz wszystkie nasze zdjęcia</strong> </aside> <footer> <h5>Stronę wykonał: Bartosz Bryniarski</h5> </footer></body></html>
Podgląd strony po tym kroku (galeria.php)
Krok 34: Skrypt 1: autor i „Wiele osób polubiło” przy ponad 40 polubieniach
Kryteria:
R.5.7 Gdy zwrócona zapytaniem wartość polubień jest większa niż 40 wyświetlony jest tekst: "Wiele osób polubiło ten obraz" , w przeciwnym wypadku tekst nie jest wyświetlony
ok. 3 min
Polecenie z arkusza
W przypadku, gdy zwrócona wartość polubień jest większa niż 40: paragraf o treści: „Autor: <imię><nazwisko>. Wiele osób polubiło ten obraz”, łamanie linii po kropce. W przeciwnym wypadku paragraf o treści: „Autor: <imię><nazwisko>”, gdzie pola w nawiasach <> zostały pobrane z bazy danych
Oba warianty paragrafu zaczynają się tak samo, więc jest jeden<p>, a warunek dopisuje tylko różnicę: kropkę, łamanie linii <br> i drugie zdanie. Próg 40 stoi w stałej POPULAR_LIKES na górze pliku - liczba ma nazwę i jedno miejsce do zmiany.
„Większa niż” to > - „Urocze jeziorko” z dokładnie 40 polubieniami dostaje sam podpis. Sprawdzone na bazie z arkusza: dłuższy napis ma 5 zdjęć (Kijów, Praga, Wrocław, motyl i dzięcioł), krótszy - 7 (R.5.7).
galeria.phpPHPzmienione: 6 linii
<?phpconst POPULAR_LIKES = 40;functione(string $text): string{returnhtmlspecialchars($text, ENT_QUOTES);}$photos = [];$dbError = '';try {$pdo = new PDO('mysql:host=localhost;dbname=galeria;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);// Skrypt 1: zapytanie 2$photos = $pdo->query('SELECT z.plik, z.tytul, z.polubienia, a.imie, a.nazwisko FROM zdjecia AS z JOIN autorzy AS a ON a.id = z.autorzy_id ORDER BY a.nazwisko' )->fetchAll();$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać zdjęć. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Galeria</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Zdjęcia</h1> </header> <asideid="topics"> <h2>Tematy zdjęć</h2> <ol> <li>Zwierzęta</li> <li>Krajobrazy</li> <li>Miasta</li> <li>Przyroda</li> <li>Samochody</li> </ol> </aside> <main><?phpforeach ($photosas$photo): ?> <figureclass="photo"> <imgsrc="<?= e($photo['plik']) ?>"alt="zdjęcie"> <figcaption> <h3><?= e($photo['tytul']) ?></h3> <p> Autor: <?= e($photo['imie']) ?><?= e($photo['nazwisko']) ?><?phpif ($photo['polubienia'] > POPULAR_LIKES): ?>.<br> Wiele osób polubiło ten obraz<?phpendif; ?> </p> </figcaption> </figure><?phpendforeach; ?> </main> <asideid="favourite"> <h2>Najbardziej lubiane</h2> <strong>Zobacz wszystkie nasze zdjęcia</strong> </aside> <footer> <h5>Stronę wykonał: Bartosz Bryniarski</h5> </footer></body></html>
Podgląd strony po tym kroku (galeria.php)
Krok 35: Skrypt 1: odnośnik „Pobierz” z atrybutem download
Kryteria: ,
R.3.2 Zapisano na stronie przynajmniej jeden odnośnik o treści „Pobierz" , wskazujący na dowolny plik graficzny. Po kliknięciu na odnośnik, zdjęcie jest pobierane - atrybut download (wygenerowana skryptem lub w HTML)
R.5.6 W każdym bloku wyświetlono tytuł, imię i nazwisko oraz odnośnik prowadzący do grafiki, której nazwa pliku jest zwrócona z bazy
ok. 2 min
Polecenie z arkusza
Odnośnik o treści „Pobierz” wskazujący na nazwę pliku zwróconą zapytaniem. Po kliknięciu na odnośnik, zdjęcie jest pobierane
Atrybut download każe zapisać plik na dysku zamiast otwierać go w przeglądarce; działa dla plików z tego samego serwera (R.3.2). Nazwa pliku przechodzi przez rawurlencode() - spacja czy polska litera w nazwie stałyby się bezpiecznym fragmentem adresu - a potem przez e() jak każdy atrybut. Każda karta ma teraz zdjęcie, tytuł, autora i odnośnik (R.5.6).
galeria.phpPHPzmienione: 1 linia
<?phpconst POPULAR_LIKES = 40;functione(string $text): string{returnhtmlspecialchars($text, ENT_QUOTES);}$photos = [];$dbError = '';try {$pdo = new PDO('mysql:host=localhost;dbname=galeria;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);// Skrypt 1: zapytanie 2$photos = $pdo->query('SELECT z.plik, z.tytul, z.polubienia, a.imie, a.nazwisko FROM zdjecia AS z JOIN autorzy AS a ON a.id = z.autorzy_id ORDER BY a.nazwisko' )->fetchAll();$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać zdjęć. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Galeria</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Zdjęcia</h1> </header> <asideid="topics"> <h2>Tematy zdjęć</h2> <ol> <li>Zwierzęta</li> <li>Krajobrazy</li> <li>Miasta</li> <li>Przyroda</li> <li>Samochody</li> </ol> </aside> <main><?phpforeach ($photosas$photo): ?> <figureclass="photo"> <imgsrc="<?= e($photo['plik']) ?>"alt="zdjęcie"> <figcaption> <h3><?= e($photo['tytul']) ?></h3> <p> Autor: <?= e($photo['imie']) ?><?= e($photo['nazwisko']) ?><?phpif ($photo['polubienia'] > POPULAR_LIKES): ?>.<br> Wiele osób polubiło ten obraz<?phpendif; ?> </p> <ahref="<?= e(rawurlencode($photo['plik'])) ?>"download>Pobierz</a> </figcaption> </figure><?phpendforeach; ?> </main> <asideid="favourite"> <h2>Najbardziej lubiane</h2> <strong>Zobacz wszystkie nasze zdjęcia</strong> </aside> <footer> <h5>Stronę wykonał: Bartosz Bryniarski</h5> </footer></body></html>
Podgląd strony po tym kroku (galeria.php)
Krok 36: Skrypt 2: zdjęcie z tytułem jako tekstem alternatywnym
Kryteria: , ,
R.2.8 W bloku prawym umieszczono obraz z tekstem alternatywnym (wygenerowany skryptem lub w HTML)
R.5.2 Ostatnią operacją na bazie jest jej zamknięcie
R.5.8 Skrypt 2 wyświetla obraz, którego źródło zwrócone zapytaniem 1, wraz z tekstem alternatywnym pobranym z pola tytuł
ok. 3 min
Polecenie z arkusza
Skrypt 2: Wysyła do bazy danych zapytanie 1. Wyświetla obraz, którego źródło zostało zwrócone zapytaniem, wraz z tekstem alternatywnym pobranym z pola tytul. Na końcu jest zamykane połączenie z serwerem.
Zapytanie 1 zwraca jeden wiersz, więc zamiast fetchAll jest fetch() - od razu ten wiersz albo false, gdy żadne zdjęcie nie ma stu polubień. W szablonie <?php if ($favourite): ?> wypisuje obraz tylko wtedy, gdy wiersz jest: źródło z plik, tekst alternatywny z tytul (R.5.8, R.2.8). Gdyby stu polubień dobiło kilka zdjęć, fetch() weźmie pierwsze.
Sprawdzone uruchomieniem na zmienionej bazie: przy 99 polubieniach blok prawy ma sam nagłówek i napis, bez ostrzeżeń PHP, a tytuł z cudzysłowem (Kijów "nocą") trafia do alt jako " i nie psuje znacznika. Połączenie zamyka $pdo = null zaraz po obu zapytaniach - ostatnia operacja na bazie (R.5.2).
galeria.phpPHPzmienione: 7 linii
<?phpconst POPULAR_LIKES = 40;functione(string $text): string{returnhtmlspecialchars($text, ENT_QUOTES);}$photos = [];$favourite = false;$dbError = '';try {$pdo = new PDO('mysql:host=localhost;dbname=galeria;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);// Skrypt 1: zapytanie 2$photos = $pdo->query('SELECT z.plik, z.tytul, z.polubienia, a.imie, a.nazwisko FROM zdjecia AS z JOIN autorzy AS a ON a.id = z.autorzy_id ORDER BY a.nazwisko' )->fetchAll();// Skrypt 2: zapytanie 1$favourite = $pdo->query('SELECT tytul, plik FROM zdjecia WHERE polubienia >= 100')->fetch();$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać zdjęć. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Galeria</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Zdjęcia</h1> </header> <asideid="topics"> <h2>Tematy zdjęć</h2> <ol> <li>Zwierzęta</li> <li>Krajobrazy</li> <li>Miasta</li> <li>Przyroda</li> <li>Samochody</li> </ol> </aside> <main><?phpforeach ($photosas$photo): ?> <figureclass="photo"> <imgsrc="<?= e($photo['plik']) ?>"alt="zdjęcie"> <figcaption> <h3><?= e($photo['tytul']) ?></h3> <p> Autor: <?= e($photo['imie']) ?><?= e($photo['nazwisko']) ?><?phpif ($photo['polubienia'] > POPULAR_LIKES): ?>.<br> Wiele osób polubiło ten obraz<?phpendif; ?> </p> <ahref="<?= e(rawurlencode($photo['plik'])) ?>"download>Pobierz</a> </figcaption> </figure><?phpendforeach; ?> </main> <asideid="favourite"> <h2>Najbardziej lubiane</h2><?phpif ($favourite): ?> <imgsrc="<?= e($favourite['plik']) ?>"alt="<?= e($favourite['tytul']) ?>"><?phpendif; ?> <strong>Zobacz wszystkie nasze zdjęcia</strong> </aside> <footer> <h5>Stronę wykonał: Bartosz Bryniarski</h5> </footer></body></html>
Podgląd strony po tym kroku (galeria.php)
Krok 37: Komunikat, gdy baza nie odpowiada
ok. 2 min
Polecenie z arkusza
Na końcu jest zamykane połączenie z serwerem.
Na początku bloku środkowego akapit z $dbError - tylko gdy catch coś do niego wpisał. Sprawdzone uruchomieniem z błędną nazwą bazy: strona się wyświetla, w bloku środkowym stoi „Nie udało się pobrać zdjęć…”, blok prawy ma sam nagłówek i napis, a w treści nie ma nazwy bazy ani kodu błędu SQL. To zwykły akapit spoza kart, więc nie dotyczy go styl ukrywający opisy.
galeria.phpPHPzmienione: 3 linie
<?phpconst POPULAR_LIKES = 40;functione(string $text): string{returnhtmlspecialchars($text, ENT_QUOTES);}$photos = [];$favourite = false;$dbError = '';try {$pdo = new PDO('mysql:host=localhost;dbname=galeria;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);// Skrypt 1: zapytanie 2$photos = $pdo->query('SELECT z.plik, z.tytul, z.polubienia, a.imie, a.nazwisko FROM zdjecia AS z JOIN autorzy AS a ON a.id = z.autorzy_id ORDER BY a.nazwisko' )->fetchAll();// Skrypt 2: zapytanie 1$favourite = $pdo->query('SELECT tytul, plik FROM zdjecia WHERE polubienia >= 100')->fetch();$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać zdjęć. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Galeria</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Zdjęcia</h1> </header> <asideid="topics"> <h2>Tematy zdjęć</h2> <ol> <li>Zwierzęta</li> <li>Krajobrazy</li> <li>Miasta</li> <li>Przyroda</li> <li>Samochody</li> </ol> </aside> <main><?phpif ($dbError !== ''): ?> <p><?= e($dbError) ?></p><?phpendif; ?><?phpforeach ($photosas$photo): ?> <figureclass="photo"> <imgsrc="<?= e($photo['plik']) ?>"alt="zdjęcie"> <figcaption> <h3><?= e($photo['tytul']) ?></h3> <p> Autor: <?= e($photo['imie']) ?><?= e($photo['nazwisko']) ?><?phpif ($photo['polubienia'] > POPULAR_LIKES): ?>.<br> Wiele osób polubiło ten obraz<?phpendif; ?> </p> <ahref="<?= e(rawurlencode($photo['plik'])) ?>"download>Pobierz</a> </figcaption> </figure><?phpendforeach; ?> </main> <asideid="favourite"> <h2>Najbardziej lubiane</h2><?phpif ($favourite): ?> <imgsrc="<?= e($favourite['plik']) ?>"alt="<?= e($favourite['tytul']) ?>"><?phpendif; ?> <strong>Zobacz wszystkie nasze zdjęcia</strong> </aside> <footer> <h5>Stronę wykonał: Bartosz Bryniarski</h5> </footer></body></html>
Podgląd strony po tym kroku (galeria.php)
Etap 5: Porównanie z wersją podstawową
Krok 38 · po tym etapie masz 37 z 37 kryteriów (pkt)
Krok 38: Co jest inaczej i którą wersję wybrać na egzaminie
ok. 2 min
Obie wersje dają komplet 37 punktów. Różnice:
wersja podstawowa
wersja nowoczesna
układ bloków
float: left + clear: both, szerokości w width
grid-template-areas, kolumny 15% 70% 15%
karty zdjęć
<div class="photo">, float
<figure class="photo"> z <figcaption>, float (arkusz)
style opisu
trzy selektory po przecinku, transition: 0.5s ease
aliasy z i a, COUNT(z.id) AS liczba_zdjec, INT UNSIGNED
baza
mysqli_connect, mysqli_fetch_row, $row[2]
PDO z wyjątkami, fetchAll / fetch, $photo['polubienia']
gdzie PHP
dwa bloki w środku HTML-a, echo z ukośnikami
dane na górze pliku, karty w zwykłym HTML-u
brak zdjęcia ze 100 polubieniami
ostrzeżenia PHP na stronie
blok bez obrazu
dane na stronie
wprost (w dobrych praktykach htmlspecialchars)
e() przy każdej wartości, rawurlencode() w adresie
Na egzaminie wersja podstawowa trzyma się funkcji z tabeli 1 arkusza i jest szybsza do napisania. Nowoczesna oddziela pobieranie danych od wyglądu i łatwiej ją czytać - karta zdjęcia stoi w szablonie jako zwykły HTML, a nie w napisach echo. Z obu dróg warto zapamiętać jedno: procentowe szerokości elementów siatki liczą się od ich kolumny, więc przy grid szerokości bloków zapisuje się w grid-template-columns, a nie w width.
Krok 39 · po tym etapie masz 37 z 37 kryteriów (pkt)
Krok 39: 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 weryfikowana była poprawność działania witryny. Umieść go w folderze z numerem zdającego. Nagraj płytę z rezultatami pracy.
W przeglądarka.txt nazwa przeglądarki, w której strona była sprawdzana przez http://localhost/.... W folderze z numerem zdającego: galeria.php, import.png, kw1.jpg - kw4.jpg, kwerendy.txt, przeglądarka.txt, styl.css i wszystkie zdjęcia z archiwum (w tym przeskalowany woodpecker.jpg). 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.