INF.03-11-26.01-SGStyczeń 2026 · 46 kroków · ok. 91 min · 39 kryteriów z klucza
Rozwiązanie krok po kroku: Portal kina KinoTEKA (Nowoczesny kod (grid, PDO))
To drugi sposób rozwiązania tego samego arkusza: portal kina KinoTEKA z bazą kino (relacja n:m aktorzy - filmy), stroną index.php z okrągłymi awatarami wszystkich aktorów i stroną aktor.php z jednym aktorem i liczbą jego filmów. Obie wersje spełniają wszystkie 39 kryteriów klucza - różni je sposób pisania kodu, nie wynik.
Czym ta wersja różni się od podstawowej:
siatka CSS zamiast float + clear i inline-block: kolumny 40% 60% na <body>, a bloki aktorów w siatce repeat(auto-fill, 15em), kolory i szerokość karty w zmiennych CSS,
blok aktora sam jest odnośnikiem (<a class="actor">) zamiast <div> owiniętego w <a>, awatary zawsze okrągłe dzięki aspect-ratio i object-fit,
zapytania z jawną listą pól i aliasami tabel,
PHP na PDO: w obu plikach dane pobierane na górze, połączenie zamknięte przed HTML-em, w HTML tylko pętla foreach i warunki, imię i nazwisko z funkcji fullName(),
identyfikator z adresu sprawdzony filter_var i wstawiony do zapytań przygotowanych, komunikat dla nieistniejącego aktora, wartości z bazy przez escape().
Kroki nadal idą zdanie po zdaniu za treścią arkusza. Podgląd w każdym kroku z PHP to wynik prawdziwego uruchomienia kodu z tego kroku w PHP 8.2 na bazie z arkusza; aktor.php pokazuje Pinto Teixeirę (id 10) z ilustracji 4, a w kroku z warunkiem - Lorenzo Di Romano (id 8), który nie gra w żadnym filmie. Baza i awatary pochodzą z prawdziwego archiwum pliki17 (załącznik do arkusza, hasło w treści zadania).
Jak liczą się punkty. Klucz ma 39 kryteriów (R.1.1 - R.5.8), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 30 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-8 · po tym etapie masz 7 z 39 kryteriów (pkt)
Krok 1: Baza danych kino
ok. 2 min
Polecenie z arkusza
Utwórz bazę danych o nazwie kino, z zestawem polskich znaków (np. utf8_unicode_ci)
Archiwum pliki17.7z ma hasło @ktorzy+f1Lmy (małpa zamiast „a”, cyfra 1 zamiast „i”). W phpMyAdmin: Nowa, nazwa kino, porównywanie utf8_unicode_ci (podpowiedź z arkusza), albo w zakładce SQL:
CREATE DATABASE kino CHARACTER SET utf8 COLLATE utf8_unicode_ci;
Tabele z kino.sql mają w definicji własne porównywanie utf8mb4_unicode_ci i to ono działa w zapytaniu 3: „Élodie” i „Jímenez” sortują się jak zwykłe „E” i „i”. Ustawienie bazy dziedziczą tylko tabele zakładane później bez własnego ustawienia.
Typowe błędy
Skrypty łączą się z bazą po nazwie - literówka w kino zabiera także punkt za połączenie (R.5.1).
Krok 2: Import danych z pliku kino.sql
ok. 2 min
Polecenie z arkusza
Z rozpakowanego archiwum zaimportuj do utworzonej bazy dane z pliku kino.sql
Zaznacz bazę kino, potem Import → kino.sql → Wykonaj. Trzy tabele z ilustracji 1: aktorzy (10 aktorów, pole plik_awatara), filmy (15 filmów, czas_trwania w minutach) i filmy_aktorzy - same pary identyfikatorów, czyli relacja n:m.
Krok 3: Zrzut ekranu import.png
Kryteria:
R.1.1 Wykonany import do bazy danych kino - plik import.png
ok. 2 min
Polecenie z arkusza
Wykonaj zrzut ekranu po imporcie. Zapisz zrzut w formacie PNG pod nazwą 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ń), wklej do edytora grafiki i zapisz jako import.png. Zrzuty zapytań są w formacie JPEG - tylko import idzie do PNG.
Typowe błędy
Wykadrowany zrzut albo zrzut bez paska zadań nie jest oceniany (uwaga w kluczu przy R.1).
Krok 4: Zapytanie 1: filmy trwające co najmniej 100 minut
Kryteria: ,
R.1.2 W pliku kwerendy.txt zapisane co najmniej jedno zapytanie SQL, wynikające z treści zadania
R.1.3 Wykonane zapytanie 1 wybierające jedynie pola id_filmu, tytul, rok_produkcji, czas_trwania z tabeli filmy dla filmów, które trwają nie krócej niż 100 minut. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT id_filmu, tytul, rok_produkcji, czas_trwania FROM filmy WHERE czas_trwania >= 100; oraz na zrzucie jest dokładnie 11 rekordów
ok. 2 min
Polecenie z arkusza
Zapytanie 1: wybierające jedynie identyfikator, tytuł, rok produkcji oraz czas trwania, dla filmów które trwają nie krócej niż 100 minut
SELECT id_filmu, tytul, rok_produkcji, czas_trwania - cztery pola, bez reżysera,
WHERE czas_trwania >= 100 - „nie krócej niż 100 minut” to większe lub równe, więc film trwający równo 100 minut też jest w wyniku.
Wynik: 11 wierszy. Każda klauzula w osobnej linii - SQL nie zwraca uwagi na znaki nowej linii. Zapisz w kwerendy.txt, zrzut jako kw1.jpg.
Typowe błędy
czas_trwania > 100 gubi film trwający równo 100 minut - 10 wierszy zamiast 11.
R.1.4 Wykonane zapytanie 2 wybierające jedynie pola imie, nazwisko, plik_awatara z tabeli aktorzy dla aktora, którego identyfikator to 3. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT imie, nazwisko, plik_awatara FROM aktorzy WHERE id_aktora = 3; oraz na zrzucie jest dokładnie 1 wiersz przedstawiający dane: Jerzy, Pasek, awatar_3.jpg
ok. 1 min
Polecenie z arkusza
Zapytanie 2: wybierające jedynie imię, nazwisko i nazwę pliku awatara aktora, którego identyfikator to 3
Trzy pola, warunek na kluczu głównym - wynik ma najwyżej jeden wiersz: Jerzy Pasek, awatar_3.jpg. To zapytanie wróci w skrypcie 2, z parametrem zamiast trójki. Zrzut: kw2.jpg.
R.1.5 Wykonane zapytanie 3 wybierające wszystkie dane o aktorach, sortując je według nazwiska i imienia alfabetycznie (rosnąco). W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT * FROM aktorzy ORDER BY nazwisko, imie; (opcjonalnie ASC, zamiast * można wymienić wszystkie pola z tabeli) oraz na zrzucie jest co najmniej 10 rekordów
ok. 2 min
Polecenie z arkusza
Zapytanie 3: wybierające wszystkie dane o aktorach, sortując je według nazwiska i imienia alfabetycznie (rosnąco)
SELECT id_aktora, imie, nazwisko, ocena, plik_awatara - wszystkie pięć pól wymienione z nazwy zamiast *. Wynik jest ten sam, ale kod mówi, czego używa, i nie zmieni się po cichu, gdy ktoś doda do tabeli kolumnę (klucz uznaje oba zapisy),
ORDER BY nazwisko, imie - najpierw nazwisko, przy równych nazwiskach imię; rosnąco to kierunek domyślny.
Wynik: 10 aktorów od Anaïs do Teixeiry - kolejność awatarów z ilustracji 2. Zrzut: kw3.jpg.
R.1.6 Wykonane zapytanie 4 wybierające jedynie identyfikator, tytuł oraz rok produkcji filmów, w których gra aktor z identyfikatorem 3. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT filmy.id_filmu, tytul, rok_produkcji FROM filmy JOIN filmy_aktorzy USING(id_filmu) WHERE filmy_aktorzy.id_aktora = 3; (możliwe również porównanie kluczy po ON lub WHERE) oraz na zrzucie jest dokładnie 11 rekordów
ok. 3 min
Polecenie z arkusza
Zapytanie 4: wybierające jedynie identyfikatory, tytuły oraz rok produkcji filmów, w których gra aktor z identyfikatorem 3. Należy posłużyć się relacją
FROM filmy AS f JOIN filmy_aktorzy AS fa - aliasy tabel,
ON fa.id_filmu = f.id_filmu - relacja z ilustracji 1,
WHERE fa.id_aktora = 3 - tylko pary z aktorem 3,
SELECT f.id_filmu, f.tytul, f.rok_produkcji - pole id_filmu jest w obu tabelach, prefiks f. mówi, z której (bez niego błąd „ambiguous”).
Wynik: 11 filmów. Z tego zapytania powstanie skrypt 3. Zrzut: kw4.jpg.
kwerendy.txtTekstzmienione: 6 linii
-- Zapytanie 1SELECT id_filmu, tytul, rok_produkcji, czas_trwaniaFROM filmyWHERE czas_trwania >= 100;-- Zapytanie 2SELECT imie, nazwisko, plik_awataraFROM aktorzyWHERE id_aktora = 3;-- Zapytanie 3SELECT id_aktora, imie, nazwisko, ocena, plik_awataraFROM aktorzyORDER BY nazwisko, imie;-- Zapytanie 4SELECT f.id_filmu, f.tytul, f.rok_produkcjiFROM filmy AS fJOIN filmy_aktorzy AS fa ON fa.id_filmu = f.id_filmuWHERE fa.id_aktora = 3;
Podgląd strony po tym kroku
Krok 8: Zapytanie 5: liczba filmów każdego aktora
Kryteria:
R.1.7 Wykonane zapytanie 5 wybierające id aktora oraz zliczające liczbę filmów alias liczba_filmow, w których grał każdy aktor, pogrupowane ze względu na identyfikator aktora. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT id_aktora, COUNT(*) AS liczba_filmow FROM filmy_aktorzy GROUP BY id_aktora; (AS opcjonalnie, zamiast * możliwe dowolne pole) oraz na zrzucie jest dokładnie 9 rekordów: 1, 8; 2, 9; 3, 11; 4, 10; 5, 9; 6, 8; 7, 11; 9, 11; 10, 7
ok. 2 min
Polecenie z arkusza
Zapytanie 5: wybierające id aktora oraz zliczające liczbę filmów, alias „liczba_filmow”, w których grał każdy aktor, pogrupowane ze względu na identyfikator aktora
COUNT(*) AS liczba_filmow - liczba wierszy w grupie pod nazwą z arkusza,
FROM filmy_aktorzy - każdy wiersz tabeli pośredniej to jeden film aktora,
GROUP BY id_aktora - jedna grupa na aktora.
Wynik: 9 wierszy (1, 8; 2, 9; 3, 11; 4, 10; 5, 9; 6, 8; 7, 11; 9, 11; 10, 7). Aktora 8 nie ma - nie gra w żadnym filmie; to Lorenzo Di Romano, przy którym skrypt 3 wypisze drugi wariant napisu. Zrzut: kw5.jpg.
kwerendy.txtTekstzmienione: 5 linii
-- Zapytanie 1SELECT id_filmu, tytul, rok_produkcji, czas_trwaniaFROM filmyWHERE czas_trwania >= 100;-- Zapytanie 2SELECT imie, nazwisko, plik_awataraFROM aktorzyWHERE id_aktora = 3;-- Zapytanie 3SELECT id_aktora, imie, nazwisko, ocena, plik_awataraFROM aktorzyORDER BY nazwisko, imie;-- Zapytanie 4SELECT f.id_filmu, f.tytul, f.rok_produkcjiFROM filmy AS fJOIN filmy_aktorzy AS fa ON fa.id_filmu = f.id_filmuWHERE fa.id_aktora = 3;-- Zapytanie 5SELECT id_aktora, COUNT(*) AS liczba_filmowFROM filmy_aktorzyGROUP BY id_aktora;
Podgląd strony po tym kroku
Etap 2: Strona HTML
Kroki 9-20 · po tym etapie masz 17 z 39 kryteriów (pkt)
Krok 9: Plik index.php w HTML5 i awatary w folderze
ok. 2 min
Polecenie z arkusza
Zapisane w języku HTML5
<!DOCTYPE html> i szkielet strony; rozszerzenie .php, bo dojdą skrypty. Do tego samego folderu trafiają awatary z katalogu img archiwum - baza podaje samą nazwę pliku.
R.2.1 W plikach index.php lub aktor.php znajduje się deklaracja HTML5 <!DOCTYPE HTML> i deklaracja języka zawartości witryny: polski np. <html lang="pl">
ok. 1 min
Polecenie z arkusza
Zadeklarowany polski język zawartości witryny
Atrybut lang="pl" - z deklaracją HTML5 to jedno kryterium.
R.2.3 W sekcji body zdefiniowany jest podział na bloki za pomocą semantycznych znaczników HTML5: header+header, main, footer (dopuszcza się zastosowanie bloków pomocniczych)
ok. 2 min
Polecenie z arkusza
Podział strony na bloki zrealizowany za pomocą semantycznych znaczników bloków języka HTML5 tak, aby po uruchomieniu w przeglądarce układ bloków na stronie był zgodny z ilustracją 3
Klucz wymaga wprost header + header, main i footer (R.2.3). Bez identyfikatorów: szerokości nagłówków ustawi siatka CSS według kolejności w kodzie, a drugi nagłówek CSS znajdzie selektorem sąsiada.
Krok 15: Blok nagłówkowy 2: paragraf z tekstem uwypuklonym
Kryteria:
R.2.5 W bloku nagłówkowym 1 umieszczony <h2> „KinoTEKA” oraz w bloku nagłówkowym 2 - <p> o treści „W naszej bazie znajdują się najlepsi aktorzy”
ok. 1 min
Polecenie z arkusza
Zawartość bloku nagłówkowego 2: paragraf o treści „W naszej bazie znajdują się najlepsi aktorzy”. Tekst ten jest zapisany w znaczniku semantycznym oznaczającym tekst uwypuklony, formatowany domyślnie jako pochylony
Tekst uwypuklony to <em> - domyślnie pochylony; <i> też pochyla, ale nie niesie znaczenia (R.2.5).
index.phpPHPzmienione: 1 linia
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h2><ahref="index.php">KinoTEKA</a></h2> </header> <header> <p><em>W naszej bazie znajdują się najlepsi aktorzy</em></p> </header> <main> </main> <footer> </footer></body></html>
Podgląd strony po tym kroku (index.php)
Krok 16: Stopka: autor w znaczniku tekstu ważnego
Kryteria: ,
R.2.6 W stopce umieszczony <p> o treści: "Autor:" z numerem zdającego
R.2.7 W bloku nagłówkowym 2 użyty znacznik semantyczny <em> do tekstu oraz w stopce użyty znacznik semantyczny <strong> do numeru zdającego
ok. 1 min
Polecenie z arkusza
Zawartość stopki: paragraf o treści „Autor: ”, dalej wstawiony numer zdającego. Numer zdającego jest zapisany w znaczniku semantycznym oznaczającym tekst ważny, formatowany domyślnie jako pogrubiony
Tekst ważny to <strong> - domyślnie pogrubiony. Na egzaminie wpisuje się numer zdającego (PESEL); tu stoi „Bartosz Bryniarski” (R.2.6, R.2.7).
index.phpPHPzmienione: 1 linia
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h2><ahref="index.php">KinoTEKA</a></h2> </header> <header> <p><em>W naszej bazie znajdują się najlepsi aktorzy</em></p> </header> <main> </main> <footer> <p>Autor: <strong>Bartosz Bryniarski</strong></p> </footer></body></html>
Podgląd strony po tym kroku (index.php)
Krok 17: index.php: tytuł strony
Kryteria:
R.2.2 W sekcji head znajduje się jawnie podany standard kodowania polskich znaków. Stronie index.php nadano tytuł "Lista aktorów | KinoTEKA " lub stronie aktor.php nadano tytuł "Informacje o aktorze | KinoTEKA"
ok. 1 min
Polecenie z arkusza
Tytuł strony widoczny na karcie przeglądarki: „Lista aktorów | KinoTEKA”
<title> z pionową kreską, jak w arkuszu - najpierw treść strony, potem nazwa serwisu, więc na wąskiej karcie widać najważniejsze. Z kodowaniem to jedno kryterium (R.2.2).
index.phpPHPzmienione: 1 linia
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Lista aktorów | KinoTEKA</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h2><ahref="index.php">KinoTEKA</a></h2> </header> <header> <p><em>W naszej bazie znajdują się najlepsi aktorzy</em></p> </header> <main> </main> <footer> <p>Autor: <strong>Bartosz Bryniarski</strong></p> </footer></body></html>
Podgląd strony po tym kroku (index.php)
Krok 18: index.php: nagłówek pierwszego stopnia
Kryteria:
R.2.4 W bloku głównym na stronie głównej umieszczony <h1> o treści: „Najlepsi aktorzy tylko w naszym kinie”
ok. 1 min
Polecenie z arkusza
Nagłówek pierwszego stopnia o treści: „Najlepsi aktorzy tylko w naszym kinie”
<h1> na początku bloku głównego (R.2.4).
index.phpPHPzmienione: 1 linia
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Lista aktorów | KinoTEKA</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h2><ahref="index.php">KinoTEKA</a></h2> </header> <header> <p><em>W naszej bazie znajdują się najlepsi aktorzy</em></p> </header> <main> <h1>Najlepsi aktorzy tylko w naszym kinie</h1> </main> <footer> <p>Autor: <strong>Bartosz Bryniarski</strong></p> </footer></body></html>
Podgląd strony po tym kroku (index.php)
Krok 19: index.php: blok wszystkich aktorów
Kryteria:
R.2.8 Na stronie głównej w bloku głównym utworzony blok wszystkich aktorów (w HTML lub skrypcie)
ok. 1 min
Polecenie z arkusza
Blok wszystkich aktorów wypełniony skryptem 1
Pusty <div id="actors"> - skrypt 1 wypisze w nim karty aktorów. Zwykły div, bo to pojemnik do ułożenia kart w siatkę, bez własnego znaczenia (R.2.8).
index.phpPHPzmienione: 2 linie
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Lista aktorów | KinoTEKA</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h2><ahref="index.php">KinoTEKA</a></h2> </header> <header> <p><em>W naszej bazie znajdują się najlepsi aktorzy</em></p> </header> <main> <h1>Najlepsi aktorzy tylko w naszym kinie</h1> <divid="actors"> </div> </main> <footer> <p>Autor: <strong>Bartosz Bryniarski</strong></p> </footer></body></html>
Podgląd strony po tym kroku (index.php)
Krok 20: aktor.php: kopia strony z innym tytułem
ok. 2 min
Polecenie z arkusza
Tytuł strony widoczny na karcie przeglądarki: „Informacje o aktorze | KinoTEKA”
aktor.php powstaje jako kopia index.php z innym tytułem i bez <h1> - tu nagłówkiem pierwszego stopnia będzie imię i nazwisko aktora ze skryptu 2. Blok wszystkich aktorów zostaje: arkusz każe wypełnić go skryptem 2.
aktor.phpPHPnowy plik
<!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Informacje o aktorze | KinoTEKA</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h2><ahref="index.php">KinoTEKA</a></h2> </header> <header> <p><em>W naszej bazie znajdują się najlepsi aktorzy</em></p> </header> <main> <divid="actors"> </div> </main> <footer> <p>Autor: <strong>Bartosz Bryniarski</strong></p> </footer></body></html>
Podgląd strony po tym kroku (aktor.php)
Etap 3: Styl CSS
Kroki 21-32 · po tym etapie masz 28 z 39 kryteriów (pkt)
Krok 21: Zmienne i krój czcionki dla wszystkich elementów
Kryteria:
R.4.1 domyślnie dla wszystkich selektorów (*): krój czcionki "Gill Sans MT", "Calibri" (dozwolona tylko taka kolejność)
ok. 2 min
Polecenie z arkusza
Domyślne formatowanie wszystkich selektorów: krój czcionki „Gill Sans MT”, a w przypadku jej braku „Calibri”
W :rootzmienne CSS: kolor odnośnika pod kursorem i szerokość karty aktora - ta wartość przyda się w dwóch regułach. Selektor *: "Gill Sans MT" w cudzysłowie (nazwa ze spacjami), potem Calibri, dokładnie w tej kolejności i bez trzeciego kroju - klucz dopuszcza tylko taki zapis (R.4.1).
styl.cssCSSnowy plik
:root {--link-hover: #E30613;--actor-width: 15em;}* {font-family: "Gill Sans MT", Calibri;}
Podgląd strony po tym kroku (index.php)
Krok 22: Ciało strony: szerokość i wyśrodkowanie
Kryteria:
R.4.3 dla ciała strony lub dla bloku wszystkich aktorów - marginesy zewnętrzne górny i dolny 0, lewy i prawy auto
ok. 1 min
Polecenie z arkusza
Dla selektora ciała strony: szerokość 90%, marginesy zewnętrzne górny i dolny 0, lewy i prawy automatycznie wyliczone przez przeglądarkę
margin: 0 auto - góra i dół 0, boki wyliczone po równo: strona na 90% szerokości stoi na środku okna. Selektor znacznika body, jak każe uwaga arkusza (R.4.3).
Krok 23: Blok nagłówkowy 1: szerokość z kolumny siatki
ok. 2 min
Polecenie z arkusza
Dla bloku nagłówkowego 1: szerokość 40%
<body> staje się siatką o kolumnach 40% 60%: pierwszy nagłówek trafia do kolumny 40%, drugi do 60% - stoją obok siebie bez float. Klucz uznaje szerokości z grid-template-columns. Blok główny i stopka trafiają na razie do pojedynczych komórek - rozciągnie je krok o układzie.
R.4.6 wyrównanie tekstu do prawej strony dla bloku nagłówkowego 2
ok. 2 min
Polecenie z arkusza
Dla bloku nagłówkowego 2: szerokość 60% oraz wyrównanie tekstu do prawej strony
Szerokość 60% daje druga kolumna siatki. Wyrównanie do prawej potrzebuje selektora drugiego nagłówka: header + header to kombinator sąsiada - <header> stojący tuż po innym <header>, czyli tylko drugi z nich (R.4.6).
R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z ilustracją 3 w arkuszu egzaminacyjnym (prawidłowo zastosowano właściwości CSS decydujące o układzie bloków na stronie, np. float i clear albo display flex albo grid albo position), (nie należy brać pod uwagę bloków aktorów)
ok. 2 min
Polecenie z arkusza
Podział strony na bloki zrealizowany za pomocą semantycznych znaczników bloków języka HTML5 tak, aby po uruchomieniu w przeglądarce układ bloków na stronie był zgodny z ilustracją 3
grid-column: 1 / -1 rozciąga blok główny i stopkę od pierwszej do ostatniej linii siatki - każde zajmuje cały wiersz pod nagłówkami (R.3.1). Bez float nie trzeba clear.
Typowe błędy
Bez tej reguły blok główny zostaje w kolumnie 40%, a stopka w kolumnie 60%.
Dla bloku wszystkich aktorów: marginesy zewnętrzne górny i dolny 0, lewy i prawy automatyczne
To samo margin: 0 auto co dla body. Blok bez własnej szerokości zajmuje cały wiersz, więc boczne marginesy wychodzą zerowe - reguła zadziała, gdy blok dostanie szerokość.
Krok 27: index.php: karty aktorów obok siebie w siatce
Kryteria: ,
R.4.4 dla bloku pojedynczego aktora - tekst wyśrodkowany
R.4.5 bloki pojedynczych aktorów na stronie głównej są ustawione obok siebie (np. float, flex, grid)
ok. 3 min
Polecenie z arkusza
Dla bloku pojedynczego aktora na stronie index.php, generowanego skryptem 1: szerokość 15 em, tekst wyśrodkowany, bloki są wyświetlane obok siebie
Karta aktora ma klasę actor: szerokość var(--actor-width) (15em) i środkowanie (R.4.4). Obok siebie stawia je siatka w bloku wszystkich aktorów: repeat(auto-fill, 15em) - tyle kolumn po 15em, ile zmieści się w szerokości, a reszta przechodzi do następnego wiersza (R.4.5). Na szerokim ekranie sześć w rzędzie, jak na ilustracji 2. Ta sama zmienna w obu regułach - zmiana szerokości karty to jedna linia.
Dla bloku pojedynczego aktora na stronie aktor.php, generowanego skryptem 2: szerokość 25 em, tekst wyśrodkowany
Na aktor.php karta jest jedna i większa - klasa actor-profile, 25em. Stoi w tym samym bloku #actors, więc zajmuje pierwszą kolumnę siatki i rozszerza ją do swojej szerokości.
R.4.2 szerokości elementów: 90% - dla selektora body, 40% - dla bloku nagłówkowego 1 60% - dla bloku nagłówkowego 2 15em - dla bloku pojedynczego aktora na stronie index.php 25em - dla bloku pojedynczego aktora na stronie aktor.php 90% - dla selektora img Wystarczą 4 z 6 ustawień
R.4.8 dla obrazu - zaokrąglenie obramowania 50%
ok. 2 min
Polecenie z arkusza
Dla selektora obrazu: szerokość 90%, zaokrąglenie obramowania 50%
width: 90% liczy się od karty, a border-radius: 50% zaokrągla rogi w koło (R.4.8). Koło wychodzi tylko z kwadratu, więc dwie właściwości dodatkowe: aspect-ratio: 1 - wysokość równa szerokości, i object-fit: cover - zdjęcie w innych proporcjach wypełnia ten kwadrat i obcina nadmiar zamiast się rozciągać. Awatary z archiwum są kwadratowe, więc tu nic się nie zmienia; zabezpieczenie dotyczy następnego zdjęcia w bazie. Komplet szerokości z klucza (R.4.2).
Krok 30: Paragraf w karcie aktora: większa czcionka
Kryteria:
R.4.7 dla selektora p znajdującego się wyłącznie w bloku pojedynczego aktora - rozmiar czcionki 125%
ok. 1 min
Polecenie z arkusza
Dla selektora paragrafu znajdującego się w bloku pojedynczego aktora: rozmiar czcionki 125%
Selektor potomka .actor p - tylko akapity w karcie; motto, stopka i napis skryptu 3 zostają w zwykłym rozmiarze. To nadal selektor znacznika p (R.4.7).
R.4.10 gdy kursor znajdzie się na odnośniku, jego kolor czcionki zmienia się na #E30613
ok. 1 min
Polecenie z arkusza
Gdy kursor znajdzie się na odnośniku: kolor czcionki #E30613
:hover (R.4.10) i ten sam kolor dla :focus-visible - karty wybranej klawiszem Tab. Na ilustracji 2 kursor stoi na drugiej karcie - „Davide Costaluca” jest czerwony.
Kroki 33-43 · po tym etapie masz 39 z 39 kryteriów (pkt)
Krok 33: index.php: połączenie PDO na górze pliku
Kryteria:
R.5.1 Dowolny ze skryptów realizuje połączenie z serwerem bazy danych i wybór bazy kino , a ostatnią operacją na bazie jest jej zamknięcie
ok. 4 min
Polecenie z arkusza
Napisane w języku PHP. Należy stosować znaczące nazewnictwo zmiennych i funkcji w języku polskim lub angielskim. Łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie kino. Na końcu skrypt zamyka połączenie z bazą
Praca z bazą stoi przed<!DOCTYPE html>: najpierw dane, potem HTML, który je tylko wypisuje. Połączenie przez PDO (klucz R.5 przyjmuje MySQLi i PDO):
DSN 'mysql:host=localhost;dbname=kino;charset=utf8mb4' - serwer, baza i kodowanie; 'root', '' - użytkownik i puste hasło,
ERRMODE_EXCEPTION - błąd bazy rzuca wyjątek, FETCH_ASSOC - wiersze jako tablice z nazwami kolumn,
$pdo = null zamyka połączenie - zapytanie stanie przed tą linią, więc zamknięcie zostanie ostatnią operacją na bazie (R.5.1),
try / catch - gdy baza nie działa, szczegóły idą do dziennika serwera, a w bloku głównym stoi ogólny komunikat $dbError,
dwie funkcje pomocnicze: escape() (htmlspecialchars(..., ENT_QUOTES) dla wartości z bazy) i fullName() - imię i nazwisko ze spacją, potrzebne na obu stronach po kilka razy.
index.phpPHPzmienione: 28 linii
<?phpfunctionescape(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES);}functionfullName(array$actor): string{return$actor['imie'] . ' ' . $actor['nazwisko'];}$actors = [];$dbError = '';try {$pdo = new PDO('mysql:host=localhost;dbname=kino;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać danych o aktorach. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Lista aktorów | KinoTEKA</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h2><ahref="index.php">KinoTEKA</a></h2> </header> <header> <p><em>W naszej bazie znajdują się najlepsi aktorzy</em></p> </header> <main><?phpif ($dbError !== ''): ?> <p><?= $dbError?></p><?phpendif; ?> <h1>Najlepsi aktorzy tylko w naszym kinie</h1> <divid="actors"> </div> </main> <footer> <p>Autor: <strong>Bartosz Bryniarski</strong></p> </footer></body></html>
Podgląd strony po tym kroku (index.php)
Krok 34: Skrypt 1: zapytanie 3
Kryteria:
R.5.2 Skrypt 1 (strona główna) zawiera instrukcję wysyłającą do bazy zapytanie 3
ok. 2 min
Polecenie z arkusza
Wysyła do bazy danych zapytanie 3
$pdo->query(...) wysyła zapytanie 3 z kwerendy.txt, a ->fetchAll() odbiera wszystkich dziesięciu aktorów do tablicy $actors (R.5.2). Bez danych od użytkownika wystarcza query.
index.phpPHPzmienione: 5 linii
<?phpfunctionescape(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES);}functionfullName(array$actor): string{return$actor['imie'] . ' ' . $actor['nazwisko'];}$actors = [];$dbError = '';try {$pdo = new PDO('mysql:host=localhost;dbname=kino;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$actors = $pdo->query('SELECT id_aktora, imie, nazwisko, ocena, plik_awatara FROM aktorzy ORDER BY nazwisko, imie' )->fetchAll();$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać danych o aktorach. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Lista aktorów | KinoTEKA</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h2><ahref="index.php">KinoTEKA</a></h2> </header> <header> <p><em>W naszej bazie znajdują się najlepsi aktorzy</em></p> </header> <main><?phpif ($dbError !== ''): ?> <p><?= $dbError?></p><?phpendif; ?> <h1>Najlepsi aktorzy tylko w naszym kinie</h1> <divid="actors"> </div> </main> <footer> <p>Autor: <strong>Bartosz Bryniarski</strong></p> </footer></body></html>
Krok 35: Skrypt 1: karta aktora z imieniem i nazwiskiem
ok. 3 min
Polecenie z arkusza
Dla każdego wiersza zwróconego z bazy tworzy blok pojedynczego aktora zawierający paragraf zawierający tekst „<IMIE> <NAZWISKO>”
W bloku wszystkich aktorów pętla foreach ($actors as $actor): w składni alternatywnej - jedna karta actor na wiersz. W akapicie fullName($actor) przez escape(). Pola po nazwach kolumn, więc kolejność pól w zapytaniu nie ma znaczenia. Dziesięć nazwisk w kolejności zapytania 3, już w siatce obok siebie.
index.phpPHPzmienione: 5 linii
<?phpfunctionescape(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES);}functionfullName(array$actor): string{return$actor['imie'] . ' ' . $actor['nazwisko'];}$actors = [];$dbError = '';try {$pdo = new PDO('mysql:host=localhost;dbname=kino;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$actors = $pdo->query('SELECT id_aktora, imie, nazwisko, ocena, plik_awatara FROM aktorzy ORDER BY nazwisko, imie' )->fetchAll();$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać danych o aktorach. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Lista aktorów | KinoTEKA</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h2><ahref="index.php">KinoTEKA</a></h2> </header> <header> <p><em>W naszej bazie znajdują się najlepsi aktorzy</em></p> </header> <main><?phpif ($dbError !== ''): ?> <p><?= $dbError?></p><?phpendif; ?> <h1>Najlepsi aktorzy tylko w naszym kinie</h1> <divid="actors"><?phpforeach ($actorsas$actor): ?> <divclass="actor"> <p><?= escape(fullName($actor)) ?></p> </div><?phpendforeach; ?> </div> </main> <footer> <p>Autor: <strong>Bartosz Bryniarski</strong></p> </footer></body></html>
Podgląd strony po tym kroku (index.php)
Krok 36: Skrypt 1: awatar z tekstem alternatywnym i atrybutem title
Kryteria: , ,
R.5.3 Skrypt 1 wyświetla obrazy oraz imię i nazwisko aktorów dla wszystkich zwróconych zapytaniem wierszy
R.3.5 Na stronie głównej obrazy znajdujące się w blokach pojedynczych aktorów mają ustawiony tekst alternatywny i atrybut title
R.2.9 Na stronie głównej w bloku wszystkich aktorów znajduje się co najmniej jeden blok pojedynczego aktora zawierający obraz oraz paragraf (w HTML lub skrypcie)
ok. 3 min
Polecenie z arkusza
Obraz, którego źródłem jest plik podany w kolumnie plik_awatara, z tekstem alternatywnym oraz atrybutem title z wartością „<IMIE> <NAZWISKO>” (Ilustracja 2, awatar 2)
src z pola plik_awatara, a imię i nazwisko trafia do alt (czytnik ekranu, brak obrazu) i title (dymek po najechaniu kursorem; R.5.3, R.3.5, R.2.9). Wartości stoją w atrybutach w cudzysłowach, więc escape() z ENT_QUOTES jest tu konieczne - nazwisko z cudzysłowem zamknęłoby atrybut. Podgląd wygląda jak ilustracja 2.
index.phpPHPzmienione: 1 linia
<?phpfunctionescape(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES);}functionfullName(array$actor): string{return$actor['imie'] . ' ' . $actor['nazwisko'];}$actors = [];$dbError = '';try {$pdo = new PDO('mysql:host=localhost;dbname=kino;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$actors = $pdo->query('SELECT id_aktora, imie, nazwisko, ocena, plik_awatara FROM aktorzy ORDER BY nazwisko, imie' )->fetchAll();$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać danych o aktorach. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Lista aktorów | KinoTEKA</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h2><ahref="index.php">KinoTEKA</a></h2> </header> <header> <p><em>W naszej bazie znajdują się najlepsi aktorzy</em></p> </header> <main><?phpif ($dbError !== ''): ?> <p><?= $dbError?></p><?phpendif; ?> <h1>Najlepsi aktorzy tylko w naszym kinie</h1> <divid="actors"><?phpforeach ($actorsas$actor): ?> <divclass="actor"> <imgsrc="<?= escape($actor['plik_awatara']) ?>"alt="<?= escape(fullName($actor)) ?>"title="<?= escape(fullName($actor)) ?>"> <p><?= escape(fullName($actor)) ?></p> </div><?phpendforeach; ?> </div> </main> <footer> <p>Autor: <strong>Bartosz Bryniarski</strong></p> </footer></body></html>
Podgląd strony po tym kroku (index.php)
Krok 37: Skrypt 1: karta aktora jako odnośnik
Kryteria:
R.3.4 Na stronie głównej po kliknięciu bloku pojedynczego aktora jest otwierana strona aktor.php?id=… , gdzie … to id wybranego aktora (wystarczy sprawdzić dla dwóch bloków)
ok. 2 min
Polecenie z arkusza
Wygenerowany blok pojedynczego aktora jest odnośnikiem do pliku aktor.php?id=<ID_AKTORA>
Karta aktora sama staje się odnośnikiem: <div class="actor"> zamienia się na <a class="actor" href="aktor.php?id=7">. Nie trzeba owijać bloku w dodatkowy <a> - odnośnik w HTML5 może zawierać obraz i akapit, a jako element siatki jest blokiem tak samo jak div, więc reguła .actor i układ działają bez zmian (R.3.4). (int) przy identyfikatorze gwarantuje liczbę w adresie.
Teraz działa też reguła a: podpisy są czarne, a pod kursorem czerwone.
index.phpPHPzmienione: 2 linie
<?phpfunctionescape(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES);}functionfullName(array$actor): string{return$actor['imie'] . ' ' . $actor['nazwisko'];}$actors = [];$dbError = '';try {$pdo = new PDO('mysql:host=localhost;dbname=kino;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$actors = $pdo->query('SELECT id_aktora, imie, nazwisko, ocena, plik_awatara FROM aktorzy ORDER BY nazwisko, imie' )->fetchAll();$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać danych o aktorach. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Lista aktorów | KinoTEKA</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h2><ahref="index.php">KinoTEKA</a></h2> </header> <header> <p><em>W naszej bazie znajdują się najlepsi aktorzy</em></p> </header> <main><?phpif ($dbError !== ''): ?> <p><?= $dbError?></p><?phpendif; ?> <h1>Najlepsi aktorzy tylko w naszym kinie</h1> <divid="actors"><?phpforeach ($actorsas$actor): ?> <aclass="actor"href="aktor.php?id=<?= (int) $actor['id_aktora'] ?>"> <imgsrc="<?= escape($actor['plik_awatara']) ?>"alt="<?= escape(fullName($actor)) ?>"title="<?= escape(fullName($actor)) ?>"> <p><?= escape(fullName($actor)) ?></p> </a><?phpendforeach; ?> </div> </main> <footer> <p>Autor: <strong>Bartosz Bryniarski</strong></p> </footer></body></html>
Podgląd strony po tym kroku (index.php)
Krok 38: aktor.php: połączenie i identyfikator z adresu
ok. 3 min
Polecenie z arkusza
Skrypt 2, na stronie aktor.php: Korzysta z pola id przesłanego do pliku metodą GET
Na górze aktor.php te same funkcje i to samo połączenie co w index.php, a przed nimi odczyt identyfikatora: $_GET['id'] przechodzi przez filter_var z FILTER_VALIDATE_INT i minimum 1. Wynik to liczba albo false - dla abc, 3 OR 1=1 i dla braku parametru (operator ?? podstawia wtedy pusty napis, bez ostrzeżenia „Undefined array key”).
aktor.phpPHPzmienione: 29 linii
<?phpfunctionescape(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES);}functionfullName(array$actor): string{return$actor['imie'] . ' ' . $actor['nazwisko'];}$actorId = filter_var($_GET['id'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);$actor = null;$dbError = '';try {$pdo = new PDO('mysql:host=localhost;dbname=kino;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać danych o aktorach. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Informacje o aktorze | KinoTEKA</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h2><ahref="index.php">KinoTEKA</a></h2> </header> <header> <p><em>W naszej bazie znajdują się najlepsi aktorzy</em></p> </header> <main><?phpif ($dbError !== ''): ?> <p><?= $dbError?></p><?phpendif; ?> <divid="actors"> </div> </main> <footer> <p>Autor: <strong>Bartosz Bryniarski</strong></p> </footer></body></html>
Podgląd strony po tym kroku (aktor.php)
Krok 39: Skrypt 2: zapytanie 2 przygotowane
Kryteria:
R.5.4 Skrypt 2 lub skrypt 3 (podstrona) zawiera instrukcje wysyłającą odpowiednio zapytanie 2 lub 4 zmodyfikowane w ten sposób, że klauzula WHERE sprawdza wartość id z metody GET
ok. 3 min
Polecenie z arkusza
Wysyła do bazy danych zapytanie 2 zmodyfikowane w ten sposób, aby uzyskiwać informacje o aktorze z identyfikatorem równym polu id pobranym metodą GET
Tylko dla poprawnego identyfikatora: zapytanie 2 z parametrem :id zamiast trójki. prepare wysyła strukturę zapytania, execute(['id' => $actorId]) - wartość osobno, więc nie da się nią dopisać warunku do SQL (R.5.4). fetch() oddaje wiersz albo false, gdy aktora nie ma; ?: null zamienia false na czytelniejsze „brak”. Podgląd: aktor.php?id=10, blok jest jeszcze pusty.
aktor.phpPHPzmienione: 7 linii
<?phpfunctionescape(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES);}functionfullName(array$actor): string{return$actor['imie'] . ' ' . $actor['nazwisko'];}$actorId = filter_var($_GET['id'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);$actor = null;$dbError = '';try {$pdo = new PDO('mysql:host=localhost;dbname=kino;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);if ($actorId !== false) {$statement = $pdo->prepare('SELECT imie, nazwisko, plik_awatara FROM aktorzy WHERE id_aktora = :id' );$statement->execute(['id' => $actorId]);$actor = $statement->fetch() ?: null; }$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać danych o aktorach. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Informacje o aktorze | KinoTEKA</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h2><ahref="index.php">KinoTEKA</a></h2> </header> <header> <p><em>W naszej bazie znajdują się najlepsi aktorzy</em></p> </header> <main><?phpif ($dbError !== ''): ?> <p><?= $dbError?></p><?phpendif; ?> <divid="actors"> </div> </main> <footer> <p>Autor: <strong>Bartosz Bryniarski</strong></p> </footer></body></html>
Krok 40: Skrypt 2: karta aktora z awatarem i nagłówkiem
Kryteria:
R.5.5 Skrypt 2 wyświetla jeden blok pojedynczego aktora z obrazem i danymi zwróconymi zapytaniem
ok. 3 min
Polecenie z arkusza
Dla zwróconego z bazy wiersza tworzy blok pojedynczego aktora zawierający obraz, którego źródłem jest plik podany w kolumnie plik_awatara, z tekstem alternatywnym oraz atrybutem title z wartością „<IMIE> <NAZWISKO>” oraz nagłówek pierwszego stopnia zawierający tekst „<IMIE> <NAZWISKO>”
Gdy aktor się znalazł: karta <article class="actor-profile"> - article to samodzielna treść (strona o jednym aktorze), z awatarem (alt i title z fullName()) i imieniem i nazwiskiem w <h1> (R.5.5). Gdy nie - „Nie znaleziono aktora” zamiast pustej karty i ostrzeżeń PHP. Podgląd: Pinto Teixeira jak na ilustracji 4.
aktor.phpPHPzmienione: 8 linii
<?phpfunctionescape(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES);}functionfullName(array$actor): string{return$actor['imie'] . ' ' . $actor['nazwisko'];}$actorId = filter_var($_GET['id'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);$actor = null;$dbError = '';try {$pdo = new PDO('mysql:host=localhost;dbname=kino;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);if ($actorId !== false) {$statement = $pdo->prepare('SELECT imie, nazwisko, plik_awatara FROM aktorzy WHERE id_aktora = :id' );$statement->execute(['id' => $actorId]);$actor = $statement->fetch() ?: null; }$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać danych o aktorach. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Informacje o aktorze | KinoTEKA</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h2><ahref="index.php">KinoTEKA</a></h2> </header> <header> <p><em>W naszej bazie znajdują się najlepsi aktorzy</em></p> </header> <main><?phpif ($dbError !== ''): ?> <p><?= $dbError?></p><?phpendif; ?> <divid="actors"><?phpif ($actor !== null): ?> <articleclass="actor-profile"> <imgsrc="<?= escape($actor['plik_awatara']) ?>"alt="<?= escape(fullName($actor)) ?>"title="<?= escape(fullName($actor)) ?>"> <h1><?= escape(fullName($actor)) ?></h1> </article><?phpelse: ?> <h1>Nie znaleziono aktora</h1><?phpendif; ?> </div> </main> <footer> <p>Autor: <strong>Bartosz Bryniarski</strong></p> </footer></body></html>
Podgląd strony po tym kroku (aktor.php)
Krok 41: Skrypt 3: zapytanie 4 i liczba rekordów
ok. 3 min
Polecenie z arkusza
Skrypt 3, na stronie aktor.php: Korzysta z pola id przesłanego do pliku metodą GET. Wysyła do bazy danych zapytanie 4 zmodyfikowane w taki sposób, że pobiera wyniki dotyczące aktora z identyfikatorem równym polu id pobranym metodą GET
Zapytanie 4 z aliasami i tym samym parametrem :id - wysyłane tylko wtedy, gdy aktor istnieje. Skrypt potrzebuje liczby filmów, nie ich listy: count($statement->fetchAll()) liczy zwrócone rekordy. PDO ma też rowCount(), ale dla SELECT nie każda baza go obsługuje - count na pobranej tablicy działa zawsze. Dla Pinto Teixeiry: 7.
aktor.phpPHPzmienione: 11 linii
<?phpfunctionescape(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES);}functionfullName(array$actor): string{return$actor['imie'] . ' ' . $actor['nazwisko'];}$actorId = filter_var($_GET['id'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);$actor = null;$filmCount = 0;$dbError = '';try {$pdo = new PDO('mysql:host=localhost;dbname=kino;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);if ($actorId !== false) {$statement = $pdo->prepare('SELECT imie, nazwisko, plik_awatara FROM aktorzy WHERE id_aktora = :id' );$statement->execute(['id' => $actorId]);$actor = $statement->fetch() ?: null; }if ($actor !== null) {$statement = $pdo->prepare('SELECT f.id_filmu, f.tytul, f.rok_produkcji FROM filmy AS f JOIN filmy_aktorzy AS fa ON fa.id_filmu = f.id_filmu WHERE fa.id_aktora = :id' );$statement->execute(['id' => $actorId]);$filmCount = count($statement->fetchAll()); }$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać danych o aktorach. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Informacje o aktorze | KinoTEKA</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h2><ahref="index.php">KinoTEKA</a></h2> </header> <header> <p><em>W naszej bazie znajdują się najlepsi aktorzy</em></p> </header> <main><?phpif ($dbError !== ''): ?> <p><?= $dbError?></p><?phpendif; ?> <divid="actors"><?phpif ($actor !== null): ?> <articleclass="actor-profile"> <imgsrc="<?= escape($actor['plik_awatara']) ?>"alt="<?= escape(fullName($actor)) ?>"title="<?= escape(fullName($actor)) ?>"> <h1><?= escape(fullName($actor)) ?></h1> </article><?phpelse: ?> <h1>Nie znaleziono aktora</h1><?phpendif; ?> </div> </main> <footer> <p>Autor: <strong>Bartosz Bryniarski</strong></p> </footer></body></html>
Jeśli liczba rekordów uzyskanych zapytaniem wynosi 0, wyświetlany jest napis „<IMIE> nie znajduje się na listach obsady znanych nam produkcji.”, gdzie <IMIE> jest imieniem aktora pobranym w skrypcie 2
Pod blokiem aktora warunek: aktor istnieje i ma zero filmów. Imię „pobrane w skrypcie 2” to $actor['imie'] - wiersz z zapytania 2 czeka w zmiennej. Porównanie ścisłe === 0, bo count zawsze zwraca liczbę całkowitą. Podgląd: aktor.php?id=8, Lorenzo Di Romano.
aktor.phpPHPzmienione: 3 linie
<?phpfunctionescape(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES);}functionfullName(array$actor): string{return$actor['imie'] . ' ' . $actor['nazwisko'];}$actorId = filter_var($_GET['id'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);$actor = null;$filmCount = 0;$dbError = '';try {$pdo = new PDO('mysql:host=localhost;dbname=kino;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);if ($actorId !== false) {$statement = $pdo->prepare('SELECT imie, nazwisko, plik_awatara FROM aktorzy WHERE id_aktora = :id' );$statement->execute(['id' => $actorId]);$actor = $statement->fetch() ?: null; }if ($actor !== null) {$statement = $pdo->prepare('SELECT f.id_filmu, f.tytul, f.rok_produkcji FROM filmy AS f JOIN filmy_aktorzy AS fa ON fa.id_filmu = f.id_filmu WHERE fa.id_aktora = :id' );$statement->execute(['id' => $actorId]);$filmCount = count($statement->fetchAll()); }$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać danych o aktorach. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Informacje o aktorze | KinoTEKA</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h2><ahref="index.php">KinoTEKA</a></h2> </header> <header> <p><em>W naszej bazie znajdują się najlepsi aktorzy</em></p> </header> <main><?phpif ($dbError !== ''): ?> <p><?= $dbError?></p><?phpendif; ?> <divid="actors"><?phpif ($actor !== null): ?> <articleclass="actor-profile"> <imgsrc="<?= escape($actor['plik_awatara']) ?>"alt="<?= escape(fullName($actor)) ?>"title="<?= escape(fullName($actor)) ?>"> <h1><?= escape(fullName($actor)) ?></h1> </article><?phpelse: ?> <h1>Nie znaleziono aktora</h1><?phpendif; ?> </div><?phpif ($actor !== null && $filmCount === 0): ?> <p><?= escape($actor['imie']) ?> nie znajduje się na listach obsady znanych nam produkcji.</p><?phpendif; ?> </main> <footer> <p>Autor: <strong>Bartosz Bryniarski</strong></p> </footer></body></html>
Podgląd strony po tym kroku (aktor.php)
Krok 43: Skrypt 3: liczba filmów aktora
Kryteria: , ,
R.5.6 Skrypt 3: jeśli liczba rekordów zwrócona zapytaniem 4 wynosi 0 (Lorenzo Di Romano) , wyświetla tekst "<imie> nie znajduje się na listach obsady znanych nam produkcji.", a w przeciwnym wypadku wyświetla tekst "<imie> znajduje się na listach obsady <X> znanych nam produkcji."
R.5.7 Skrypt 3 w miejscu <X> wyświetla liczbę rekordów zwróconą przesłanym zapytaniem
R.5.8 W skryptach zastosowana przynajmniej jedna zmienna oraz nazwy wszystkich zmiennych są w języku polskim lub angielskim i odzwierciedlają cel zastosowania
ok. 3 min
Polecenie z arkusza
W przeciwnym wypadku wyświetlany jest napis „<IMIE> znajduje się na listach obsady <X> znanych nam produkcji.”, gdzie <IMIE> jest imieniem aktora pobranym w skrypcie 2, a <X> oznacza liczbę rekordów uzyskanych zapytaniem 4
Gałąź elseif ($actor !== null) - aktor istnieje i ma filmy; w miejscu <X> liczba $filmCount (R.5.6, R.5.7). Podgląd dla id 10: „Pinto znajduje się na listach obsady 7 znanych nam produkcji.”, jak na ilustracji 4.
Sprawdzone złe adresy: ?id=99, ?id=abc, ?id=3 OR 1=1, puste ?id= i samo aktor.php - zawsze „Nie znaleziono aktora”, bez napisu o filmach i bez ostrzeżeń PHP. Nazwy zmiennych i funkcji mówią, co przechowują i robią (R.5.8): $pdo, $actors, $actor, $actorId, $statement, $filmCount, $dbError, escape(), fullName().
aktor.phpPHPzmienione: 2 linie
<?phpfunctionescape(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES);}functionfullName(array$actor): string{return$actor['imie'] . ' ' . $actor['nazwisko'];}$actorId = filter_var($_GET['id'] ?? '', FILTER_VALIDATE_INT, ['options' => ['min_range' => 1]]);$actor = null;$filmCount = 0;$dbError = '';try {$pdo = new PDO('mysql:host=localhost;dbname=kino;charset=utf8mb4', 'root', '', [ PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC, ]);if ($actorId !== false) {$statement = $pdo->prepare('SELECT imie, nazwisko, plik_awatara FROM aktorzy WHERE id_aktora = :id' );$statement->execute(['id' => $actorId]);$actor = $statement->fetch() ?: null; }if ($actor !== null) {$statement = $pdo->prepare('SELECT f.id_filmu, f.tytul, f.rok_produkcji FROM filmy AS f JOIN filmy_aktorzy AS fa ON fa.id_filmu = f.id_filmu WHERE fa.id_aktora = :id' );$statement->execute(['id' => $actorId]);$filmCount = count($statement->fetchAll()); }$pdo = null;} catch (PDOException $exception) {error_log($exception->getMessage());$dbError = 'Nie udało się pobrać danych o aktorach. Spróbuj ponownie za chwilę.';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Informacje o aktorze | KinoTEKA</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h2><ahref="index.php">KinoTEKA</a></h2> </header> <header> <p><em>W naszej bazie znajdują się najlepsi aktorzy</em></p> </header> <main><?phpif ($dbError !== ''): ?> <p><?= $dbError?></p><?phpendif; ?> <divid="actors"><?phpif ($actor !== null): ?> <articleclass="actor-profile"> <imgsrc="<?= escape($actor['plik_awatara']) ?>"alt="<?= escape(fullName($actor)) ?>"title="<?= escape(fullName($actor)) ?>"> <h1><?= escape(fullName($actor)) ?></h1> </article><?phpelse: ?> <h1>Nie znaleziono aktora</h1><?phpendif; ?> </div><?phpif ($actor !== null && $filmCount === 0): ?> <p><?= escape($actor['imie']) ?> nie znajduje się na listach obsady znanych nam produkcji.</p><?phpelseif ($actor !== null): ?> <p><?= escape($actor['imie']) ?> znajduje się na listach obsady <?= $filmCount?> znanych nam produkcji.</p><?phpendif; ?> </main> <footer> <p>Autor: <strong>Bartosz Bryniarski</strong></p> </footer></body></html>
Podgląd strony po tym kroku (aktor.php)
Etap 5: Porównanie z wersją podstawową
Kroki 44-45 · po tym etapie masz 39 z 39 kryteriów (pkt)
Krok 44: Co jest inaczej i dlaczego
ok. 3 min
Polecenie z arkusza
Ocenie podlegać będzie 5 rezultatów: operacje na bazie danych, zawartość witryny internetowej, działanie witryny internetowej, styl CSS witryny internetowej, skrypt połączenia z bazą.
Obie wersje dają te same punkty. Różnice:
wersja podstawowa
ta wersja
układ
float: left + clear: both, identyfikatory logo, motto
Czas przeznaczony na wykonanie zadania wynosi 150 minut.
Wersja podstawowa, gdy liczy się czas albo PDO nie jest opanowane: mysqli_num_rows z tabeli 1 arkusza od razu daje liczbę filmów, a skrypty stoją tam, gdzie widać ich efekt.
Ta wersja, gdy PDO i siatka są pisane z pamięci. repeat(auto-fill, ...) warto znać - jedna linia zastępuje ręczne liczenie, ile kart zmieści się w rzędzie.
Mieszanie też daje pełne punkty: siatka z tej wersji i mysqli_* z podstawowej. Funkcje escape() i fullName() są powtórzone w obu plikach - w większym projekcie trafiłyby do osobnego pliku dołączanego przez require.
Krok 46 · po tym etapie masz 39 z 39 kryteriów (pkt)
Krok 46: 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/…/index.php.
W folderze z numerem zdającego: aktor.php, import.png, index.php, kwerendy.txt, kw1.jpg - kw5.jpg, przeglądarka.txt, styl.css i awatary awatar_1.jpg - awatar_10.jpg. Po nagraniu sprawdź odczyt płyty.
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.