Serwis używa plików cookies do analizy ruchu i poprawy działania. Szczegóły
EE.09-01-21.06-SGCzerwiec 2021 · 33 kroki · ok. 79 min · 34 kryteria z klucza
Rozwiązanie krok po kroku: Portal społecznościowy - lista przyjaciół (Nowoczesny kod (flex, PDO))
To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza dane z czterema zapytaniami, wykadrowane zdjęcie 100 × 100 px i strona lista.php z listą znajomych - i tak samo spełnia każde kryterium klucza. Inna jest droga:
flexbox zamiast float: strona jest kontenerem, który zawija bloki - baner i blok główny zajmują cały wiersz, a dwie stopki po 50% stają obok siebie bez float i bez clear,
karta znajomego w <article>: zdjęcie i opis to dwa bloki w karcie ułożonej flexboksem, linia <hr> zamyka kartę,
zmienne CSS dla kolorów z arkusza - każdy kolor jest wpisany raz,
PHP na PDO na górze pliku lista.php, przed HTML-em, a dane połączenia w osobnym pliku config.php,
szablon z pętlą foreach: HTML karty stoi w pliku jak zwykły HTML, a PHP wstawia tylko wartości - każdą przez htmlspecialchars,
komunikat zamiast błędu PHP, gdy baza nie odpowiada.
To arkusz EE.09 z czerwca 2021 - kwalifikacji formuły 2017, którą zastąpiła INF.03. Kroki bazy i grafiki są takie same jak w wersji podstawowej. Baza i zdjęcia pochodzą z oryginalnego archiwum CKE zad1.zip (załącznik na stronie arkusza), zapytania i zrzuty są policzone na MariaDB 10.4, a podgląd od kroków z PHP to wynik prawdziwego uruchomienia kodu w PHP 8.2 (klucz każe sprawdzać w XAMPP 7.4.8 - użyte konstrukcje działają w obu wersjach).
Jak liczą się punkty. Klucz ma 34 kryteria (R.1.1 - R.5.7), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 26 punktów. Przy każdym kroku jest napisane, które kryteria spełnia; klucz przyjmuje zarówno mysqli, jak i PDO.
Suwak i strzałki obok niego - albo ←→ na klawiaturze - przechodzą krok po kroku; wyjaśnienie, pliki i podgląd zmieniają się razem z krokiem.
Lista „Etap” przenosi od razu do wybranego etapu.
„Rozwiązuję samodzielnie” chowa rozwiązanie kroku - najpierw własna próba, potem odsłonięcie.
„Tryb prezentacji” pokazuje same kroki, bez menu strony - na rzutnik.
Etap 1: Baza danych
Kroki 1-7 · po tym etapie masz 6 z 34 kryteriów (pkt)
Krok 1: Folder zdającego i archiwum zad1
ok. 2 min
Polecenie z arkusza
Na pulpicie znajduje się archiwum ZIP o nazwie zad1.zip zabezpieczone hasłem: List@Przyjaciol& Należy utworzyć folder o nazwie odpowiadającej numerowi PESEL zdającego lub w przypadku jego braku innemu numerowi, którym został podpisany arkusz. Archiwum należy rozpakować do tego folderu.
Na pulpicie tworzysz folder nazwany numerem PESEL - w tym rozwiązaniu w jego miejscu stoi imię i nazwisko autora, Bartosz Bryniarski. Archiwum zad1.zip otwierasz programem 7-Zip (albo innym programem do archiwów ZIP) i rozpakowujesz do tego folderu, podając hasło List@Przyjaciol& (wielkość liter, @ i & mają znaczenie). W środku jest plik dane.sql i osiem zdjęć: osoba1.jpg - osoba8.jpg.
Arkusz podkreśla, że pliki strony muszą leżeć w tym samym folderze co zdjęcia - skrypt wstawi ich nazwy z bazy bez żadnej ścieżki.
Typowe błędy
Pliki zapisane poza folderem zdającego (np. w htdocs bez kopii w folderze) nie trafią na płytę - a ocenia się tylko to, co jest na płycie.
Krok 2: XAMPP i pusta baza dane
ok. 2 min
Polecenie z arkusza
Uruchom usługi MySQL i Apache za pomocą XAMPP Control Panel. Za pomocą narzędzia phpMyAdmin wykonaj podane operacje na bazie danych: Utwórz bazę danych o nazwie dane
W XAMPP Control Panel klikasz Start przy Apache i MySQL, potem w przeglądarce otwierasz http://localhost/phpmyadmin. Nowa w lewym panelu, nazwa dane, Utwórz. Porównywanie napisów możesz zostawić domyślne - tabele z pliku i tak przyniosą własne kodowanie (latin1).
Typowe błędy
Nazwa bazy musi brzmieć dokładnie dane - skrypt PHP łączy się z bazą po nazwie (R.5.1).
Krok 3: Import pliku dane.sql i zrzut egzamin.jpg
Kryteria:
R.1.1 Wykonano import tabel do bazy danych dane , czynność udokumentowano plikiem o nazwie egzamin w formacie JPEG
ok. 3 min
Polecenie z arkusza
Do bazy dane zaimportuj plik dane.sql z rozpakowanego archiwum. Wykonaj zrzut ekranu po imporcie. Zrzut zapisz w folderze z numerem PESEL lub w przypadku jego braku w folderze z innym numerem, którym został podpisany arkusz, w formacie JPEG i nazwij egzamin. Nie kadruj zrzutu. Powinien on obejmować cały ekran monitora, z widocznym paskiem zadań. Na zrzucie powinny być widoczne elementy wskazujące na poprawnie wykonany import tabel
Zaznacz bazę dane, karta Import, Wybierz plik → dane.sql, Wykonaj. Plik tworzy pięć tabel: hobby (16 rekordów), osoby (40 rekordów) oraz puste producenci, produkty i rodzaje - do zadania potrzebne są tylko dwie pierwsze.
„Elementy wskazujące na poprawnie wykonany import” to zielony komunikat o sukcesie i lista tabel w lewym panelu albo na karcie Struktura bazy. Zrzut całego ekranu z paskiem zadań zapisujesz jako egzamin.jpg - w tym arkuszu w formacie JPEG, a zrzuty zapytań w PNG.
Typowe błędy
Ten zrzut ma nazwę egzamin i format JPEG (nie import, nie PNG). Wykadrowany zrzut albo zrzut bez paska zadań nie jest oceniany (uwaga w kluczu przy R.1).
Podgląd strony po tym kroku
Krok 4: Zapytanie 1: osoby z hobby 1, 2 albo 6
Kryteria: ,
R.1.2 Zapisano plik tekstowy o nazwie kwerendy zawierający co najmniej jedno zapytanie SQL, wynikające z treści zadania
R.1.3 Utworzono zapytanie 1 wybierające jedynie pola imie, nazwisko, opis, zdjecie z tabeli osoby , dla wszystkich osób, których hobby ma id równe 1, 2, 6. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT imie, nazwisko, opis, zdjecie FROM osoby WHERE Hobby_id = 1 OR Hobby_id = 2 OR Hobby_id = 6; oraz wynik działania zapytania udokumentowano zrzutem, na którym widocznych jest dokładnie 13 rekordów, jedynie pola imie, nazwisko, opis, zdjecie
ok. 4 min
Polecenie z arkusza
Zapisz i wykonaj zapytania SQL działające na bazie dane. Zapytania zapisz w pliku kwerendy.txt, w folderze z numerem PESEL lub w przypadku jego braku w folderze z innym numerem, którym został podpisany arkusz. Wykonaj zrzuty ekranu przedstawiające wyniki działania kwerend. Zrzuty zapisz w formacie PNG i nadaj im nazwy kw1, kw2, kw3, kw4. Zrzuty powinny obejmować cały ekran monitora z widocznym paskiem zadań. Zapytanie 1: wybierające jedynie pola imie, nazwisko, opis, zdjecie z tabeli osoby, dla wszystkich osób, których hobby ma id jest jedną z wartości: 1, 2, 6
Zaznacz bazę dane, karta SQL. Zapytanie klauzula po klauzuli:
SELECT imie, nazwisko, opis, zdjecie - dokładnie cztery kolumny z arkusza („jedynie” wyklucza SELECT *),
FROM osoby,
WHERE Hobby_id = 1 OR Hobby_id = 2 OR Hobby_id = 6 - identyfikator hobby siedzi w tabeli osoby, w kolumnie Hobby_id (klucz obcy do hobby.id), więc złączenie nie jest potrzebne. Trzy warunki łączy OR - wystarczy, że spełniony jest jeden.
Wynik: 13 osób (muzyka, film i komputery), jak w kluczu. Zapytanie kopiujesz do pliku kwerendy.txt, a cały ekran zapisujesz jako kw1.png. To samo zapytanie wyśle później skrypt strony.
Typowe błędy
AND zamiast OR szuka osób, które mają jednocześnie trzy różne hobby - wynik jest pusty. Ten sam warunek można zapisać krócej: WHERE Hobby_id IN (1, 2, 6).
kwerendy.txtTekstnowy plik
-- Zapytanie 1SELECT imie, nazwisko, opis, zdjecie FROM osoby WHERE Hobby_id = 1 OR Hobby_id = 2 OR Hobby_id = 6;
Podgląd strony po tym kroku
Krok 5: Zapytanie 2: osoby urodzone po 2000 roku z nazwą hobby
Kryteria:
R.1.4 Utworzono zapytanie 2 wybierające jedynie pola id i nazwisko z tabeli osoby oraz odpowiadające im pole nazwa z tabeli hobby dla osób, które urodziły się po 2000 roku. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT osoby.id, nazwisko, nazwa FROM osoby JOIN hobby ON osoby.hobby_id = hobby.id WHERE rok_urodzenia > 2000; możliwe też INNER JOIN lub bez JOIN z porównaniem kluczy w warunku oraz wynik działania zapytania udokumentowano zrzutem, na którym widocznych jest dokładnie 16 rekordów, jedynie pola osoby.id, nazwisko, nazwa
ok. 4 min
Polecenie z arkusza
Zapytanie 2: wybierające jedynie pola id i nazwisko z tabeli osoby oraz odpowiadające im pole nazwa z tabeli hobby dla osób, które urodziły się po 2000 roku
SELECT osoby.id, nazwisko, nazwa - kolumna id jest w obu tabelach, więc trzeba wskazać, z której ją brać (osoby.id); nazwisko i nazwa są jednoznaczne,
FROM osoby JOIN hobby ON osoby.Hobby_id = hobby.id - złączenie: do każdej osoby dołączany jest wiersz hobby o numerze z jej kolumny Hobby_id,
WHERE rok_urodzenia > 2000 - „po 2000 roku” to rok 2001 i późniejsze, więc znak >, nie >=.
Wynik: 16 rekordów z trzema kolumnami, jak w kluczu. Zrzut: kw2.png.
Typowe błędy
Samo SELECT id, … przy złączeniu kończy się błędem #1052 („Column 'id' in field list is ambiguous”). Bez warunku ON (albo porównania kluczy w WHERE) baza zwróci każdą osobę z każdym hobby - setki wierszy.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT imie, nazwisko, opis, zdjecie FROM osoby WHERE Hobby_id = 1 OR Hobby_id = 2 OR Hobby_id = 6;-- Zapytanie 2SELECT osoby.id, nazwisko, nazwa FROM osoby JOIN hobby ON osoby.Hobby_id = hobby.id WHERE rok_urodzenia > 2000;
Podgląd strony po tym kroku
Krok 6: Zapytanie 3: pierwsze pięć Monik
Kryteria:
R.1.5 Utworzono zapytanie 3 wybierające jedynie pola id i zdjecie z tabeli osoby dla osób które mają na imię Monika. Kwerenda wybiera początkowe 5 wierszy, należy jawnie wskazać liczbę wierszy. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT id, zdjecie FROM osoby WHERE imie='Monika' LIMIT 5; oraz wynik działania zapytania udokumentowano zrzutem, na którym widocznych jest dokładnie 5 rekordów, dane: 2 osoba1.jpg; 4 osoba2.jpg; 7 osoba1.jpg; 12 osoba1.jpg; 17 osoba4.jpg
ok. 2 min
Polecenie z arkusza
Zapytanie 3: wybierające jedynie pola id i zdjecie z tabeli osoby dla osób, które mają na imię Monika. Kwerenda wybiera pierwsze 5 wierszy, należy jawnie wskazać liczbę wierszy
SELECT id, zdjecie FROM osoby,
WHERE imie = 'Monika' - napis w apostrofach,
LIMIT 5 - „jawnie wskazana liczba wierszy”: baza zwraca najwyżej pięć pierwszych pasujących rekordów.
Monik jest w tabeli więcej niż pięć, a phpMyAdmin i tak sam dopisałby LIMIT 0, 25 - dlatego arkusz każe wpisać limit w zapytaniu. Wynik: 2 osoba1.jpg, 4 osoba2.jpg, 7 osoba1.jpg, 12 osoba1.jpg, 17 osoba4.jpg - dokładnie te pary wymienia klucz. Zrzut: kw3.png.
kwerendy.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT imie, nazwisko, opis, zdjecie FROM osoby WHERE Hobby_id = 1 OR Hobby_id = 2 OR Hobby_id = 6;-- Zapytanie 2SELECT osoby.id, nazwisko, nazwa FROM osoby JOIN hobby ON osoby.Hobby_id = hobby.id WHERE rok_urodzenia > 2000;-- Zapytanie 3SELECT id, zdjecie FROM osoby WHERE imie = 'Monika' LIMIT 5;
Podgląd strony po tym kroku
Krok 7: Zapytanie 4: tabela wpisy
Kryteria:
R.1.6 Utworzono zapytanie 4 tworzące tabelę o nazwie wpisy , kluczu głównym id typu całkowitego dodatniego oraz polach wpis typu tekstowego i uzytkownik_id typu całkowitego dodatniego. Klucz główny i pole uzytkownik_id nie mogą przyjmować wartości pustych. Klucz główny powinien być jawnie zapisany oraz automatycznie inkrementowany. W pliku z kwerendami lub na zrzucie istnieje zapis np.: CREATE TABLE wpisy ( id INTEGER UNSIGNED NOT NULL AUTO_INCREMENT, wpis TEXT, (dopuszczalna dowolna nazwa dla pola tekstowego) uzytkownik_id INTEGER UNSIGNED NOT NULL, PRIMARY KEY(id) ); (dowolne typy całkowite i tekstowe) oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczny jest komunikat o poprawnie wykonanej kwerendzie lub utworzona tabela
ok. 4 min
Polecenie z arkusza
Zapytanie 4: tworzące tabelę o nazwie wpisy z następującymi polami: id, klucz główny jawnie zapisany, typu całkowitego dodatniego, automatycznie inkrementowany, nie może przyjmować wartości pustych; wpis typu tekstowego; uzytkownik_id typu całkowitego dodatniego, nie może przyjmować wartości pustych.
Każde wymaganie ma w zapytaniu swoje słowo:
id INT UNSIGNED NOT NULL AUTO_INCREMENT, - INT to typ całkowity, UNSIGNED - bez znaku, czyli tylko liczby nieujemne („dodatni”), NOT NULL - pole nie może być puste, AUTO_INCREMENT - baza sama nada kolejny numer,
wpis TEXT, - typ tekstowy na dłuższy wpis,
uzytkownik_id INT UNSIGNED NOT NULL, - liczba całkowita dodatnia, obowiązkowa,
PRIMARY KEY (id) - klucz główny zapisany jawnie, osobną klauzulą.
phpMyAdmin odpowiada „MySQL zwrócił pusty wynik” - CREATE TABLE niczego nie wybiera, a w lewym panelu pojawia się tabela wpisy. Zrzut: kw4.png. Plik kwerendy.txt ma komplet czterech zapytań.
Typowe błędy
AUTO_INCREMENT działa tylko na kolumnie, która jest kluczem - bez PRIMARY KEY (id) baza zgłosi błąd #1075.
kwerendy.txtTekstzmienione: 8 linii
-- Zapytanie 1SELECT imie, nazwisko, opis, zdjecie FROM osoby WHERE Hobby_id = 1 OR Hobby_id = 2 OR Hobby_id = 6;-- Zapytanie 2SELECT osoby.id, nazwisko, nazwa FROM osoby JOIN hobby ON osoby.Hobby_id = hobby.id WHERE rok_urodzenia > 2000;-- Zapytanie 3SELECT id, zdjecie FROM osoby WHERE imie = 'Monika' LIMIT 5;-- Zapytanie 4CREATE TABLE wpisy ( id INT UNSIGNED NOT NULL AUTO_INCREMENT, wpis TEXT, uzytkownik_id INT UNSIGNED NOT NULL, PRIMARY KEY (id));
Podgląd strony po tym kroku
Etap 2: Grafika i strona HTML
Kroki 8-17 · po tym etapie masz 16 z 34 kryteriów (pkt)
Krok 8: Grafika: kadr 1:1 z twarzą
ok. 2 min
Polecenie z arkusza
Plik osoba1.jpg, wypakowany z archiwum, skadrowany do proporcji 1:1, tak aby była widoczna twarz osoby.
osoba1.jpg z archiwum ma 300 × 200 px - jest poziomy, a strona pokazuje zdjęcia w kółku, więc potrzebny jest kwadrat. W GIMP-ie narzędzie Zaznaczenie prostokątne z opcją Stała: proporcje 1:1, zaznaczenie 200 × 200 px (cała wysokość zdjęcia) przesunięte tak, żeby twarz była w środku - tutaj od x = 35 px - i Obraz → Przytnij do zaznaczenia. Podgląd pokazuje kadr przed skalowaniem.
Typowe błędy
Kadr z lewej krawędzi zdjęcia (x = 0) ucina twarz z prawej strony - klucz wymaga, żeby twarz była widoczna (R.2.1).
Podgląd strony po tym kroku
Krok 9: Grafika: rozmiar dokładnie 100 × 100 px
Kryteria:
R.2.1 Plik osoba1.jpg zawiera widoczną twarz osoby, rozmiar zdjęcia dokładnie 100 px na 100 px
ok. 2 min
Polecenie z arkusza
Przeskalowany do rozmiaru dokładnie 100 px na 100 px. Pozostałe pliki graficzne są przygotowane i nie należy ich zmieniać
Obraz → Skaluj obraz: szerokość 100, wysokość 100 - kadr jest kwadratowy, więc proporcje się nie zmieniają. Zapis pod tą samą nazwą: Plik → Nadpisz osoba1.jpg (zwykłe Zapisz w GIMP-ie zapisuje format .xcf). Zdjęcia osoba2.jpg - osoba8.jpg mają już 100 × 100 px i zostają bez zmian.
Typowe błędy
Skalowanie całego zdjęcia 300 × 200 do 100 × 100 bez kadrowania spłaszczy twarz - klucz wymaga proporcji 1:1 uzyskanej kadrem.
Podgląd strony po tym kroku
Krok 10: Plik lista.php i kodowanie polskich znaków
Kryteria: ,
R.2.2 Witrynę internetową zapisano w pliku PHP o nazwie lista
R.2.3 Zastosowano właściwy standard kodowania polskich znaków dla witryny
ok. 2 min
Polecenie z arkusza
Składa się ze strony o nazwie lista.php. Zastosowany właściwy standard kodowania polskich znaków
Plik lista.php w folderze zdającego, obok zdjęć - rozszerzenie .php, bo na jego górze stanie skrypt. Szkielet HTML5 z lang="pl" i od razu <meta charset="UTF-8"> jako pierwszy element <head>; sam plik zapisujesz w UTF-8. Nazwę pliku (R.2.2) i kodowanie (R.2.3) sprawdza klucz.
Tytuł strony widoczny na karcie przeglądarki: „Lista przyjaciół”
<title> z napisem na karcie przeglądarki (R.2.4). Linia viewport wyżej to dodatek spoza arkusza: telefon nie pomniejsza strony do szerokości ekranu komputera, tylko pokazuje ją w swojej szerokości.
R.3.3 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl.css , formatowanie pochodzi jedynie z tego arkusza Uwaga: kryterium to nie jest spełnione również, gdy źle przypisano co najmniej jeden selektor do bloku (np. kropka zamiast #)
ok. 1 min
Polecenie z arkusza
Arkusz stylów w pliku o nazwie styl.css prawidłowo połączony z kodem strony
<link rel="stylesheet" href="styl.css"> w <head>. Całe formatowanie pochodzi z tego pliku - w HTML-u nie ma atrybutów style (R.3.3).
Typowe błędy
Klucz uznaje R.3.3 tylko wtedy, gdy wszystkie selektory trafiają w bloki - selektor klasy zapisany z # odbiera punkt.
R.2.5 Wygląd strony zdefiniowano dzieląc ją na bloki: baner, główny i dwa bloki stopki. Zastosowano znaczniki sekcji
ok. 2 min
Polecenie z arkusza
Podział strony na bloki umiejscowione jeden pod drugim: baner, główny i dwa obok siebie bloki stopki. Podział zrealizowany za pomocą znaczników sekcji tak, aby wygląd układu bloków strony po uruchomieniu w przeglądarce, był zgodny z Obrazem 2
Znaczniki semantyczne HTML5 w kolejności z obrazu 2: <header>, blok główny i dwie stopki <footer>. Blok główny zostaje znacznikiem <section> z identyfikatorem main - arkusz każe dzielić stronę znacznikami sekcji, a znacznik <main> w środku sekcji byłby zbędnym poziomem. Karty znajomych wygeneruje skrypt, więc w pliku ich nie ma (R.2.5).
Krok 15: Blok główny: zainteresowania jako lista punktowana
Kryteria:
R.2.7 W bloku głównym umieszczono listę punktowaną z trzema elementami, znaczniki <ul>, <li> oraz ich zamknięcia
ok. 2 min
Polecenie z arkusza
Zawartość bloku głównego: Nagłówek drugiego stopnia o treści „Moje zainteresowania”. Lista punktowana (nieuporządkowana) z elementami: muzyka, film, komputery
Nagłówek <h2> i lista nieuporządkowana <ul> z trzema elementami <li> (R.2.7).
R.2.6 W bloku banera zapisano nagłówek h1: "Portal Społecznościowy - moje konto" oraz w bloku głównym nagłówki h2: "Moje zainteresowania" i "Moi znajomi"
ok. 1 min
Polecenie z arkusza
Nagłówek drugiego stopnia o treści „Moi znajomi”
Drugi <h2> - pod nim staną karty znajomych. Komplet nagłówków: <h1> i dwa <h2> (R.2.6).
R.2.8 W pierwszym bloku stopki umieszczono numer PESEL zdającego
R.3.2 Na stronie umieszczono odnośnik pocztowy "napisz do mnie" łączący z adresem email (mailto:[email protected])
ok. 2 min
Polecenie z arkusza
Zawartość pierwszego bloku stopki: napis o treści „Stronę wykonał: ”, dalej wstawiony numer PESEL zdającego lub w przypadku jego braku inny numer, którym został podpisany arkusz. Zawartość drugiego bloku stopki: odnośnik pocztowy o treści „napisz do mnie” prowadzący do adresu „[email protected]”
W pierwszej stopce na egzaminie wpisujesz swój numer PESEL - tu w jego miejscu stoi Bartosz Bryniarski (R.2.8). W drugiej odnośnik z przedrostkiem mailto: - otwiera program pocztowy z adresatem [email protected] (R.3.2).
Kroki 18-25 · po tym etapie masz 27 z 34 kryteriów (pkt)
Krok 18: Plik styl.css: kolory jako zmienne i krój Georgia
Kryteria:
R.4.1 Ustawiono domyślny krój czcionki dla całej strony na Georgia (dla selektora * lub body lub html lub kontenera całej strony)
ok. 2 min
Polecenie z arkusza
Cechy formatowania CSS: Domyślne dla całej strony: krój czcionki Georgia
Na górze pliku zmienne CSS w :root - trzy kolory z arkusza, każdy wpisany raz. Reguły odwołują się do nich przez var(--dark-green); zmiana odcienia to poprawka w jednym miejscu. Potem selektor uniwersalny * z krojem Georgia (R.4.1).
R.4.9 Ustawiono przynajmniej jeden pasek przewijania (przesuwania) dla bloku głównego, np. overflow: scroll; (lub auto)
ok. 2 min
Polecenie z arkusza
Dla bloku głównego: kolor tła #E1E2E1, wysokość 400px, marginesy wewnętrzne 20 px, paski przesuwania (scroll)
overflow: auto pokazuje pasek przewijania tylko wtedy, gdy treść się nie mieści - przy 13 znajomych to pasek pionowy, a zbędnego paska poziomego nie ma. Klucz przyjmuje scroll albo auto (R.4.9).
Dla bloku zdjęcie: szerokość 20%. Dla bloku opis: blok jest wyświetlany obok zdjęcia
Karta znajomego (klasa friend) to flexbox: jej bloki stają w rzędzie. Blok zdjęcia (klasa photo) ma szerokość 20% karty, a opis dostaje flex: 1 - zajmuje całą resztę rzędu obok zdjęcia. Nie ma float, więc nie trzeba niczego „czyścić”. flex-wrap: wrap pozwala linii z następnego kroku przejść do nowego rzędu. Reguły zadziałają, gdy skrypt wypisze karty.
Dla bloku linia: linia jest wyświetlana pod zdjęciem i opisem zgodnie z obrazem 2
W wersji nowoczesnej linia to sam znacznik <hr> na końcu karty - klucz przy R.5.4 dopuszcza linię bez bloku. Szerokość 100% karty nie mieści się już w rzędzie obok zdjęcia i opisu, więc dzięki flex-wrap linia przechodzi do nowego rzędu, pod spodem.
Krok 23: Układ strony: flexbox, dwie stopki obok siebie
Kryteria: , ,
R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z Obrazem 2 w arkuszu egzaminacyjnym (nie uwzględnia się zestawu bloków zawierających: zdjecie, opis, linia)
R.4.4 Ustawiono wysokość bloków dla banera i stopek na 20px oraz bloku głównego na 400px
R.4.5 Ustawiono szerokość stopek na 50% oraz bloku zawierającego zdjęcie na 20%
ok. 3 min
Polecenie z arkusza
Dla obu bloków stopki: kolor tła #005B4F, biały kolor czcionki, wyrównanie tekstu do środka, szerokość 50%, wysokość 20 px
body też jest flexboksem z zawijaniem. Baner i blok główny dostają flex: 1 1 100% - każdy zajmuje cały rząd (dzięki 1 w drugiej wartości blok główny z padding zwęża się do szerokości strony, zamiast z niej wystawać). Dwie stopki po 50% mieszczą się w jednym rzędzie - obok siebie, jak na obrazie 2, bez float (R.3.1). Wysokości (R.4.4) i szerokości bloków (R.4.5) są ustawione.
Typowe błędy
Bez flex: 1 1 100% baner i blok główny stanęłyby w jednym rzędzie z wąskimi stopkami.
Dodatek wynikający z flexboksa - klucz go nie ocenia. W układzie blokowym margines <h1> „wypływa” poza baner (baner przesuwa się w dół). Element flexboksa trzyma marginesy dziecka w sobie, więc nagłówek zsunąłby się w pasku 20 px i wystawał na blok główny. margin: 0 dla nagłówka w banerze trzyma napis w pasku.
Krok 25: Obraz, nagłówki drugiego stopnia i odnośnik
Kryteria: , , , ,
R.4.2 Ustawiono kolor tła #005B4F dla banera i stopek, #E1E2E1 dla bloku głównego oraz #00897B dla selektora h2
R.4.3 Ustawiono biały kolor czcionki dla banera i stopek oraz selektora a
R.4.6 Ustawiono wyrównanie tekstu do środka dla banera i stopek oraz dla selektora h2
R.4.7 Ustawiono marginesy wewnętrzne (padding) dla bloku głównego i selektora img na 20px
R.4.10 Ustawiono zakrzywienie obramowania 100% dla obrazu, np. border-radius: 100%; poprawne również dla wartości z przedziału <50%, 100%)
ok. 3 min
Polecenie z arkusza
Dla selektora obrazu: marginesy wewnętrzne 20 px, zakrzywienie obramowania 100% (tak, że obraz jest wyświetlany w kształcie koła). Dla selektora nagłówka drugiego stopnia: wyrównanie tekstu do środka, tło #00897B. Dla selektora odnośnika: biały kolor czcionki
Trzy reguły dla selektorów znacznikówimg, h2 i a - klucz zaznacza, że kryteria R.4.2, R.4.3, R.4.6 i R.4.7 spełnia tylko selektor, a nie klasa. Zdjęcie w kółku (R.4.10), marginesy wewnętrzne bloku głównego i obrazu (R.4.7), kolory tła (R.4.2), biały tekst banera, stopek i odnośnika (R.4.3) oraz wyśrodkowania (R.4.6). Arkusz stylów jest kompletny.
Typowe błędy
Reguła .friend img albo .photo img wygląda tak samo, ale według uwagi w kluczu nie spełnia R.4.7.
Kroki 26-31 · po tym etapie masz 34 z 34 kryteriów (pkt)
Krok 26: Plik config.php i połączenie PDO na górze strony
Kryteria:
R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy dane
ok. 4 min
Polecenie z arkusza
Efekt działania skryptu. Skrypt: Napisany w języku PHP. Skrypt łączy się z serwerem bazy danych na localhost, użytkownik root bez hasła, baza danych o nazwie dane
Dane połączenia trafiają do osobnego pliku config.php jako stałe: adres w formacie PDO (mysql:host=localhost;dbname=dane;charset=utf8mb4 - serwer, baza dane i kodowanie), użytkownik root i puste hasło. Strona dołącza go instrukcją require - gdy baza zmieni adres albo hasło, poprawka jest w jednym pliku.
Skrypt stoi na górze lista.php, przed <!DOCTYPE html>: najpierw pobiera dane, potem HTML je wyświetla. new PDO(…) łączy się z serwerem i wybiera bazę (R.5.1); opcja ERRMODE_EXCEPTION zamienia każdy błąd bazy w wyjątek, który łapie blok catch - zamiast ostrzeżeń PHP strona dostanie komunikat w zmiennej $error.
Od tego kroku podgląd to wynik prawdziwego uruchomienia w PHP 8.2 - na razie bez zmian.
config.phpPHPnowy plik
<?php// dane połączenia z bazą - w jednym miejscu, poza plikiem stronyconst DB_DSN = 'mysql:host=localhost;dbname=dane;charset=utf8mb4';const DB_USER = 'root';const DB_PASSWORD = '';
lista.phpPHPzmienione: 11 linii
<?phprequire'config.php';$friends = [];$error = '';try {$pdo = new PDO(DB_DSN, DB_USER, DB_PASSWORD, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);} catch (PDOException $exception) {$error = 'nie udało się pobrać listy znajomych, spróbuj ponownie za chwilę';}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Lista przyjaciół</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Portal Społecznościowy - moje konto</h1> </header> <sectionid="main"> <h2>Moje zainteresowania</h2> <ul> <li>muzyka</li> <li>film</li> <li>komputery</li> </ul> <h2>Moi znajomi</h2> </section> <footer> Stronę wykonał: Bartosz Bryniarski </footer> <footer> <ahref="mailto:[email protected]">napisz do mnie</a> </footer></body></html>
Podgląd strony po tym kroku (lista.php)
Krok 27: Zapytanie 1, fetchAll i zamknięcie połączenia
Kryteria: ,
R.5.2 Ostatnią operacją na bazie jest jej zamknięcie (zgodne ze składnią)
R.5.3 Skrypt wysyła do bazy danych zapytanie 1
ok. 4 min
Polecenie z arkusza
Wysyła do bazy danych zapytanie 1. Po wykonaniu operacji na bazie danych skrypt zamyka połączenie z serwerem.
$pdo->query('…') wysyła zapytanie 1 skopiowane z kwerendy.txt (R.5.3). Zapytanie nie ma żadnej danej od użytkownika, więc zapytanie przygotowane nie jest potrzebne,
fetchAll(PDO::FETCH_ASSOC) od razu odczytuje wszystkie 13 wierszy do tablicy $friends; każdy wiersz ma klucze z nazw kolumn: imie, nazwisko, opis, zdjecie - czytelniej niż numery [0]-[3],
PDO nie ma funkcji close - połączenie zamyka się, gdy znika ostatnie odwołanie do obiektu. Dlatego $statement = null i $pdo = null w bloku finally, który wykona się zawsze, po sukcesie i po wyjątku (R.5.2).
Dane są w tablicy, połączenie zamknięte - HTML strony może je teraz wypisać.
lista.phpPHPzmienione: 5 linii
<?phprequire'config.php';$friends = [];$error = '';try {$pdo = new PDO(DB_DSN, DB_USER, DB_PASSWORD, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);$statement = $pdo->query('SELECT imie, nazwisko, opis, zdjecie FROM osoby WHERE Hobby_id = 1 OR Hobby_id = 2 OR Hobby_id = 6;');$friends = $statement->fetchAll(PDO::FETCH_ASSOC);} catch (PDOException $exception) {$error = 'nie udało się pobrać listy znajomych, spróbuj ponownie za chwilę';} finally {$statement = null;$pdo = null;}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Lista przyjaciół</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Portal Społecznościowy - moje konto</h1> </header> <sectionid="main"> <h2>Moje zainteresowania</h2> <ul> <li>muzyka</li> <li>film</li> <li>komputery</li> </ul> <h2>Moi znajomi</h2> </section> <footer> Stronę wykonał: Bartosz Bryniarski </footer> <footer> <ahref="mailto:[email protected]">napisz do mnie</a> </footer></body></html>
R.5.5 Wyświetlono obraz o nazwie pobranej z bazy danych oraz tekście alternatywnym „przyjaciel”
R.5.7 Wyświetlono wszystkie wiersze zwrócone zapytaniem w oparciu o pętlę
ok. 4 min
Polecenie z arkusza
Dane z każdego zwróconego zapytaniem wiersza podzielone są na trzy bloki: blok zdjęcie, w którym jest wyświetlony obraz o nazwie pobranej z bazy danych oraz tekście alternatywnym „przyjaciel”
Pod nagłówkiem „Moi znajomi” stoi szablon: zwykły HTML karty, a PHP tylko w miejscach na wartości.
<?php foreach ($friends as $friend): ?> … <?php endforeach; ?> - zapis pętli z dwukropkiem jest czytelny w środku HTML-u. Pętla przechodzi przez wszystkie 13 wierszy (R.5.7),
<?= … ?> to skrót od <?php echo … ?>,
funkcja e() na górze pliku to krótsza nazwa dla htmlspecialchars - każda wartość z bazy przechodzi przez nią przed wstawieniem do HTML-u, także nazwa pliku w src. Stały tekst alternatywny „przyjaciel” (R.5.5).
Podgląd: 13 kart ze zdjęciami w kółku.
lista.phpPHPzmienione: 10 linii
<?phprequire'config.php';$friends = [];$error = '';try {$pdo = new PDO(DB_DSN, DB_USER, DB_PASSWORD, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);$statement = $pdo->query('SELECT imie, nazwisko, opis, zdjecie FROM osoby WHERE Hobby_id = 1 OR Hobby_id = 2 OR Hobby_id = 6;');$friends = $statement->fetchAll(PDO::FETCH_ASSOC);} catch (PDOException $exception) {$error = 'nie udało się pobrać listy znajomych, spróbuj ponownie za chwilę';} finally {$statement = null;$pdo = null;}functione(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Lista przyjaciół</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Portal Społecznościowy - moje konto</h1> </header> <sectionid="main"> <h2>Moje zainteresowania</h2> <ul> <li>muzyka</li> <li>film</li> <li>komputery</li> </ul> <h2>Moi znajomi</h2><?phpforeach ($friendsas$friend): ?> <articleclass="friend"> <sectionclass="photo"><imgsrc="<?= e($friend['zdjecie']) ?>"alt="przyjaciel"></section> </article><?phpendforeach; ?> </section> <footer> Stronę wykonał: Bartosz Bryniarski </footer> <footer> <ahref="mailto:[email protected]">napisz do mnie</a> </footer></body></html>
Podgląd strony po tym kroku (lista.php)
Krok 29: Blok opis: imię i nazwisko oraz ostatni wpis
Kryteria:
R.5.6 Wyświetlono nagłówek trzeciego stopnia z imieniem i nazwiskiem pobranym z bazy danych oraz paragraf (akapit) o treści: „Ostatni wpis: <opis>”, gdzie pole <opis> jest pobrane z bazy danych
ok. 2 min
Polecenie z arkusza
blok opis, w którym jest wyświetlony nagłówek trzeciego stopnia z imieniem i nazwiskiem pobranym z bazy danych oraz paragraf (akapit) o treści: „Ostatni wpis: <opis>”, gdzie pole <opis> jest pobrane z bazy danych
Drugi blok karty (klasa description): <h3> z imieniem i nazwiskiem oraz <p> „Ostatni wpis: ” z opisem (R.5.6). Flexbox karty stawia go obok zdjęcia. Brak polskich liter w części opisów („Z checia”, „dzi? rano”) pochodzi z pliku dane.sql z archiwum CKE.
lista.phpPHPzmienione: 4 linie
<?phprequire'config.php';$friends = [];$error = '';try {$pdo = new PDO(DB_DSN, DB_USER, DB_PASSWORD, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);$statement = $pdo->query('SELECT imie, nazwisko, opis, zdjecie FROM osoby WHERE Hobby_id = 1 OR Hobby_id = 2 OR Hobby_id = 6;');$friends = $statement->fetchAll(PDO::FETCH_ASSOC);} catch (PDOException $exception) {$error = 'nie udało się pobrać listy znajomych, spróbuj ponownie za chwilę';} finally {$statement = null;$pdo = null;}functione(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Lista przyjaciół</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Portal Społecznościowy - moje konto</h1> </header> <sectionid="main"> <h2>Moje zainteresowania</h2> <ul> <li>muzyka</li> <li>film</li> <li>komputery</li> </ul> <h2>Moi znajomi</h2><?phpforeach ($friendsas$friend): ?> <articleclass="friend"> <sectionclass="photo"><imgsrc="<?= e($friend['zdjecie']) ?>"alt="przyjaciel"></section> <sectionclass="description"> <h3><?= e($friend['imie']) ?><?= e($friend['nazwisko']) ?></h3> <p>Ostatni wpis: <?= e($friend['opis']) ?></p> </section> </article><?phpendforeach; ?> </section> <footer> Stronę wykonał: Bartosz Bryniarski </footer> <footer> <ahref="mailto:[email protected]">napisz do mnie</a> </footer></body></html>
Podgląd strony po tym kroku (lista.php)
Krok 30: Linia pozioma na końcu karty
Kryteria:
R.5.4 Dane zwróconego zapytaniem wiersza są podzielone na bloki zawierające: zdjęcie i opis obok siebie, pod spodem linia. W przypadku linii dopuszcza się użycie jej bez znacznika bloku. Zapis jest poprawny składniowo.
ok. 1 min
Polecenie z arkusza
blok linia, w którym jest wyświetlona linia pozioma
<hr> zamyka kartę - reguła .friend hr przenosi linię pod zdjęcie i opis. Każdy wiersz z bazy to zdjęcie i opis obok siebie, pod spodem linia (R.5.4; klucz dopuszcza linię bez osobnego bloku). Strona spełnia wszystkie kryteria arkusza.
lista.phpPHPzmienione: 1 linia
<?phprequire'config.php';$friends = [];$error = '';try {$pdo = new PDO(DB_DSN, DB_USER, DB_PASSWORD, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);$statement = $pdo->query('SELECT imie, nazwisko, opis, zdjecie FROM osoby WHERE Hobby_id = 1 OR Hobby_id = 2 OR Hobby_id = 6;');$friends = $statement->fetchAll(PDO::FETCH_ASSOC);} catch (PDOException $exception) {$error = 'nie udało się pobrać listy znajomych, spróbuj ponownie za chwilę';} finally {$statement = null;$pdo = null;}functione(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Lista przyjaciół</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Portal Społecznościowy - moje konto</h1> </header> <sectionid="main"> <h2>Moje zainteresowania</h2> <ul> <li>muzyka</li> <li>film</li> <li>komputery</li> </ul> <h2>Moi znajomi</h2><?phpforeach ($friendsas$friend): ?> <articleclass="friend"> <sectionclass="photo"><imgsrc="<?= e($friend['zdjecie']) ?>"alt="przyjaciel"></section> <sectionclass="description"> <h3><?= e($friend['imie']) ?><?= e($friend['nazwisko']) ?></h3> <p>Ostatni wpis: <?= e($friend['opis']) ?></p> </section> <hr> </article><?phpendforeach; ?> </section> <footer> Stronę wykonał: Bartosz Bryniarski </footer> <footer> <ahref="mailto:[email protected]">napisz do mnie</a> </footer></body></html>
Podgląd strony po tym kroku (lista.php)
Krok 31: Komunikat, gdy baza nie odpowiada
ok. 2 min
Dodatek spoza arkusza - klucz go nie ocenia. Gdy serwer MySQL jest wyłączony albo hasło się nie zgadza, wyjątek z bloku try łapie catch, a tablica $friends zostaje pusta. Pod nagłówkiem „Moi znajomi” pojawia się wtedy komunikat ze zmiennej $error, a reszta strony - baner, zainteresowania, stopki - wyświetla się normalnie. Sprawdzone na prawdziwym PHP ze złym hasłem w config.php: podgląd pokazuje ten stan, bez treści błędu z bazy na ekranie.
lista.phpPHPzmienione: 3 linie
<?phprequire'config.php';$friends = [];$error = '';try {$pdo = new PDO(DB_DSN, DB_USER, DB_PASSWORD, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);$statement = $pdo->query('SELECT imie, nazwisko, opis, zdjecie FROM osoby WHERE Hobby_id = 1 OR Hobby_id = 2 OR Hobby_id = 6;');$friends = $statement->fetchAll(PDO::FETCH_ASSOC);} catch (PDOException $exception) {$error = 'nie udało się pobrać listy znajomych, spróbuj ponownie za chwilę';} finally {$statement = null;$pdo = null;}functione(?string $text): string{returnhtmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');}?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <metaname="viewport"content="width=device-width, initial-scale=1"> <title>Lista przyjaciół</title> <linkrel="stylesheet"href="styl.css"></head><body> <header> <h1>Portal Społecznościowy - moje konto</h1> </header> <sectionid="main"> <h2>Moje zainteresowania</h2> <ul> <li>muzyka</li> <li>film</li> <li>komputery</li> </ul> <h2>Moi znajomi</h2><?phpif ($error !== ''): ?> <p><?= e($error) ?></p><?phpendif; ?><?phpforeach ($friendsas$friend): ?> <articleclass="friend"> <sectionclass="photo"><imgsrc="<?= e($friend['zdjecie']) ?>"alt="przyjaciel"></section> <sectionclass="description"> <h3><?= e($friend['imie']) ?><?= e($friend['nazwisko']) ?></h3> <p>Ostatni wpis: <?= e($friend['opis']) ?></p> </section> <hr> </article><?phpendforeach; ?> </section> <footer> Stronę wykonał: Bartosz Bryniarski </footer> <footer> <ahref="mailto:[email protected]">napisz do mnie</a> </footer></body></html>
Podgląd strony po tym kroku (lista.php)
Etap 5: Porównanie z wersją podstawową
Krok 32 · po tym etapie masz 34 z 34 kryteriów (pkt)
Krok 32: Co jest inaczej i którą wersję wybrać na egzaminie
ok. 3 min
Obie wersje dają komplet 34 punktów. Różnice:
wersja podstawowa
wersja nowoczesna
układ strony
float: left stopek
body jako flexbox z zawijaniem
zdjęcie i opis
float: left obu bloków, linia z clear: both
karta <article> jako flexbox, opis z flex: 1
linia
<hr> w bloku z klasą line
sam <hr> w karcie (klucz to dopuszcza)
kolory
wpisane w każdej regule
zmienne --dark-green, --green, --grey
biblioteka
mysqli (tabela 1 arkusza)
PDO z wyjątkami, dane połączenia w config.php
gdzie jest skrypt
w bloku głównym, HTML w echo
na górze pliku, w bloku głównym szablon z foreach
odczyt wierszy
mysqli_fetch_row, numery [0] - [3]
fetchAll, nazwy kolumn
wypisywanie danych
wprost (w etapie „Dobre praktyki” - htmlspecialchars)
każda wartość przez e()
zamknięcie bazy
mysqli_close po pętli
$pdo = null w finally, przed HTML-em
baza wyłączona
ostrzeżenia i błąd PHP
komunikat, reszta strony działa
Sprawdzone na prawdziwym PHP: wpis <b>Hej!</b> <script>…</script> w opisie wyświetla się w wersji nowoczesnej dosłownie, a skrypt się nie wykonuje.
Na egzaminie wersja podstawowa trzyma się funkcji z tabeli 1 arkusza i łatwiej ją napisać bez dokumentacji. Wersja nowoczesna oddziela pobieranie danych od HTML-u i jest odporna na wpisy z HTML-em - klucz przyjmuje obie.
Krok 33 · po tym etapie masz 34 z 34 kryteriów (pkt)
Krok 33: Plik przeglądarka.txt i nagranie płyty
ok. 5 min
Polecenie z arkusza
UWAGA: po zakończeniu pracy utwórz w folderze z numerem PESEL lub w przypadku jego braku w folderze z innym numerem, którym został podpisany arkusz plik tekstowy o nazwie przeglądarka.txt. Zapisz w nim nazwę przeglądarki internetowej, w której weryfikowałeś poprawność działania witryny. Nagraj płytę z rezultatami pracy.
W przeglądarka.txt nazwa przeglądarki, w której sprawdzana była strona. W folderze z numerem zdającego: egzamin.jpg, kw1.png - kw4.png, kwerendy.txt, lista.php, config.php (bez niego strona się nie połączy z bazą), osoba1.jpg - osoba8.jpg, przeglądarka.txt i styl.css. 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.