Przejdź do głównej treści
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.

  1. 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.

  2. 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).

  3. 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
    Podgląd strony po kroku 3
  4. 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.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT imie, nazwisko, opis, zdjecie FROM osoby WHERE Hobby_id = 1 OR Hobby_id = 2 OR Hobby_id = 6;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. 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.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT imie, nazwisko, opis, zdjecie FROM osoby WHERE Hobby_id = 1 OR Hobby_id = 2 OR Hobby_id = 6;
    
    -- Zapytanie 2
    SELECT osoby.id, nazwisko, nazwa FROM osoby JOIN hobby ON osoby.Hobby_id = hobby.id WHERE rok_urodzenia > 2000;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. 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.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT imie, nazwisko, opis, zdjecie FROM osoby WHERE Hobby_id = 1 OR Hobby_id = 2 OR Hobby_id = 6;
    
    -- Zapytanie 2
    SELECT osoby.id, nazwisko, nazwa FROM osoby JOIN hobby ON osoby.Hobby_id = hobby.id WHERE rok_urodzenia > 2000;
    
    -- Zapytanie 3
    SELECT id, zdjecie FROM osoby WHERE imie = 'Monika' LIMIT 5;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. 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.txt Tekst zmienione: 8 linii
    -- Zapytanie 1
    SELECT imie, nazwisko, opis, zdjecie FROM osoby WHERE Hobby_id = 1 OR Hobby_id = 2 OR Hobby_id = 6;
    
    -- Zapytanie 2
    SELECT osoby.id, nazwisko, nazwa FROM osoby JOIN hobby ON osoby.Hobby_id = hobby.id WHERE rok_urodzenia > 2000;
    
    -- Zapytanie 3
    SELECT id, zdjecie FROM osoby WHERE imie = 'Monika' LIMIT 5;
    
    -- Zapytanie 4
    CREATE 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
    Podgląd strony po kroku 7
  8. 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
    Podgląd strony po kroku 8
  9. 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
    Podgląd strony po kroku 9
  10. 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.

    lista.php PHP nowy plik
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (lista.php)
  11. Krok 11: Tytuł strony

    Kryteria:

    R.2.4 Nadano tytuł strony: "Lista przyjaciół”

    ok. 1 min
    Polecenie z arkusza

    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.

    lista.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Lista przyjaciół</title>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (lista.php)
  12. Krok 12: Podpięcie arkusza stylów styl.css

    Kryteria:

    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.

    lista.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Lista przyjaciół</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (lista.php)
  13. Krok 13: Bloki: baner, główny i dwie stopki

    Kryteria:

    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).

    lista.php PHP zmienione: 8 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Lista przyjaciół</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
        </header>
        <section id="main">
        </section>
        <footer>
        </footer>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (lista.php)
  14. Krok 14: Baner: nagłówek pierwszego stopnia

    ok. 1 min
    Polecenie z arkusza

    Zawartość banera: nagłówek pierwszego stopnia o treści „Portal Społecznościowy - moje konto”

    <h1> w <header> - jedyny nagłówek pierwszego stopnia na stronie.

    lista.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Lista przyjaciół</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal Społecznościowy - moje konto</h1>
        </header>
        <section id="main">
        </section>
        <footer>
        </footer>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (lista.php)
  15. 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).

    lista.php PHP zmienione: 6 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Lista przyjaciół</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal Społecznościowy - moje konto</h1>
        </header>
        <section id="main">
            <h2>Moje zainteresowania</h2>
            <ul>
                <li>muzyka</li>
                <li>film</li>
                <li>komputery</li>
            </ul>
        </section>
        <footer>
        </footer>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (lista.php)
  16. Krok 16: Blok główny: nagłówek „Moi znajomi”

    Kryteria:

    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).

    lista.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Lista przyjaciół</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal Społecznościowy - moje konto</h1>
        </header>
        <section id="main">
            <h2>Moje zainteresowania</h2>
            <ul>
                <li>muzyka</li>
                <li>film</li>
                <li>komputery</li>
            </ul>
            <h2>Moi znajomi</h2>
        </section>
        <footer>
        </footer>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (lista.php)
  17. Krok 17: Stopki: autor strony i odnośnik pocztowy

    Kryteria: ,

    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).

    lista.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Lista przyjaciół</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal Społecznościowy - moje konto</h1>
        </header>
        <section id="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>
            <a href="mailto:[email protected]">napisz do mnie</a>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (lista.php)
  18. Etap 3: Styl CSS

    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).

    styl.css CSS nowy plik
    :root {
        --dark-green: #005B4F;
        --green: #00897B;
        --grey: #E1E2E1;
    }
    * {
        font-family: Georgia;
    }
    Podgląd strony po tym kroku (lista.php)
  19. Krok 19: Baner: tło, kolor, wyśrodkowanie, rozmiar czcionki, wysokość

    Kryteria:

    R.4.8 Ustawiono rozmiar czcionki banera na 50%

    ok. 2 min
    Polecenie z arkusza

    Dla banera: kolor tła #005B4F, biały kolor czcionki, wyrównanie tekstu do środka, rozmiar czcionki 50%, wysokość 20 px

    Kolor tła ze zmiennej, reszta wprost z arkusza. font-size: 50% sprawia, że <h1> (2em) ma 16 px i mieści się w pasku 20 px (R.4.8).

    styl.css CSS zmienione: 7 linii
    :root {
        --dark-green: #005B4F;
        --green: #00897B;
        --grey: #E1E2E1;
    }
    * {
        font-family: Georgia;
    }
    header {
        background-color: var(--dark-green);
        color: white;
        text-align: center;
        font-size: 50%;
        height: 20px;
    }
    Podgląd strony po tym kroku (lista.php)
  20. Krok 20: Blok główny: tło, wysokość, marginesy wewnętrzne, paski przewijania

    Kryteria:

    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).

    styl.css CSS zmienione: 6 linii
    :root {
        --dark-green: #005B4F;
        --green: #00897B;
        --grey: #E1E2E1;
    }
    * {
        font-family: Georgia;
    }
    header {
        background-color: var(--dark-green);
        color: white;
        text-align: center;
        font-size: 50%;
        height: 20px;
    }
    #main {
        background-color: var(--grey);
        height: 400px;
        padding: 20px;
        overflow: auto;
    }
    Podgląd strony po tym kroku (lista.php)
  21. Krok 21: Karta znajomego: zdjęcie 20% i opis obok

    ok. 3 min
    Polecenie z arkusza

    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.

    styl.css CSS zmienione: 10 linii
    :root {
        --dark-green: #005B4F;
        --green: #00897B;
        --grey: #E1E2E1;
    }
    * {
        font-family: Georgia;
    }
    header {
        background-color: var(--dark-green);
        color: white;
        text-align: center;
        font-size: 50%;
        height: 20px;
    }
    #main {
        background-color: var(--grey);
        height: 400px;
        padding: 20px;
        overflow: auto;
    }
    .friend {
        display: flex;
        flex-wrap: wrap;
    }
    .photo {
        width: 20%;
    }
    .description {
        flex: 1;
    }
    Podgląd strony po tym kroku (lista.php)
  22. Krok 22: Linia pod zdjęciem i opisem

    ok. 1 min
    Polecenie z arkusza

    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.

    styl.css CSS zmienione: 3 linie
    :root {
        --dark-green: #005B4F;
        --green: #00897B;
        --grey: #E1E2E1;
    }
    * {
        font-family: Georgia;
    }
    header {
        background-color: var(--dark-green);
        color: white;
        text-align: center;
        font-size: 50%;
        height: 20px;
    }
    #main {
        background-color: var(--grey);
        height: 400px;
        padding: 20px;
        overflow: auto;
    }
    .friend {
        display: flex;
        flex-wrap: wrap;
    }
    .photo {
        width: 20%;
    }
    .description {
        flex: 1;
    }
    .friend hr {
        width: 100%;
    }
    Podgląd strony po tym kroku (lista.php)
  23. 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.

    styl.css CSS zmienione: 14 linii
    :root {
        --dark-green: #005B4F;
        --green: #00897B;
        --grey: #E1E2E1;
    }
    * {
        font-family: Georgia;
    }
    header {
        background-color: var(--dark-green);
        color: white;
        text-align: center;
        font-size: 50%;
        height: 20px;
    }
    #main {
        background-color: var(--grey);
        height: 400px;
        padding: 20px;
        overflow: auto;
    }
    .friend {
        display: flex;
        flex-wrap: wrap;
    }
    .photo {
        width: 20%;
    }
    .description {
        flex: 1;
    }
    .friend hr {
        width: 100%;
    }
    body {
        display: flex;
        flex-wrap: wrap;
    }
    header, #main {
        flex: 1 1 100%;
    }
    footer {
        background-color: var(--dark-green);
        color: white;
        text-align: center;
        width: 50%;
        height: 20px;
    }
    Podgląd strony po tym kroku (lista.php)
  24. Krok 24: Nagłówek banera bez marginesów

    ok. 1 min

    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.

    styl.css CSS zmienione: 3 linie
    :root {
        --dark-green: #005B4F;
        --green: #00897B;
        --grey: #E1E2E1;
    }
    * {
        font-family: Georgia;
    }
    header {
        background-color: var(--dark-green);
        color: white;
        text-align: center;
        font-size: 50%;
        height: 20px;
    }
    #main {
        background-color: var(--grey);
        height: 400px;
        padding: 20px;
        overflow: auto;
    }
    .friend {
        display: flex;
        flex-wrap: wrap;
    }
    .photo {
        width: 20%;
    }
    .description {
        flex: 1;
    }
    .friend hr {
        width: 100%;
    }
    body {
        display: flex;
        flex-wrap: wrap;
    }
    header, #main {
        flex: 1 1 100%;
    }
    footer {
        background-color: var(--dark-green);
        color: white;
        text-align: center;
        width: 50%;
        height: 20px;
    }
    header h1 {
        margin: 0;
    }
    Podgląd strony po tym kroku (lista.php)
  25. 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ów img, 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.

    styl.css CSS zmienione: 11 linii
    :root {
        --dark-green: #005B4F;
        --green: #00897B;
        --grey: #E1E2E1;
    }
    * {
        font-family: Georgia;
    }
    header {
        background-color: var(--dark-green);
        color: white;
        text-align: center;
        font-size: 50%;
        height: 20px;
    }
    #main {
        background-color: var(--grey);
        height: 400px;
        padding: 20px;
        overflow: auto;
    }
    .friend {
        display: flex;
        flex-wrap: wrap;
    }
    .photo {
        width: 20%;
    }
    .description {
        flex: 1;
    }
    .friend hr {
        width: 100%;
    }
    body {
        display: flex;
        flex-wrap: wrap;
    }
    header, #main {
        flex: 1 1 100%;
    }
    footer {
        background-color: var(--dark-green);
        color: white;
        text-align: center;
        width: 50%;
        height: 20px;
    }
    header h1 {
        margin: 0;
    }
    img {
        padding: 20px;
        border-radius: 100%;
    }
    h2 {
        text-align: center;
        background-color: var(--green);
    }
    a {
        color: white;
    }
    Podgląd strony po tym kroku (lista.php)
  26. Etap 4: Skrypt PHP

    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.php PHP nowy plik
    <?php
    // dane połączenia z bazą - w jednym miejscu, poza plikiem strony
    const DB_DSN = 'mysql:host=localhost;dbname=dane;charset=utf8mb4';
    const DB_USER = 'root';
    const DB_PASSWORD = '';
    lista.php PHP zmienione: 11 linii
    <?php
    require '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>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Lista przyjaciół</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal Społecznościowy - moje konto</h1>
        </header>
        <section id="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>
            <a href="mailto:[email protected]">napisz do mnie</a>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (lista.php)
  27. 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.php PHP zmienione: 5 linii
    <?php
    require '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>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Lista przyjaciół</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal Społecznościowy - moje konto</h1>
        </header>
        <section id="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>
            <a href="mailto:[email protected]">napisz do mnie</a>
        </footer>
    </body>
    </html>

    Podgląd strony bez zmian - taki jak po kroku 26.

  28. Krok 28: Szablon: pętla foreach i blok zdjęcie

    Kryteria: ,

    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.php PHP zmienione: 10 linii
    <?php
    require '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;
    }
    
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Lista przyjaciół</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal Społecznościowy - moje konto</h1>
        </header>
        <section id="main">
            <h2>Moje zainteresowania</h2>
            <ul>
                <li>muzyka</li>
                <li>film</li>
                <li>komputery</li>
            </ul>
            <h2>Moi znajomi</h2>
            <?php foreach ($friends as $friend): ?>
            <article class="friend">
                <section class="photo"><img src="<?= e($friend['zdjecie']) ?>" alt="przyjaciel"></section>
            </article>
            <?php endforeach; ?>
        </section>
        <footer>
            Stronę wykonał: Bartosz Bryniarski
        </footer>
        <footer>
            <a href="mailto:[email protected]">napisz do mnie</a>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (lista.php)
  29. 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.php PHP zmienione: 4 linie
    <?php
    require '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;
    }
    
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Lista przyjaciół</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal Społecznościowy - moje konto</h1>
        </header>
        <section id="main">
            <h2>Moje zainteresowania</h2>
            <ul>
                <li>muzyka</li>
                <li>film</li>
                <li>komputery</li>
            </ul>
            <h2>Moi znajomi</h2>
            <?php foreach ($friends as $friend): ?>
            <article class="friend">
                <section class="photo"><img src="<?= e($friend['zdjecie']) ?>" alt="przyjaciel"></section>
                <section class="description">
                    <h3><?= e($friend['imie']) ?> <?= e($friend['nazwisko']) ?></h3>
                    <p>Ostatni wpis: <?= e($friend['opis']) ?></p>
                </section>
            </article>
            <?php endforeach; ?>
        </section>
        <footer>
            Stronę wykonał: Bartosz Bryniarski
        </footer>
        <footer>
            <a href="mailto:[email protected]">napisz do mnie</a>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (lista.php)
  30. 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.php PHP zmienione: 1 linia
    <?php
    require '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;
    }
    
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Lista przyjaciół</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal Społecznościowy - moje konto</h1>
        </header>
        <section id="main">
            <h2>Moje zainteresowania</h2>
            <ul>
                <li>muzyka</li>
                <li>film</li>
                <li>komputery</li>
            </ul>
            <h2>Moi znajomi</h2>
            <?php foreach ($friends as $friend): ?>
            <article class="friend">
                <section class="photo"><img src="<?= e($friend['zdjecie']) ?>" alt="przyjaciel"></section>
                <section class="description">
                    <h3><?= e($friend['imie']) ?> <?= e($friend['nazwisko']) ?></h3>
                    <p>Ostatni wpis: <?= e($friend['opis']) ?></p>
                </section>
                <hr>
            </article>
            <?php endforeach; ?>
        </section>
        <footer>
            Stronę wykonał: Bartosz Bryniarski
        </footer>
        <footer>
            <a href="mailto:[email protected]">napisz do mnie</a>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (lista.php)
  31. 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.php PHP zmienione: 3 linie
    <?php
    require '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;
    }
    
    function e(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES, 'UTF-8');
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Lista przyjaciół</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Portal Społecznościowy - moje konto</h1>
        </header>
        <section id="main">
            <h2>Moje zainteresowania</h2>
            <ul>
                <li>muzyka</li>
                <li>film</li>
                <li>komputery</li>
            </ul>
            <h2>Moi znajomi</h2>
            <?php if ($error !== ''): ?>
            <p><?= e($error) ?></p>
            <?php endif; ?>
            <?php foreach ($friends as $friend): ?>
            <article class="friend">
                <section class="photo"><img src="<?= e($friend['zdjecie']) ?>" alt="przyjaciel"></section>
                <section class="description">
                    <h3><?= e($friend['imie']) ?> <?= e($friend['nazwisko']) ?></h3>
                    <p>Ostatni wpis: <?= e($friend['opis']) ?></p>
                </section>
                <hr>
            </article>
            <?php endforeach; ?>
        </section>
        <footer>
            Stronę wykonał: Bartosz Bryniarski
        </footer>
        <footer>
            <a href="mailto:[email protected]">napisz do mnie</a>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (lista.php)
  32. 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 podstawowawersja nowoczesna
    układ stronyfloat: left stopekbody jako flexbox z zawijaniem
    zdjęcie i opisfloat: left obu bloków, linia z clear: bothkarta <article> jako flexbox, opis z flex: 1
    linia<hr> w bloku z klasą linesam <hr> w karcie (klucz to dopuszcza)
    kolorywpisane w każdej regulezmienne --dark-green, --green, --grey
    bibliotekamysqli (tabela 1 arkusza)PDO z wyjątkami, dane połączenia w config.php
    gdzie jest skryptw bloku głównym, HTML w echona górze pliku, w bloku głównym szablon z foreach
    odczyt wierszymysqli_fetch_row, numery [0] - [3]fetchAll, nazwy kolumn
    wypisywanie danychwprost (w etapie „Dobre praktyki” - htmlspecialchars)każda wartość przez e()
    zamknięcie bazymysqli_close po pętli$pdo = null w finally, przed HTML-em
    baza wyłączonaostrzeżenia i błąd PHPkomunikat, 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.

    Podgląd strony bez zmian - taki jak po kroku 31.

  33. Etap 6: Oddanie pracy

    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.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

    Podgląd strony bez zmian - taki jak po kroku 31.

Podgląd

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: . 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.

Potwierdzenie

Informacja