Przejdź do głównej treści
EE.09-03-22.06-SGCzerwiec 2022 · 39 kroków · ok. 77 min · 36 kryteriów z klucza

Rozwiązanie krok po kroku: Opinie klientów hurtowni spożywczej (Nowoczesny kod (grid, PDO))

To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza hurtownia z czterema zapytaniami, zdjęcie ewa.jpg przeskalowane do szerokości 150 px i strona opinie.php z przewijanym blokiem opinii i czterema blokami stopki - i tak samo spełnia każde kryterium klucza. Inna jest droga:

  • jedna stopka <footer> jako siatka: cztery bloki stopki to sekcje w jej środku, a ich szerokości 25% opisuje grid-template-columns - bez float,
  • znaczniki opisujące rolę bloków: blok opinii to <article> - samodzielna wypowiedź klienta,
  • zmienne CSS dla kolorów z arkusza - Tomato, biały i DimGray są wpisane raz,
  • PHP na PDO na górze pliku, przed HTML-em: połączenie i oba zapytania w bloku try, zamknięcie połączenia w finally. W HTML-u zostają tylko pętle i wypisanie danych,
  • każda wartość z bazy przechodzi przez htmlspecialchars, a awaria bazy daje zwykłe zdanie zamiast komunikatu PHP z danymi serwera.

To arkusz EE.09 z czerwca 2022 - kwalifikacji formuły 2017, którą zastąpiła INF.03. Baza i zdjęcia pochodzą z oryginalnego archiwum CKE zad3.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 8.1.2 - użyte konstrukcje działają w obu wersjach).

Jak liczą się punkty. Klucz ma 36 kryteriów (R.1.1 - R.5.8), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 27 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 36 kryteriów (pkt)

    Krok 1: Folder zdającego i archiwum zad3.zip

    ok. 2 min
    Polecenie z arkusza

    Na pulpicie znajduje się archiwum o nazwie zad3.zip zabezpieczone hasłem: #_Opini3% Archiwum należy rozpakować. Na pulpicie konta Egzamin należy utworzyć folder. Jako nazwy folderu należy użyć numeru zdającego, którym został podpisany arkusz. Rozpakowane pliki należy umieścić w tym folderze.

    Folder na pulpicie nazwany numerem PESEL (w tym rozwiązaniu w jego miejscu stoi Bartosz Bryniarski), do niego archiwum zad3.zip rozpakowane z hasłem #_Opini3%. W środku: baza.sql i pięć zdjęć klientów. Zdjęcia zostają obok strony - skrypt wpisze ich nazwy z bazy do atrybutu src.

  2. Krok 2: XAMPP i pusta baza hurtownia

    ok. 2 min
    Polecenie z arkusza

    Za pomocą narzędzia phpMyAdmin należy wykonać operacje na bazie danych: Utworzenie bazy danych o nazwie: hurtownia z zestawem polskich znaków (np. utf8_unicode_ci)

    Start przy Apache i MySQL, potem http://localhost/phpmyadmin, Nowa, nazwa hurtownia, porównywanie utf8_unicode_ci, Utwórz.

  3. Krok 3: Import pliku baza.sql i zrzut import.jpg

    Kryteria:

    R.1.1 Wykonano import tabel do bazy danych hurtownia , czynność udokumentowano plikiem o nazwie import w formacie JPEG

    ok. 3 min
    Polecenie z arkusza

    Import tabel do bazy hurtownia z pliku baza.sql. Czynność należy udokumentować zrzutem ekranu, na którym powinny być widoczne elementy wskazujące na poprawnie wykonany import tabel. Zrzut należy zapisać w formacie JPEG, pod nazwą import. Powinien on obejmować cały ekran monitora, z widocznym paskiem zadań.

    Baza hurtownia, karta Import, plik baza.sql, Wykonaj. Po imporcie są trzy tabele: klienci (9 rekordów), opinie (6) i typy (3). Zrzut całego ekranu z komunikatem o sukcesie - import.jpg.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 3
  4. Krok 4: Zapytanie 1: trzech klientów z największą liczbą punktów

    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 posortowane malejąco według punktów jedynie imię, nazwisko i punkty trzech klientów, których liczba punktów jest najwyższa. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT imie, nazwisko, punkty FROM klienci ORDER BY punkty DESC LIMIT 3; oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczne są jedynie 3 wiersze w kolejności: Jan Nowak 3305, Judy Beck 3055, Ewa Nowakowska 2045

    ok. 3 min
    Polecenie z arkusza

    Zapytania SQL na bazie hurtownia zapisane do pliku kwerendy.txt. Wykonanie kwerend należy udokumentować zrzutami w formacie PNG o nazwach kw1, kw2, kw3, kw4. Zrzuty powinny obejmować cały ekran monitora z widocznym paskiem zadań. Zapytanie 1: wybierające z tabeli Klienci posortowane malejąco według punktów jedynie imię, nazwisko i punkty trzech klientów, których liczba punktów jest najwyższa

    ORDER BY punkty DESC sortuje od największej liczby punktów, LIMIT 3 zostawia trzy pierwsze wiersze. Wynik: Jan Nowak 3305, Judy Beck 3055, Ewa Nowakowska 2045. Zapytanie do kwerendy.txt (R.1.2), zrzut - kw1.png (R.1.3).

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT imie, nazwisko, punkty
    FROM klienci
    ORDER BY punkty DESC
    LIMIT 3;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: liczba klientów każdego typu

    Kryteria:

    R.1.4 Utworzono zapytanie 2 wybierające nazwę z tabeli Typy oraz liczbę klientów przypisanych do każdego typu. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa, COUNT(nazwa) FROM klienci JOIN typy ON Typy_id = typy.id GROUP BY nazwa; (możliwe COUNT(*), GROUP BY(Typy_id), GROUP BY (Typy.id), porównanie kluczy w WHERE) oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczne są dokładnie 3 wiersze: platynowy 2, złoty 3, zwykły 4

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 2: wybierające nazwę z tabeli Typy oraz liczbę klientów przypisanych do każdego typu. Należy posłużyć się relacją

    Złączenie typów z klientami po Typy_id, COUNT(*) i GROUP BY nazwa - jedna liczba na typ. Wynik: platynowy 2, złoty 3, zwykły 4. Zrzut: kw2.png.

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    SELECT imie, nazwisko, punkty
    FROM klienci
    ORDER BY punkty DESC
    LIMIT 3;
    
    -- Zapytanie 2
    SELECT nazwa, COUNT(*)
    FROM typy
    JOIN klienci ON typy.id = klienci.Typy_id
    GROUP BY nazwa;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: zdjęcia, imiona i opinie klientów typu 2 i 3

    Kryteria:

    R.1.5 Utworzono zapytanie 3 wybierające jedynie pola zdjecie, imie z tabeli klienci oraz odpowiadające im pole opinia z tabeli opinie dla klientów, których typ ma id o wartości 2, 3. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT zdjecie, imie, opinia FROM klienci JOIN opinie ON klienci.id = klienci_id WHERE Typy_id=2 OR Typy_id=3; (lub porównanie kluczy po WHERE) oraz wynik działania zapytania udokumentowano zrzutem, na którym widocznych jest dokładnie 5 wierszy, jedynie pola zdjecie, imie, opinia dla imion: Anna, Ewa, Jan, John, Judy

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie pola zdjecie, imie z tabeli Klienci oraz odpowiadające im pole opinia z tabeli opinie dla klientów, których typ ma id o wartościach 2, 3. Należy posłużyć się relacją

    Złączenie klientów z opiniami po Klienci_id i warunek Typy_id IN (2, 3). Wynik: 5 wierszy - Anna, Ewa, Jan, John i Judy. Zrzut: kw3.png. To zapytanie wykona skrypt 1.

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    SELECT imie, nazwisko, punkty
    FROM klienci
    ORDER BY punkty DESC
    LIMIT 3;
    
    -- Zapytanie 2
    SELECT nazwa, COUNT(*)
    FROM typy
    JOIN klienci ON typy.id = klienci.Typy_id
    GROUP BY nazwa;
    
    -- Zapytanie 3
    SELECT zdjecie, imie, opinia
    FROM klienci
    JOIN opinie ON klienci.id = opinie.Klienci_id
    WHERE Typy_id IN (2, 3);
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: nowa kolumna adres

    Kryteria:

    R.1.6 Utworzono zapytanie 4 dodające do tabeli klienci kolumnę o nazwie adres i typie napisowym, maksymalnie 50-znakowym. W pliku z kwerendami lub na zrzucie istnieje zapis np.: ALTER TABLE klienci ADD adres VARCHAR(50); (lub ADD COLUMN, typ text jest błędem) oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczny jest komunikat o pomyślnie wykonanym zapytaniu lub tabela z dodaną kolumną

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 4: dodające do tabeli Klienci kolumnę o nazwie adres i typie napisowym, maksymalnie 50-znakowym

    ALTER TABLE zmienia strukturę tabeli, ADD adres VARCHAR(50) dokłada kolumnę napisową do 50 znaków (typ TEXT klucz uznaje za błąd). Komunikat „MySQL zwrócił pusty wynik” i kolumna adres w strukturze tabeli - zrzut kw4.png.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT imie, nazwisko, punkty
    FROM klienci
    ORDER BY punkty DESC
    LIMIT 3;
    
    -- Zapytanie 2
    SELECT nazwa, COUNT(*)
    FROM typy
    JOIN klienci ON typy.id = klienci.Typy_id
    GROUP BY nazwa;
    
    -- Zapytanie 3
    SELECT zdjecie, imie, opinia
    FROM klienci
    JOIN opinie ON klienci.id = opinie.Klienci_id
    WHERE Typy_id IN (2, 3);
    
    -- Zapytanie 4
    ALTER TABLE klienci ADD adres VARCHAR(50);
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Grafika i strona HTML

    Kroki 8-19 · po tym etapie masz 13 z 36 kryteriów (pkt)

    Krok 8: Zdjęcie ewa.jpg: szerokość 150 px z zachowaniem proporcji

    Kryteria:

    R.2.1 Obraz w pliku ewa .jpg przeskalowano z zachowaniem proporcji do szerokości 150 px, odpowiadająca temu wysokość wynosi 200 px (± 2 px)

    ok. 2 min
    Polecenie z arkusza

    Cechy grafiki ewa.jpg: Przeskalowana z zachowaniem proporcji do szerokości 150 px

    Obraz → Skaluj obraz z zamkniętym łańcuchem, szerokość 150 px - wysokość wylicza się sama: z 317 × 423 px wychodzi 150 × 200 px, tyle samo co pozostałe zdjęcia. Eksport pod nazwą ewa.jpg (R.2.1).

    Podgląd strony po tym kroku
    Podgląd strony po kroku 8
  9. Krok 9: Plik opinie.php z deklaracją HTML5

    Kryteria:

    R.2.2 Witrynę internetową zapisano w pliku o nazwie opinie.php

    ok. 1 min
    Polecenie z arkusza

    Cechy witryny: Składa się ze strony o nazwie opinie.php Zapisana w języku HTML5

    Plik opinie.php (R.2.2) od razu z <!DOCTYPE html> w pierwszej linii i szkieletem <html>, <head>, <body>.

    opinie.php PHP nowy plik
    <!DOCTYPE html>
    <html>
    <head>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  10. Krok 10: Kodowanie polskich znaków i język strony

    Kryteria:

    R.2.3 Zapisano jawnie standard kodowania polskich znaków i deklarację HTML5 <!DOCTYPE HTML> oraz zadeklarowano dla witryny język polski (np. lang = 'pl')

    ok. 1 min
    Polecenie z arkusza

    Jawnie zastosowany właściwy standard kodowania polskich znaków Zadeklarowany język witryny: polski

    <meta charset="UTF-8"> jako pierwszy element <head> i lang="pl" w znaczniku <html> - deklaracja HTML5, kodowanie i język są gotowe (R.2.3).

    opinie.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  11. Krok 11: Tytuł strony i viewport

    Kryteria:

    R.2.4 Nadano tytuł strony: Opinie klientów

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony widoczny na karcie przeglądarki: „Opinie klientów”

    <title> z tytułem z arkusza (R.2.4) i <meta name="viewport"> - bez niej telefon pokazuje stronę jak pomniejszony ekran komputera.

    opinie.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>Opinie klientów</title>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  12. Krok 12: Podpięcie arkusza stylów styl3.css

    Kryteria:

    R.3.3 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl3.css , formatowanie pochodzi jedynie z tego arkusza Uwaga: kryterium to nie jest spełnione również, gdy zdający źle przypisał co najmniej jeden selektor do bloku (np. kropka zamiast #)

    ok. 1 min
    Polecenie z arkusza

    Arkusz stylów w pliku o nazwie styl3.css prawidłowo połączony z kodem strony

    <link rel="stylesheet" href="styl3.css"> - całe formatowanie pochodzi z tego pliku (R.3.3).

    opinie.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>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  13. Krok 13: Bloki: baner, główny i stopka z czterema sekcjami

    Kryteria:

    R.2.5 Wygląd strony zdefiniowano dzieląc ją na bloki: baner, główny i cztery bloki stopki. Zastosowano znaczniki sekcji

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki: baner, główny i cztery 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

    Baner <header>, blok główny <main> i jedna stopka <footer> z czterema sekcjami <section> - to one są czterema blokami stopki (R.2.5). Stopka jest jedna, bo jest jedną częścią strony: kontenerem, który za chwilę ułoży swoje sekcje w siatkę. Selektor footer section trafi w każdą z nich bez klas i identyfikatorów.

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

    ok. 1 min
    Polecenie z arkusza

    Zawartość banera: nagłówek pierwszego stopnia o treści: „Hurtownia spożywcza”

    <h1> w <header>.

    opinie.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>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
        </main>
        <footer>
            <section>
            </section>
            <section>
            </section>
            <section>
            </section>
            <section>
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  15. Krok 15: Blok główny: nagłówek drugiego stopnia

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku głównego: Nagłówek drugiego stopnia o treści: „Opinie naszych klientów”

    <h2> w <main>; bloki opinii dopisze pod nim PHP.

    opinie.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>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
            <h2>Opinie naszych klientów</h2>
        </main>
        <footer>
            <section>
            </section>
            <section>
            </section>
            <section>
            </section>
            <section>
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  16. Krok 16: Pierwszy blok stopki: nagłówek i odnośnik „Sklep 1”

    Kryteria:

    R.3.2 Na stronie umieszczono odnośnik, którego kliknięcie inicjuje otwarcie strony http://sklep.pl/

    ok. 1 min
    Polecenie z arkusza

    Zawartość pierwszego bloku stopki: Nagłówek trzeciego stopnia o treści: „Współpracują z nami” Odnośnik o treści „Sklep 1” prowadzący do adresu http://sklep.pl/

    <h3> i odnośnik z pełnym adresem, razem z http:// (R.3.2).

    opinie.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>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
            <h2>Opinie naszych klientów</h2>
        </main>
        <footer>
            <section>
                <h3>Współpracują z nami</h3>
                <a href="http://sklep.pl/">Sklep 1</a>
            </section>
            <section>
            </section>
            <section>
            </section>
            <section>
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  17. Krok 17: Drugi blok stopki: nagłówek i lista numerowana

    ok. 1 min
    Polecenie z arkusza

    Zawartość drugiego bloku stopki: Nagłówek trzeciego stopnia o treści: „Nasi top klienci” Lista numerowana (uporządkowana), a w niej efekt działania skryptu 2

    <h3> i pusta lista <ol> - elementy wypisze PHP.

    opinie.php PHP zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
            <h2>Opinie naszych klientów</h2>
        </main>
        <footer>
            <section>
                <h3>Współpracują z nami</h3>
                <a href="http://sklep.pl/">Sklep 1</a>
            </section>
            <section>
                <h3>Nasi top klienci</h3>
                <ol>
                </ol>
            </section>
            <section>
            </section>
            <section>
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  18. Krok 18: Trzeci blok stopki: nagłówek i telefon

    ok. 1 min
    Polecenie z arkusza

    Zawartość trzeciego bloku stopki: Nagłówek trzeciego stopnia o treści: „Skontaktuj się” Paragraf o treści: „telefon: 111222333”

    <h3> i paragraf. Numer jest odnośnikiem tel: - na telefonie dotknięcie od razu wybiera numer. Treść paragrafu zostaje dokładnie „telefon: 111222333”.

    opinie.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>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
            <h2>Opinie naszych klientów</h2>
        </main>
        <footer>
            <section>
                <h3>Współpracują z nami</h3>
                <a href="http://sklep.pl/">Sklep 1</a>
            </section>
            <section>
                <h3>Nasi top klienci</h3>
                <ol>
                </ol>
            </section>
            <section>
                <h3>Skontaktuj się</h3>
                <p>telefon: <a href="tel:111222333">111222333</a></p>
            </section>
            <section>
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  19. Krok 19: Czwarty blok stopki: autor

    ok. 1 min
    Polecenie z arkusza

    Zawartość czwartego bloku stopki: Nagłówek trzeciego stopnia o treści: „Autor: ”, dalej wstawiony numer zdającego

    <h3> - na egzaminie po dwukropku wpisujesz swój numer PESEL; tu stoi Bartosz Bryniarski.

    opinie.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>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
            <h2>Opinie naszych klientów</h2>
        </main>
        <footer>
            <section>
                <h3>Współpracują z nami</h3>
                <a href="http://sklep.pl/">Sklep 1</a>
            </section>
            <section>
                <h3>Nasi top klienci</h3>
                <ol>
                </ol>
            </section>
            <section>
                <h3>Skontaktuj się</h3>
                <p>telefon: <a href="tel:111222333">111222333</a></p>
            </section>
            <section>
                <h3>Autor: Bartosz Bryniarski</h3>
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  20. Etap 3: Styl CSS

    Kroki 20-26 · po tym etapie masz 18 z 36 kryteriów (pkt)

    Krok 20: Plik styl3.css: kolory jako zmienne i krój dla wszystkich

    Kryteria:

    R.4.1 Ustawiono domyślną wartość dla wszystkich selektorów stylu CSS (*): krój czcionki Tahoma, sans-serif (czcionka Tahoma jako pierwsza)

    ok. 2 min
    Polecenie z arkusza

    Styl CSS zdefiniowany w całości w zewnętrznym pliku o nazwie styl3.css. Domyślna wartość dla wszystkich selektorów stylu CSS: krój czcionki Tahoma, w przypadku braku sans-serif

    Na początku zmienne CSS w :root: cztery kolory z arkusza dostają nazwy. Tomato i biały występują po kilka razy (baner, stopka, odnośnik), więc zmiana motywu to kilka linii na górze pliku.

    Selektor * obejmuje każdy element: Tahoma, sans-serif (R.4.1).

    styl3.css CSS nowy plik
    :root {
        --accent: Tomato;
        --light: white;
        --main-bg: Seashell;
        --muted: DimGray;
    }
    
    * {
        font-family: Tahoma, sans-serif;
    }
    Podgląd strony po tym kroku (opinie.php)
  21. Krok 21: Baner: tło, biały tekst, marginesy wewnętrzne

    ok. 1 min
    Polecenie z arkusza

    Dla banera: kolor tła Tomato, biały kolor czcionki, marginesy wewnętrzne 10 px

    Kolory ze zmiennych --accent i --light, padding: 10px.

    styl3.css CSS zmienione: 6 linii
    :root {
        --accent: Tomato;
        --light: white;
        --main-bg: Seashell;
        --muted: DimGray;
    }
    
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header {
        background-color: var(--accent);
        color: var(--light);
        padding: 10px;
    }
    Podgląd strony po tym kroku (opinie.php)
  22. Krok 22: Blok główny: tło, wysokość i przewijanie

    ok. 1 min
    Polecenie z arkusza

    Dla bloku głównego: kolor tła Seashell, wysokość 500 px, paski przewijania dodawane są, gdy zawartość bloku jest większa od jego rozmiaru

    Stała wysokość 500 px i overflow: auto - pasek przewijania pojawia się dopiero wtedy, gdy opinie się nie mieszczą.

    styl3.css CSS zmienione: 6 linii
    :root {
        --accent: Tomato;
        --light: white;
        --main-bg: Seashell;
        --muted: DimGray;
    }
    
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header {
        background-color: var(--accent);
        color: var(--light);
        padding: 10px;
    }
    
    main {
        background-color: var(--main-bg);
        height: 500px;
        overflow: auto;
    }
    Podgląd strony po tym kroku (opinie.php)
  23. Krok 23: Układ: stopka jako siatka czterech kolumn

    Kryteria:

    R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z Obrazem 2 w arkuszu egzaminacyjnym, nie należy uwzględniać bloków z opiniami (prawidłowo zastosowano właściwości CSS układające bloki na stronie, np. float albo display flex albo grid)

    ok. 3 min
    Polecenie z arkusza

    Wspólne dla wszystkich bloków stopki: szerokość 25%. 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

    Stopka staje się siatką: display: grid i grid-template-columns: repeat(4, 25%) - cztery kolumny po 25%. repeat(4, …) to skrót czterokrotnego wpisania 25%. Sekcje wypełniają kolumny po kolei, więc cztery bloki stoją obok siebie, a stopka zajmuje całą szerokość pod blokiem głównym - bez float i bez obawy, że czwarty blok spadnie do nowego wiersza (R.3.1).

    styl3.css CSS zmienione: 5 linii
    :root {
        --accent: Tomato;
        --light: white;
        --main-bg: Seashell;
        --muted: DimGray;
    }
    
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header {
        background-color: var(--accent);
        color: var(--light);
        padding: 10px;
    }
    
    main {
        background-color: var(--main-bg);
        height: 500px;
        overflow: auto;
    }
    
    footer {
        display: grid;
        grid-template-columns: repeat(4, 25%);
    }
    Podgląd strony po tym kroku (opinie.php)
  24. Krok 24: Bloki stopki: tło, kolor, wysokość

    Kryteria: ,

    R.4.2 Ustawiono kolor tła Tomato dla banera i wszystkich bloków stopki, Seashell dla bloku głównego

    R.4.4 Ustawiono wysokość bloku głównego 500px i wszystkich bloków stopki 160px oraz szerokość wszystkich bloków stopki 25% (width lub grid-template-columns lub flex). Dla bloku głównego ustawiono paski przewijania overflow:auto;

    ok. 2 min
    Polecenie z arkusza

    Wspólne dla wszystkich bloków stopki: kolor tła Tomato, biały kolor czcionki, wysokość 160 px

    Selektor potomka footer section - każda sekcja wewnątrz stopki. Kolory tła banera, bloku głównego i bloków stopki są ustawione (R.4.2); wysokości, przewijanie i szerokości 25% przez grid-template-columns - też (klucz przyjmuje tę właściwość, R.4.4).

    styl3.css CSS zmienione: 6 linii
    :root {
        --accent: Tomato;
        --light: white;
        --main-bg: Seashell;
        --muted: DimGray;
    }
    
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header {
        background-color: var(--accent);
        color: var(--light);
        padding: 10px;
    }
    
    main {
        background-color: var(--main-bg);
        height: 500px;
        overflow: auto;
    }
    
    footer {
        display: grid;
        grid-template-columns: repeat(4, 25%);
    }
    
    footer section {
        background-color: var(--accent);
        color: var(--light);
        height: 160px;
    }
    Podgląd strony po tym kroku (opinie.php)
  25. Krok 25: Nagłówki h1 i h2: wyśrodkowanie

    ok. 1 min
    Polecenie z arkusza

    Dla selektorów nagłówka pierwszego i drugiego stopnia: wyrównanie tekstu do środka

    Dwa selektory znaczników po przecinku - arkusz wymaga selektorów znaczników dla nagłówków, obrazu i cytatu.

    styl3.css CSS zmienione: 5 linii
    :root {
        --accent: Tomato;
        --light: white;
        --main-bg: Seashell;
        --muted: DimGray;
    }
    
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header {
        background-color: var(--accent);
        color: var(--light);
        padding: 10px;
    }
    
    main {
        background-color: var(--main-bg);
        height: 500px;
        overflow: auto;
    }
    
    footer {
        display: grid;
        grid-template-columns: repeat(4, 25%);
    }
    
    footer section {
        background-color: var(--accent);
        color: var(--light);
        height: 160px;
    }
    
    h1,
    h2 {
        text-align: center;
    }
    Podgląd strony po tym kroku (opinie.php)
  26. Krok 26: Odnośniki: biały kolor i widoczny fokus

    Kryteria:

    R.4.3 Ustawiono biały kolor czcionki dla banera, wszystkich bloków stopki i odnośnika

    ok. 1 min
    Polecenie z arkusza

    Dla odnośnika: biały kolor czcionki

    Biały kolor odnośników (R.4.3 - baner, bloki stopki i odnośnik). :focus-visible dokłada białą ramkę wokół odnośnika wybranego klawiszem Tab - na pomarańczowym tle domyślna ramka przeglądarki jest słabo widoczna.

    styl3.css CSS zmienione: 9 linii
    :root {
        --accent: Tomato;
        --light: white;
        --main-bg: Seashell;
        --muted: DimGray;
    }
    
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header {
        background-color: var(--accent);
        color: var(--light);
        padding: 10px;
    }
    
    main {
        background-color: var(--main-bg);
        height: 500px;
        overflow: auto;
    }
    
    footer {
        display: grid;
        grid-template-columns: repeat(4, 25%);
    }
    
    footer section {
        background-color: var(--accent);
        color: var(--light);
        height: 160px;
    }
    
    h1,
    h2 {
        text-align: center;
    }
    
    a {
        color: var(--light);
    }
    
    a:focus-visible {
        outline: 2px solid var(--light);
        outline-offset: 2px;
    }
    Podgląd strony po tym kroku (opinie.php)
  27. Etap 4: Skrypty PHP

    Kroki 27-33 · po tym etapie masz 30 z 36 kryteriów (pkt)

    Krok 27: Kod PHP na górze pliku i połączenie z bazą hurtownia

    Kryteria:

    R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy hurtownia

    ok. 4 min
    Polecenie z arkusza

    Skrypty łączą się z serwerem bazy danych na localhost, użytkownik root bez hasła, baza danych o nazwie hurtownia

    Oba skrypty arkusza korzystają z tej samej bazy. Cały kod PHP stoi więc na samej górze opinie.php, przed <!DOCTYPE html>: najpierw komunikacja z bazą, potem HTML, który tylko wypisuje przygotowane dane.

    • e() - krótka funkcja z htmlspecialchars: każdy tekst z bazy zamienia <, >, &, cudzysłów i apostrof na encje, więc nie stanie się kodem strony,
    • new PDO(...) - serwer localhost, baza hurtownia i kodowanie w jednym napisie, potem root i puste hasło (R.5.1). ERRMODE_EXCEPTION - każdy błąd bazy rzuca wyjątek, FETCH_ASSOC - wiersze przychodzą jako tablice z nazwami kolumn ($opinion['imie'] zamiast $row[1]),
    • catch - gdy baza nie działa, szczegóły trafiają do dziennika błędów serwera (error_log), a w bloku głównym pojawia się zwykłe zdanie (trzy linie pod <h2>).

    PDO zamiast funkcji mysqli z tabeli 1 - klucz przyjmuje obie biblioteki (uwaga przy R.5). Od tego kroku podgląd to wynik prawdziwego uruchomienia strony - na razie wygląda jak wcześniej.

    opinie.php PHP zmienione: 21 linii
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $dbError = false;
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=hurtownia;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $exception) {
        $dbError = true;
        error_log($exception->getMessage());
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
            <h2>Opinie naszych klientów</h2>
    <?php if ($dbError): ?>
            <p>Opinie są chwilowo niedostępne, spróbuj ponownie za chwilę.</p>
    <?php endif; ?>
        </main>
        <footer>
            <section>
                <h3>Współpracują z nami</h3>
                <a href="http://sklep.pl/">Sklep 1</a>
            </section>
            <section>
                <h3>Nasi top klienci</h3>
                <ol>
                </ol>
            </section>
            <section>
                <h3>Skontaktuj się</h3>
                <p>telefon: <a href="tel:111222333">111222333</a></p>
            </section>
            <section>
                <h3>Autor: Bartosz Bryniarski</h3>
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  28. Krok 28: Skrypt 1: zapytanie 3, blok opinii i zdjęcie

    Kryteria: , ,

    R.5.3 Przynajmniej jedno zapytanie zostało wysłane do bazy: skrypt 1 - zapytanie 3 lub skrypt 2 - zapytanie 1

    R.5.5 Skrypt 1 dla każdego zwróconego wiersza wyświetla obraz, którego nazwa pliku jest pobrana z bazy

    R.2.8 W bloku głównym umieszczono przynajmniej jeden obraz z tekstem alternatywnym "klient" (w skrypcie lub HTML)

    ok. 4 min
    Polecenie z arkusza

    Działanie skryptu 1: Wysyła do bazy zapytanie 3. Każdy zwrócony zapytaniem wiersz wypisuje w osobnym bloku opinii, według wzoru: Obraz, którego nazwa pliku została pobrana z bazy danych, z tekstem alternatywnym „klient”

    • $pdo->query(...) - zapytanie 3 bez zmian (R.5.3); nie ma w nim danych od użytkownika, więc wystarczy zwykłe query,
    • ->fetchAll() - od razu tablica wszystkich pięciu wierszy w $opinions. Tablica jest na początku pusta ([]) - przy awarii bazy pętla po prostu nic nie wypisze,
    • w HTML-u pętla foreach w składni alternatywnej (foreach (...): … endforeach;) - przy przeplataniu PHP z HTML-em łatwiej ją czytać niż klamry,
    • każdy wiersz to <article class="opinion"> - samodzielna wypowiedź klienta; klasa, bo bloków jest kilka (klucz przy R.4.8: identyfikator jest błędem),
    • <img src="<?= e($opinion['zdjecie']) ?>" alt="klient"> - nazwa pliku z bazy (R.5.5) i tekst alternatywny z arkusza (R.2.8). width i height rezerwują miejsce na zdjęcie, zanim się wczyta.

    Podgląd: pięć zdjęć jedno pod drugim - bloki nie mają jeszcze stylu.

    opinie.php PHP zmienione: 10 linii
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $dbError = false;
    $opinions = [];
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=hurtownia;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // skrypt 1 - zapytanie 3: opinie klientów złotych i platynowych
        $opinions = $pdo->query('SELECT zdjecie, imie, opinia FROM klienci JOIN opinie ON klienci.id = opinie.Klienci_id WHERE Typy_id IN (2, 3);')
            ->fetchAll();
    } catch (PDOException $exception) {
        $dbError = true;
        error_log($exception->getMessage());
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
            <h2>Opinie naszych klientów</h2>
    <?php if ($dbError): ?>
            <p>Opinie są chwilowo niedostępne, spróbuj ponownie za chwilę.</p>
    <?php endif; ?>
    <?php foreach ($opinions as $opinion): ?>
            <article class="opinion">
                <img src="<?= e($opinion['zdjecie']) ?>" alt="klient" width="150" height="200">
            </article>
    <?php endforeach; ?>
        </main>
        <footer>
            <section>
                <h3>Współpracują z nami</h3>
                <a href="http://sklep.pl/">Sklep 1</a>
            </section>
            <section>
                <h3>Nasi top klienci</h3>
                <ol>
                </ol>
            </section>
            <section>
                <h3>Skontaktuj się</h3>
                <p>telefon: <a href="tel:111222333">111222333</a></p>
            </section>
            <section>
                <h3>Autor: Bartosz Bryniarski</h3>
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  29. Krok 29: Skrypt 1: opinia w cytacie

    Kryteria: ,

    R.2.9 W bloku głównym umieszczono przynajmniej jeden cytat, zastosowano znacznik <blockquote> lub <q> lub <cite> (w skrypcie lub HTML)

    R.5.4 Skrypt 1 dla każdego zwróconego wiersza tworzy blok, w którym znajdują się obraz i tekst

    ok. 2 min
    Polecenie z arkusza

    Cytat (element HTML) zawierający opinię, której treść została pobrana z bazy

    <blockquote> - dłuższy cytat, tu słowa klienta (R.2.9), przez e(). Każdy blok ma obraz i tekst (R.5.4).

    opinie.php PHP zmienione: 1 linia
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $dbError = false;
    $opinions = [];
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=hurtownia;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // skrypt 1 - zapytanie 3: opinie klientów złotych i platynowych
        $opinions = $pdo->query('SELECT zdjecie, imie, opinia FROM klienci JOIN opinie ON klienci.id = opinie.Klienci_id WHERE Typy_id IN (2, 3);')
            ->fetchAll();
    } catch (PDOException $exception) {
        $dbError = true;
        error_log($exception->getMessage());
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
            <h2>Opinie naszych klientów</h2>
    <?php if ($dbError): ?>
            <p>Opinie są chwilowo niedostępne, spróbuj ponownie za chwilę.</p>
    <?php endif; ?>
    <?php foreach ($opinions as $opinion): ?>
            <article class="opinion">
                <img src="<?= e($opinion['zdjecie']) ?>" alt="klient" width="150" height="200">
                <blockquote><?= e($opinion['opinia']) ?></blockquote>
            </article>
    <?php endforeach; ?>
        </main>
        <footer>
            <section>
                <h3>Współpracują z nami</h3>
                <a href="http://sklep.pl/">Sklep 1</a>
            </section>
            <section>
                <h3>Nasi top klienci</h3>
                <ol>
                </ol>
            </section>
            <section>
                <h3>Skontaktuj się</h3>
                <p>telefon: <a href="tel:111222333">111222333</a></p>
            </section>
            <section>
                <h3>Autor: Bartosz Bryniarski</h3>
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  30. Krok 30: Skrypt 1: imię klienta w nagłówku czwartego stopnia

    Kryteria: ,

    R.5.6 Skrypt 1 dla każdego zwróconego wiersza wyświetla opinię oraz imię pobrane z bazy

    R.2.6 W banerze zapisano nagłówek h1: "Hurtownia spożywcza", w bloku głównym zapisano nagłówek h2: "Opinie naszych klientów" oraz przynajmniej jeden h4 (w skrypcie lub HTML), w blokach stopki przynajmniej jeden nagłówek h3 "Współpracują z nami" lub "Nasi top klienci", lub "Skontaktuj się", lub "Autor:" oraz paragraf "telefon 111222333"

    ok. 2 min
    Polecenie z arkusza

    Nagłówek czwartego stopnia, a w nim pobrane z bazy imię klienta

    <h4> z imieniem pod cytatem - skrypt 1 wypisuje opinię i imię (R.5.6). Strona ma komplet nagłówków <h1> - <h4> i paragraf z telefonem (R.2.6). Podgląd: Anna, Ewa, Jan, John i Judy.

    opinie.php PHP zmienione: 1 linia
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $dbError = false;
    $opinions = [];
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=hurtownia;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // skrypt 1 - zapytanie 3: opinie klientów złotych i platynowych
        $opinions = $pdo->query('SELECT zdjecie, imie, opinia FROM klienci JOIN opinie ON klienci.id = opinie.Klienci_id WHERE Typy_id IN (2, 3);')
            ->fetchAll();
    } catch (PDOException $exception) {
        $dbError = true;
        error_log($exception->getMessage());
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
            <h2>Opinie naszych klientów</h2>
    <?php if ($dbError): ?>
            <p>Opinie są chwilowo niedostępne, spróbuj ponownie za chwilę.</p>
    <?php endif; ?>
    <?php foreach ($opinions as $opinion): ?>
            <article class="opinion">
                <img src="<?= e($opinion['zdjecie']) ?>" alt="klient" width="150" height="200">
                <blockquote><?= e($opinion['opinia']) ?></blockquote>
                <h4><?= e($opinion['imie']) ?></h4>
            </article>
    <?php endforeach; ?>
        </main>
        <footer>
            <section>
                <h3>Współpracują z nami</h3>
                <a href="http://sklep.pl/">Sklep 1</a>
            </section>
            <section>
                <h3>Nasi top klienci</h3>
                <ol>
                </ol>
            </section>
            <section>
                <h3>Skontaktuj się</h3>
                <p>telefon: <a href="tel:111222333">111222333</a></p>
            </section>
            <section>
                <h3>Autor: Bartosz Bryniarski</h3>
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  31. Krok 31: Skrypt 2: zapytanie 1 i elementy listy

    Kryteria: ,

    R.5.8 Skrypt 2 wyświetla zwrócone wiersze jako elementy listy

    R.2.7 W drugim bloku stopki umieszczono listę numerowaną, zastosowano znaczniki <ol>, <li> (w skrypcie lub HTML)

    ok. 3 min
    Polecenie z arkusza

    Działanie skryptu 2: Wysyła do bazy zapytanie 1 i wypisuje zwrócone wiersze jako elementy listy

    Na górze pliku zapytanie 1 i fetchAll() do $topClients, w liście <ol> pętla foreach z elementami <li> (R.5.8, R.2.7) - na razie imię i nazwisko, oba przez e().

    opinie.php PHP zmienione: 8 linii
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $dbError = false;
    $opinions = [];
    $topClients = [];
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=hurtownia;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // skrypt 1 - zapytanie 3: opinie klientów złotych i platynowych
        $opinions = $pdo->query('SELECT zdjecie, imie, opinia FROM klienci JOIN opinie ON klienci.id = opinie.Klienci_id WHERE Typy_id IN (2, 3);')
            ->fetchAll();
    
        // skrypt 2 - zapytanie 1: trzech klientów z największą liczbą punktów
        $topClients = $pdo->query('SELECT imie, nazwisko, punkty FROM klienci ORDER BY punkty DESC LIMIT 3;')
            ->fetchAll();
    } catch (PDOException $exception) {
        $dbError = true;
        error_log($exception->getMessage());
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
            <h2>Opinie naszych klientów</h2>
    <?php if ($dbError): ?>
            <p>Opinie są chwilowo niedostępne, spróbuj ponownie za chwilę.</p>
    <?php endif; ?>
    <?php foreach ($opinions as $opinion): ?>
            <article class="opinion">
                <img src="<?= e($opinion['zdjecie']) ?>" alt="klient" width="150" height="200">
                <blockquote><?= e($opinion['opinia']) ?></blockquote>
                <h4><?= e($opinion['imie']) ?></h4>
            </article>
    <?php endforeach; ?>
        </main>
        <footer>
            <section>
                <h3>Współpracują z nami</h3>
                <a href="http://sklep.pl/">Sklep 1</a>
            </section>
            <section>
                <h3>Nasi top klienci</h3>
                <ol>
    <?php foreach ($topClients as $client): ?>
                    <li><?= e($client['imie']) ?> <?= e($client['nazwisko']) ?></li>
    <?php endforeach; ?>
                </ol>
            </section>
            <section>
                <h3>Skontaktuj się</h3>
                <p>telefon: <a href="tel:111222333">111222333</a></p>
            </section>
            <section>
                <h3>Autor: Bartosz Bryniarski</h3>
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  32. Krok 32: Skrypt 2: punkty według wzoru

    Kryteria:

    R.5.7 Skrypt 2 wyświetla zwrócone wiersze według wzoru: „<imie> <nazwisko>, <punkty> pkt.”, gdzie pola <> są pobrane z bazy

    ok. 1 min
    Polecenie z arkusza

    według wzoru: „<imie> <nazwisko>, <punkty> pkt.”, gdzie pola <> są pobrane z bazy

    Po nazwisku przecinek, punkty i „pkt.” (R.5.7). Punkty przechodzą przez (int) - liczba całkowita nie zawiera znaczników, więc nie potrzebuje e(). Podgląd: „Jan Nowak, 3305 pkt.”.

    opinie.php PHP zmienione: 1 linia
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $dbError = false;
    $opinions = [];
    $topClients = [];
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=hurtownia;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // skrypt 1 - zapytanie 3: opinie klientów złotych i platynowych
        $opinions = $pdo->query('SELECT zdjecie, imie, opinia FROM klienci JOIN opinie ON klienci.id = opinie.Klienci_id WHERE Typy_id IN (2, 3);')
            ->fetchAll();
    
        // skrypt 2 - zapytanie 1: trzech klientów z największą liczbą punktów
        $topClients = $pdo->query('SELECT imie, nazwisko, punkty FROM klienci ORDER BY punkty DESC LIMIT 3;')
            ->fetchAll();
    } catch (PDOException $exception) {
        $dbError = true;
        error_log($exception->getMessage());
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
            <h2>Opinie naszych klientów</h2>
    <?php if ($dbError): ?>
            <p>Opinie są chwilowo niedostępne, spróbuj ponownie za chwilę.</p>
    <?php endif; ?>
    <?php foreach ($opinions as $opinion): ?>
            <article class="opinion">
                <img src="<?= e($opinion['zdjecie']) ?>" alt="klient" width="150" height="200">
                <blockquote><?= e($opinion['opinia']) ?></blockquote>
                <h4><?= e($opinion['imie']) ?></h4>
            </article>
    <?php endforeach; ?>
        </main>
        <footer>
            <section>
                <h3>Współpracują z nami</h3>
                <a href="http://sklep.pl/">Sklep 1</a>
            </section>
            <section>
                <h3>Nasi top klienci</h3>
                <ol>
    <?php foreach ($topClients as $client): ?>
                    <li><?= e($client['imie']) ?> <?= e($client['nazwisko']) ?>, <?= (int) $client['punkty'] ?> pkt.</li>
    <?php endforeach; ?>
                </ol>
            </section>
            <section>
                <h3>Skontaktuj się</h3>
                <p>telefon: <a href="tel:111222333">111222333</a></p>
            </section>
            <section>
                <h3>Autor: Bartosz Bryniarski</h3>
            </section>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (opinie.php)
  33. Krok 33: Zamknięcie połączenia z bazą

    Kryteria:

    R.5.2 Ostatnią operacją na bazie jest jej zamknięcie (zgodne ze składnią)

    ok. 2 min
    Polecenie z arkusza

    Po wykonaniu operacji na bazie danych zamykane jest połączenie z serwerem

    PDO nie ma funkcji close - połączenie zamyka się, gdy znika ostatnie odwołanie do obiektu: $pdo = null. Stoi w bloku finally, który wykona się zawsze po try - po udanych zapytaniach i po wyjątku (R.5.2).

    Opinie i lista klientów są już w zmiennych $opinions i $topClients, więc HTML wypisuje je po zamknięciu połączenia.

    opinie.php PHP zmienione: 3 linie
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $dbError = false;
    $opinions = [];
    $topClients = [];
    $pdo = null;
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=hurtownia;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // skrypt 1 - zapytanie 3: opinie klientów złotych i platynowych
        $opinions = $pdo->query('SELECT zdjecie, imie, opinia FROM klienci JOIN opinie ON klienci.id = opinie.Klienci_id WHERE Typy_id IN (2, 3);')
            ->fetchAll();
    
        // skrypt 2 - zapytanie 1: trzech klientów z największą liczbą punktów
        $topClients = $pdo->query('SELECT imie, nazwisko, punkty FROM klienci ORDER BY punkty DESC LIMIT 3;')
            ->fetchAll();
    } catch (PDOException $exception) {
        $dbError = true;
        error_log($exception->getMessage());
    } finally {
        $pdo = null;    // zamknięcie połączenia - ostatnia operacja na bazie
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Opinie klientów</title>
        <link rel="stylesheet" href="styl3.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia spożywcza</h1>
        </header>
        <main>
            <h2>Opinie naszych klientów</h2>
    <?php if ($dbError): ?>
            <p>Opinie są chwilowo niedostępne, spróbuj ponownie za chwilę.</p>
    <?php endif; ?>
    <?php foreach ($opinions as $opinion): ?>
            <article class="opinion">
                <img src="<?= e($opinion['zdjecie']) ?>" alt="klient" width="150" height="200">
                <blockquote><?= e($opinion['opinia']) ?></blockquote>
                <h4><?= e($opinion['imie']) ?></h4>
            </article>
    <?php endforeach; ?>
        </main>
        <footer>
            <section>
                <h3>Współpracują z nami</h3>
                <a href="http://sklep.pl/">Sklep 1</a>
            </section>
            <section>
                <h3>Nasi top klienci</h3>
                <ol>
    <?php foreach ($topClients as $client): ?>
                    <li><?= e($client['imie']) ?> <?= e($client['nazwisko']) ?>, <?= (int) $client['punkty'] ?> pkt.</li>
    <?php endforeach; ?>
                </ol>
            </section>
            <section>
                <h3>Skontaktuj się</h3>
                <p>telefon: <a href="tel:111222333">111222333</a></p>
            </section>
            <section>
                <h3>Autor: Bartosz Bryniarski</h3>
            </section>
        </footer>
    </body>
    </html>

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

  34. Etap 5: Styl bloków opinii

    Kroki 34-37 · po tym etapie masz 36 z 36 kryteriów (pkt)

    Krok 34: Bloki opinii: tło, kolor, wymiary, wyśrodkowanie i cień

    Kryteria: ,

    R.4.8 Ustawiono styl dla bloków opinii (np. klasą, id jest błędem): biały kolor tła, kolor czcionki DimGray, szerokość 60%, wysokość 220px, marginesy zewnętrzne (margin) ustawione są parami na 40px i auto

    R.4.9 Ustawiono cień dla bloku opinii bez przesunięcia, rozmyty o 10 px, o rozmiarze 5 px, kolorze DimGray np. box-shadow: 0 0 10px 5px DimGray

    ok. 3 min
    Polecenie z arkusza

    Dla bloków opinii generowanych skryptem: biały kolor tła, kolor czcionki DimGray, szerokość 60%, wysokość 220 px, marginesy zewnętrzne pionowe 40 px, poziome automatycznie wyliczane przez przeglądarkę, cień bez przesunięcia, rozmyty o 10 px, o rozmiarze 5 px, kolorze DimGray

    Selektor klasy .opinion. margin: 40px auto - 40 px w pionie, a w poziomie auto stawia blok o szerokości 60% na środku (R.4.8). box-shadow: 0 0 10px 5px - bez przesunięcia, rozmycie 10 px, rozmiar 5 px - w kolorze ze zmiennej --muted, tej samej co tekst (R.4.9).

    styl3.css CSS zmienione: 9 linii
    :root {
        --accent: Tomato;
        --light: white;
        --main-bg: Seashell;
        --muted: DimGray;
    }
    
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header {
        background-color: var(--accent);
        color: var(--light);
        padding: 10px;
    }
    
    main {
        background-color: var(--main-bg);
        height: 500px;
        overflow: auto;
    }
    
    footer {
        display: grid;
        grid-template-columns: repeat(4, 25%);
    }
    
    footer section {
        background-color: var(--accent);
        color: var(--light);
        height: 160px;
    }
    
    h1,
    h2 {
        text-align: center;
    }
    
    a {
        color: var(--light);
    }
    
    a:focus-visible {
        outline: 2px solid var(--light);
        outline-offset: 2px;
    }
    
    .opinion {
        background-color: var(--light);
        color: var(--muted);
        width: 60%;
        height: 220px;
        margin: 40px auto;
        box-shadow: 0 0 10px 5px var(--muted);
    }
    Podgląd strony po tym kroku (opinie.php)
  35. Krok 35: Zdjęcie: opływanie tekstem, marginesy, zaokrąglone rogi

    Kryteria:

    R.4.10 Ustawiono dla obrazu: opływanie tekstem po prawej jego stronie (np. float: left) i zaokrąglenie rogów o 10px, np. border-radius: 10px;

    ok. 2 min
    Polecenie z arkusza

    Dla selektora obrazu: opływany tekstem po jego prawej stronie, marginesy zewnętrzne 10 px, rogi obrazu zaokrąglone o 10 px

    float: left - tekst opływa zdjęcie po prawej, margin: 10px, border-radius: 10px (R.4.10). Tu float jest właściwym narzędziem: arkusz opisuje opływanie tekstem, a do tego służy właśnie ta właściwość. Zdjęcie z marginesami ma 220 px - tyle co blok.

    styl3.css CSS zmienione: 6 linii
    :root {
        --accent: Tomato;
        --light: white;
        --main-bg: Seashell;
        --muted: DimGray;
    }
    
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header {
        background-color: var(--accent);
        color: var(--light);
        padding: 10px;
    }
    
    main {
        background-color: var(--main-bg);
        height: 500px;
        overflow: auto;
    }
    
    footer {
        display: grid;
        grid-template-columns: repeat(4, 25%);
    }
    
    footer section {
        background-color: var(--accent);
        color: var(--light);
        height: 160px;
    }
    
    h1,
    h2 {
        text-align: center;
    }
    
    a {
        color: var(--light);
    }
    
    a:focus-visible {
        outline: 2px solid var(--light);
        outline-offset: 2px;
    }
    
    .opinion {
        background-color: var(--light);
        color: var(--muted);
        width: 60%;
        height: 220px;
        margin: 40px auto;
        box-shadow: 0 0 10px 5px var(--muted);
    }
    
    img {
        float: left;
        margin: 10px;
        border-radius: 10px;
    }
    Podgląd strony po tym kroku (opinie.php)
  36. Krok 36: Cytat: marginesy wewnętrzne, rozmiar i pochylenie

    ok. 1 min
    Polecenie z arkusza

    Dla selektora cytatu: marginesy wewnętrzne 10 px, czcionka o rozmiarze 150%, pochylona

    Selektor znacznika blockquote: padding: 10px, font-size: 150%, font-style: italic.

    styl3.css CSS zmienione: 6 linii
    :root {
        --accent: Tomato;
        --light: white;
        --main-bg: Seashell;
        --muted: DimGray;
    }
    
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header {
        background-color: var(--accent);
        color: var(--light);
        padding: 10px;
    }
    
    main {
        background-color: var(--main-bg);
        height: 500px;
        overflow: auto;
    }
    
    footer {
        display: grid;
        grid-template-columns: repeat(4, 25%);
    }
    
    footer section {
        background-color: var(--accent);
        color: var(--light);
        height: 160px;
    }
    
    h1,
    h2 {
        text-align: center;
    }
    
    a {
        color: var(--light);
    }
    
    a:focus-visible {
        outline: 2px solid var(--light);
        outline-offset: 2px;
    }
    
    .opinion {
        background-color: var(--light);
        color: var(--muted);
        width: 60%;
        height: 220px;
        margin: 40px auto;
        box-shadow: 0 0 10px 5px var(--muted);
    }
    
    img {
        float: left;
        margin: 10px;
        border-radius: 10px;
    }
    
    blockquote {
        padding: 10px;
        font-size: 150%;
        font-style: italic;
    }
    Podgląd strony po tym kroku (opinie.php)
  37. Krok 37: Nagłówek h4: do prawej, margines, rozmiar i pochylenie

    Kryteria: , ,

    R.4.5 Ustawiono cechy czcionki dla selektora cytatu i h4: pochylenie i rozmiar czcionki 150%

    R.4.6 Ustawiono wyrównanie tekstu do środka dla selektora h1 i h2 oraz do prawej strony dla nagłówka h4

    R.4.7 Ustawiono marginesy zewnętrzne (margin) 10px dla selektora img oraz prawy (margin- right) 40px dla nagłówka h4 oraz marginesy wewnętrzne (padding) 10px dla banera i cytatu

    ok. 2 min
    Polecenie z arkusza

    Dla selektora nagłówka czwartego stopnia: wyrównanie tekstu do prawej strony, margines zewnętrzny prawy 40 px, czcionka o rozmiarze 150%, pochylona

    Selektor znacznika h4. Gotowe są cechy czcionki cytatu i h4 (R.4.5), wyrównanie h1, h2 i h4 (R.4.6) oraz marginesy obrazu, h4, banera i cytatu (R.4.7). Podgląd odpowiada obrazowi 2.

    styl3.css CSS zmienione: 7 linii
    :root {
        --accent: Tomato;
        --light: white;
        --main-bg: Seashell;
        --muted: DimGray;
    }
    
    * {
        font-family: Tahoma, sans-serif;
    }
    
    header {
        background-color: var(--accent);
        color: var(--light);
        padding: 10px;
    }
    
    main {
        background-color: var(--main-bg);
        height: 500px;
        overflow: auto;
    }
    
    footer {
        display: grid;
        grid-template-columns: repeat(4, 25%);
    }
    
    footer section {
        background-color: var(--accent);
        color: var(--light);
        height: 160px;
    }
    
    h1,
    h2 {
        text-align: center;
    }
    
    a {
        color: var(--light);
    }
    
    a:focus-visible {
        outline: 2px solid var(--light);
        outline-offset: 2px;
    }
    
    .opinion {
        background-color: var(--light);
        color: var(--muted);
        width: 60%;
        height: 220px;
        margin: 40px auto;
        box-shadow: 0 0 10px 5px var(--muted);
    }
    
    img {
        float: left;
        margin: 10px;
        border-radius: 10px;
    }
    
    blockquote {
        padding: 10px;
        font-size: 150%;
        font-style: italic;
    }
    
    h4 {
        text-align: right;
        margin-right: 40px;
        font-size: 150%;
        font-style: italic;
    }
    Podgląd strony po tym kroku (opinie.php)
  38. Etap 6: Porównanie z wersją podstawową

    Krok 38 · po tym etapie masz 36 z 36 kryteriów (pkt)

    Krok 38: Co jest inaczej i którą wersję wybrać na egzaminie

    ok. 3 min

    Obie wersje dają komplet 36 punktów. Różnice:

    wersja podstawowawersja nowoczesna
    bloki stopkicztery <footer> z float: leftjedna <footer> z czterema <section> na siatce
    szerokość 25%widthgrid-template-columns: repeat(4, 25%)
    blok opinii<div class="opinion"><article class="opinion">
    kolorywpisane w każdej regulezmienne w :root
    bibliotekamysqli (tabela 1 arkusza)PDO z wyjątkami
    gdzie jest koddwa bloki PHP w HTML-una górze pliku, w HTML-u tylko pętle
    odczyt wierszymysqli_fetch_row, $row[2]fetchAll, $opinion['opinia']
    wypisywanie danychecho wprost, htmlspecialchars dopisane w „Dobrych praktykach”e() od początku
    awaria bazy„Fatal error” ze ścieżką pliku, dopiero potem try / catchzdanie dla odwiedzającego od początku
    zamknięcie bazymysqli_close po liście$pdo = null w finally

    Sprawdzone na prawdziwym PHP: skrypt wpisany w treść opinii i atrybut onerror wstrzyknięty przez nazwę zdjęcia wyświetlają się jako tekst, a przy usuniętej bazie strona pokazuje komunikat i całą stopkę.

    Na egzaminie wersja podstawowa trzyma się funkcji z tabeli 1 i łatwiej ją napisać bez dokumentacji. Nowoczesna oddziela komunikację z bazą od HTML-u i jest odporna na złe dane i awarię bazy - a klucz przyjmuje obie.

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

  39. Etap 7: Oddanie pracy

    Krok 39 · po tym etapie masz 36 z 36 kryteriów (pkt)

    Krok 39: 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, 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 folderze: anna.jpg, ewa.jpg, import.jpg, janek.jpg, john.jpg, judy.jpg, kw1.png - kw4.png, kwerendy.txt, opinie.php, przeglądarka.txt i styl3.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 37.

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