Przejdź do głównej treści
INF.03-05-24.06-SGCzerwiec 2024 · 40 kroków · ok. 87 min · 41 kryteriów z klucza

Rozwiązanie krok po kroku: Aplikacja komisu samochodowego (Nowoczesny kod (flex, PDO))

To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza kupauto z czterema zapytaniami, przeskalowane zdjęcie Yarisa i strona KupAuto.php z ofertą dnia, czterema ofertami wyróżnionymi i wyszukiwaniem samochodów wybranej marki - i tak samo spełnia każde kryterium klucza. Inna jest droga:

  • układ bez sztuczek: bloki główne stoją jeden pod drugim w zwykłym przepływie (siatka nic by tu nie dodała), a bloki ofert układa kontener z display: flex i zawijaniem zamiast inline-block - bez odstępów ze spacji w kodzie i bez vertical-align,
  • kolory w zmiennych CSS, blok oferty dnia z display: flow-root - pływające zdjęcie nigdy z niego nie wystaje,
  • PHP na PDO nad HTML-em: wszystkie cztery zapytania wykonane i połączenie zamknięte, zanim zacznie się strona; jedna funkcja fetchRows() do zapytań przygotowanych,
  • marka z białej listy: Skrypt 4 pyta bazę tylko o markę, która jest na liście z zapytania 1, i przekazuje ją jako parametr - ' OR '1'='1 ani apostrof w nazwie nic nie zepsują,
  • jedna funkcja-szablon showOffer() dla bloków ofert ze Skryptów 2 i 4, lista pamięta wybraną markę, e() przy każdej wartości z bazy, ORDER BY przed LIMIT.

Na końcu krótkie porównanie z wersją podstawową (zakładka obok). Baza i zdjęcia pochodzą z prawdziwego archiwum pliki5, zapytania zostały wykonane na tej bazie w MariaDB z XAMPP-a, a podgląd strony od etapu PHP to wynik prawdziwego uruchomienia kodu z danego kroku w PHP 8.2; od kroku ze Skryptem 4 pokazuje stan z obrazu 3, czyli po wybraniu marki „Opel” i kliknięciu „Wyszukaj”.

Jak liczą się punkty. Klucz ma 41 kryteriów (R.1.1 - R.5.10), każde za 1 punkt. Próg zdania to 75%, czyli 31 punktów. Przy każdym kroku jest napisane, które kryteria spełnia.

  1. Etap 1: Baza danych

    Kroki 1-7 · po tym etapie masz 6 z 41 kryteriów (pkt)

    Krok 1: Baza danych kupauto

    ok. 2 min
    Polecenie z arkusza

    Utwórz bazę danych o nazwie kupauto, z zestawem polskich znaków (np. utf8_unicode_ci)

    Archiwum pliki5.zip rozpakuj hasłem S@moCHody^ (małpa zamiast „a”, daszek na końcu) do folderu z numerem zdającego. W phpMyAdmin Nowa → kupauto, porównywanie utf8_unicode_ci, albo zapytaniem:

    CREATE DATABASE kupauto CHARACTER SET utf8 COLLATE utf8_unicode_ci;
    
    Typowe błędy

    Nazwa bazy musi brzmieć dokładnie kupauto - skrypty łączą się z nią po nazwie (R.5.1).

  2. Krok 2: Import tabel z pliku baza.sql

    ok. 2 min
    Polecenie z arkusza

    Do bazy zaimportuj tabele z pliku baza.sql z rozpakowanego archiwum

    Import → baza.sql. Dwie tabele z obrazu 1: marki (9 marek) i samochody (17 samochodów z kluczem obcym marki_id) - jedna marka, wiele samochodów. Pole wyrozniony typu TINYINT(1): 1 - samochód wyróżniony, 0 - zwykły.

  3. Krok 3: Zrzut ekranu import.png

    Kryteria:

    R.1.1 Wykonano import tabel do bazy danych kupauto czynność udokumentowano plikiem o nazwie import w formacie PNG

    ok. 2 min
    Polecenie z arkusza

    Wykonaj zrzut ekranu po imporcie. Zrzut zapisz w formacie PNG i nazwij import. Nie kadruj zrzutu. Powinien on obejmować cały ekran monitora, z widocznym paskiem zadań. Na zrzucie powinny być widoczne elementy wskazujące na poprawnie wykonany import tabel.

    Cały ekran z paskiem zadań, zapis jako import.png. Uwaga na formaty: import w PNG, zrzuty zapytań w JPEG (kw1.jpg - kw4.jpg).

    Typowe błędy

    Wykadrowany zrzut albo zrzut bez paska zadań nie jest oceniany (uwaga w kluczu przy R.1).

  4. Krok 4: Zapytanie 1: nazwy marek

    Kryteria: ,

    R.1.2 Zapisano plik kwerendy.txt zawierający co najmniej jedno zapytanie SQL, wynikające z treści zadania

    R.1.3 Wykonano zapytanie 1 wybierające jedynie nazwy marek samochodów. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa FROM marki; oraz wynik działania udokumentowano zrzutem, na którym jest widocznych dokładnie 9 wierszy, nazwy wszystkich marek: Audi, BMW, Volkswagen, Opel, Ford, Mercedes, Toyota, Fiat, Jeep

    ok. 1 min
    Polecenie z arkusza

    Zapytanie 1: wybierające jedynie nazwy marek samochodów

    Jedno pole, bez identyfikatora („jedynie nazwy”). Wynik: 9 marek od Audi do Jeep, jak w kluczu. To zapytanie wypełni listę rozwijaną (Skrypt 3), a jego wynik posłuży też jako lista dozwolonych marek w Skrypcie 4. Zapytania trafiają do kwerendy.txt, zrzut do kw1.jpg.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT nazwa
    FROM marki;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: samochód o identyfikatorze 10

    Kryteria:

    R.1.4 Wykonano zapytanie 2 wybierające jedynie pola: model, rocznik, przebieg, paliwo, cena, zdjecie dla samochodu o id równym 10. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT model, rocznik, przebieg, paliwo, cena, zdjecie FROM samochody WHERE id=10; oraz wynik działania udokumentowano zrzutem, na którym jest widoczny dokładnie 1 wiersz: Yaris 2022 0 Benzyna 94000 ToyotaYaris.jpg

    ok. 1 min
    Polecenie z arkusza

    Zapytanie 2: wybierające jedynie pola: model, rocznik, przebieg, paliwo, cena, zdjecie dla samochodu o id równym 10

    id jest kluczem głównym, więc WHERE id = 10 zwraca najwyżej jeden wiersz: Yaris 2022 0 Benzyna 94000 ToyotaYaris.jpg, jak w kluczu. Zrzut: kw2.jpg.

    kwerendy.txt Tekst zmienione: 5 linii
    -- Zapytanie 1
    SELECT nazwa
    FROM marki;
    
    -- Zapytanie 2
    SELECT model, rocznik, przebieg, paliwo, cena, zdjecie
    FROM samochody
    WHERE id = 10;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: cztery oferty wyróżnione z nazwą marki

    Kryteria:

    R.1.5 Wykonano zapytanie 3 wybierające jedynie pole nazwa z tabeli marki i odpowiadające jej pola: model, rocznik, cena, zdjecie z tabeli samochody jedynie dla samochodów wyróżnionych. Wybierane są dokładnie 4 wiersze. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa, model, rocznik, cena, zdjecie FROM samochody JOIN marki ON marki_id = marki.id WHERE wyrozniony=1 LIMIT 4; (możliwe również INNER JOIN lub bez JOIN porównanie kluczy po WHERE) oraz wynik działania udokumentowano zrzutem, na którym są widoczne dokładnie 4 wiersze, jedynie pola nazwa, model, rocznik, cena, zdjecie dla modeli: A8 TDI QUattro, A8, Astra, Corsa

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie pole nazwa z tabeli marki i odpowiadające jej pola: model, rocznik, cena, zdjecie z tabeli samochody jedynie dla samochodów wyróżnionych. Zapytanie wybiera dokładnie 4 wiersze. Należy posłużyć się relacją

    • aliasy s (samochody) i m (marki), JOIN ... ON m.id = s.marki_id - relacja z obrazu 1,
    • WHERE s.wyrozniony = 1 - tylko wyróżnione; jest ich 6, a arkusz chce dokładnie 4, stąd LIMIT 4 (klucz podaje go wprost),
    • ORDER BY s.id przed LIMIT - bez sortowania baza może oddać „pierwsze cztery” w dowolnej kolejności, więc wynik LIMIT nie jest gwarantowany. Z sortowaniem po identyfikatorze zawsze są to te same cztery samochody.

    Wynik: A8 TDI QUattro, A8, Astra, Corsa - jak w kluczu. Zrzut: kw3.jpg.

    Typowe błędy

    Bez LIMIT 4 zapytanie zwraca 6 wierszy, a klucz wymaga na zrzucie dokładnie 4.

    kwerendy.txt Tekst zmienione: 8 linii
    -- Zapytanie 1
    SELECT nazwa
    FROM marki;
    
    -- Zapytanie 2
    SELECT model, rocznik, przebieg, paliwo, cena, zdjecie
    FROM samochody
    WHERE id = 10;
    
    -- Zapytanie 3
    SELECT m.nazwa, s.model, s.rocznik, s.cena, s.zdjecie
    FROM samochody AS s
    JOIN marki AS m ON m.id = s.marki_id
    WHERE s.wyrozniony = 1
    ORDER BY s.id
    LIMIT 4;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: samochody marki Audi

    Kryteria:

    R.1.6 Wykonano zapytanie 4 wybierające jedynie pola: model, cena, zdjecie z tabeli samochody dla samochodów o nazwie marki Audi. Należy wykorzystać relację. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT model, cena, zdjecie FROM samochody JOIN marki ON marki_id = marki.id WHERE nazwa = "Audi"; (możliwe również INNER JOIN lub bez JOIN porównanie kluczy po WHERE) oraz wynik działania udokumentowano zrzutem, na którym są widoczne są dokładnie 4 wiersze, jedynie pola model, cena, zdjecie, dla modeli: A3, A8 TDI QUattro, A3, A8

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 4: wybierające jedynie pola: model, cena, zdjecie z tabeli samochody dla samochodów o nazwie marki Audi. Należy posłużyć się relacją

    Warunek na nazwę marki przez złączenie (m.nazwa = 'Audi'), a nie marki_id = 1 - arkusz każe posłużyć się relacją, a Skrypt 4 dostanie z listy właśnie nazwę. Wynik: A3, A8 TDI QUattro, A3, A8, jak w kluczu. Zrzut: kw4.jpg.

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    SELECT nazwa
    FROM marki;
    
    -- Zapytanie 2
    SELECT model, rocznik, przebieg, paliwo, cena, zdjecie
    FROM samochody
    WHERE id = 10;
    
    -- Zapytanie 3
    SELECT m.nazwa, s.model, s.rocznik, s.cena, s.zdjecie
    FROM samochody AS s
    JOIN marki AS m ON m.id = s.marki_id
    WHERE s.wyrozniony = 1
    ORDER BY s.id
    LIMIT 4;
    
    -- Zapytanie 4
    SELECT s.model, s.cena, s.zdjecie
    FROM samochody AS s
    JOIN marki AS m ON m.id = s.marki_id
    WHERE m.nazwa = 'Audi';
    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 41 kryteriów (pkt)

    Krok 8: ToyotaYaris.jpg: szerokość 500 px

    Kryteria:

    R.2.1 Grafikę ToyotaYaris.jpg przeskalowano z zachowaniem proporcji do szerokości, dokładnie 500 px, odpowiadająca temu wysokość wynosi 332 px (±5 px)

    ok. 2 min
    Polecenie z arkusza

    Grafikę ToyotaYaris.jpg, wypakowaną z archiwum, należy przeskalować z zachowaniem proporcji do szerokości, dokładnie 500 px

    W GIMP-ie Obraz → Skaluj obraz z zamkniętym łańcuchem proporcji, szerokość 500. Oryginał ma 640 × 425 px, więc wysokość wychodzi 332 px, jak w kluczu (332 ± 5). Zapis pod tą samą nazwą (Plik → Nadpisz ToyotaYaris.jpg), bo tę nazwę ma pole zdjecie w bazie.

    Typowe błędy

    Zapis pod nową nazwą - strona i tak wczyta oryginał, bo nazwę pliku bierze z bazy.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 8
  9. Krok 9: Plik KupAuto.php: HTML5, język, kodowanie

    Kryteria:

    R.2.2 Witrynę internetową zapisano w pliku KupAuto.php oraz zapisano jawnie standard kodowania polskich znaków i deklarację HTML5 <!DOCTYPE HTML> i zadeklarowano język witryny polski np.<HTML lang="pl">. Strona zawiera sekcje head i body

    ok. 2 min
    Polecenie z arkusza

    Składa się ze strony o nazwie KupAuto.php zapisanej w języku HTML5. Zastosowany język witryny polski. Jawnie zastosowany właściwy standard kodowania polskich znaków

    Plik KupAuto.php (wielkie K i A, jak w arkuszu): <!DOCTYPE html>, <html lang="pl">, <meta charset="UTF-8"> jako pierwszy element <head> i sekcja <body> (R.2.2).

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

    Kryteria:

    R.2.3 Nadano tytuł stronie: Komis aut

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony widoczny na karcie przeglądarki: „Komis aut”

    <title> (R.2.3) i viewport, żeby telefon nie pomniejszał strony.

    KupAuto.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>Komis aut</title>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (KupAuto.php)
  11. Krok 11: Podpięcie arkusza stylów

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

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

    Nazwa styl.css; całe formatowanie z tego pliku, bez atrybutów style (R.3.6).

    Typowe błędy

    Klucz odbiera R.3.6 także wtedy, gdy choć jeden selektor nie trafia w blok (np. kropka zamiast # przy identyfikatorze).

    KupAuto.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>Komis aut</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (KupAuto.php)
  12. Krok 12: Bloki: baner, trzy bloki główne w main, stopka

    Kryteria:

    R.2.4 Układ strony zdefiniowano dzieląc ją na bloki: banera, trzy główne oraz stopkę. Zastosowano semantyczne znaczniki sekcji HTML5 zgodnie z ich znaczeniem, w tym do części nagłówkowej - header, stopki - footer, dopuszcza się zagnieżdżenie bloków div w znacznikach semantycznych

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki: baner, trzy bloki główne umiejscowione jeden pod drugim, na dole blok stopki. Podział zrealizowany wyłącznie za pomocą semantycznych znaczników sekcji języka HTML5 tak, aby po uruchomieniu w przeglądarce wygląd układu bloków był zgodny z obrazem 2

    Baner <header>, stopka <footer> (klucz R.2.4 wymienia je wprost). Trzy bloki główne to trzy <section> - każdy jest odrębną częścią oferty komisu - zebrane w <main>, który oznacza główną treść strony: czytnik ekranu pozwala do niego przeskoczyć z pominięciem banera. Identyfikatory od treści: day (oferta dnia), featured (wyróżnione), search (wyszukiwanie).

    KupAuto.php PHP zmienione: 12 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Komis aut</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
        </header>
        <main>
            <section id="day">
            </section>
            <section id="featured">
            </section>
            <section id="search">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (KupAuto.php)
  13. Krok 13: Baner: nagłówek z uwydatnioną nazwą

    Kryteria:

    R.2.6 Frazę „KupAuto!” zapisano za pomocą znacznika semantycznego <em>

    ok. 1 min
    Polecenie z arkusza

    Zawartość banera: nagłówek pierwszego stopnia o treści „KupAuto! Internetowy Komis Samochodowy”. Fraza „KupAuto!” jest zapisana za pomocą znacznika semantycznego oznaczającego uwydatnienie i zapisywanego czcionką pochyłą

    Uwydatnienie to <em> - przeglądarka pisze je kursywą (R.2.6). <i> też daje kursywę, ale nic nie znaczy.

    Typowe błędy

    <i>KupAuto!</i> wygląda tak samo, ale klucz wymaga <em>.

    KupAuto.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>Komis aut</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1><em>KupAuto!</em> Internetowy Komis Samochodowy</h1>
        </header>
        <main>
            <section id="day">
            </section>
            <section id="featured">
            </section>
            <section id="search">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (KupAuto.php)
  14. Krok 14: Drugi blok główny: nagłówek i kontener ofert

    ok. 2 min
    Polecenie z arkusza

    Zawartość drugiego bloku głównego: Nagłówek drugiego stopnia o treści „Oferty Wyróżnione”. Efekt działania skryptu 2

    <h2> i pod nim pusty <div class="offers"> - kontener, w którym Skrypt 2 postawi cztery bloki ofert. Wspólny rodzic samych ofert pozwoli ułożyć je w rzędzie jedną regułą CSS, bez dotykania nagłówka (klucz dopuszcza div wewnątrz znaczników semantycznych). Pierwszy blok główny zostaje pusty - całą jego zawartość wypisze Skrypt 1.

    KupAuto.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>Komis aut</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1><em>KupAuto!</em> Internetowy Komis Samochodowy</h1>
        </header>
        <main>
            <section id="day">
            </section>
            <section id="featured">
                <h2>Oferty Wyróżnione</h2>
                <div class="offers">
                </div>
            </section>
            <section id="search">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (KupAuto.php)
  15. Krok 15: Trzeci blok główny: nagłówek, formularz POST, lista

    Kryteria:

    R.3.5 Formularz wysyła dane metodą POST do skryptu

    ok. 3 min
    Polecenie z arkusza

    Zawartość trzeciego bloku głównego: Nagłówek drugiego stopnia o treści „Wybierz markę”. Formularz wysyłający dane metodą bezpieczną, zawierający elementy: Listę rozwijalną wypełnioną skryptem 3

    • method="post" - „metoda bezpieczna” w rozumieniu arkusza: marka jedzie w treści żądania, nie w adresie. W specyfikacji HTTP „bezpieczne” są co prawda metody, które niczego nie zmieniają na serwerze (GET i HEAD), ale arkusz używa tego słowa w innym sensie, a klucz R.3.5 wymaga POST; bez action formularz wysyła dane do tego samego pliku,
    • <select name="brand"> - pod tą nazwą marka trafi do skryptu; opcje dopisze Skrypt 3,
    • aria-labelledby="search-title" - lista nie ma własnej etykiety, więc wskazuje nagłówek „Wybierz markę” jako swój opis; czytnik ekranu przeczyta go przy liście.
    Typowe błędy

    Lista bez name nic nie wysyła - Skrypt 4 nigdy nie dostanie marki.

    KupAuto.php PHP zmienione: 5 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Komis aut</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1><em>KupAuto!</em> Internetowy Komis Samochodowy</h1>
        </header>
        <main>
            <section id="day">
            </section>
            <section id="featured">
                <h2>Oferty Wyróżnione</h2>
                <div class="offers">
                </div>
            </section>
            <section id="search">
                <h2 id="search-title">Wybierz markę</h2>
                <form method="post">
                    <select name="brand" aria-labelledby="search-title">
                    </select>
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (KupAuto.php)
  16. Krok 16: Przycisk „Wyszukaj”

    Kryteria:

    R.2.9 W trzecim bloku głównym umieszczono formularz z listą rozwijalną i przyciskiem "Wyszukaj"

    ok. 1 min
    Polecenie z arkusza

    Przycisk o treści: „Wyszukaj”

    <button type="submit"> wysyła formularz (R.2.9). Pod formularzem stanie wynik Skryptu 4.

    KupAuto.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>Komis aut</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1><em>KupAuto!</em> Internetowy Komis Samochodowy</h1>
        </header>
        <main>
            <section id="day">
            </section>
            <section id="featured">
                <h2>Oferty Wyróżnione</h2>
                <div class="offers">
                </div>
            </section>
            <section id="search">
                <h2 id="search-title">Wybierz markę</h2>
                <form method="post">
                    <select name="brand" aria-labelledby="search-title">
                    </select>
                    <button type="submit">Wyszukaj</button>
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (KupAuto.php)
  17. Krok 17: Stopka: autor i odnośnik

    Kryteria: ,

    R.2.5 W banerze zapisano nagłówek h1: "KupAuto! Internetowy Komis Samochodowy" , w drugim bloku głównym h2: "Oferty Wyróżnione" , w trzecim bloku głównym h2: "Wybierz markę" oraz w stopce dwa paragrafy z numerem zdającego i z odnośnikiem

    R.3.4 Odnośnik o treści "Znajdź nas także" inicjuje otwarcie strony "http://firmy.pl/komis"

    ok. 2 min
    Polecenie z arkusza

    Zawartość stopki: Paragraf (akapit) o treści: „Stronę wykonał: ”, dalej wstawiony numer zdającego. Paragraf z odnośnikiem o treści „Znajdź nas także”, prowadzącym do strony http://firmy.pl/komis

    Dwa akapity. Na egzaminie po „Stronę wykonał:” wpisujesz swój numer zdającego (PESEL); tu stoi autor rozwiązania, Bartosz Bryniarski. Adres odnośnika przepisany z http:// (R.3.4). Komplet nagłówków i akapitów z R.2.5.

    KupAuto.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>Komis aut</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1><em>KupAuto!</em> Internetowy Komis Samochodowy</h1>
        </header>
        <main>
            <section id="day">
            </section>
            <section id="featured">
                <h2>Oferty Wyróżnione</h2>
                <div class="offers">
                </div>
            </section>
            <section id="search">
                <h2 id="search-title">Wybierz markę</h2>
                <form method="post">
                    <select name="brand" aria-labelledby="search-title">
                    </select>
                    <button type="submit">Wyszukaj</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
            <p><a href="http://firmy.pl/komis">Znajdź nas także</a></p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (KupAuto.php)
  18. Etap 3: Styl CSS

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

    Krok 18: Plik styl.css: zmienne i krój Roboto albo szeryfowy

    Kryteria:

    R.4.1 Ustawiono domyślne formatowanie wszystkich selektorów (dla *): krój czcionki Roboto, Serif (tylko taka kolejność jest prawidłowa)

    ok. 2 min
    Polecenie z arkusza

    Domyślne formatowanie wszystkich selektorów: krój czcionki Roboto, w przypadku jej braku - Serif

    Crimson występuje w banerze, stopce, ramkach i nagłówkach h4 - zapisany raz w zmiennej --accent, obok teł bloków. Krój: Roboto, a gdy go brak - ogólna rodzina serif; klucz R.4.1 uznaje tylko tę kolejność. Na komputerze bez Roboto strona dostaje krój szeryfowy, jak na obrazie 2.

    Typowe błędy

    font-family: serif, Roboto - odwrotna kolejność, klucz jej nie uznaje.

    styl.css CSS nowy plik
    :root {
        --accent: Crimson;
        --day-bg: MistyRose;
        --offer-bg: Snow;
        --light-text: white;
    }
    
    * {
        font-family: Roboto, serif;
    }
    Podgląd strony po tym kroku (KupAuto.php)
  19. Krok 19: Baner: kolory, wyśrodkowanie, odstępy liter i słów

    ok. 2 min
    Polecenie z arkusza

    Dla banera: kolor tła Crimson, biały kolor czcionki, wyrównanie tekstu do środka, odległości pomiędzy literami 5 px, odległości pomiędzy słowami 30 px, marginesy wewnętrzne 10 px

    Odstęp między literami to letter-spacing, między słowami - word-spacing; stąd rozstrzelony napis z obrazu 2. Kolory i wyśrodkowanie zaliczą się razem ze stopką i nagłówkami h4.

    styl.css CSS zmienione: 9 linii
    :root {
        --accent: Crimson;
        --day-bg: MistyRose;
        --offer-bg: Snow;
        --light-text: white;
    }
    
    * {
        font-family: Roboto, serif;
    }
    
    header {
        background-color: var(--accent);
        color: var(--light-text);
        text-align: center;
        letter-spacing: 5px;
        word-spacing: 30px;
        padding: 10px;
    }
    Podgląd strony po tym kroku (KupAuto.php)
  20. Krok 20: Pierwszy blok główny: tło, wysokość, marginesy, ramka

    Kryteria: ,

    R.4.7 Ustawiono marginesy wewnętrzne (padding) 10px dla banera i pierwszego bloku głównego oraz marginesy zewnętrzne (margin) górny i dolny 10px, lewy i prawy 50px dla pierwszego bloku głównego

    R.4.9 Ustawiono obramowanie dla pierwszego bloku głównego: 1px solid Crimson

    ok. 3 min
    Polecenie z arkusza

    Dla pierwszego bloku głównego: kolor tła MistyRose, wysokość 300 px, marginesy wewnętrzne 10 px, zewnętrzne górny i dolny 10 px, lewy i prawy 50 px, obramowanie linią ciągłą o szerokości 1 px i kolorze Crimson

    margin: 10px 50px - pierwsza wartość to góra i dół, druga - boki. Ramka 1px solid w kolorze ze zmiennej (R.4.9), padding i marginesy (R.4.7).

    display: flow-root to dodatek: blok zamyka w sobie pływające elementy. Zdjęcie oferty dnia będzie float: left; dziś blok ma stałą wysokość, więc i tak by nie wystawało, ale po zdjęciu height (np. na telefonie) pływające zdjęcie wyszłoby poza ramkę i wjechało pod nagłówek „Oferty Wyróżnione”. flow-root zastępuje dawne sztuczki z overflow: hidden czy clearfix.

    styl.css CSS zmienione: 9 linii
    :root {
        --accent: Crimson;
        --day-bg: MistyRose;
        --offer-bg: Snow;
        --light-text: white;
    }
    
    * {
        font-family: Roboto, serif;
    }
    
    header {
        background-color: var(--accent);
        color: var(--light-text);
        text-align: center;
        letter-spacing: 5px;
        word-spacing: 30px;
        padding: 10px;
    }
    
    #day {
        display: flow-root;
        height: 300px;
        padding: 10px;
        margin: 10px 50px;
        border: 1px solid var(--accent);
        background-color: var(--day-bg);
    }
    Podgląd strony po tym kroku (KupAuto.php)
  21. Krok 21: Zdjęcie oferty dnia: po lewej, na całą wysokość

    Kryteria: ,

    R.3.3 W głównym bloku pierwszym obraz wyświetla się po lewej stronie tekstu

    R.4.4 Ustawiono wysokość pierwszego bloku głównego 300px oraz 100% jedynie dla obrazu z pierwszego bloku głównego

    ok. 2 min
    Polecenie z arkusza

    Dla obrazu jedynie z pierwszego bloku głównego: oblewanie tekstem po prawej stronie (obraz jest po lewej stronie) wysokość 100%

    Tu zostaje float: left - arkusz mówi wprost o oblewaniu tekstem, a to jest dokładnie opływanie: nagłówki i akapit stają z prawej strony zdjęcia (R.3.3). Selektor #day img dotyczy tylko tego bloku; height: 100% liczy się od wysokości bloku, czyli 300 px (R.4.4).

    Typowe błędy

    Samo img { height: 100% } rozciągnęłoby także zdjęcia w blokach ofert do wysokości bloku 450 px.

    styl.css CSS zmienione: 5 linii
    :root {
        --accent: Crimson;
        --day-bg: MistyRose;
        --offer-bg: Snow;
        --light-text: white;
    }
    
    * {
        font-family: Roboto, serif;
    }
    
    header {
        background-color: var(--accent);
        color: var(--light-text);
        text-align: center;
        letter-spacing: 5px;
        word-spacing: 30px;
        padding: 10px;
    }
    
    #day {
        display: flow-root;
        height: 300px;
        padding: 10px;
        margin: 10px 50px;
        border: 1px solid var(--accent);
        background-color: var(--day-bg);
    }
    
    #day img {
        float: left;
        height: 100%;
    }
    Podgląd strony po tym kroku (KupAuto.php)
  22. Krok 22: Bloki ofert obok siebie: kontener flex

    Kryteria:

    R.4.8 Ustawiono styl bloków z ofertami w drugim i trzecim bloku głównym: kolor tła Snow, wysokość 450 px, szerokość 21%, marginesy zewnętrzne 1%, wyrównanie tekstu do środka, obramowanie linią ciągłą o szerokości 1 px i kolorze Crimson (np. za pomocą klasy, id jest błędem)

    ok. 3 min
    Polecenie z arkusza

    Dla bloków z ofertami generowanych skryptem w drugim i trzecim bloku głównym: kolor tła Snow, wysokość 450 px, szerokość 21%, marginesy zewnętrzne 1%, wyrównanie tekstu do środka, obramowanie linią ciągłą o szerokości 1 px i kolorze Crimson

    • klasa .offer - bloków jest wiele, a identyfikator musi być jedyny (klucz R.4.8: „id jest błędem”); szerokość 21% z marginesem 1% z każdej strony to 23% na blok, czyli cztery w rzędzie,
    • .offers { display: flex; flex-wrap: wrap; } - kontener ustawia bloki obok siebie, a piąty przenosi do nowego wiersza. W wersji podstawowej robi to inline-block, które traktuje bloki jak słowa: między nimi zostają odstępy ze spacji i nowych linii w kodzie, a wyrównanie trzeba poprawiać vertical-align. W kontenerze flex tych odstępów nie ma.

    Siatka (grid) nie pasuje tu tak dobrze: szerokość 21% z arkusza liczyłaby się od kolumny siatki, a nie od bloku. Elementy flex liczą procent od kontenera, więc wartości z arkusza działają wprost.

    styl.css CSS zmienione: 14 linii
    :root {
        --accent: Crimson;
        --day-bg: MistyRose;
        --offer-bg: Snow;
        --light-text: white;
    }
    
    * {
        font-family: Roboto, serif;
    }
    
    header {
        background-color: var(--accent);
        color: var(--light-text);
        text-align: center;
        letter-spacing: 5px;
        word-spacing: 30px;
        padding: 10px;
    }
    
    #day {
        display: flow-root;
        height: 300px;
        padding: 10px;
        margin: 10px 50px;
        border: 1px solid var(--accent);
        background-color: var(--day-bg);
    }
    
    #day img {
        float: left;
        height: 100%;
    }
    
    .offers {
        display: flex;
        flex-wrap: wrap;
    }
    
    .offer {
        width: 21%;
        height: 450px;
        margin: 1%;
        text-align: center;
        border: 1px solid var(--accent);
        background-color: var(--offer-bg);
    }
    Podgląd strony po tym kroku (KupAuto.php)
  23. Krok 23: Stopka: kolory

    Kryteria:

    R.4.2 Ustawiono kolor tła Crimson dla banera i stopki oraz MistyRose dla pierwszego bloku głównego

    ok. 1 min
    Polecenie z arkusza

    Dla stopki: kolor tła Crimson, biały kolor czcionki

    Te same zmienne co baner. Komplet kolorów tła z R.4.2: Crimson banera i stopki, MistyRose pierwszego bloku.

    styl.css CSS zmienione: 5 linii
    :root {
        --accent: Crimson;
        --day-bg: MistyRose;
        --offer-bg: Snow;
        --light-text: white;
    }
    
    * {
        font-family: Roboto, serif;
    }
    
    header {
        background-color: var(--accent);
        color: var(--light-text);
        text-align: center;
        letter-spacing: 5px;
        word-spacing: 30px;
        padding: 10px;
    }
    
    #day {
        display: flow-root;
        height: 300px;
        padding: 10px;
        margin: 10px 50px;
        border: 1px solid var(--accent);
        background-color: var(--day-bg);
    }
    
    #day img {
        float: left;
        height: 100%;
    }
    
    .offers {
        display: flex;
        flex-wrap: wrap;
    }
    
    .offer {
        width: 21%;
        height: 450px;
        margin: 1%;
        text-align: center;
        border: 1px solid var(--accent);
        background-color: var(--offer-bg);
    }
    
    footer {
        background-color: var(--accent);
        color: var(--light-text);
    }
    Podgląd strony po tym kroku (KupAuto.php)
  24. Krok 24: Obrazy nie szersze niż blok

    Kryteria:

    R.4.10 Ustawiono maksymalną szerokość selektora img: max-width: 100%

    ok. 1 min
    Polecenie z arkusza

    Dla selektora obrazu: maksymalna szerokość 100%

    Selektor znacznika img (uwaga arkusza, R.4.10). max-width nie powiększa obrazu - tylko nie pozwala mu wyjść poza blok oferty.

    styl.css CSS zmienione: 4 linie
    :root {
        --accent: Crimson;
        --day-bg: MistyRose;
        --offer-bg: Snow;
        --light-text: white;
    }
    
    * {
        font-family: Roboto, serif;
    }
    
    header {
        background-color: var(--accent);
        color: var(--light-text);
        text-align: center;
        letter-spacing: 5px;
        word-spacing: 30px;
        padding: 10px;
    }
    
    #day {
        display: flow-root;
        height: 300px;
        padding: 10px;
        margin: 10px 50px;
        border: 1px solid var(--accent);
        background-color: var(--day-bg);
    }
    
    #day img {
        float: left;
        height: 100%;
    }
    
    .offers {
        display: flex;
        flex-wrap: wrap;
    }
    
    .offer {
        width: 21%;
        height: 450px;
        margin: 1%;
        text-align: center;
        border: 1px solid var(--accent);
        background-color: var(--offer-bg);
    }
    
    footer {
        background-color: var(--accent);
        color: var(--light-text);
    }
    
    img {
        max-width: 100%;
    }
    Podgląd strony po tym kroku (KupAuto.php)
  25. Krok 25: Nagłówki czwartego stopnia i odnośnik

    Kryteria: , ,

    R.4.3 Ustawiono biały kolor czcionki dla banera, stopki i odnośnika oraz Crimson dla selektora h4

    R.4.5 Ustawiono cechy tekstu banera: odległości pomiędzy literami (letter-spacing) 5px, odległości pomiędzy słowami (word-spacing) 30px oraz rozmiar czcionki nagłówka czwartego stopnia 180%

    R.4.6 Ustawiono wyrównanie tekstu do środka dla banera i nagłówka czwartego stopnia

    ok. 2 min
    Polecenie z arkusza

    Dla selektora nagłówka czwartego stopnia: rozmiar czcionki 180%, wyrównanie tekstu do środka, kolor czcionki Crimson. Dla odnośnika: biały kolor czcionki

    Selektory znaczników h4 i a - tak każe uwaga arkusza. Komplet kryteriów tekstu: odstępy liter i słów oraz rozmiar 180% (R.4.5), wyśrodkowanie banera i h4 (R.4.6), biel banera, stopki i odnośnika oraz Crimson h4 (R.4.3). Bez białego koloru odnośnik byłby niebieski na czerwonym tle.

    styl.css CSS zmienione: 10 linii
    :root {
        --accent: Crimson;
        --day-bg: MistyRose;
        --offer-bg: Snow;
        --light-text: white;
    }
    
    * {
        font-family: Roboto, serif;
    }
    
    header {
        background-color: var(--accent);
        color: var(--light-text);
        text-align: center;
        letter-spacing: 5px;
        word-spacing: 30px;
        padding: 10px;
    }
    
    #day {
        display: flow-root;
        height: 300px;
        padding: 10px;
        margin: 10px 50px;
        border: 1px solid var(--accent);
        background-color: var(--day-bg);
    }
    
    #day img {
        float: left;
        height: 100%;
    }
    
    .offers {
        display: flex;
        flex-wrap: wrap;
    }
    
    .offer {
        width: 21%;
        height: 450px;
        margin: 1%;
        text-align: center;
        border: 1px solid var(--accent);
        background-color: var(--offer-bg);
    }
    
    footer {
        background-color: var(--accent);
        color: var(--light-text);
    }
    
    img {
        max-width: 100%;
    }
    
    h4 {
        font-size: 180%;
        text-align: center;
        color: var(--accent);
    }
    
    a {
        color: var(--light-text);
    }
    Podgląd strony po tym kroku (KupAuto.php)
  26. Krok 26: Układ bloków zgodny z obrazem 2

    Kryteria:

    R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z obrazem 2 w arkuszu - nie należy brać pod uwagę bloków z ofertami (prawidłowo zastosowano właściwości CSS decydujące o układzie strony, np. float i clear albo display flex albo grid)

    ok. 1 min
    Polecenie z arkusza

    Podział strony na bloki: baner, trzy bloki główne umiejscowione jeden pod drugim, na dole blok stopki. Podział zrealizowany wyłącznie za pomocą semantycznych znaczników sekcji języka HTML5 tak, aby po uruchomieniu w przeglądarce wygląd układu bloków był zgodny z obrazem 2

    Bez dodatkowej reguły: header, main, section i footer są elementami blokowymi, więc stają jeden pod drugim na całą szerokość - dokładnie układ z obrazu 2 (R.3.1). Siatka albo flex na <body> niczego by tu nie dodały; najprostszy kod, który robi swoje, jest najlepszy.

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

  27. Etap 4: Skrypty PHP

    Kroki 27-38 · po tym etapie masz 41 z 41 kryteriów (pkt)

    Krok 27: Połączenie PDO i funkcja fetchRows()

    Kryteria:

    R.5.1 Skrypt zawiera instrukcje połączenia z serwerem bazy danych i wyboru bazy kupauto

    ok. 4 min
    Polecenie z arkusza

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

    Praca z bazą stoi przed <!DOCTYPE html>: najpierw dane, potem HTML, który je tylko wypisuje.

    • PDO z DSN mysql:host=localhost;dbname=kupauto;charset=utf8mb4, użytkownik root, puste hasło (R.5.1); ERRMODE_EXCEPTION i FETCH_ASSOC,
    • fetchRows() - cztery skrypty wysyłają cztery zapytania, więc przygotowanie, wykonanie z parametrami i odbiór wierszy stoją w jednej funkcji. Zapytanie ($statement) żyje tylko wewnątrz funkcji, więc po jej powrocie nic nie trzyma połączenia,
    • $pdo = null zamyka połączenie po wszystkich skryptach, try / catch daje ogólny komunikat przy awarii, e() zabezpiecza wartości wstawiane w HTML.

    Od tego kroku podgląd to wynik prawdziwego uruchomienia.

    KupAuto.php PHP zmienione: 27 linii
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES);
    }
    
    function fetchRows(PDO $pdo, string $sql, array $params = []): array
    {
        $statement = $pdo->prepare($sql);
        $statement->execute($params);
        return $statement->fetchAll();
    }
    
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=kupauto;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać ofert. 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>Komis aut</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1><em>KupAuto!</em> Internetowy Komis Samochodowy</h1>
        </header>
        <main>
            <section id="day">
            </section>
            <section id="featured">
                <h2>Oferty Wyróżnione</h2>
                <div class="offers">
                </div>
            </section>
            <section id="search">
                <h2 id="search-title">Wybierz markę</h2>
                <form method="post">
                    <select name="brand" aria-labelledby="search-title">
                    </select>
                    <button type="submit">Wyszukaj</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
            <p><a href="http://firmy.pl/komis">Znajdź nas także</a></p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (KupAuto.php)
  28. Krok 28: Skrypt 1: zapytanie 2

    Kryteria: ,

    R.5.3 Skrypt 1 zawiera instrukcje wysyłające do bazy danych zapytanie 2 lub skrypt 2: zapytanie 3 lub skrypt 3: zapytanie 1

    R.5.10 Zastosowano znaczące nazewnictwo dla wszystkich zmiennych lub funkcji w języku polskim lub angielskim. Nazwy odzwierciedlają cel zastosowania

    ok. 2 min
    Polecenie z arkusza

    Skrypt 1: Wysyła do bazy danych zapytanie 2

    Zapytanie 2 z parametrem :id, a wartość ze stałej OFFER_OF_THE_DAY_ID - numer oferty dnia ma nazwę i jedno miejsce do zmiany (R.5.3). fetchRows() zwraca listę wierszy; [0] ?? null bierze pierwszy, a gdy samochodu o tym numerze nie ma - null zamiast ostrzeżenia. Nazwy po angielsku mówią, co przechowują (R.5.10).

    KupAuto.php PHP zmienione: 9 linii
    <?php
    const OFFER_OF_THE_DAY_ID = 10;
    
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES);
    }
    
    function fetchRows(PDO $pdo, string $sql, array $params = []): array
    {
        $statement = $pdo->prepare($sql);
        $statement->execute($params);
        return $statement->fetchAll();
    }
    
    $dayOffer = null;
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=kupauto;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // Skrypt 1: zapytanie 2
        $dayOffer = fetchRows($pdo,
            'SELECT model, rocznik, przebieg, paliwo, cena, zdjecie FROM samochody WHERE id = :id',
            ['id' => OFFER_OF_THE_DAY_ID]
        )[0] ?? null;
    
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać ofert. 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>Komis aut</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1><em>KupAuto!</em> Internetowy Komis Samochodowy</h1>
        </header>
        <main>
            <section id="day">
            </section>
            <section id="featured">
                <h2>Oferty Wyróżnione</h2>
                <div class="offers">
                </div>
            </section>
            <section id="search">
                <h2 id="search-title">Wybierz markę</h2>
                <form method="post">
                    <select name="brand" aria-labelledby="search-title">
                    </select>
                    <button type="submit">Wyszukaj</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
            <p><a href="http://firmy.pl/komis">Znajdź nas także</a></p>
        </footer>
    </body>
    </html>

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

  29. Krok 29: Skrypt 1: zdjęcie oferty dnia

    ok. 2 min
    Polecenie z arkusza

    W pierwszym bloku głównym wyświetla dane pobrane z bazy danych, w następujący sposób: Obraz, którego źródłem jest pole zdjecie, a tekstem alternatywnym „oferta dnia”

    Szablon pierwszego bloku stoi w <?php if ($dayOffer): ?> - bez oferty blok zostaje pusty zamiast pokazać zepsuty obraz. Źródło z kolumny zdjecie po nazwie. Podgląd: przeskalowane zdjęcie Yarisa po lewej, na całą wysokość bloku.

    KupAuto.php PHP zmienione: 3 linie
    <?php
    const OFFER_OF_THE_DAY_ID = 10;
    
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES);
    }
    
    function fetchRows(PDO $pdo, string $sql, array $params = []): array
    {
        $statement = $pdo->prepare($sql);
        $statement->execute($params);
        return $statement->fetchAll();
    }
    
    $dayOffer = null;
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=kupauto;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // Skrypt 1: zapytanie 2
        $dayOffer = fetchRows($pdo,
            'SELECT model, rocznik, przebieg, paliwo, cena, zdjecie FROM samochody WHERE id = :id',
            ['id' => OFFER_OF_THE_DAY_ID]
        )[0] ?? null;
    
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać ofert. 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>Komis aut</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1><em>KupAuto!</em> Internetowy Komis Samochodowy</h1>
        </header>
        <main>
            <section id="day">
                <?php if ($dayOffer): ?>
                    <img src="<?= e($dayOffer['zdjecie']) ?>" alt="oferta dnia">
                <?php endif; ?>
            </section>
            <section id="featured">
                <h2>Oferty Wyróżnione</h2>
                <div class="offers">
                </div>
            </section>
            <section id="search">
                <h2 id="search-title">Wybierz markę</h2>
                <form method="post">
                    <select name="brand" aria-labelledby="search-title">
                    </select>
                    <button type="submit">Wyszukaj</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
            <p><a href="http://firmy.pl/komis">Znajdź nas także</a></p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (KupAuto.php)
  30. Krok 30: Skrypt 1: model, dane i cena

    Kryteria:

    R.5.5 Skrypt 1 wyświetla w pierwszym bloku głównym dane zwrócone z bazy danych: zdjęcie, model, rocznik, przebieg, rodzaj paliwa oraz cenę

    ok. 3 min
    Polecenie z arkusza

    Nagłówek czwartego stopnia o treści „Oferta Dnia: Toyota ”, następnie pole model. Paragraf o treści „Rocznik: <rocznik>, przebieg: <przebieg>, rodzaj paliwa: <paliwo>”, gdzie pola w nawiasach <> są pobrane z bazy danych. Nagłówek czwartego stopnia o treści „Cena: ” następnie pole cena

    Trzy linie szablonu: „Toyota” jest stałym tekstem (zapytanie 2 nie pobiera marki), reszta to pola po nazwach. Pierwszy blok wygląda jak na obrazie 2: „Oferta Dnia: Toyota Yaris”, „Rocznik: 2022, przebieg: 0, rodzaj paliwa: Benzyna”, „Cena: 94000” obok zdjęcia (R.5.5).

    KupAuto.php PHP zmienione: 3 linie
    <?php
    const OFFER_OF_THE_DAY_ID = 10;
    
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES);
    }
    
    function fetchRows(PDO $pdo, string $sql, array $params = []): array
    {
        $statement = $pdo->prepare($sql);
        $statement->execute($params);
        return $statement->fetchAll();
    }
    
    $dayOffer = null;
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=kupauto;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // Skrypt 1: zapytanie 2
        $dayOffer = fetchRows($pdo,
            'SELECT model, rocznik, przebieg, paliwo, cena, zdjecie FROM samochody WHERE id = :id',
            ['id' => OFFER_OF_THE_DAY_ID]
        )[0] ?? null;
    
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać ofert. 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>Komis aut</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1><em>KupAuto!</em> Internetowy Komis Samochodowy</h1>
        </header>
        <main>
            <section id="day">
                <?php if ($dayOffer): ?>
                    <img src="<?= e($dayOffer['zdjecie']) ?>" alt="oferta dnia">
                    <h4>Oferta Dnia: Toyota <?= e($dayOffer['model']) ?></h4>
                    <p>Rocznik: <?= e($dayOffer['rocznik']) ?>, przebieg: <?= e($dayOffer['przebieg']) ?>, rodzaj paliwa: <?= e($dayOffer['paliwo']) ?></p>
                    <h4>Cena: <?= e($dayOffer['cena']) ?></h4>
                <?php endif; ?>
            </section>
            <section id="featured">
                <h2>Oferty Wyróżnione</h2>
                <div class="offers">
                </div>
            </section>
            <section id="search">
                <h2 id="search-title">Wybierz markę</h2>
                <form method="post">
                    <select name="brand" aria-labelledby="search-title">
                    </select>
                    <button type="submit">Wyszukaj</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
            <p><a href="http://firmy.pl/komis">Znajdź nas także</a></p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (KupAuto.php)
  31. Krok 31: Skrypt 2: osobny blok dla każdej oferty

    Kryteria:

    R.3.2 W głównym bloku drugim lub trzecim każda oferta jest umieszczona w osobnym bloku, bloki są wyświetlone obok siebie (w skrypcie lub HTML), dopuszcza się że bloki są puste

    ok. 4 min
    Polecenie z arkusza

    Skrypt 2: Wysyła do bazy danych zapytanie 3. Każdy zwrócony wiersz jest wyświetlony w osobnym bloku, co razem daje cztery bloki. Bloki są wyświetlone obok siebie w jednej linii. W każdym bloku znajduje się: Obraz, którego nazwą pliku źródłowego jest pole zdjecie, a tekstem alternatywnym pole model

    Zapytanie 3 przez fetchRows() do tablicy $featured. Blok oferty pojawi się na stronie dwa razy (Skrypty 2 i 4), więc od razu stoi w funkcji-szablonie showOffer(): ?> wewnątrz funkcji przechodzi do zwykłego HTML-u, a <?php wraca do PHP - przy wywołaniu funkcja wypisuje swój kawałek strony. Definicja na górze pliku niczego nie wypisuje.

    W kontenerze .offers pętla foreach wywołuje showOffer($car) dla każdego wiersza: cztery bloki offer w jednym rzędzie, ze zdjęciem i modelem w tekście alternatywnym (R.3.2).

    KupAuto.php PHP zmienione: 21 linii
    <?php
    const OFFER_OF_THE_DAY_ID = 10;
    
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES);
    }
    
    function fetchRows(PDO $pdo, string $sql, array $params = []): array
    {
        $statement = $pdo->prepare($sql);
        $statement->execute($params);
        return $statement->fetchAll();
    }
    
    function showOffer(array $car): void
    {
        ?>
        <div class="offer">
            <img src="<?= e($car['zdjecie']) ?>" alt="<?= e($car['model']) ?>">
        </div>
        <?php
    }
    
    $dayOffer = null;
    $featured = [];
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=kupauto;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // Skrypt 1: zapytanie 2
        $dayOffer = fetchRows($pdo,
            'SELECT model, rocznik, przebieg, paliwo, cena, zdjecie FROM samochody WHERE id = :id',
            ['id' => OFFER_OF_THE_DAY_ID]
        )[0] ?? null;
    
        // Skrypt 2: zapytanie 3
        $featured = fetchRows($pdo,
            'SELECT m.nazwa, s.model, s.rocznik, s.cena, s.zdjecie
             FROM samochody AS s
             JOIN marki AS m ON m.id = s.marki_id
             WHERE s.wyrozniony = 1
             ORDER BY s.id
             LIMIT 4'
        );
    
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać ofert. 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>Komis aut</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1><em>KupAuto!</em> Internetowy Komis Samochodowy</h1>
        </header>
        <main>
            <section id="day">
                <?php if ($dayOffer): ?>
                    <img src="<?= e($dayOffer['zdjecie']) ?>" alt="oferta dnia">
                    <h4>Oferta Dnia: Toyota <?= e($dayOffer['model']) ?></h4>
                    <p>Rocznik: <?= e($dayOffer['rocznik']) ?>, przebieg: <?= e($dayOffer['przebieg']) ?>, rodzaj paliwa: <?= e($dayOffer['paliwo']) ?></p>
                    <h4>Cena: <?= e($dayOffer['cena']) ?></h4>
                <?php endif; ?>
            </section>
            <section id="featured">
                <h2>Oferty Wyróżnione</h2>
                <div class="offers">
                    <?php foreach ($featured as $car) { showOffer($car); } ?>
                </div>
            </section>
            <section id="search">
                <h2 id="search-title">Wybierz markę</h2>
                <form method="post">
                    <select name="brand" aria-labelledby="search-title">
                    </select>
                    <button type="submit">Wyszukaj</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
            <p><a href="http://firmy.pl/komis">Znajdź nas także</a></p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (KupAuto.php)
  32. Krok 32: Skrypt 2: marka z modelem, rocznik i cena

    Kryteria: , ,

    R.2.7 W pierwszym i drugim bloku głównym umieszczono przynajmniej dla jednej oferty nazwę i model samochodu oraz cenę w nagłówku h4 oraz inne dane w paragrafie (w skrypcie lub HTML)

    R.2.8 W pierwszym i drugim bloku głównym umieszczono przynajmniej jedno zdjęcie z tekstem alternatywnym (w skrypcie lub HTML)

    R.5.6 Skrypt 2 wyświetla w drugim bloku głównym dane zwrócone z bazy danych dotyczące czterech samochodów: zdjęcie, markę, model, rocznik oraz cenę

    ok. 3 min
    Polecenie z arkusza

    Nagłówek czwartego stopnia z nazwą marki i modelem, pobranymi z bazy. Paragraf o treści „Rocznik: ”, następnie pole rocznik. Nagłówek czwartego stopnia o treści „Cena: ” następnie pole cena

    Szablon bloku rośnie o nagłówek z marką i modelem (marka przychodzi dzięki złączeniu w zapytaniu 3), akapit z rocznikiem i cenę. Rocznik pokazuje tylko Skrypt 2, dlatego funkcja dostała drugi parametr bool $showYear - wywołanie showOffer($car, true) mówi wprost, co zobaczy użytkownik, zamiast zgadywać po zawartości wiersza.

    Podgląd: cztery bloki jak na obrazie 2 - Audi A8 TDI QUattro, Audi A8, Opel Astra, Opel Corsa (R.5.6). Pierwszy i drugi blok mają już nazwy z modelami i ceny w <h4>, dane w akapitach (R.2.7) i zdjęcia z tekstem alternatywnym (R.2.8).

    KupAuto.php PHP zmienione: 7 linii
    <?php
    const OFFER_OF_THE_DAY_ID = 10;
    
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES);
    }
    
    function fetchRows(PDO $pdo, string $sql, array $params = []): array
    {
        $statement = $pdo->prepare($sql);
        $statement->execute($params);
        return $statement->fetchAll();
    }
    
    function showOffer(array $car, bool $showYear): void
    {
        ?>
        <div class="offer">
            <img src="<?= e($car['zdjecie']) ?>" alt="<?= e($car['model']) ?>">
            <h4><?= e($car['nazwa']) ?> <?= e($car['model']) ?></h4>
            <?php if ($showYear): ?>
                <p>Rocznik: <?= e($car['rocznik']) ?></p>
            <?php endif; ?>
            <h4>Cena: <?= e($car['cena']) ?></h4>
        </div>
        <?php
    }
    
    $dayOffer = null;
    $featured = [];
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=kupauto;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // Skrypt 1: zapytanie 2
        $dayOffer = fetchRows($pdo,
            'SELECT model, rocznik, przebieg, paliwo, cena, zdjecie FROM samochody WHERE id = :id',
            ['id' => OFFER_OF_THE_DAY_ID]
        )[0] ?? null;
    
        // Skrypt 2: zapytanie 3
        $featured = fetchRows($pdo,
            'SELECT m.nazwa, s.model, s.rocznik, s.cena, s.zdjecie
             FROM samochody AS s
             JOIN marki AS m ON m.id = s.marki_id
             WHERE s.wyrozniony = 1
             ORDER BY s.id
             LIMIT 4'
        );
    
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać ofert. 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>Komis aut</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1><em>KupAuto!</em> Internetowy Komis Samochodowy</h1>
        </header>
        <main>
            <section id="day">
                <?php if ($dayOffer): ?>
                    <img src="<?= e($dayOffer['zdjecie']) ?>" alt="oferta dnia">
                    <h4>Oferta Dnia: Toyota <?= e($dayOffer['model']) ?></h4>
                    <p>Rocznik: <?= e($dayOffer['rocznik']) ?>, przebieg: <?= e($dayOffer['przebieg']) ?>, rodzaj paliwa: <?= e($dayOffer['paliwo']) ?></p>
                    <h4>Cena: <?= e($dayOffer['cena']) ?></h4>
                <?php endif; ?>
            </section>
            <section id="featured">
                <h2>Oferty Wyróżnione</h2>
                <div class="offers">
                    <?php foreach ($featured as $car) { showOffer($car, true); } ?>
                </div>
            </section>
            <section id="search">
                <h2 id="search-title">Wybierz markę</h2>
                <form method="post">
                    <select name="brand" aria-labelledby="search-title">
                    </select>
                    <button type="submit">Wyszukaj</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
            <p><a href="http://firmy.pl/komis">Znajdź nas także</a></p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (KupAuto.php)
  33. Krok 33: Skrypt 3: marki jako elementy listy

    Kryteria:

    R.5.7 Skrypt 3 dodaje do listy rozwijalnej jako elementy <option> nazwy marek samochodów zwrócone zapytaniem

    ok. 2 min
    Polecenie z arkusza

    Skrypt 3: Wysyła do bazy danych zapytanie 1. Zwrócone zapytaniem nazwy marek samochodów są wyświetlone jako elementy listy rozwijalnej

    Zapytanie 1 zwraca wiersze z jedną kolumną; array_column(..., 'nazwa') robi z nich zwykłą listę napisów ['Audi', 'BMW', ...] - taka przyda się w Skrypcie 4 do sprawdzenia marki. W <select> pętla wypisuje <option> dla każdej marki; bez atrybutu value przeglądarka wyśle treść opcji, czyli nazwę (R.5.7). Na liście 9 marek, pierwsza Audi.

    KupAuto.php PHP zmienione: 7 linii
    <?php
    const OFFER_OF_THE_DAY_ID = 10;
    
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES);
    }
    
    function fetchRows(PDO $pdo, string $sql, array $params = []): array
    {
        $statement = $pdo->prepare($sql);
        $statement->execute($params);
        return $statement->fetchAll();
    }
    
    function showOffer(array $car, bool $showYear): void
    {
        ?>
        <div class="offer">
            <img src="<?= e($car['zdjecie']) ?>" alt="<?= e($car['model']) ?>">
            <h4><?= e($car['nazwa']) ?> <?= e($car['model']) ?></h4>
            <?php if ($showYear): ?>
                <p>Rocznik: <?= e($car['rocznik']) ?></p>
            <?php endif; ?>
            <h4>Cena: <?= e($car['cena']) ?></h4>
        </div>
        <?php
    }
    
    $dayOffer = null;
    $featured = [];
    $brands = [];
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=kupauto;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // Skrypt 1: zapytanie 2
        $dayOffer = fetchRows($pdo,
            'SELECT model, rocznik, przebieg, paliwo, cena, zdjecie FROM samochody WHERE id = :id',
            ['id' => OFFER_OF_THE_DAY_ID]
        )[0] ?? null;
    
        // Skrypt 2: zapytanie 3
        $featured = fetchRows($pdo,
            'SELECT m.nazwa, s.model, s.rocznik, s.cena, s.zdjecie
             FROM samochody AS s
             JOIN marki AS m ON m.id = s.marki_id
             WHERE s.wyrozniony = 1
             ORDER BY s.id
             LIMIT 4'
        );
    
        // Skrypt 3: zapytanie 1
        $brands = array_column(fetchRows($pdo, 'SELECT nazwa FROM marki'), 'nazwa');
    
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać ofert. 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>Komis aut</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1><em>KupAuto!</em> Internetowy Komis Samochodowy</h1>
        </header>
        <main>
            <section id="day">
                <?php if ($dayOffer): ?>
                    <img src="<?= e($dayOffer['zdjecie']) ?>" alt="oferta dnia">
                    <h4>Oferta Dnia: Toyota <?= e($dayOffer['model']) ?></h4>
                    <p>Rocznik: <?= e($dayOffer['rocznik']) ?>, przebieg: <?= e($dayOffer['przebieg']) ?>, rodzaj paliwa: <?= e($dayOffer['paliwo']) ?></p>
                    <h4>Cena: <?= e($dayOffer['cena']) ?></h4>
                <?php endif; ?>
            </section>
            <section id="featured">
                <h2>Oferty Wyróżnione</h2>
                <div class="offers">
                    <?php foreach ($featured as $car) { showOffer($car, true); } ?>
                </div>
            </section>
            <section id="search">
                <h2 id="search-title">Wybierz markę</h2>
                <form method="post">
                    <select name="brand" aria-labelledby="search-title">
                        <?php foreach ($brands as $brand): ?>
                            <option><?= e($brand) ?></option>
                        <?php endforeach; ?>
                    </select>
                    <button type="submit">Wyszukaj</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
            <p><a href="http://firmy.pl/komis">Znajdź nas także</a></p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (KupAuto.php)
  34. Krok 34: Skrypt 4: tylko po wyborze marki z listy

    Kryteria:

    R.5.8 Skrypt 4 jest wykonywany tylko, gdy wybrano opcję z listy rozwijalnej

    ok. 3 min
    Polecenie z arkusza

    Skrypt 4: Po zatwierdzeniu formularza tylko, gdy użytkownik wybrał element listy rozwijalnej wysyła do bazy danych zmodyfikowane zapytanie 4, w ten sposób, że jest w nim sprawdzana nazwa marki wybrana w liście

    filter_input(INPUT_POST, 'brand') czyta wybór: null, gdy formularza nie wysłano. Warunek Skryptu 4 sprawdza dwie rzeczy:

    • is_string - przyszedł napis (a nie brak danych czy tablica),
    • in_array($selectedBrand, $brands, true) - marka jest na liście z zapytania 1, czyli „użytkownik wybrał element listy rozwijalnej” dosłownie. To biała lista: wartość spoza listy, choćby spreparowana ręcznie, w ogóle nie dojdzie do zapytania (R.5.8).

    Od tego kroku podgląd pokazuje stronę po wybraniu „Opel” i kliknięciu „Wyszukaj”.

    KupAuto.php PHP zmienione: 6 linii
    <?php
    const OFFER_OF_THE_DAY_ID = 10;
    
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES);
    }
    
    function fetchRows(PDO $pdo, string $sql, array $params = []): array
    {
        $statement = $pdo->prepare($sql);
        $statement->execute($params);
        return $statement->fetchAll();
    }
    
    function showOffer(array $car, bool $showYear): void
    {
        ?>
        <div class="offer">
            <img src="<?= e($car['zdjecie']) ?>" alt="<?= e($car['model']) ?>">
            <h4><?= e($car['nazwa']) ?> <?= e($car['model']) ?></h4>
            <?php if ($showYear): ?>
                <p>Rocznik: <?= e($car['rocznik']) ?></p>
            <?php endif; ?>
            <h4>Cena: <?= e($car['cena']) ?></h4>
        </div>
        <?php
    }
    
    $dayOffer = null;
    $featured = [];
    $brands = [];
    $brandCars = [];
    $selectedBrand = filter_input(INPUT_POST, 'brand');
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=kupauto;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // Skrypt 1: zapytanie 2
        $dayOffer = fetchRows($pdo,
            'SELECT model, rocznik, przebieg, paliwo, cena, zdjecie FROM samochody WHERE id = :id',
            ['id' => OFFER_OF_THE_DAY_ID]
        )[0] ?? null;
    
        // Skrypt 2: zapytanie 3
        $featured = fetchRows($pdo,
            'SELECT m.nazwa, s.model, s.rocznik, s.cena, s.zdjecie
             FROM samochody AS s
             JOIN marki AS m ON m.id = s.marki_id
             WHERE s.wyrozniony = 1
             ORDER BY s.id
             LIMIT 4'
        );
    
        // Skrypt 3: zapytanie 1
        $brands = array_column(fetchRows($pdo, 'SELECT nazwa FROM marki'), 'nazwa');
    
        // Skrypt 4: tylko po wybraniu marki z listy
        if (is_string($selectedBrand) && in_array($selectedBrand, $brands, true)) {
        }
    
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać ofert. 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>Komis aut</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1><em>KupAuto!</em> Internetowy Komis Samochodowy</h1>
        </header>
        <main>
            <section id="day">
                <?php if ($dayOffer): ?>
                    <img src="<?= e($dayOffer['zdjecie']) ?>" alt="oferta dnia">
                    <h4>Oferta Dnia: Toyota <?= e($dayOffer['model']) ?></h4>
                    <p>Rocznik: <?= e($dayOffer['rocznik']) ?>, przebieg: <?= e($dayOffer['przebieg']) ?>, rodzaj paliwa: <?= e($dayOffer['paliwo']) ?></p>
                    <h4>Cena: <?= e($dayOffer['cena']) ?></h4>
                <?php endif; ?>
            </section>
            <section id="featured">
                <h2>Oferty Wyróżnione</h2>
                <div class="offers">
                    <?php foreach ($featured as $car) { showOffer($car, true); } ?>
                </div>
            </section>
            <section id="search">
                <h2 id="search-title">Wybierz markę</h2>
                <form method="post">
                    <select name="brand" aria-labelledby="search-title">
                        <?php foreach ($brands as $brand): ?>
                            <option><?= e($brand) ?></option>
                        <?php endforeach; ?>
                    </select>
                    <button type="submit">Wyszukaj</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
            <p><a href="http://firmy.pl/komis">Znajdź nas także</a></p>
        </footer>
    </body>
    </html>

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

  35. Krok 35: Skrypt 4: zapytanie 4 z parametrem :brand

    Kryteria:

    R.5.4 Skrypt 4 zawiera instrukcje wysyłające do bazy danych zmodyfikowane zapytanie 4, w ten sposób, że jest w nim sprawdzana nazwa marki wybrana w liście

    ok. 3 min
    Polecenie z arkusza

    Wysyła do bazy danych zmodyfikowane zapytanie 4, w ten sposób, że jest w nim sprawdzana nazwa marki wybrana w liście

    Zapytanie 4 z dwiema zmianami: w miejscu 'Audi' stoi parametr :brand, a wartość podaje fetchRows() osobno - baza nigdy nie potraktuje marki jak kodu SQL, nawet z apostrofem (R.5.4). Na początek listy pól trafia m.nazwa, bo blok oferty pokazuje markę „pobraną z bazy danych” (klucz R.5.9 też o niej mówi), a ORDER BY s.id ustala kolejność wyników - taką jak na obrazie 3.

    Biała lista z poprzedniego kroku i parametr to dwie niezależne zapory: jedna pilnuje sensu wartości, druga - bezpieczeństwa zapytania.

    KupAuto.php PHP zmienione: 8 linii
    <?php
    const OFFER_OF_THE_DAY_ID = 10;
    
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES);
    }
    
    function fetchRows(PDO $pdo, string $sql, array $params = []): array
    {
        $statement = $pdo->prepare($sql);
        $statement->execute($params);
        return $statement->fetchAll();
    }
    
    function showOffer(array $car, bool $showYear): void
    {
        ?>
        <div class="offer">
            <img src="<?= e($car['zdjecie']) ?>" alt="<?= e($car['model']) ?>">
            <h4><?= e($car['nazwa']) ?> <?= e($car['model']) ?></h4>
            <?php if ($showYear): ?>
                <p>Rocznik: <?= e($car['rocznik']) ?></p>
            <?php endif; ?>
            <h4>Cena: <?= e($car['cena']) ?></h4>
        </div>
        <?php
    }
    
    $dayOffer = null;
    $featured = [];
    $brands = [];
    $brandCars = [];
    $selectedBrand = filter_input(INPUT_POST, 'brand');
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=kupauto;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // Skrypt 1: zapytanie 2
        $dayOffer = fetchRows($pdo,
            'SELECT model, rocznik, przebieg, paliwo, cena, zdjecie FROM samochody WHERE id = :id',
            ['id' => OFFER_OF_THE_DAY_ID]
        )[0] ?? null;
    
        // Skrypt 2: zapytanie 3
        $featured = fetchRows($pdo,
            'SELECT m.nazwa, s.model, s.rocznik, s.cena, s.zdjecie
             FROM samochody AS s
             JOIN marki AS m ON m.id = s.marki_id
             WHERE s.wyrozniony = 1
             ORDER BY s.id
             LIMIT 4'
        );
    
        // Skrypt 3: zapytanie 1
        $brands = array_column(fetchRows($pdo, 'SELECT nazwa FROM marki'), 'nazwa');
    
        // Skrypt 4: tylko po wybraniu marki z listy
        if (is_string($selectedBrand) && in_array($selectedBrand, $brands, true)) {
            $brandCars = fetchRows($pdo,
                'SELECT m.nazwa, s.model, s.cena, s.zdjecie
                 FROM samochody AS s
                 JOIN marki AS m ON m.id = s.marki_id
                 WHERE m.nazwa = :brand
                 ORDER BY s.id',
                ['brand' => $selectedBrand]
            );
        }
    
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać ofert. 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>Komis aut</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1><em>KupAuto!</em> Internetowy Komis Samochodowy</h1>
        </header>
        <main>
            <section id="day">
                <?php if ($dayOffer): ?>
                    <img src="<?= e($dayOffer['zdjecie']) ?>" alt="oferta dnia">
                    <h4>Oferta Dnia: Toyota <?= e($dayOffer['model']) ?></h4>
                    <p>Rocznik: <?= e($dayOffer['rocznik']) ?>, przebieg: <?= e($dayOffer['przebieg']) ?>, rodzaj paliwa: <?= e($dayOffer['paliwo']) ?></p>
                    <h4>Cena: <?= e($dayOffer['cena']) ?></h4>
                <?php endif; ?>
            </section>
            <section id="featured">
                <h2>Oferty Wyróżnione</h2>
                <div class="offers">
                    <?php foreach ($featured as $car) { showOffer($car, true); } ?>
                </div>
            </section>
            <section id="search">
                <h2 id="search-title">Wybierz markę</h2>
                <form method="post">
                    <select name="brand" aria-labelledby="search-title">
                        <?php foreach ($brands as $brand): ?>
                            <option><?= e($brand) ?></option>
                        <?php endforeach; ?>
                    </select>
                    <button type="submit">Wyszukaj</button>
                </form>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
            <p><a href="http://firmy.pl/komis">Znajdź nas także</a></p>
        </footer>
    </body>
    </html>

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

  36. Krok 36: Skrypt 4: bloki samochodów wybranej marki

    Kryteria:

    R.5.9 Skrypt 4 wyświetla w trzecim bloku głównym dane zwrócone z bazy danych dotyczące samochodów wybranej marki: zdjęcie, nazwę marki, model i cenę

    ok. 3 min
    Polecenie z arkusza

    Każdy zwrócony wiersz jest wyświetlony w osobnym bloku. Bloki są wyświetlone obok siebie. W każdym bloku znajduje się: Obraz, którego nazwą pliku źródłowego jest pole zdjecie, a tekstem alternatywnym pole model. Nagłówek czwartego stopnia z nazwą marki i modelem, pobranymi z bazy danych. Nagłówek czwartego stopnia o treści „Cena: ” następnie pole cena

    Pod formularzem drugi kontener .offers i ta sama funkcja - tym razem showOffer($car, false), bez rocznika. Zmiana wyglądu bloku oferty to teraz jedno miejsce w kodzie dla obu skryptów.

    Podgląd po wybraniu „Opel”: pięć bloków - Astra, Corsa, Vectra, Corsa w pierwszym wierszu i Corsa w drugim, jak na obrazie 3 (R.5.9). Sprawdzone uruchomieniem: wysłanie ' OR '1'='1 albo Alfa'Romeo z pominięciem listy nie dodaje żadnego bloku i nie daje błędu - wartości nie ma na białej liście.

    KupAuto.php PHP zmienione: 3 linie
    <?php
    const OFFER_OF_THE_DAY_ID = 10;
    
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES);
    }
    
    function fetchRows(PDO $pdo, string $sql, array $params = []): array
    {
        $statement = $pdo->prepare($sql);
        $statement->execute($params);
        return $statement->fetchAll();
    }
    
    function showOffer(array $car, bool $showYear): void
    {
        ?>
        <div class="offer">
            <img src="<?= e($car['zdjecie']) ?>" alt="<?= e($car['model']) ?>">
            <h4><?= e($car['nazwa']) ?> <?= e($car['model']) ?></h4>
            <?php if ($showYear): ?>
                <p>Rocznik: <?= e($car['rocznik']) ?></p>
            <?php endif; ?>
            <h4>Cena: <?= e($car['cena']) ?></h4>
        </div>
        <?php
    }
    
    $dayOffer = null;
    $featured = [];
    $brands = [];
    $brandCars = [];
    $selectedBrand = filter_input(INPUT_POST, 'brand');
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=kupauto;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // Skrypt 1: zapytanie 2
        $dayOffer = fetchRows($pdo,
            'SELECT model, rocznik, przebieg, paliwo, cena, zdjecie FROM samochody WHERE id = :id',
            ['id' => OFFER_OF_THE_DAY_ID]
        )[0] ?? null;
    
        // Skrypt 2: zapytanie 3
        $featured = fetchRows($pdo,
            'SELECT m.nazwa, s.model, s.rocznik, s.cena, s.zdjecie
             FROM samochody AS s
             JOIN marki AS m ON m.id = s.marki_id
             WHERE s.wyrozniony = 1
             ORDER BY s.id
             LIMIT 4'
        );
    
        // Skrypt 3: zapytanie 1
        $brands = array_column(fetchRows($pdo, 'SELECT nazwa FROM marki'), 'nazwa');
    
        // Skrypt 4: tylko po wybraniu marki z listy
        if (is_string($selectedBrand) && in_array($selectedBrand, $brands, true)) {
            $brandCars = fetchRows($pdo,
                'SELECT m.nazwa, s.model, s.cena, s.zdjecie
                 FROM samochody AS s
                 JOIN marki AS m ON m.id = s.marki_id
                 WHERE m.nazwa = :brand
                 ORDER BY s.id',
                ['brand' => $selectedBrand]
            );
        }
    
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać ofert. 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>Komis aut</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1><em>KupAuto!</em> Internetowy Komis Samochodowy</h1>
        </header>
        <main>
            <section id="day">
                <?php if ($dayOffer): ?>
                    <img src="<?= e($dayOffer['zdjecie']) ?>" alt="oferta dnia">
                    <h4>Oferta Dnia: Toyota <?= e($dayOffer['model']) ?></h4>
                    <p>Rocznik: <?= e($dayOffer['rocznik']) ?>, przebieg: <?= e($dayOffer['przebieg']) ?>, rodzaj paliwa: <?= e($dayOffer['paliwo']) ?></p>
                    <h4>Cena: <?= e($dayOffer['cena']) ?></h4>
                <?php endif; ?>
            </section>
            <section id="featured">
                <h2>Oferty Wyróżnione</h2>
                <div class="offers">
                    <?php foreach ($featured as $car) { showOffer($car, true); } ?>
                </div>
            </section>
            <section id="search">
                <h2 id="search-title">Wybierz markę</h2>
                <form method="post">
                    <select name="brand" aria-labelledby="search-title">
                        <?php foreach ($brands as $brand): ?>
                            <option><?= e($brand) ?></option>
                        <?php endforeach; ?>
                    </select>
                    <button type="submit">Wyszukaj</button>
                </form>
                <div class="offers">
                    <?php foreach ($brandCars as $car) { showOffer($car, false); } ?>
                </div>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
            <p><a href="http://firmy.pl/komis">Znajdź nas także</a></p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (KupAuto.php)
  37. Krok 37: Lista pamięta wybraną markę

    ok. 2 min
    Polecenie z arkusza

    Zwrócone zapytaniem nazwy marek samochodów są wyświetlone jako elementy listy rozwijalnej

    Na obrazie 3 w liście stoi „Opel”, a pod spodem Ople. Po wysłaniu formularza strona ładuje się od nowa, więc lista sama by wróciła do „Audi”. Atrybut selected przy opcji równej $selectedBrand (porównanie ===, bez zamiany typów) zostawia w liście wybraną markę. Arkusz tego nie wymaga.

    KupAuto.php PHP zmienione: 1 linia
    <?php
    const OFFER_OF_THE_DAY_ID = 10;
    
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES);
    }
    
    function fetchRows(PDO $pdo, string $sql, array $params = []): array
    {
        $statement = $pdo->prepare($sql);
        $statement->execute($params);
        return $statement->fetchAll();
    }
    
    function showOffer(array $car, bool $showYear): void
    {
        ?>
        <div class="offer">
            <img src="<?= e($car['zdjecie']) ?>" alt="<?= e($car['model']) ?>">
            <h4><?= e($car['nazwa']) ?> <?= e($car['model']) ?></h4>
            <?php if ($showYear): ?>
                <p>Rocznik: <?= e($car['rocznik']) ?></p>
            <?php endif; ?>
            <h4>Cena: <?= e($car['cena']) ?></h4>
        </div>
        <?php
    }
    
    $dayOffer = null;
    $featured = [];
    $brands = [];
    $brandCars = [];
    $selectedBrand = filter_input(INPUT_POST, 'brand');
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=kupauto;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // Skrypt 1: zapytanie 2
        $dayOffer = fetchRows($pdo,
            'SELECT model, rocznik, przebieg, paliwo, cena, zdjecie FROM samochody WHERE id = :id',
            ['id' => OFFER_OF_THE_DAY_ID]
        )[0] ?? null;
    
        // Skrypt 2: zapytanie 3
        $featured = fetchRows($pdo,
            'SELECT m.nazwa, s.model, s.rocznik, s.cena, s.zdjecie
             FROM samochody AS s
             JOIN marki AS m ON m.id = s.marki_id
             WHERE s.wyrozniony = 1
             ORDER BY s.id
             LIMIT 4'
        );
    
        // Skrypt 3: zapytanie 1
        $brands = array_column(fetchRows($pdo, 'SELECT nazwa FROM marki'), 'nazwa');
    
        // Skrypt 4: tylko po wybraniu marki z listy
        if (is_string($selectedBrand) && in_array($selectedBrand, $brands, true)) {
            $brandCars = fetchRows($pdo,
                'SELECT m.nazwa, s.model, s.cena, s.zdjecie
                 FROM samochody AS s
                 JOIN marki AS m ON m.id = s.marki_id
                 WHERE m.nazwa = :brand
                 ORDER BY s.id',
                ['brand' => $selectedBrand]
            );
        }
    
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać ofert. 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>Komis aut</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1><em>KupAuto!</em> Internetowy Komis Samochodowy</h1>
        </header>
        <main>
            <section id="day">
                <?php if ($dayOffer): ?>
                    <img src="<?= e($dayOffer['zdjecie']) ?>" alt="oferta dnia">
                    <h4>Oferta Dnia: Toyota <?= e($dayOffer['model']) ?></h4>
                    <p>Rocznik: <?= e($dayOffer['rocznik']) ?>, przebieg: <?= e($dayOffer['przebieg']) ?>, rodzaj paliwa: <?= e($dayOffer['paliwo']) ?></p>
                    <h4>Cena: <?= e($dayOffer['cena']) ?></h4>
                <?php endif; ?>
            </section>
            <section id="featured">
                <h2>Oferty Wyróżnione</h2>
                <div class="offers">
                    <?php foreach ($featured as $car) { showOffer($car, true); } ?>
                </div>
            </section>
            <section id="search">
                <h2 id="search-title">Wybierz markę</h2>
                <form method="post">
                    <select name="brand" aria-labelledby="search-title">
                        <?php foreach ($brands as $brand): ?>
                            <option<?= $brand === $selectedBrand ? ' selected' : '' ?>><?= e($brand) ?></option>
                        <?php endforeach; ?>
                    </select>
                    <button type="submit">Wyszukaj</button>
                </form>
                <div class="offers">
                    <?php foreach ($brandCars as $car) { showOffer($car, false); } ?>
                </div>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
            <p><a href="http://firmy.pl/komis">Znajdź nas także</a></p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (KupAuto.php)
  38. Krok 38: Komunikat, gdy baza nie odpowiada; zamknięcie połączenia

    Kryteria:

    R.5.2 Ostatnią operacją na bazie jest jej zamknięcie

    ok. 2 min
    Polecenie z arkusza

    Na końcu jest zamykane połączenie z serwerem.

    Połączenie zamyka $pdo = null zaraz po czterech zapytaniach - ostatnia operacja na bazie (R.5.2); żadne zapytanie go nie trzyma, bo każde żyło tylko wewnątrz fetchRows(). Na początku <main> akapit z $dbError, gdy catch coś do niego wpisał. Sprawdzone uruchomieniem z błędną nazwą bazy: strona się wyświetla z komunikatem „Nie udało się pobrać ofert…”, bez bloków ofert, bez nazwy bazy i kodu błędu SQL.

    KupAuto.php PHP zmienione: 3 linie
    <?php
    const OFFER_OF_THE_DAY_ID = 10;
    
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES);
    }
    
    function fetchRows(PDO $pdo, string $sql, array $params = []): array
    {
        $statement = $pdo->prepare($sql);
        $statement->execute($params);
        return $statement->fetchAll();
    }
    
    function showOffer(array $car, bool $showYear): void
    {
        ?>
        <div class="offer">
            <img src="<?= e($car['zdjecie']) ?>" alt="<?= e($car['model']) ?>">
            <h4><?= e($car['nazwa']) ?> <?= e($car['model']) ?></h4>
            <?php if ($showYear): ?>
                <p>Rocznik: <?= e($car['rocznik']) ?></p>
            <?php endif; ?>
            <h4>Cena: <?= e($car['cena']) ?></h4>
        </div>
        <?php
    }
    
    $dayOffer = null;
    $featured = [];
    $brands = [];
    $brandCars = [];
    $selectedBrand = filter_input(INPUT_POST, 'brand');
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=kupauto;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    
        // Skrypt 1: zapytanie 2
        $dayOffer = fetchRows($pdo,
            'SELECT model, rocznik, przebieg, paliwo, cena, zdjecie FROM samochody WHERE id = :id',
            ['id' => OFFER_OF_THE_DAY_ID]
        )[0] ?? null;
    
        // Skrypt 2: zapytanie 3
        $featured = fetchRows($pdo,
            'SELECT m.nazwa, s.model, s.rocznik, s.cena, s.zdjecie
             FROM samochody AS s
             JOIN marki AS m ON m.id = s.marki_id
             WHERE s.wyrozniony = 1
             ORDER BY s.id
             LIMIT 4'
        );
    
        // Skrypt 3: zapytanie 1
        $brands = array_column(fetchRows($pdo, 'SELECT nazwa FROM marki'), 'nazwa');
    
        // Skrypt 4: tylko po wybraniu marki z listy
        if (is_string($selectedBrand) && in_array($selectedBrand, $brands, true)) {
            $brandCars = fetchRows($pdo,
                'SELECT m.nazwa, s.model, s.cena, s.zdjecie
                 FROM samochody AS s
                 JOIN marki AS m ON m.id = s.marki_id
                 WHERE m.nazwa = :brand
                 ORDER BY s.id',
                ['brand' => $selectedBrand]
            );
        }
    
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać ofert. 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>Komis aut</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1><em>KupAuto!</em> Internetowy Komis Samochodowy</h1>
        </header>
        <main>
            <?php if ($dbError !== ''): ?>
                <p><?= e($dbError) ?></p>
            <?php endif; ?>
            <section id="day">
                <?php if ($dayOffer): ?>
                    <img src="<?= e($dayOffer['zdjecie']) ?>" alt="oferta dnia">
                    <h4>Oferta Dnia: Toyota <?= e($dayOffer['model']) ?></h4>
                    <p>Rocznik: <?= e($dayOffer['rocznik']) ?>, przebieg: <?= e($dayOffer['przebieg']) ?>, rodzaj paliwa: <?= e($dayOffer['paliwo']) ?></p>
                    <h4>Cena: <?= e($dayOffer['cena']) ?></h4>
                <?php endif; ?>
            </section>
            <section id="featured">
                <h2>Oferty Wyróżnione</h2>
                <div class="offers">
                    <?php foreach ($featured as $car) { showOffer($car, true); } ?>
                </div>
            </section>
            <section id="search">
                <h2 id="search-title">Wybierz markę</h2>
                <form method="post">
                    <select name="brand" aria-labelledby="search-title">
                        <?php foreach ($brands as $brand): ?>
                            <option<?= $brand === $selectedBrand ? ' selected' : '' ?>><?= e($brand) ?></option>
                        <?php endforeach; ?>
                    </select>
                    <button type="submit">Wyszukaj</button>
                </form>
                <div class="offers">
                    <?php foreach ($brandCars as $car) { showOffer($car, false); } ?>
                </div>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
            <p><a href="http://firmy.pl/komis">Znajdź nas także</a></p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (KupAuto.php)
  39. Etap 5: Porównanie z wersją podstawową

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

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

    ok. 2 min

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

    wersja podstawowawersja nowoczesna
    bloki ofert w rzędziedisplay: inline-block + vertical-align: topkontener .offers z display: flex; flex-wrap: wrap
    blok oferty dniastała wysokość trzyma pływające zdjęciedodatkowo display: flow-root
    kolorywpisane w każdej regulezmienne CSS w :root
    zapytanie 3LIMIT 4 bez sortowaniaORDER BY s.id LIMIT 4, aliasy tabel
    bazamysqli_connect, mysqli_fetch_row, $car[4]PDO, fetchRows() z parametrami, $car['zdjecie']
    Skrypt 4marka wklejona do SQL (w dobrych praktykach zapytanie przygotowane)biała lista z zapytania 1 + parametr :brand
    blok ofertydwie kopie echo (w dobrych praktykach funkcja)funkcja-szablon showOffer($car, $showYear) od początku
    gdzie PHPcztery bloki w środku HTML-adane na górze pliku, połączenie zamknięte przed HTML-em

    Na egzaminie wersja podstawowa trzyma się funkcji z tabeli 1 arkusza i jest szybsza do napisania - każdy skrypt stoi tam, gdzie pojawia się jego wynik. Nowoczesna wymaga kilku minut więcej, ale skrypt formularza jest odporny na spreparowane dane, a wygląd bloku oferty zmienia się w jednym miejscu. Formularz tylko wyszukuje, więc token CSRF nie jest tu potrzebny - ma sens przy formularzach, które coś zapisują.

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

  40. Etap 6: Oddanie pracy

    Krok 40 · po tym etapie masz 41 z 41 kryteriów (pkt)

    Krok 40: Plik przeglądarka.txt i nagranie płyty

    ok. 5 min
    Polecenie z arkusza

    Po zakończeniu pracy utwórz plik tekstowy o nazwie przeglądarka.txt. Zapisz w nim nazwę przeglądarki internetowej, w której weryfikowana była poprawność działania witryny. Umieść go w folderze z numerem zdającego. Nagraj płytę z rezultatami pracy.

    W przeglądarka.txt nazwa przeglądarki, w której strona była sprawdzana przez http://localhost/.... W folderze z numerem zdającego: KupAuto.php, styl.css, kwerendy.txt, import.png, kw1.jpg - kw4.jpg, przeglądarka.txt i zdjęcia z archiwum (w tym przeskalowany ToyotaYaris.jpg). 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 38.

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.