Przejdź do głównej treści
INF.03-10-26.01-SGStyczeń 2026 · 48 kroków · ok. 90 min · 38 kryteriów z klucza

Rozwiązanie krok po kroku: Blog kulinarny z bazą danych (Nowoczesny kod (grid, PDO))

To drugi sposób rozwiązania tego samego arkusza: blog kulinarny z bazą przepisy (relacje n:1 i n:m), separatorem wyciętym z grafiki i stroną przepisy.php, na której wszystko - rodzaj, nazwa, trudność, alergeny, przepis i zdjęcie w tle sekcji - zależy od identyfikatora potrawy z adresu. Obie wersje spełniają wszystkie 38 kryteriów klucza - różni je sposób pisania kodu, nie wynik.

Czym ta wersja różni się od podstawowej:

  • siatka CSS 10% 45% 45% zamiast flex, kolory w zmiennych CSS, tło nagłówka zapisem rgb(211 211 211 / 0.3),
  • odnośniki potraw generowane pętlą z tablicy PHP w bloku <nav>, każdy w osobnej linii dzięki display: block zamiast <br>,
  • zapytania z aliasami tabel,
  • PHP na PDO: cztery zapytania na górze pliku, wszystkie przygotowane i wysyłane przez jedną funkcję fetchForDish(); połączenie zamknięte przed HTML-em,
  • identyfikator z adresu sprawdzony filter_var z wartością domyślną 7, trudność z tablicy zamiast drabinki if, nazwa pliku w stylu inline zakodowana rawurlencode, komunikat dla potrawy spoza bazy, wartości z bazy przez escape().

Kroki nadal idą zdanie po zdaniu za treścią arkusza. Podgląd w każdym kroku z PHP to wynik prawdziwego uruchomienia kodu z tego kroku w PHP 8.2 na bazie z arkusza - stan początkowy, czyli jagnięcina, jak na ilustracji 2. Baza i zdjęcia pochodzą z prawdziwego archiwum pliki14 (załącznik do arkusza, hasło w treści zadania).

Jak liczą się punkty. Klucz ma 38 kryteriów (R.1.1 - R.5.10), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 29 punktów. Przy każdym kroku jest napisane, które kryteria spełnia; egzaminator ocenia spełnienie kryteriów z klucza, nie zgodność z tym kodem.

  1. Etap 1: Baza danych

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

    Krok 1: Baza danych przepisy

    ok. 2 min
    Polecenie z arkusza

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

    Archiwum pliki14.7z ma hasło BLog+#KuLinarnY@ (wielkie i małe litery na przemian, znaki +, #, @); w środku przepisy.sql, lista.txt, separator.png i zdjęcia potraw. W phpMyAdmin: Nowa, nazwa przepisy, porównywanie utf8_polish_ci, albo w zakładce SQL:

    CREATE DATABASE przepisy CHARACTER SET utf8 COLLATE utf8_polish_ci;
    
    Typowe błędy

    Skrypty łączą się z bazą po nazwie - literówka w przepisy zabiera także punkt za połączenie (R.5.1).

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

    ok. 2 min
    Polecenie z arkusza

    Z rozpakowanego archiwum zaimportuj tabele z pliku przepisy.sql do utworzonej bazy

    Zaznacz bazę przepisy, potem Import → przepisy.sql → Wykonaj. Cztery tabele z ilustracji 1: potrawy (klucz obcy idRodzaje - relacja n:1 z rodzaje), alergeny i tabela pośrednicząca lista_alergenow, która łączy potrawy z alergenami (relacja n:m).

  3. Krok 3: Zrzut ekranu import.png

    Kryteria:

    R.1.1 Wykonany import tabel do bazy danych przepisy - obraz import.png

    ok. 2 min
    Polecenie z arkusza

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

    Print Screen (cały ekran z paskiem zadań), wklej do edytora grafiki i zapisz jako import.png. Zrzuty zapytań są w formacie JPEG - tylko import idzie do PNG.

    Typowe błędy

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

  4. Krok 4: Zapytanie 1: nazwa, trudność i kalorie potrawy 7

    Kryteria: ,

    R.1.2 W pliku kwerendy.txt zapisane co najmniej jedno zapytanie SQL, wynikające z treści zadania

    R.1.3 Wykonane zapytanie 1 wybierające jedynie nazwę, trudność i kalorie potrawy o identyfikatorze równym 7. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa, trudnosc, kalorie FROM potrawy WHERE idPotrawy = 7; oraz na zrzucie widoczne są dane: Jagnięcina, 3, 225

    ok. 1 min
    Polecenie z arkusza

    Zapytanie 1: wybierające jedynie nazwę, trudność i kalorie potrawy o identyfikatorze równym 7

    Trzy pola z tabeli potrawy, warunek na kluczu głównym idPotrawy. Wynik: Jagnięcina, trudność 3, 225 kalorii. Każda klauzula w osobnej linii - SQL nie zwraca uwagi na znaki nowej linii. Zapisz w kwerendy.txt, zrzut jako kw1.jpg.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT nazwa, trudnosc, kalorie
    FROM potrawy
    WHERE idPotrawy = 7;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: rodzaj potrawy 7

    Kryteria:

    R.1.4 Wykonane zapytanie 2 wybierające jedynie nazwę potrawy i odpowiadające jej pole rodzaj z tabeli rodzaje dla potrawy o identyfikatorze równym 7. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa, rodzaj FROM potrawy JOIN rodzaje USING(idRodzaje) WHERE idPotrawy = 7; (lub porównanie kluczy po ON lub WHERE ) oraz na zrzucie widoczne są dane: Jagnięcina, dania mięsne

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 2: wybierające jedynie nazwę potrawy i odpowiadające jej pole rodzaj z tabeli rodzaje dla potrawy o identyfikatorze równym 7. Należy posłużyć się relacją

    • FROM potrawy AS p JOIN rodzaje AS r - aliasy tabel,
    • ON r.idRodzaje = p.idRodzaje - relacja n:1: wiele potraw tego samego rodzaju (klucz uznaje też krótsze USING(idRodzaje), bo kolumna ma w obu tabelach tę samą nazwę),
    • WHERE p.idPotrawy = 7,
    • SELECT p.nazwa, r.rodzaj.

    Wynik: Jagnięcina, dania mięsne. Zrzut: kw2.jpg.

    kwerendy.txt Tekst zmienione: 6 linii
    -- Zapytanie 1
    SELECT nazwa, trudnosc, kalorie
    FROM potrawy
    WHERE idPotrawy = 7;
    
    -- Zapytanie 2
    SELECT p.nazwa, r.rodzaj
    FROM potrawy AS p
    JOIN rodzaje AS r ON r.idRodzaje = p.idRodzaje
    WHERE p.idPotrawy = 7;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: alergeny potrawy 7 przez relację n:m

    Kryteria:

    R.1.5 Wykonane zapytanie 3 wybierające jedynie nazwę potrawy i odpowiadające jej nazwy alergenów dla potrawy o identyfikatorze równym 7. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa, alergen FROM potrawy JOIN lista_alergenow USING(idPotrawy) JOIN alergeny USING(idAlergeny) WHERE idPotrawy=7; (lub porównanie kluczy po ON lub WHERE ) oraz na zrzucie widoczne są dane: Jagnięcina, seler

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie nazwę potrawy i odpowiadające jej nazwy alergenów dla potrawy o identyfikatorze równym 7. Zapytanie wykorzystuje relację n:m pomiędzy trzema tabelami

    • FROM potrawy AS p JOIN lista_alergenow AS la ON la.idPotrawy = p.idPotrawy - od potrawy do tabeli pośredniczącej,
    • JOIN alergeny AS a ON a.idAlergeny = la.idAlergeny - od tabeli pośredniczącej do alergenu,
    • WHERE p.idPotrawy = 7.

    Przy trzech tabelach aliasy skracają zapytanie najbardziej. Jagnięcina ma jeden alergen: seler; kociołek - trzy, i wtedy wynik ma trzy wiersze. Zrzut: kw3.jpg.

    kwerendy.txt Tekst zmienione: 7 linii
    -- Zapytanie 1
    SELECT nazwa, trudnosc, kalorie
    FROM potrawy
    WHERE idPotrawy = 7;
    
    -- Zapytanie 2
    SELECT p.nazwa, r.rodzaj
    FROM potrawy AS p
    JOIN rodzaje AS r ON r.idRodzaje = p.idRodzaje
    WHERE p.idPotrawy = 7;
    
    -- Zapytanie 3
    SELECT p.nazwa, a.alergen
    FROM potrawy AS p
    JOIN lista_alergenow AS la ON la.idPotrawy = p.idPotrawy
    JOIN alergeny AS a ON a.idAlergeny = la.idAlergeny
    WHERE p.idPotrawy = 7;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: przepis i plik zdjęcia

    Kryteria:

    R.1.6 Wykonane zapytanie 4 wybierające jedynie pola przepis i plik dla potrawy o identyfikatorze równym 7. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT przepis, plik FROM potrawy WHERE idPotrawy = 7; oraz na zrzucie widoczne są dane: "Mięso oczyść ...." i jagniecina.jpg

    ok. 1 min
    Polecenie z arkusza

    Zapytanie 4: wybierające jedynie pola przepis i plik dla potrawy o identyfikatorze równym 7

    Dwa pola: długi tekst przepisu („Mięso oczyść z błon…”) i nazwa pliku jagniecina.jpg. phpMyAdmin skraca długi tekst w podglądzie. Zrzut: kw4.jpg.

    kwerendy.txt Tekst zmienione: 5 linii
    -- Zapytanie 1
    SELECT nazwa, trudnosc, kalorie
    FROM potrawy
    WHERE idPotrawy = 7;
    
    -- Zapytanie 2
    SELECT p.nazwa, r.rodzaj
    FROM potrawy AS p
    JOIN rodzaje AS r ON r.idRodzaje = p.idRodzaje
    WHERE p.idPotrawy = 7;
    
    -- Zapytanie 3
    SELECT p.nazwa, a.alergen
    FROM potrawy AS p
    JOIN lista_alergenow AS la ON la.idPotrawy = p.idPotrawy
    JOIN alergeny AS a ON a.idAlergeny = la.idAlergeny
    WHERE p.idPotrawy = 7;
    
    -- Zapytanie 4
    SELECT przepis, plik
    FROM potrawy
    WHERE idPotrawy = 7;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Grafika i strona HTML

    Kroki 8-25 · po tym etapie masz 17 z 38 kryteriów (pkt)

    Krok 8: Grafika separator.png: kadr do pierwszego separatora

    Kryteria:

    R.2.1 Grafika separator.png jest skadrowana tak, że jest widoczny tylko separator zgodny z ilustracją 3. Grafika jest przycięta do krawędzi separatora (sprawdzić bez powiększania)

    ok. 3 min
    Polecenie z arkusza

    Cechy grafiki separator.png: Grafikę należy skadrować tak, aby był widoczny jedynie separator pierwszy od góry, po lewej stronie. Grafikę należy dociąć do krawędzi separatora (ilustracja 3, ramka przedstawia kadr obrazu).

    Grafika jest taka sama w obu wersjach. W GIMP-ie zaznacz pierwszy separator od góry po lewej z zapasem (bez sąsiednich ozdobników), Obraz → Przytnij do zaznaczenia, potem Obraz → Przytnij do zawartości - krawędzie dosuną się do linii i ornamentu. Wynik: 317 × 57 px, eksport do PNG z przezroczystością (R.2.1).

    Podgląd strony po tym kroku
    Podgląd strony po kroku 8
  9. Krok 9: Plik przepisy.php w HTML5

    ok. 2 min
    Polecenie z arkusza

    Składa się ze strony o nazwie przepisy.php. Zapisana w języku HTML5

    <!DOCTYPE html> i szkielet. Rozszerzenie .php, bo dojdą skrypty - strona otwiera się przez http://localhost/....

    przepisy.php PHP nowy plik
    <!DOCTYPE html>
    <html>
    <head>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (przepisy.php)
  10. Krok 10: Język strony

    Kryteria:

    R.2.2 W pliku przepisy.php znajduje się deklaracja HTML5 <!DOCTYPE HTML> i deklaracja języka zawartości witryny: polski np. <html lang="pl">

    ok. 1 min
    Polecenie z arkusza

    Zadeklarowany polski język zawartości witryny

    Atrybut lang="pl" - z deklaracją HTML5 to jedno kryterium.

    przepisy.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (przepisy.php)
  11. Krok 11: Kodowanie polskich znaków

    ok. 1 min
    Polecenie z arkusza

    Jawnie zastosowany właściwy standard kodowania polskich znaków

    <meta charset="UTF-8"> jako pierwszy element <head>.

    przepisy.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (przepisy.php)
  12. Krok 12: Tytuł strony

    Kryteria:

    R.2.3 W sekcji head znajduje się jawnie podany standard kodowania polskich znaków i tytuł strony "Blog kulinarny"

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony „Blog kulinarny”

    <title> w <head>. Kodowanie i tytuł to razem jedno kryterium.

    przepisy.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Blog kulinarny</title>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (przepisy.php)
  13. Krok 13: Podpięcie arkusza stylów

    Kryteria:

    R.3.2 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl.css formatowanie CSS pochodzi jedynie z tego arkusza (oprócz stylu inline sekcji)

    ok. 1 min
    Polecenie z arkusza

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

    Plik styl.css. Jedynym wyjątkiem od „cały CSS w pliku” będzie styl inline tła sekcji, którego arkusz wprost wymaga.

    przepisy.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Blog kulinarny</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (przepisy.php)
  14. Krok 14: Bloki: boczny, główny i sekcja

    Kryteria:

    R.2.4 W sekcji body zdefiniowany jest podział na bloki za pomocą semantycznych znaczników HTML5: aside+main+section (dopuszcza się zastosowanie bloków pomocniczych)

    ok. 1 min
    Polecenie z arkusza

    Podział strony na bloki zrealizowany za pomocą semantycznych znaczników bloków języka HTML5 tak, aby po uruchomieniu w przeglądarce układ bloków na stronie był zgodny z ilustracją 4

    Trzy bloki z ilustracji 4: aside, main i section (R.2.4). Kolejność w kodzie jest kolejnością kolumn siatki, którą ustawi CSS - identyfikatory nie są potrzebne.

    przepisy.php PHP zmienione: 6 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Blog kulinarny</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
        </aside>
        <main>
        </main>
        <section>
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (przepisy.php)
  15. Krok 15: Odnośnik „Sernik” z identyfikatorem 1 - z tablicy

    ok. 3 min
    Polecenie z arkusza

    Zawartość bloku bocznego: Każdy odnośnik prowadzi do pliku przepisy.php oraz przesyła do niego metodą GET wartość id odpowiadającą wybranej potrawie, są to 1 dla odnośnika Sernik, 2 dla Sałatka itd.

    Dziesięć odnośników różni się tylko numerem i nazwą, więc na górze pliku powstaje tablica $dishes (klucz = identyfikator potrawy, wartość = nazwa z lista.txt), a w bloku <nav> pętla foreach w składni alternatywnej wypisuje <a href="przepisy.php?id=1">Sernik</a> (tabela 3 arkusza). <?= ... ?> to skrót od <?php echo ... ?>.

    Nazw nie bierze się z bazy, bo baza ma inne: „Sernik z brzoskwiniami”, „Prosta sałatka” - a klucz sprawdza krótkie nazwy z lista.txt. Od tego kroku plik zawiera PHP, więc podgląd to wynik jego uruchomienia.

    przepisy.php PHP zmienione: 8 linii
    <?php
    $dishes = [1 => 'Sernik'];
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Blog kulinarny</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
            <nav aria-label="Potrawy">
    <?php foreach ($dishes as $id => $name): ?>
                <a href="przepisy.php?id=<?= $id ?>"><?= $name ?></a>
    <?php endforeach; ?>
            </nav>
        </aside>
        <main>
        </main>
        <section>
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (przepisy.php)
  16. Krok 16: Dziesięć odnośników potraw

    Kryteria: ,

    R.2.6 W bloku bocznym znajduje się 10 odnośników o treści: Sernik, Sałatka, Pankejki, Nugetsy, Łosoś, Kociołek, Jagnięcina, Hamburgery, Eklerki, Churros

    R.3.4 Kliknięcie dowolnego odnośnika odświeża stronę przepisy.php oraz przesyła wartość id potrawy, której odnośnik kliknięto, URL jest postaci, np.: przepisy.php?id=1 (sprawdzić dla przynajmniej trzech odnośników)

    ok. 3 min
    Polecenie z arkusza

    10 odnośników z nazwami potraw (można skopiować z pliku lista.txt): Sernik, Sałatka ... Każdy odnośnik umieszczony jest w osobnej linii

    Tablica ma teraz dziesięć par skopiowanych z lista.txt, każda w osobnej linii (przecinek po ostatniej jest dozwolony). Numer i nazwa stoją w jednej parze, więc nie mogą się rozjechać jak przy kopiowaniu linii HTML (R.2.6, R.3.4).

    „Każdy odnośnik w osobnej linii” zrobi CSS (display: block w regule a) zamiast <br> po każdym odnośniku - na razie odnośniki stoją obok siebie.

    przepisy.php PHP zmienione: 12 linii
    <?php
    $dishes = [
        1 => 'Sernik',
        2 => 'Sałatka',
        3 => 'Pankejki',
        4 => 'Nugetsy',
        5 => 'Łosoś',
        6 => 'Kociołek',
        7 => 'Jagnięcina',
        8 => 'Hamburgery',
        9 => 'Eklerki',
        10 => 'Churros',
    ];
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Blog kulinarny</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
            <nav aria-label="Potrawy">
    <?php foreach ($dishes as $id => $name): ?>
                <a href="przepisy.php?id=<?= $id ?>"><?= $name ?></a>
    <?php endforeach; ?>
            </nav>
        </aside>
        <main>
        </main>
        <section>
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (przepisy.php)
  17. Krok 17: Autor pod odnośnikami

    ok. 1 min
    Polecenie z arkusza

    Paragraf o treści „Autor: ”, dalej wstawiony numer zdającego

    Akapit pod blokiem odnośników. Na egzaminie po dwukropku wpisujesz swój numer zdającego (PESEL) - tu stoi imię i nazwisko autora rozwiązania.

    przepisy.php PHP zmienione: 1 linia
    <?php
    $dishes = [
        1 => 'Sernik',
        2 => 'Sałatka',
        3 => 'Pankejki',
        4 => 'Nugetsy',
        5 => 'Łosoś',
        6 => 'Kociołek',
        7 => 'Jagnięcina',
        8 => 'Hamburgery',
        9 => 'Eklerki',
        10 => 'Churros',
    ];
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Blog kulinarny</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
            <nav aria-label="Potrawy">
    <?php foreach ($dishes as $id => $name): ?>
                <a href="przepisy.php?id=<?= $id ?>"><?= $name ?></a>
    <?php endforeach; ?>
            </nav>
            <p>Autor: Bartosz Bryniarski</p>
        </aside>
        <main>
        </main>
        <section>
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (przepisy.php)
  18. Krok 18: Blok główny: nagłówek na rodzaj potrawy

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku głównego: Nagłówek pierwszego stopnia wypełniony skryptem 1. Efekt działania skryptu 2

    Pusty <h1> - rodzaj potrawy wpisze skrypt 1. Pod nim staną nazwa i trudność ze skryptu 2.

    przepisy.php PHP zmienione: 1 linia
    <?php
    $dishes = [
        1 => 'Sernik',
        2 => 'Sałatka',
        3 => 'Pankejki',
        4 => 'Nugetsy',
        5 => 'Łosoś',
        6 => 'Kociołek',
        7 => 'Jagnięcina',
        8 => 'Hamburgery',
        9 => 'Eklerki',
        10 => 'Churros',
    ];
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Blog kulinarny</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
            <nav aria-label="Potrawy">
    <?php foreach ($dishes as $id => $name): ?>
                <a href="przepisy.php?id=<?= $id ?>"><?= $name ?></a>
    <?php endforeach; ?>
            </nav>
            <p>Autor: Bartosz Bryniarski</p>
        </aside>
        <main>
            <h1></h1>
        </main>
        <section>
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (przepisy.php)
  19. Krok 19: Separator

    Kryteria:

    R.2.7 W bloku głównym znajduje się obraz separatora z tekstem alternatywnym "przepis"

    ok. 1 min
    Polecenie z arkusza

    Obraz separator.png z tekstem alternatywnym „przepis”

    <img> z grafiką przygotowaną na początku.

    przepisy.php PHP zmienione: 1 linia
    <?php
    $dishes = [
        1 => 'Sernik',
        2 => 'Sałatka',
        3 => 'Pankejki',
        4 => 'Nugetsy',
        5 => 'Łosoś',
        6 => 'Kociołek',
        7 => 'Jagnięcina',
        8 => 'Hamburgery',
        9 => 'Eklerki',
        10 => 'Churros',
    ];
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Blog kulinarny</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
            <nav aria-label="Potrawy">
    <?php foreach ($dishes as $id => $name): ?>
                <a href="przepisy.php?id=<?= $id ?>"><?= $name ?></a>
    <?php endforeach; ?>
            </nav>
            <p>Autor: Bartosz Bryniarski</p>
        </aside>
        <main>
            <h1></h1>
            <img src="separator.png" alt="przepis">
        </main>
        <section>
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (przepisy.php)
  20. Krok 20: Akapit na alergeny

    ok. 1 min
    Polecenie z arkusza

    Paragraf o treści „Alergeny: ”, dalej w paragrafie występują nazwy alergenów wypisane skryptem 3

    Skrypt 3 wpisze nazwy wewnątrz tego akapitu, po dwukropku.

    przepisy.php PHP zmienione: 1 linia
    <?php
    $dishes = [
        1 => 'Sernik',
        2 => 'Sałatka',
        3 => 'Pankejki',
        4 => 'Nugetsy',
        5 => 'Łosoś',
        6 => 'Kociołek',
        7 => 'Jagnięcina',
        8 => 'Hamburgery',
        9 => 'Eklerki',
        10 => 'Churros',
    ];
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Blog kulinarny</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
            <nav aria-label="Potrawy">
    <?php foreach ($dishes as $id => $name): ?>
                <a href="przepisy.php?id=<?= $id ?>"><?= $name ?></a>
    <?php endforeach; ?>
            </nav>
            <p>Autor: Bartosz Bryniarski</p>
        </aside>
        <main>
            <h1></h1>
            <img src="separator.png" alt="przepis">
            <p>Alergeny: </p>
        </main>
        <section>
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (przepisy.php)
  21. Krok 21: Nagłówek „Składniki”

    ok. 1 min
    Polecenie z arkusza

    Nagłówek drugiego stopnia o treści „Składniki”

    <h2>.

    przepisy.php PHP zmienione: 1 linia
    <?php
    $dishes = [
        1 => 'Sernik',
        2 => 'Sałatka',
        3 => 'Pankejki',
        4 => 'Nugetsy',
        5 => 'Łosoś',
        6 => 'Kociołek',
        7 => 'Jagnięcina',
        8 => 'Hamburgery',
        9 => 'Eklerki',
        10 => 'Churros',
    ];
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Blog kulinarny</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
            <nav aria-label="Potrawy">
    <?php foreach ($dishes as $id => $name): ?>
                <a href="przepisy.php?id=<?= $id ?>"><?= $name ?></a>
    <?php endforeach; ?>
            </nav>
            <p>Autor: Bartosz Bryniarski</p>
        </aside>
        <main>
            <h1></h1>
            <img src="separator.png" alt="przepis">
            <p>Alergeny: </p>
            <h2>Składniki</h2>
        </main>
        <section>
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (przepisy.php)
  22. Krok 22: Lista składników

    Kryteria:

    R.2.8 W bloku głównym znajduje się jedna lista punktowana z czterema elementami, znaczniki <ul> i <li>

    ok. 1 min
    Polecenie z arkusza

    Lista punktowana (nieuporządkowana) wypełniona elementami z pliku lista.txt

    <ul> z czterema <li> z drugiej części lista.txt - tekst zastępczy, ten sam przy każdej potrawie (R.2.8).

    przepisy.php PHP zmienione: 6 linii
    <?php
    $dishes = [
        1 => 'Sernik',
        2 => 'Sałatka',
        3 => 'Pankejki',
        4 => 'Nugetsy',
        5 => 'Łosoś',
        6 => 'Kociołek',
        7 => 'Jagnięcina',
        8 => 'Hamburgery',
        9 => 'Eklerki',
        10 => 'Churros',
    ];
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Blog kulinarny</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
            <nav aria-label="Potrawy">
    <?php foreach ($dishes as $id => $name): ?>
                <a href="przepisy.php?id=<?= $id ?>"><?= $name ?></a>
    <?php endforeach; ?>
            </nav>
            <p>Autor: Bartosz Bryniarski</p>
        </aside>
        <main>
            <h1></h1>
            <img src="separator.png" alt="przepis">
            <p>Alergeny: </p>
            <h2>Składniki</h2>
            <ul>
                <li>Lorem 1 kg</li>
                <li>Ipsum 2 szt.</li>
                <li>Dolor 200 g</li>
                <li>Sit amet (szczypta)</li>
            </ul>
        </main>
        <section>
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (przepisy.php)
  23. Krok 23: Akapit na przepis

    ok. 1 min
    Polecenie z arkusza

    Paragraf wypełniony skryptem 4

    Pusty <p> na końcu bloku głównego - skrypt 4 wpisze w nim przepis.

    przepisy.php PHP zmienione: 1 linia
    <?php
    $dishes = [
        1 => 'Sernik',
        2 => 'Sałatka',
        3 => 'Pankejki',
        4 => 'Nugetsy',
        5 => 'Łosoś',
        6 => 'Kociołek',
        7 => 'Jagnięcina',
        8 => 'Hamburgery',
        9 => 'Eklerki',
        10 => 'Churros',
    ];
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Blog kulinarny</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
            <nav aria-label="Potrawy">
    <?php foreach ($dishes as $id => $name): ?>
                <a href="przepisy.php?id=<?= $id ?>"><?= $name ?></a>
    <?php endforeach; ?>
            </nav>
            <p>Autor: Bartosz Bryniarski</p>
        </aside>
        <main>
            <h1></h1>
            <img src="separator.png" alt="przepis">
            <p>Alergeny: </p>
            <h2>Składniki</h2>
            <ul>
                <li>Lorem 1 kg</li>
                <li>Ipsum 2 szt.</li>
                <li>Dolor 200 g</li>
                <li>Sit amet (szczypta)</li>
            </ul>
            <p></p>
        </main>
        <section>
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (przepisy.php)
  24. Krok 24: Sekcja: tło ze stylu inline

    Kryteria:

    R.3.3 Do bloku sekcji przypisane jest formatowanie inline tła w postaci obrazu

    ok. 2 min
    Polecenie z arkusza

    Zawartość bloku sekcji: Przypisany jak atrybut znacznika bloku styl CSS inline: tło wypełnione obrazem, którego nazwa pliku została pobrana skryptem 4

    Styl inline to atrybut style w samym znaczniku - arkusz wprost go wymaga (R.3.3), bo zdjęcie zależy od potrawy, a plik CSS jest stały. Na razie nazwa pliku wpisana na stałe; skrypt 4 podmieni ją na nazwę z bazy.

    przepisy.php PHP zmienione: 1 linia
    <?php
    $dishes = [
        1 => 'Sernik',
        2 => 'Sałatka',
        3 => 'Pankejki',
        4 => 'Nugetsy',
        5 => 'Łosoś',
        6 => 'Kociołek',
        7 => 'Jagnięcina',
        8 => 'Hamburgery',
        9 => 'Eklerki',
        10 => 'Churros',
    ];
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Blog kulinarny</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
            <nav aria-label="Potrawy">
    <?php foreach ($dishes as $id => $name): ?>
                <a href="przepisy.php?id=<?= $id ?>"><?= $name ?></a>
    <?php endforeach; ?>
            </nav>
            <p>Autor: Bartosz Bryniarski</p>
        </aside>
        <main>
            <h1></h1>
            <img src="separator.png" alt="przepis">
            <p>Alergeny: </p>
            <h2>Składniki</h2>
            <ul>
                <li>Lorem 1 kg</li>
                <li>Ipsum 2 szt.</li>
                <li>Dolor 200 g</li>
                <li>Sit amet (szczypta)</li>
            </ul>
            <p></p>
        </main>
        <section style="background-image: url('jagniecina.jpg');">
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (przepisy.php)
  25. Krok 25: Sekcja: nagłówek bloga

    Kryteria:

    R.2.5 W bloku bocznym znajduje się <p> z numerem zdającego, w bloku głównym <h1> i <p> (w HTML lub skrypcie ) oraz <h2> "Składniki", w bloku sekcji <h1>: "Blog Kulinarny"

    ok. 1 min
    Polecenie z arkusza

    Nagłówek pierwszego stopnia o treści „Blog Kulinarny”

    Drugi <h1> na stronie, w sekcji. Komplet nagłówków i akapitów z klucza (R.2.5).

    przepisy.php PHP zmienione: 1 linia
    <?php
    $dishes = [
        1 => 'Sernik',
        2 => 'Sałatka',
        3 => 'Pankejki',
        4 => 'Nugetsy',
        5 => 'Łosoś',
        6 => 'Kociołek',
        7 => 'Jagnięcina',
        8 => 'Hamburgery',
        9 => 'Eklerki',
        10 => 'Churros',
    ];
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Blog kulinarny</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
            <nav aria-label="Potrawy">
    <?php foreach ($dishes as $id => $name): ?>
                <a href="przepisy.php?id=<?= $id ?>"><?= $name ?></a>
    <?php endforeach; ?>
            </nav>
            <p>Autor: Bartosz Bryniarski</p>
        </aside>
        <main>
            <h1></h1>
            <img src="separator.png" alt="przepis">
            <p>Alergeny: </p>
            <h2>Składniki</h2>
            <ul>
                <li>Lorem 1 kg</li>
                <li>Ipsum 2 szt.</li>
                <li>Dolor 200 g</li>
                <li>Sit amet (szczypta)</li>
            </ul>
            <p></p>
        </main>
        <section style="background-image: url('jagniecina.jpg');">
            <h1>Blog Kulinarny</h1>
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (przepisy.php)
  26. Etap 3: Styl CSS

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

    Krok 26: Plik styl.css: zmienne kolorów, krój i wyrównanie

    Kryteria:

    R.4.1 domyślne formatowanie selektorów (*): krój czcionki Garamond, wyrównanie tekstu do środka

    ok. 2 min
    Polecenie z arkusza

    Domyślnie dla wszystkich selektorów: krój czcionki Garamond, wyrównanie tekstu do środka

    Kolory z arkusza w zmiennych CSS w :root. Tło nagłówka zapisane nowszą składnią rgb(211 211 211 / 0.3): składowe po spacjach, krycie po ukośniku - to ten sam kolor co rgba(211, 211, 211, 0.3) i tak też pokazuje go inspektor.

    Selektor *: krój Garamond z rodziną zapasową serif i środkowanie (R.4.1).

    styl.css CSS nowy plik
    :root {
        --sidebar-background: #3A3C3E;
        --heading-background: rgb(211 211 211 / 0.3);
    }
    
    * {
        font-family: Garamond, serif;
        text-align: center;
    }
    Podgląd strony po tym kroku (przepisy.php)
  27. Krok 27: Blok boczny: tło, kolor i wysokość

    ok. 1 min
    Polecenie z arkusza

    Dla bloku bocznego: kolor tła #3A3C3E, biały kolor czcionki, szerokość 10%, wysokość 600 px

    Ciemne tło i biały tekst. Szerokości 10% nie ma w tej regule - da ją kolumna siatki w kroku o układzie.

    styl.css CSS zmienione: 6 linii
    :root {
        --sidebar-background: #3A3C3E;
        --heading-background: rgb(211 211 211 / 0.3);
    }
    
    * {
        font-family: Garamond, serif;
        text-align: center;
    }
    
    aside {
        background-color: var(--sidebar-background);
        color: white;
        height: 600px;
    }
    Podgląd strony po tym kroku (przepisy.php)
  28. Krok 28: Blok główny: wysokość i paski przewijania

    ok. 1 min
    Polecenie z arkusza

    Dla bloku głównego: szerokość 45%, wysokość 600 px, paski przewijania widoczne jedynie w przypadku przepełnienia bloku

    overflow: auto - pasek przewijania pojawia się tylko wtedy, gdy treść nie mieści się w 600 px. Szerokość 45% - z siatki.

    styl.css CSS zmienione: 5 linii
    :root {
        --sidebar-background: #3A3C3E;
        --heading-background: rgb(211 211 211 / 0.3);
    }
    
    * {
        font-family: Garamond, serif;
        text-align: center;
    }
    
    aside {
        background-color: var(--sidebar-background);
        color: white;
        height: 600px;
    }
    
    main {
        height: 600px;
        overflow: auto;
    }
    Podgląd strony po tym kroku (przepisy.php)
  29. Krok 29: Blok sekcji: dopasowanie tła i wysokość

    Kryteria:

    R.4.5 przepełnienie bloku: blok główny auto styl bloku sekcji: background-size: cover;

    ok. 2 min
    Polecenie z arkusza

    Dla bloku sekcji: wymiary obrazu tła są automatycznie dostosowane do wymiaru bloku przez obcięcie lub rozciągnięcie obrazu, biały kolor czcionki, szerokość 45%, wysokość 600 px

    background-size: cover (tabela 2 arkusza) skaluje obraz, żeby przykrył cały blok, i obcina nadmiar. Obraz przychodzi ze stylu inline, dopasowanie - z pliku CSS; obie deklaracje działają razem (R.4.5).

    styl.css CSS zmienione: 6 linii
    :root {
        --sidebar-background: #3A3C3E;
        --heading-background: rgb(211 211 211 / 0.3);
    }
    
    * {
        font-family: Garamond, serif;
        text-align: center;
    }
    
    aside {
        background-color: var(--sidebar-background);
        color: white;
        height: 600px;
    }
    
    main {
        height: 600px;
        overflow: auto;
    }
    
    section {
        background-size: cover;
        color: white;
        height: 600px;
    }
    Podgląd strony po tym kroku (przepisy.php)
  30. Krok 30: Układ bloków zgodny z ilustracją 4

    Kryteria: ,

    R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z ilustracją 4 w arkuszu egzaminacyjnym (prawidłowo zastosowano właściwości CSS decydujące o układzie bloków na stronie, np. float i clear albo display flex albo grid albo position)

    R.4.4 wysokość: blok boczny, główny, sekcji - 600px szerokość: blok boczny - 10%, główny i sekcji - 45% (width lub grid-template-columns lub flex )

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki zrealizowany za pomocą semantycznych znaczników bloków języka HTML5 tak, aby po uruchomieniu w przeglądarce układ bloków na stronie był zgodny z ilustracją 4

    <body> staje się siatką o trzech kolumnach: grid-template-columns: 10% 45% 45%. aside, main i section trafiają po kolei do kolumn - stoją obok siebie, a szerokości z arkusza wynikają z kolumn (R.3.1). Klucz dopuszcza szerokości z grid-template-columns, więc razem z wysokościami z poprzednich reguł komplet wymiarów jest gotowy (R.4.4).

    styl.css CSS zmienione: 5 linii
    :root {
        --sidebar-background: #3A3C3E;
        --heading-background: rgb(211 211 211 / 0.3);
    }
    
    * {
        font-family: Garamond, serif;
        text-align: center;
    }
    
    aside {
        background-color: var(--sidebar-background);
        color: white;
        height: 600px;
    }
    
    main {
        height: 600px;
        overflow: auto;
    }
    
    section {
        background-size: cover;
        color: white;
        height: 600px;
    }
    
    body {
        display: grid;
        grid-template-columns: 10% 45% 45%;
    }
    Podgląd strony po tym kroku (przepisy.php)
  31. Krok 31: Nagłówki pierwszego stopnia

    Kryteria: ,

    R.4.2 kolor tła: blok boczny - #3A3C3E, selektor h1 - RGBA(211, 211, 211, 0.3) (lub z opacity )

    R.4.6 styl nagłówka pierwszego stopnia: marginesy wewnętrzne (padding ): 20px góra i dół marginesy zewnętrzne (margin ): 0

    ok. 2 min
    Polecenie z arkusza

    Dla selektora nagłówka pierwszego stopnia: kolor tła RGB 211, 211, 211, przezroczystość 0.3, marginesy wewnętrzne górny i dolny 20 px pozostałe 0, marginesy zewnętrzne 0, rozmiar czcionki 2 em

    Przezroczystość w samym kolorze tła, nie opacity - opacity przezroczyściłaby też napis. padding: 20px 0 - góra i dół 20 px, boki 0. Selektor znacznika h1 (uwaga arkusza) obejmuje oba nagłówki (R.4.2, R.4.6).

    styl.css CSS zmienione: 7 linii
    :root {
        --sidebar-background: #3A3C3E;
        --heading-background: rgb(211 211 211 / 0.3);
    }
    
    * {
        font-family: Garamond, serif;
        text-align: center;
    }
    
    aside {
        background-color: var(--sidebar-background);
        color: white;
        height: 600px;
    }
    
    main {
        height: 600px;
        overflow: auto;
    }
    
    section {
        background-size: cover;
        color: white;
        height: 600px;
    }
    
    body {
        display: grid;
        grid-template-columns: 10% 45% 45%;
    }
    
    h1 {
        background-color: var(--heading-background);
        padding: 20px 0;
        margin: 0;
        font-size: 2em;
    }
    Podgląd strony po tym kroku (przepisy.php)
  32. Krok 32: Nagłówki drugiego stopnia: kapitaliki

    ok. 1 min
    Polecenie z arkusza

    Dla selektora nagłówka drugiego stopnia: rozmiar czcionki 2 em, tekst pisany kapitalikami

    Kapitaliki to font-variant: small-caps - małe litery jak pomniejszone wielkie; text-transform: uppercase dałby same wielkie.

    styl.css CSS zmienione: 5 linii
    :root {
        --sidebar-background: #3A3C3E;
        --heading-background: rgb(211 211 211 / 0.3);
    }
    
    * {
        font-family: Garamond, serif;
        text-align: center;
    }
    
    aside {
        background-color: var(--sidebar-background);
        color: white;
        height: 600px;
    }
    
    main {
        height: 600px;
        overflow: auto;
    }
    
    section {
        background-size: cover;
        color: white;
        height: 600px;
    }
    
    body {
        display: grid;
        grid-template-columns: 10% 45% 45%;
    }
    
    h1 {
        background-color: var(--heading-background);
        padding: 20px 0;
        margin: 0;
        font-size: 2em;
    }
    
    h2 {
        font-size: 2em;
        font-variant: small-caps;
    }
    Podgląd strony po tym kroku (przepisy.php)
  33. Krok 33: Elementy listy do lewej

    Kryteria:

    R.4.9 styl selektora li: wyrównanie tekstu do lewej strony

    ok. 1 min
    Polecenie z arkusza

    Dla selektora elementu listy: wyrównanie tekstu do lewej strony

    Selektor li nadpisuje środkowanie z *.

    styl.css CSS zmienione: 4 linie
    :root {
        --sidebar-background: #3A3C3E;
        --heading-background: rgb(211 211 211 / 0.3);
    }
    
    * {
        font-family: Garamond, serif;
        text-align: center;
    }
    
    aside {
        background-color: var(--sidebar-background);
        color: white;
        height: 600px;
    }
    
    main {
        height: 600px;
        overflow: auto;
    }
    
    section {
        background-size: cover;
        color: white;
        height: 600px;
    }
    
    body {
        display: grid;
        grid-template-columns: 10% 45% 45%;
    }
    
    h1 {
        background-color: var(--heading-background);
        padding: 20px 0;
        margin: 0;
        font-size: 2em;
    }
    
    h2 {
        font-size: 2em;
        font-variant: small-caps;
    }
    
    li {
        text-align: left;
    }
    Podgląd strony po tym kroku (przepisy.php)
  34. Krok 34: Odnośniki: każdy w osobnej linii

    Kryteria: , ,

    R.4.3 kolor czcionki: blok boczny i sekcji - biały, selektor a - Gainsboro

    R.4.7 cechy czcionki: selektor h2 font-variant:small-caps; (font-variant-caps) odnośnik: tekst pochylony np. font-style: italic;

    R.4.8 rozmiar czcionki: nagłówek pierwszego i drugiego stopnia - 2em, odnośnik - 120%

    ok. 2 min
    Polecenie z arkusza

    Dla selektora odnośnika: kolor czcionki Gainsboro, rozmiar czcionki 120%, czcionka pochylona

    Kolor, rozmiar i kursywa z arkusza (R.4.3, R.4.7, R.4.8), a do tego display: block: odnośnik zajmuje całą szerokość bloku bocznego, więc każdy stoi w osobnej linii - zamiast <br> po każdym. Przy okazji kliknąć można w całą szerokość wiersza, nie tylko w napis.

    styl.css CSS zmienione: 7 linii
    :root {
        --sidebar-background: #3A3C3E;
        --heading-background: rgb(211 211 211 / 0.3);
    }
    
    * {
        font-family: Garamond, serif;
        text-align: center;
    }
    
    aside {
        background-color: var(--sidebar-background);
        color: white;
        height: 600px;
    }
    
    main {
        height: 600px;
        overflow: auto;
    }
    
    section {
        background-size: cover;
        color: white;
        height: 600px;
    }
    
    body {
        display: grid;
        grid-template-columns: 10% 45% 45%;
    }
    
    h1 {
        background-color: var(--heading-background);
        padding: 20px 0;
        margin: 0;
        font-size: 2em;
    }
    
    h2 {
        font-size: 2em;
        font-variant: small-caps;
    }
    
    li {
        text-align: left;
    }
    
    a {
        display: block;
        color: Gainsboro;
        font-size: 120%;
        font-style: italic;
    }
    Podgląd strony po tym kroku (przepisy.php)
  35. Krok 35: Odnośnik pod kursorem i z klawiatury

    Kryteria:

    R.4.10 gdy kursor znajdzie się na odnośniku, jego kolor czcionki zmienia się na biały

    ok. 1 min
    Polecenie z arkusza

    Gdy kursor znajdzie się na odnośniku, jego kolor czcionki zmienia się na biały

    :hover (R.4.10) i ten sam wygląd dla :focus-visible - odnośnika wybranego klawiszem Tab.

    styl.css CSS zmienione: 5 linii
    :root {
        --sidebar-background: #3A3C3E;
        --heading-background: rgb(211 211 211 / 0.3);
    }
    
    * {
        font-family: Garamond, serif;
        text-align: center;
    }
    
    aside {
        background-color: var(--sidebar-background);
        color: white;
        height: 600px;
    }
    
    main {
        height: 600px;
        overflow: auto;
    }
    
    section {
        background-size: cover;
        color: white;
        height: 600px;
    }
    
    body {
        display: grid;
        grid-template-columns: 10% 45% 45%;
    }
    
    h1 {
        background-color: var(--heading-background);
        padding: 20px 0;
        margin: 0;
        font-size: 2em;
    }
    
    h2 {
        font-size: 2em;
        font-variant: small-caps;
    }
    
    li {
        text-align: left;
    }
    
    a {
        display: block;
        color: Gainsboro;
        font-size: 120%;
        font-style: italic;
    }
    
    a:hover,
    a:focus-visible {
        color: white;
    }
    Podgląd strony po tym kroku (przepisy.php)
  36. Etap 4: Skrypty PHP

    Kroki 36-45 · po tym etapie masz 38 z 38 kryteriów (pkt)

    Krok 36: Połączenie PDO na górze pliku

    Kryteria:

    R.5.1 Skrypt realizuje połączenie z serwerem bazy danych i wybór bazy przepisy oraz ostatnią operacją na bazie jest jej zamknięcie

    ok. 4 min
    Polecenie z arkusza

    Napisane w języku PHP. Należy stosować znaczące nazewnictwo zmiennych w języku polskim lub angielskim. Łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie przepisy. Na końcu skrypt zamyka połączenie z bazą

    Praca z bazą stoi przed <!DOCTYPE html>, obok tablicy potraw: najpierw dane, potem HTML, który je tylko wypisuje. Połączenie przez PDO (klucz R.5 przyjmuje MySQLi i PDO):

    • DSN 'mysql:host=localhost;dbname=przepisy;charset=utf8mb4' - serwer, baza i kodowanie; 'root', '' - użytkownik i puste hasło,
    • ERRMODE_EXCEPTION - błąd bazy rzuca wyjątek, FETCH_ASSOC - wiersze jako tablice z nazwami kolumn,
    • $pdo = null zamyka połączenie - cztery zapytania staną przed tą linią, więc zamknięcie zostanie ostatnią operacją na bazie (R.5.1),
    • try / catch - gdy baza nie działa, szczegóły idą do dziennika serwera, a w bloku głównym stoi ogólny komunikat $dbError,
    • funkcja escape() to krótka nazwa htmlspecialchars(..., ENT_QUOTES) dla wartości z bazy.
    przepisy.php PHP zmienione: 21 linii
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    $dishes = [
        1 => 'Sernik',
        2 => 'Sałatka',
        3 => 'Pankejki',
        4 => 'Nugetsy',
        5 => 'Łosoś',
        6 => 'Kociołek',
        7 => 'Jagnięcina',
        8 => 'Hamburgery',
        9 => 'Eklerki',
        10 => 'Churros',
    ];
    
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=przepisy;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ć przepisu. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Blog kulinarny</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
            <nav aria-label="Potrawy">
    <?php foreach ($dishes as $id => $name): ?>
                <a href="przepisy.php?id=<?= $id ?>"><?= $name ?></a>
    <?php endforeach; ?>
            </nav>
            <p>Autor: Bartosz Bryniarski</p>
        </aside>
        <main>
    <?php if ($dbError !== ''): ?>
            <p><?= $dbError ?></p>
    <?php endif; ?>
            <h1></h1>
            <img src="separator.png" alt="przepis">
            <p>Alergeny: </p>
            <h2>Składniki</h2>
            <ul>
                <li>Lorem 1 kg</li>
                <li>Ipsum 2 szt.</li>
                <li>Dolor 200 g</li>
                <li>Sit amet (szczypta)</li>
            </ul>
            <p></p>
        </main>
        <section style="background-image: url('jagniecina.jpg');">
            <h1>Blog Kulinarny</h1>
        </section>
    </body>
    </html>

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

  37. Krok 37: Identyfikator potrawy: 7 albo wartość z adresu

    ok. 3 min
    Polecenie z arkusza

    Jeśli do strony została przesłana wartość metodą GET, zmienna jest inicjowana tą wartością. W przeciwnym wypadku, zmienna jest inicjowana wartością 7. Dalej w opisie skryptów zmienna ta jest nazywana ID

    Zmienna $dishId (ID z arkusza) startuje z wartością 7 - jagnięcina przy pierwszym wejściu. Gdy w adresie jest ?id=... (isset), wartość przechodzi przez filter_var z FILTER_VALIDATE_INT: liczba całkowita od 1 trafia do zmiennej, a cokolwiek innego (abc, 7 OR 1=1, -3) zamienia się na default = 7. Zmienna zawsze jest więc liczbą - nic innego nie dotrze do zapytań.

    przepisy.php PHP zmienione: 4 linie
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    $dishes = [
        1 => 'Sernik',
        2 => 'Sałatka',
        3 => 'Pankejki',
        4 => 'Nugetsy',
        5 => 'Łosoś',
        6 => 'Kociołek',
        7 => 'Jagnięcina',
        8 => 'Hamburgery',
        9 => 'Eklerki',
        10 => 'Churros',
    ];
    
    $dishId = 7;
    if (isset($_GET['id'])) {
        $dishId = filter_var($_GET['id'], FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'default' => 7]]);
    }
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=przepisy;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ć przepisu. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Blog kulinarny</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
            <nav aria-label="Potrawy">
    <?php foreach ($dishes as $id => $name): ?>
                <a href="przepisy.php?id=<?= $id ?>"><?= $name ?></a>
    <?php endforeach; ?>
            </nav>
            <p>Autor: Bartosz Bryniarski</p>
        </aside>
        <main>
    <?php if ($dbError !== ''): ?>
            <p><?= $dbError ?></p>
    <?php endif; ?>
            <h1></h1>
            <img src="separator.png" alt="przepis">
            <p>Alergeny: </p>
            <h2>Składniki</h2>
            <ul>
                <li>Lorem 1 kg</li>
                <li>Ipsum 2 szt.</li>
                <li>Dolor 200 g</li>
                <li>Sit amet (szczypta)</li>
            </ul>
            <p></p>
        </main>
        <section style="background-image: url('jagniecina.jpg');">
            <h1>Blog Kulinarny</h1>
        </section>
    </body>
    </html>

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

  38. Krok 38: Skrypt 1: zapytanie 2 przez funkcję fetchForDish()

    Kryteria:

    R.5.3 Przynajmniej jeden skrypt wysyła zapytanie, zmodyfikowane w ten sposób że sprawdzana jest wartość ze zmiennej zdefiniowanej w R.5.2

    ok. 3 min
    Polecenie z arkusza

    Skrypt 1: Wysyła do bazy danych zapytanie 2, zmodyfikowane tak, że sprawdzana jest zmienna ID

    Wszystkie cztery skrypty wysyłają zapytanie o tę samą potrawę, więc wysyłanie zapisane jest raz - w funkcji fetchForDish($pdo, $sql, $dishId): prepare wysyła zapytanie z parametrem :id, execute(['id' => $dishId]) - wartość osobno, fetchAll() oddaje wszystkie wiersze.

    Skrypt 1: zapytanie 2 z aliasami, w którym zamiast 7 stoi :id (R.5.3). Wynik ma najwyżej jeden wiersz, więc [0] ?? null bierze pierwszy albo null, gdy potrawy nie ma.

    przepisy.php PHP zmienione: 11 linii
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    function fetchForDish(PDO $pdo, string $sql, int $dishId): array
    {
        $statement = $pdo->prepare($sql);
        $statement->execute(['id' => $dishId]);
        return $statement->fetchAll();
    }
    
    $dishes = [
        1 => 'Sernik',
        2 => 'Sałatka',
        3 => 'Pankejki',
        4 => 'Nugetsy',
        5 => 'Łosoś',
        6 => 'Kociołek',
        7 => 'Jagnięcina',
        8 => 'Hamburgery',
        9 => 'Eklerki',
        10 => 'Churros',
    ];
    
    $dishId = 7;
    if (isset($_GET['id'])) {
        $dishId = filter_var($_GET['id'], FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'default' => 7]]);
    }
    $category = null;
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=przepisy;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $category = fetchForDish($pdo,
            'SELECT p.nazwa, r.rodzaj FROM potrawy AS p
             JOIN rodzaje AS r ON r.idRodzaje = p.idRodzaje WHERE p.idPotrawy = :id', $dishId)[0] ?? null;
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać przepisu. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Blog kulinarny</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
            <nav aria-label="Potrawy">
    <?php foreach ($dishes as $id => $name): ?>
                <a href="przepisy.php?id=<?= $id ?>"><?= $name ?></a>
    <?php endforeach; ?>
            </nav>
            <p>Autor: Bartosz Bryniarski</p>
        </aside>
        <main>
    <?php if ($dbError !== ''): ?>
            <p><?= $dbError ?></p>
    <?php endif; ?>
            <h1></h1>
            <img src="separator.png" alt="przepis">
            <p>Alergeny: </p>
            <h2>Składniki</h2>
            <ul>
                <li>Lorem 1 kg</li>
                <li>Ipsum 2 szt.</li>
                <li>Dolor 200 g</li>
                <li>Sit amet (szczypta)</li>
            </ul>
            <p></p>
        </main>
        <section style="background-image: url('jagniecina.jpg');">
            <h1>Blog Kulinarny</h1>
        </section>
    </body>
    </html>

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

  39. Krok 39: Skrypt 1: rodzaj potrawy w nagłówku

    Kryteria:

    R.5.4 Na stronie wyświetlany jest rodzaj potrawy zwrócony z bazy danych

    ok. 1 min
    Polecenie z arkusza

    Wyświetla wartość zwróconą polem rodzaj

    W <h1> bloku głównego pole rodzaj przez escape(); ?? '' daje pusty napis, gdy potrawy nie ma. Dla jagnięciny: „dania mięsne” na półprzezroczystym pasku (R.5.4).

    przepisy.php PHP zmienione: 1 linia
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    function fetchForDish(PDO $pdo, string $sql, int $dishId): array
    {
        $statement = $pdo->prepare($sql);
        $statement->execute(['id' => $dishId]);
        return $statement->fetchAll();
    }
    
    $dishes = [
        1 => 'Sernik',
        2 => 'Sałatka',
        3 => 'Pankejki',
        4 => 'Nugetsy',
        5 => 'Łosoś',
        6 => 'Kociołek',
        7 => 'Jagnięcina',
        8 => 'Hamburgery',
        9 => 'Eklerki',
        10 => 'Churros',
    ];
    
    $dishId = 7;
    if (isset($_GET['id'])) {
        $dishId = filter_var($_GET['id'], FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'default' => 7]]);
    }
    $category = null;
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=przepisy;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $category = fetchForDish($pdo,
            'SELECT p.nazwa, r.rodzaj FROM potrawy AS p
             JOIN rodzaje AS r ON r.idRodzaje = p.idRodzaje WHERE p.idPotrawy = :id', $dishId)[0] ?? null;
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać przepisu. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Blog kulinarny</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
            <nav aria-label="Potrawy">
    <?php foreach ($dishes as $id => $name): ?>
                <a href="przepisy.php?id=<?= $id ?>"><?= $name ?></a>
    <?php endforeach; ?>
            </nav>
            <p>Autor: Bartosz Bryniarski</p>
        </aside>
        <main>
    <?php if ($dbError !== ''): ?>
            <p><?= $dbError ?></p>
    <?php endif; ?>
            <h1><?= escape($category['rodzaj'] ?? '') ?></h1>
            <img src="separator.png" alt="przepis">
            <p>Alergeny: </p>
            <h2>Składniki</h2>
            <ul>
                <li>Lorem 1 kg</li>
                <li>Ipsum 2 szt.</li>
                <li>Dolor 200 g</li>
                <li>Sit amet (szczypta)</li>
            </ul>
            <p></p>
        </main>
        <section style="background-image: url('jagniecina.jpg');">
            <h1>Blog Kulinarny</h1>
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (przepisy.php)
  40. Krok 40: Skrypt 2: zapytanie 1 i nazwa potrawy

    ok. 2 min
    Polecenie z arkusza

    Skrypt 2: Wysyła do bazy danych zapytanie 1, zmodyfikowane tak, że sprawdzana jest zmienna ID. Wyświetla: Nagłówek drugiego stopnia z nazwą potrawy

    Ta sama funkcja z zapytaniem 1 - jedna linia zamiast czterech. W HTML-u, między nagłówkiem a separatorem, warunek: gdy potrawa się znalazła, jej nazwa w <h2> (kapitaliki z CSS).

    przepisy.php PHP zmienione: 6 linii
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    function fetchForDish(PDO $pdo, string $sql, int $dishId): array
    {
        $statement = $pdo->prepare($sql);
        $statement->execute(['id' => $dishId]);
        return $statement->fetchAll();
    }
    
    $dishes = [
        1 => 'Sernik',
        2 => 'Sałatka',
        3 => 'Pankejki',
        4 => 'Nugetsy',
        5 => 'Łosoś',
        6 => 'Kociołek',
        7 => 'Jagnięcina',
        8 => 'Hamburgery',
        9 => 'Eklerki',
        10 => 'Churros',
    ];
    
    $dishId = 7;
    if (isset($_GET['id'])) {
        $dishId = filter_var($_GET['id'], FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'default' => 7]]);
    }
    $category = null;
    $dish = null;
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=przepisy;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $category = fetchForDish($pdo,
            'SELECT p.nazwa, r.rodzaj FROM potrawy AS p
             JOIN rodzaje AS r ON r.idRodzaje = p.idRodzaje WHERE p.idPotrawy = :id', $dishId)[0] ?? null;
        $dish = fetchForDish($pdo,
            'SELECT nazwa, trudnosc, kalorie FROM potrawy WHERE idPotrawy = :id', $dishId)[0] ?? null;
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać przepisu. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Blog kulinarny</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
            <nav aria-label="Potrawy">
    <?php foreach ($dishes as $id => $name): ?>
                <a href="przepisy.php?id=<?= $id ?>"><?= $name ?></a>
    <?php endforeach; ?>
            </nav>
            <p>Autor: Bartosz Bryniarski</p>
        </aside>
        <main>
    <?php if ($dbError !== ''): ?>
            <p><?= $dbError ?></p>
    <?php endif; ?>
            <h1><?= escape($category['rodzaj'] ?? '') ?></h1>
    <?php if ($dish !== null): ?>
            <h2><?= escape($dish['nazwa']) ?></h2>
    <?php endif; ?>
            <img src="separator.png" alt="przepis">
            <p>Alergeny: </p>
            <h2>Składniki</h2>
            <ul>
                <li>Lorem 1 kg</li>
                <li>Ipsum 2 szt.</li>
                <li>Dolor 200 g</li>
                <li>Sit amet (szczypta)</li>
            </ul>
            <p></p>
        </main>
        <section style="background-image: url('jagniecina.jpg');">
            <h1>Blog Kulinarny</h1>
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (przepisy.php)
  41. Krok 41: Skrypt 2: trudność z tablicy i kalorie

    Kryteria: ,

    R.5.5 Na stronie wyświetlana jest trudność na podstawie pola trudnosc z bazy danych: „łatwe” (trudnosc = 1) lub „średnie” (trudnosc = 2) lub „trudne” (trudnosc = 3)

    R.5.6 Na stronie wyświetlana jest nazwa potrawy i wartość kalorii, które zostały zwrócone zapytaniem

    ok. 3 min
    Polecenie z arkusza

    Paragraf o treści „Trudność: <trudnosc>, Kalorie: <kalorie>”, pola <> są ustalone na podstawie wartości z bazy danych, a <trudnosc> przyjmuje wartości: „łatwe” dla trudnosc=1, „średnie” dla trudnosc=2, „trudne” dla trudnosc=3

    Zamiana liczby na słowo to tłumaczenie według tabelki, więc na górze pliku stoi tablica $levels = [1 => 'łatwe', 2 => 'średnie', 3 => 'trudne'], a w akapicie $levels[$dish['trudnosc']] (R.5.5). Gdyby w bazie pojawiła się trudność 4, ?? 'nieznana' zamiast ostrzeżenia wypisze słowo. Kalorie z bazy bez zmian (R.5.6). Sprawdzone: sałatka - łatwe, sernik - średnie, kociołek - trudne.

    przepisy.php PHP zmienione: 2 linie
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    function fetchForDish(PDO $pdo, string $sql, int $dishId): array
    {
        $statement = $pdo->prepare($sql);
        $statement->execute(['id' => $dishId]);
        return $statement->fetchAll();
    }
    
    $dishes = [
        1 => 'Sernik',
        2 => 'Sałatka',
        3 => 'Pankejki',
        4 => 'Nugetsy',
        5 => 'Łosoś',
        6 => 'Kociołek',
        7 => 'Jagnięcina',
        8 => 'Hamburgery',
        9 => 'Eklerki',
        10 => 'Churros',
    ];
    
    $levels = [1 => 'łatwe', 2 => 'średnie', 3 => 'trudne'];
    $dishId = 7;
    if (isset($_GET['id'])) {
        $dishId = filter_var($_GET['id'], FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'default' => 7]]);
    }
    $category = null;
    $dish = null;
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=przepisy;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $category = fetchForDish($pdo,
            'SELECT p.nazwa, r.rodzaj FROM potrawy AS p
             JOIN rodzaje AS r ON r.idRodzaje = p.idRodzaje WHERE p.idPotrawy = :id', $dishId)[0] ?? null;
        $dish = fetchForDish($pdo,
            'SELECT nazwa, trudnosc, kalorie FROM potrawy WHERE idPotrawy = :id', $dishId)[0] ?? null;
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać przepisu. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Blog kulinarny</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
            <nav aria-label="Potrawy">
    <?php foreach ($dishes as $id => $name): ?>
                <a href="przepisy.php?id=<?= $id ?>"><?= $name ?></a>
    <?php endforeach; ?>
            </nav>
            <p>Autor: Bartosz Bryniarski</p>
        </aside>
        <main>
    <?php if ($dbError !== ''): ?>
            <p><?= $dbError ?></p>
    <?php endif; ?>
            <h1><?= escape($category['rodzaj'] ?? '') ?></h1>
    <?php if ($dish !== null): ?>
            <h2><?= escape($dish['nazwa']) ?></h2>
            <p>Trudność: <?= $levels[$dish['trudnosc']] ?? 'nieznana' ?>, Kalorie: <?= escape($dish['kalorie']) ?></p>
    <?php endif; ?>
            <img src="separator.png" alt="przepis">
            <p>Alergeny: </p>
            <h2>Składniki</h2>
            <ul>
                <li>Lorem 1 kg</li>
                <li>Ipsum 2 szt.</li>
                <li>Dolor 200 g</li>
                <li>Sit amet (szczypta)</li>
            </ul>
            <p></p>
        </main>
        <section style="background-image: url('jagniecina.jpg');">
            <h1>Blog Kulinarny</h1>
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (przepisy.php)
  42. Krok 42: Skrypt 3: alergeny oddzielone spacją

    Kryteria:

    R.5.7 Na stronie wyświetlane są alergeny zwrócone zapytaniem, zastosowano pętlę dla wszystkich zwróconych rekordów

    ok. 3 min
    Polecenie z arkusza

    Skrypt 3: Wysyła do bazy danych zapytanie 3, zmodyfikowane tak, że sprawdzana jest zmienna ID. Wyświetla oddzielone spacją nazwy wszystkich alergenów zwróconych z bazy

    Zapytanie 3 przez tę samą funkcję. Pętla foreach przechodzi po wszystkich zwróconych wierszach i zbiera same nazwy do tablicy $allergenNames (klucz wymaga pętli dla wszystkich rekordów - R.5.7). W akapicie implode(' ', $allergenNames) skleja je spacją - tylko między nazwami, bez spacji na końcu.

    Dla jagnięciny: „seler”; po kliknięciu „Kociołek” (?id=6): „gluten laktoza orzechy”.

    przepisy.php PHP zmienione: 9 linii
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    function fetchForDish(PDO $pdo, string $sql, int $dishId): array
    {
        $statement = $pdo->prepare($sql);
        $statement->execute(['id' => $dishId]);
        return $statement->fetchAll();
    }
    
    $dishes = [
        1 => 'Sernik',
        2 => 'Sałatka',
        3 => 'Pankejki',
        4 => 'Nugetsy',
        5 => 'Łosoś',
        6 => 'Kociołek',
        7 => 'Jagnięcina',
        8 => 'Hamburgery',
        9 => 'Eklerki',
        10 => 'Churros',
    ];
    
    $levels = [1 => 'łatwe', 2 => 'średnie', 3 => 'trudne'];
    $dishId = 7;
    if (isset($_GET['id'])) {
        $dishId = filter_var($_GET['id'], FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'default' => 7]]);
    }
    $category = null;
    $dish = null;
    $allergenNames = [];
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=przepisy;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $category = fetchForDish($pdo,
            'SELECT p.nazwa, r.rodzaj FROM potrawy AS p
             JOIN rodzaje AS r ON r.idRodzaje = p.idRodzaje WHERE p.idPotrawy = :id', $dishId)[0] ?? null;
        $dish = fetchForDish($pdo,
            'SELECT nazwa, trudnosc, kalorie FROM potrawy WHERE idPotrawy = :id', $dishId)[0] ?? null;
        $allergenRows = fetchForDish($pdo,
            'SELECT p.nazwa, a.alergen FROM potrawy AS p
             JOIN lista_alergenow AS la ON la.idPotrawy = p.idPotrawy
             JOIN alergeny AS a ON a.idAlergeny = la.idAlergeny WHERE p.idPotrawy = :id', $dishId);
        foreach ($allergenRows as $allergenRow) {
            $allergenNames[] = $allergenRow['alergen'];
        }
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać przepisu. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Blog kulinarny</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
            <nav aria-label="Potrawy">
    <?php foreach ($dishes as $id => $name): ?>
                <a href="przepisy.php?id=<?= $id ?>"><?= $name ?></a>
    <?php endforeach; ?>
            </nav>
            <p>Autor: Bartosz Bryniarski</p>
        </aside>
        <main>
    <?php if ($dbError !== ''): ?>
            <p><?= $dbError ?></p>
    <?php endif; ?>
            <h1><?= escape($category['rodzaj'] ?? '') ?></h1>
    <?php if ($dish !== null): ?>
            <h2><?= escape($dish['nazwa']) ?></h2>
            <p>Trudność: <?= $levels[$dish['trudnosc']] ?? 'nieznana' ?>, Kalorie: <?= escape($dish['kalorie']) ?></p>
    <?php endif; ?>
            <img src="separator.png" alt="przepis">
            <p>Alergeny: <?= escape(implode(' ', $allergenNames)) ?></p>
            <h2>Składniki</h2>
            <ul>
                <li>Lorem 1 kg</li>
                <li>Ipsum 2 szt.</li>
                <li>Dolor 200 g</li>
                <li>Sit amet (szczypta)</li>
            </ul>
            <p></p>
        </main>
        <section style="background-image: url('jagniecina.jpg');">
            <h1>Blog Kulinarny</h1>
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (przepisy.php)
  43. Krok 43: Skrypt 4: zapytanie 4 i przepis

    Kryteria:

    R.5.8 Na stronie wyświetlana jest zawartość pola przepis

    ok. 2 min
    Polecenie z arkusza

    Skrypt 4: Wysyła do bazy danych zapytanie 4, zmodyfikowane tak, że sprawdzana jest zmienna ID. Wyświetla wartość pola przepis zwróconą zapytaniem

    Czwarte zapytanie przez funkcję, ostatnie przed $pdo = null - zamknięcie połączenia nadal jest ostatnią operacją na bazie. Pole przepis w akapicie na końcu bloku głównego (R.5.8). Przepis jagnięciny jest długi - w węższym oknie, jak na ilustracji 2, w bloku głównym pojawia się pasek przewijania.

    przepisy.php PHP zmienione: 4 linie
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    function fetchForDish(PDO $pdo, string $sql, int $dishId): array
    {
        $statement = $pdo->prepare($sql);
        $statement->execute(['id' => $dishId]);
        return $statement->fetchAll();
    }
    
    $dishes = [
        1 => 'Sernik',
        2 => 'Sałatka',
        3 => 'Pankejki',
        4 => 'Nugetsy',
        5 => 'Łosoś',
        6 => 'Kociołek',
        7 => 'Jagnięcina',
        8 => 'Hamburgery',
        9 => 'Eklerki',
        10 => 'Churros',
    ];
    
    $levels = [1 => 'łatwe', 2 => 'średnie', 3 => 'trudne'];
    $dishId = 7;
    if (isset($_GET['id'])) {
        $dishId = filter_var($_GET['id'], FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'default' => 7]]);
    }
    $category = null;
    $dish = null;
    $allergenNames = [];
    $recipe = null;
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=przepisy;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $category = fetchForDish($pdo,
            'SELECT p.nazwa, r.rodzaj FROM potrawy AS p
             JOIN rodzaje AS r ON r.idRodzaje = p.idRodzaje WHERE p.idPotrawy = :id', $dishId)[0] ?? null;
        $dish = fetchForDish($pdo,
            'SELECT nazwa, trudnosc, kalorie FROM potrawy WHERE idPotrawy = :id', $dishId)[0] ?? null;
        $allergenRows = fetchForDish($pdo,
            'SELECT p.nazwa, a.alergen FROM potrawy AS p
             JOIN lista_alergenow AS la ON la.idPotrawy = p.idPotrawy
             JOIN alergeny AS a ON a.idAlergeny = la.idAlergeny WHERE p.idPotrawy = :id', $dishId);
        foreach ($allergenRows as $allergenRow) {
            $allergenNames[] = $allergenRow['alergen'];
        }
        $recipe = fetchForDish($pdo,
            'SELECT przepis, plik FROM potrawy WHERE idPotrawy = :id', $dishId)[0] ?? null;
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać przepisu. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Blog kulinarny</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
            <nav aria-label="Potrawy">
    <?php foreach ($dishes as $id => $name): ?>
                <a href="przepisy.php?id=<?= $id ?>"><?= $name ?></a>
    <?php endforeach; ?>
            </nav>
            <p>Autor: Bartosz Bryniarski</p>
        </aside>
        <main>
    <?php if ($dbError !== ''): ?>
            <p><?= $dbError ?></p>
    <?php endif; ?>
            <h1><?= escape($category['rodzaj'] ?? '') ?></h1>
    <?php if ($dish !== null): ?>
            <h2><?= escape($dish['nazwa']) ?></h2>
            <p>Trudność: <?= $levels[$dish['trudnosc']] ?? 'nieznana' ?>, Kalorie: <?= escape($dish['kalorie']) ?></p>
    <?php endif; ?>
            <img src="separator.png" alt="przepis">
            <p>Alergeny: <?= escape(implode(' ', $allergenNames)) ?></p>
            <h2>Składniki</h2>
            <ul>
                <li>Lorem 1 kg</li>
                <li>Ipsum 2 szt.</li>
                <li>Dolor 200 g</li>
                <li>Sit amet (szczypta)</li>
            </ul>
            <p><?= escape($recipe['przepis'] ?? '') ?></p>
        </main>
        <section style="background-image: url('jagniecina.jpg');">
            <h1>Blog Kulinarny</h1>
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (przepisy.php)
  44. Krok 44: Tło sekcji z nazwy pliku z bazy

    Kryteria: , ,

    R.5.2 Zmienna identyfikatora potrawy jest inicjowana wartością 7, następnie wartością przesłaną metodą GET do strony. Wygenerowana zawartość strony (w tym tło sekcji) zawsze dotyczy jednej potrawy - zgodnej z klikniętym odnośnikiem, w stanie początkowym zawartość dotyczy jagnięciny (sprawdzić dla co najmniej trzech odnośników)

    R.5.9 Tło sekcji jest wyświetlane z pliku o nazwie zwróconej zapytaniem w skrypcie 4

    R.5.10 W skryptach zastosowana przynajmniej jedna zmienna oraz nazwy wszystkich zmiennych są w języku polskim lub angielskim i odzwierciedlają cel zastosowania

    ok. 3 min
    Polecenie z arkusza

    Pole plik pobrane zapytaniem w tym skrypcie powinno zostać użyte do ustawienia tła sekcji stylem CSS inline, o czym jest mowa w części „Cechy witryny”, „Zawartość bloku sekcji”

    W stylu inline zamiast stałej nazwy stoi pole plik z zapytania 4 (R.5.9). Nazwa pliku trafia do adresu w CSS, więc przechodzi przez dwie funkcje: rawurlencode zamienia spacje i znaki specjalne na zapis adresu (moje zdjęcie.jpg → moje%20zdj%C4%99cie.jpg), a escape() pilnuje cudzysłowów atrybutu. Gdy potrawy nie ma, sekcja nie dostaje stylu.

    Teraz cała strona zależy od jednej zmiennej $dishId: przy pierwszym wejściu jagnięcina, po kliknięciu - potrawa z odnośnika, razem ze zdjęciem w tle (sprawdzone dla sernika, łososia i churros; R.5.2). Nazwy zmiennych i funkcji mówią, co przechowują i robią (R.5.10): $dishes, $levels, $dishId, $category, $dish, $allergenRows, $allergenNames, $recipe, $pdo, $dbError, escape(), fetchForDish().

    przepisy.php PHP zmienione: 5 linii
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    function fetchForDish(PDO $pdo, string $sql, int $dishId): array
    {
        $statement = $pdo->prepare($sql);
        $statement->execute(['id' => $dishId]);
        return $statement->fetchAll();
    }
    
    $dishes = [
        1 => 'Sernik',
        2 => 'Sałatka',
        3 => 'Pankejki',
        4 => 'Nugetsy',
        5 => 'Łosoś',
        6 => 'Kociołek',
        7 => 'Jagnięcina',
        8 => 'Hamburgery',
        9 => 'Eklerki',
        10 => 'Churros',
    ];
    
    $levels = [1 => 'łatwe', 2 => 'średnie', 3 => 'trudne'];
    $dishId = 7;
    if (isset($_GET['id'])) {
        $dishId = filter_var($_GET['id'], FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'default' => 7]]);
    }
    $category = null;
    $dish = null;
    $allergenNames = [];
    $recipe = null;
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=przepisy;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $category = fetchForDish($pdo,
            'SELECT p.nazwa, r.rodzaj FROM potrawy AS p
             JOIN rodzaje AS r ON r.idRodzaje = p.idRodzaje WHERE p.idPotrawy = :id', $dishId)[0] ?? null;
        $dish = fetchForDish($pdo,
            'SELECT nazwa, trudnosc, kalorie FROM potrawy WHERE idPotrawy = :id', $dishId)[0] ?? null;
        $allergenRows = fetchForDish($pdo,
            'SELECT p.nazwa, a.alergen FROM potrawy AS p
             JOIN lista_alergenow AS la ON la.idPotrawy = p.idPotrawy
             JOIN alergeny AS a ON a.idAlergeny = la.idAlergeny WHERE p.idPotrawy = :id', $dishId);
        foreach ($allergenRows as $allergenRow) {
            $allergenNames[] = $allergenRow['alergen'];
        }
        $recipe = fetchForDish($pdo,
            'SELECT przepis, plik FROM potrawy WHERE idPotrawy = :id', $dishId)[0] ?? null;
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać przepisu. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Blog kulinarny</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
            <nav aria-label="Potrawy">
    <?php foreach ($dishes as $id => $name): ?>
                <a href="przepisy.php?id=<?= $id ?>"><?= $name ?></a>
    <?php endforeach; ?>
            </nav>
            <p>Autor: Bartosz Bryniarski</p>
        </aside>
        <main>
    <?php if ($dbError !== ''): ?>
            <p><?= $dbError ?></p>
    <?php endif; ?>
            <h1><?= escape($category['rodzaj'] ?? '') ?></h1>
    <?php if ($dish !== null): ?>
            <h2><?= escape($dish['nazwa']) ?></h2>
            <p>Trudność: <?= $levels[$dish['trudnosc']] ?? 'nieznana' ?>, Kalorie: <?= escape($dish['kalorie']) ?></p>
    <?php endif; ?>
            <img src="separator.png" alt="przepis">
            <p>Alergeny: <?= escape(implode(' ', $allergenNames)) ?></p>
            <h2>Składniki</h2>
            <ul>
                <li>Lorem 1 kg</li>
                <li>Ipsum 2 szt.</li>
                <li>Dolor 200 g</li>
                <li>Sit amet (szczypta)</li>
            </ul>
            <p><?= escape($recipe['przepis'] ?? '') ?></p>
        </main>
    <?php if ($recipe !== null): ?>
        <section style="background-image: url('<?= escape(rawurlencode($recipe['plik'])) ?>');">
    <?php else: ?>
        <section>
    <?php endif; ?>
            <h1>Blog Kulinarny</h1>
        </section>
    </body>
    </html>

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

  45. Krok 45: Potrawa spoza bazy

    ok. 2 min
    Polecenie z arkusza

    Jeśli do strony została przesłana wartość metodą GET, zmienna jest inicjowana tą wartością. W przeciwnym wypadku, zmienna jest inicjowana wartością 7

    Sprawdzone adresy: ?id=99 (liczba poprawna, ale takiej potrawy nie ma) - wszystkie zapytania zwracają zero wierszy, strona pokazuje „Nie ma potrawy o numerze 99.” zamiast pustych nagłówków i ostrzeżeń PHP, a sekcja nie ma tła. ?id=7 OR 1=1 - filter_var odrzuca tekst i zostaje jagnięcina; wersja z egzaminu pokazałaby w tym miejscu alergeny wszystkich potraw. Podgląd: przepisy.php?id=99. Klucz tego nie sprawdza.

    przepisy.php PHP zmienione: 2 linie
    <?php
    function escape(?string $text): string
    {
        return htmlspecialchars($text ?? '', ENT_QUOTES);
    }
    
    function fetchForDish(PDO $pdo, string $sql, int $dishId): array
    {
        $statement = $pdo->prepare($sql);
        $statement->execute(['id' => $dishId]);
        return $statement->fetchAll();
    }
    
    $dishes = [
        1 => 'Sernik',
        2 => 'Sałatka',
        3 => 'Pankejki',
        4 => 'Nugetsy',
        5 => 'Łosoś',
        6 => 'Kociołek',
        7 => 'Jagnięcina',
        8 => 'Hamburgery',
        9 => 'Eklerki',
        10 => 'Churros',
    ];
    
    $levels = [1 => 'łatwe', 2 => 'średnie', 3 => 'trudne'];
    $dishId = 7;
    if (isset($_GET['id'])) {
        $dishId = filter_var($_GET['id'], FILTER_VALIDATE_INT, ['options' => ['min_range' => 1, 'default' => 7]]);
    }
    $category = null;
    $dish = null;
    $allergenNames = [];
    $recipe = null;
    $dbError = '';
    
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=przepisy;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $category = fetchForDish($pdo,
            'SELECT p.nazwa, r.rodzaj FROM potrawy AS p
             JOIN rodzaje AS r ON r.idRodzaje = p.idRodzaje WHERE p.idPotrawy = :id', $dishId)[0] ?? null;
        $dish = fetchForDish($pdo,
            'SELECT nazwa, trudnosc, kalorie FROM potrawy WHERE idPotrawy = :id', $dishId)[0] ?? null;
        $allergenRows = fetchForDish($pdo,
            'SELECT p.nazwa, a.alergen FROM potrawy AS p
             JOIN lista_alergenow AS la ON la.idPotrawy = p.idPotrawy
             JOIN alergeny AS a ON a.idAlergeny = la.idAlergeny WHERE p.idPotrawy = :id', $dishId);
        foreach ($allergenRows as $allergenRow) {
            $allergenNames[] = $allergenRow['alergen'];
        }
        $recipe = fetchForDish($pdo,
            'SELECT przepis, plik FROM potrawy WHERE idPotrawy = :id', $dishId)[0] ?? null;
        $pdo = null;
    } catch (PDOException $exception) {
        error_log($exception->getMessage());
        $dbError = 'Nie udało się pobrać przepisu. Spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Blog kulinarny</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <aside>
            <nav aria-label="Potrawy">
    <?php foreach ($dishes as $id => $name): ?>
                <a href="przepisy.php?id=<?= $id ?>"><?= $name ?></a>
    <?php endforeach; ?>
            </nav>
            <p>Autor: Bartosz Bryniarski</p>
        </aside>
        <main>
    <?php if ($dbError !== ''): ?>
            <p><?= $dbError ?></p>
    <?php elseif ($dish === null): ?>
            <p>Nie ma potrawy o numerze <?= $dishId ?>.</p>
    <?php endif; ?>
            <h1><?= escape($category['rodzaj'] ?? '') ?></h1>
    <?php if ($dish !== null): ?>
            <h2><?= escape($dish['nazwa']) ?></h2>
            <p>Trudność: <?= $levels[$dish['trudnosc']] ?? 'nieznana' ?>, Kalorie: <?= escape($dish['kalorie']) ?></p>
    <?php endif; ?>
            <img src="separator.png" alt="przepis">
            <p>Alergeny: <?= escape(implode(' ', $allergenNames)) ?></p>
            <h2>Składniki</h2>
            <ul>
                <li>Lorem 1 kg</li>
                <li>Ipsum 2 szt.</li>
                <li>Dolor 200 g</li>
                <li>Sit amet (szczypta)</li>
            </ul>
            <p><?= escape($recipe['przepis'] ?? '') ?></p>
        </main>
    <?php if ($recipe !== null): ?>
        <section style="background-image: url('<?= escape(rawurlencode($recipe['plik'])) ?>');">
    <?php else: ?>
        <section>
    <?php endif; ?>
            <h1>Blog Kulinarny</h1>
        </section>
    </body>
    </html>
    Podgląd strony po tym kroku (przepisy.php)
  46. Etap 5: Porównanie z wersją podstawową

    Kroki 46-47 · po tym etapie masz 38 z 38 kryteriów (pkt)

    Krok 46: Co jest inaczej i dlaczego

    ok. 3 min
    Polecenie z arkusza

    Ocenie podlegać będzie 5 rezultatów: operacje na bazie danych, zawartość witryny internetowej, działanie witryny internetowej, styl CSS witryny internetowej, skrypt połączenia z bazą.

    Obie wersje dają te same punkty. Różnice:

    wersja podstawowata wersja
    układdisplay: flex, width każdego blokusiatka 10% 45% 45%
    odnośniki10 linii HTML z <br>tablica PHP, <nav>, display: block
    identyfikatorisset i surowa wartość z adresufilter_var z default 7
    zapytania w PHPcztery bloki mysqli_query / mysqli_fetch_rowfunkcja fetchForDish(), parametr :id
    trudnośćdrabinka if / elseif / elsetablica $levels
    alergenyecho w pętli, spacja także po ostatnimpętla zbiera nazwy, implode(' ', …)
    tło sekcji<?php echo $file; ?>rawurlencode + escape()
    potrawa spoza bazyostrzeżenia PHP, puste nagłówkikomunikat

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

  47. Krok 47: Którą wersję wybrać na egzaminie

    ok. 2 min
    Polecenie z arkusza

    Czas przeznaczony na wykonanie zadania wynosi 150 minut.

    • Wersja podstawowa, gdy liczy się czas albo PDO nie jest opanowane: cztery krótkie skrypty z funkcjami z tabeli 1 arkusza stoją tam, gdzie widać ich efekt.
    • Ta wersja, gdy funkcje i PDO są pisane z pamięci. Funkcja fetchForDish() opłaca się tu wyjątkowo: cztery zapytania różnią się tylko treścią SQL, więc błąd w przygotowaniu zapytania poprawia się w jednym miejscu.

    Mieszanie też daje pełne punkty: siatka i tablica $levels z tej wersji, a mysqli_* z podstawowej.

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

  48. Etap 6: Oddanie pracy

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

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

    ok. 5 min
    Polecenie z arkusza

    UWAGA: 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/…/przepisy.php.

    W folderze z numerem zdającego: zdjęcia potraw z archiwum, import.png, kw1.jpg - kw4.jpg, kwerendy.txt, przeglądarka.txt, przepisy.php, separator.png, styl.css. Arkusz wymienia kociołek.jpg z „ł”, ale w archiwum i w bazie jest kociolek.jpg - zostaje nazwa z archiwum, bo tej szuka strona. Po nagraniu sprawdź odczyt płyty.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

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

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.