Przejdź do głównej treści
INF.03-12-25.06-SGCzerwiec 2025 · 44 kroki · ok. 77 min · 37 kryteriów z klucza

Rozwiązanie krok po kroku: Przychodnia Medica (Wersja podstawowa)

Rozwiązanie buduje stronę abonamentów przychodni: baza medica z relacją wiele do wielu (abonamenty i cechy połączone tabelą szczegolyabonamentu) i czterema zapytaniami (SELECT bez warunku, ROUND(AVG()) z aliasem, złączenie trzech tabel z warunkiem, LIKE z OR), ikona przeskalowana do 32 × 32 px jako favicon, strona medica.php z blokiem artykułu na tle obrazu i trzema sekcjami, style w styl.css z cieniem, zaokrągleniem, odstępem liter i punktorem z obrazu, oraz dwa skrypty PHP: pakiety z cenami i opisami oraz lista cech, powielona trzy razy dla trzech abonamentów.

Kroki idą zdanie po zdaniu za treścią arkusza - każdy zaczyna się poleceniem z arkusza, a kod pokazuje dokładnie te linie, które je spełniają. Kod PHP jest najpierw pisany funkcjami z tabeli 1 arkusza (proceduralne mysqli), łącznie z trzema kopiami skryptu 2, bo tak każe arkusz - a na końcu całość zostaje przerobiona na wersję z prawdziwych projektów: jedna funkcja z zapytaniem przygotowanym zamiast trzech kopii, escapowanie danych i wiersze czytane po nazwach kolumn. W tym arkuszu nie ma formularza ani JavaScriptu.

To jest wersja podstawowa. W zakładce „Nowoczesny kod (grid, PDO)” jest drugi sposób rozwiązania tego samego arkusza: sekcje na siatce CSS, PHP na PDO i jedno zapytanie przygotowane zamiast trzech kopii skryptu 2 od pierwszej linii - obie wersje są zgodne z kluczem.

Baza i grafiki pochodzą z prawdziwego archiwum pliki3 (załącznik do pobrania na stronie arkusza, hasło w treści zadania). Zrzuty wyników zapytań są odtworzone z tej bazy - na egzaminie robisz je sam, z całego ekranu.

Jak liczą się punkty. Klucz ma 37 kryteriów (R.1.1 - R.5.10), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 28 punktów - można stracić 9 kryteriów i nadal zdać. 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 37 kryteriów (pkt)

    Krok 1: Baza danych medica

    Kryteria:

    R.1.1 Wykonany import tabel do bazy danych medica, czynność udokumentowana w pliku import.png

    ok. 2 min
    Polecenie z arkusza

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

    W przeglądarce otwierasz http://localhost/phpmyadmin, klikasz Nowa w lewym panelu, wpisujesz nazwę medica i z listy metod porównywania wybierasz utf8_unicode_ci. To ogólne porównywanie Unicode, nie polskie (polskie to utf8_polish_ci). Na wyniki zapytań i tak nie wpływa: tabele z baza.sql mają zestaw utf8mb4 zapisany we własnej definicji (porównywanie utf8mb4_general_ci), więc wzorzec %opieką% w zapytaniu 4 znajduje opis abonamentu „Dziecko” przy każdym wyborze (sprawdzone).

    Typowe błędy

    Nazwa bazy musi być dokładnie medica - skrypty PHP łączą się z bazą po nazwie, więc literówka odetnie punkty także za skrypty (R.5.1).

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

    Kryteria:

    R.1.1 Wykonany import tabel do bazy danych medica, czynność udokumentowana w pliku import.png

    ok. 2 min
    Polecenie z arkusza

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

    Wejdź do bazy medica (musi być zaznaczona w lewym panelu), karta Import, Wybierz plik → baza.sql z rozpakowanego archiwum, Wykonaj.

    Po imporcie są trzy tabele z ilustracji 1: abonamenty (id, nazwa, cena, opis - 3 pakiety: Standard, Premium, Dziecko), cechy (id, cecha - 12 cech, np. „Lekarz POZ”) i szczegolyabonamentu (id, Abonamenty_id, Cechy_id - 22 przypisania). To klasyczna relacja n:m: jeden abonament ma wiele cech, a jedna cecha (np. Kardiolog) należy do wielu abonamentów - dlatego potrzebna jest tabela pośrednia z dwoma kluczami obcymi. Uwaga na wielkość liter: kolumny Abonamenty_id i Cechy_id zaczynają się wielką literą, a nazwa tabeli po imporcie jest małymi.

    Typowe błędy

    Import bez wcześniejszego wejścia do bazy medica tworzy tabele w innej bazie (np. test) - skrypty nie znajdą wtedy tabeli abonamenty.

  3. Krok 3: Zrzut ekranu import.png

    Kryteria:

    R.1.1 Wykonany import tabel do bazy danych medica, czynność udokumentowana w pliku import.png

    ok. 2 min
    Polecenie z arkusza

    Wykonaj zrzut ekranu po imporcie. Zrzut zapisz 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.

    Klawisz Print Screen (cały ekran, nie Alt+PrtScr, który łapie tylko aktywne okno), wklejasz do edytora grafiki i zapisujesz jako import.png w folderze z numerem zdającego. Uwaga na formaty w tym arkuszu: zrzut importu w PNG, zrzuty kwerend w JPEG. Na zrzucie ma być widać komunikat phpMyAdmin o pomyślnym imporcie albo trzy tabele w bazie.

    Typowe błędy

    Wykadrowany zrzut (samo okno przeglądarki, bez paska zadań) nie jest oceniany - klucz mówi to wprost. Zły format pliku to też strata punktu.

  4. Krok 4: Zapytanie 1: nazwa, cena i opis abonamentów

    Kryteria: ,

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

    R.1.3 Wykonane zapytanie 1 wybierające jedynie nazwę, cenę i opis wszystkich abonamentów. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa, cena, opis FROM abonamenty; oraz wynik działania udokumentowany zrzutem, na którym są widoczne dokładnie 3 rekordy, jedynie pola nazwa, cena i opis, pola nazwy mają wartość: Standard, Premium, Dziecko

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 1: wybierające jedynie nazwę, cenę i opis wszystkich abonamentów.

    Zakładka SQL w phpMyAdmin (przy zaznaczonej bazie medica). SELECT nazwa, cena, opis FROM abonamenty - trzy kolumny w kolejności z arkusza (bez id, bo „jedynie”), bez warunku („wszystkich”) i bez sortowania. Wynik: 3 wiersze: Standard, Premium, Dziecko - dokładnie jak w kluczu. Zapytanie kopiujesz do pliku kwerendy.txt i robisz zrzut: kw1.jpg. To samo zapytanie wyśle skrypt 1 do bloku artykułu.

    Typowe błędy

    SELECT * zwróci też id - klucz R.1.3 sprawdza „jedynie pola nazwa, cena i opis”.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT nazwa, cena, opis FROM abonamenty;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: średnia cena z zaokrągleniem i aliasem

    Kryteria:

    R.1.4 Wykonane zapytanie 2 liczące średnią cenę abonamentów. Cena jest zaokrąglona do dwóch miejsc po przecinku oraz nazwano ją „Srednio_abonament”. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT ROUND(AVG(cena), 2) AS Srednio_abonament FROM abonamenty; (AS opcjonalne) oraz wynik działania udokumentowany zrzutem, na którym jest widoczna kolumna „Srednio_abonament” z wartością 266,67

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 2: liczące średnią cenę abonamentów. Cena jest zaokrąglona do dwóch miejsc po przecinku oraz nadano nazwę kolumny (alias) na „Srednio_abonament”.

    Funkcja w funkcji: AVG(cena) liczy średnią z trzech cen (200, 500, 100), a ROUND(..., 2) zaokrągla wynik do dwóch miejsc po przecinku - drugi argument ROUND to liczba miejsc. AS Srednio_abonament nadaje kolumnie alias z arkusza; nazwa bez polskich znaków i spacji nie wymaga cudzysłowu. Bez GROUP BY, bo średnia jest z całej tabeli - wynik to jeden wiersz: 266.67 (klucz podaje tę wartość). Zrzut: kw2.jpg.

    Typowe błędy

    AVG(ROUND(cena, 2)) zaokrągla każdą cenę osobno (bez efektu, to liczby całkowite), a średnia zostaje z długim ułamkiem 266.6667. Zaokrąglij wynik średniej.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT nazwa, cena, opis FROM abonamenty;
    
    -- Zapytanie 2
    SELECT ROUND(AVG(cena), 2) AS Srednio_abonament FROM abonamenty;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: cechy abonamentu o id 1 przez relację n:m

    Kryteria:

    R.1.5 Wykonane zapytanie 3 wybierające jedynie nazwę abonamentu i odpowiadającą mu cechę dla abonamentu o id równym 1. Należy zastosować pełną relację n..n. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa, cecha FROM abonamenty JOIN szczegolyabonamentu ON abonamenty.id = Abonamenty_id JOIN cechy ON cechy.id = Cechy_id WHERE abonamenty.id = 1; (możliwe też INNER JOIN lub porównanie kluczy po WHERE, kolejność tabel dowolna) oraz wynik działania udokumentowany zrzutem, na którym widocznych jest dokładnie 6 rekordów, jedynie pola nazwa i cecha, nazwa to Standard, cechy to: Lekarz POZ, Endokrynolog, Kardiolog, Ginekolog, Urolog, Podstawowe badania laboratoryjne

    ok. 4 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie nazwę abonamentu i odpowiadającą mu cechę dla abonamentu o id równym 1. Należy zastosować pełną relację n:m.

    Nazwa jest w abonamenty, cecha w cechy, a to, która cecha należy do którego abonamentu, wie tylko tabela pośrednia - „pełna relacja n:m” to złączenie trzech tabel:

    • SELECT nazwa, cecha - jedynie te dwie kolumny.
    • FROM abonamenty JOIN szczegolyabonamentu ON abonamenty.id = Abonamenty_id - do abonamentu dokładasz jego wiersze z tabeli pośredniej; id występuje w kilku tabelach, więc poprzedzasz je nazwą tabeli, a Abonamenty_id jest tylko w jednej, więc nie trzeba.
    • JOIN cechy ON cechy.id = Cechy_id - do każdego wiersza pośredniego dokładasz nazwę cechy.
    • WHERE abonamenty.id = 1 - tylko pierwszy abonament. Warunek na końcu, po wszystkich złączeniach.

    Wynik: dokładnie 6 wierszy, nazwa Standard i cechy od Lekarza POZ do Podstawowych badań laboratoryjnych - jak w kluczu. Zrzut: kw3.jpg. To zapytanie wyśle skrypt 2, a jego kopie podstawią 2 i 3 w miejsce 1.

    Typowe błędy

    WHERE id = 1 bez nazwy tabeli to błąd „column id is ambiguous” - trzy tabele mają kolumnę id. Pominięcie tabeli pośredniej nie ma po czym złączyć abonamentów z cechami.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT nazwa, cena, opis FROM abonamenty;
    
    -- Zapytanie 2
    SELECT ROUND(AVG(cena), 2) AS Srednio_abonament FROM abonamenty;
    
    -- Zapytanie 3
    SELECT nazwa, cecha FROM abonamenty JOIN szczegolyabonamentu ON abonamenty.id = Abonamenty_id JOIN cechy ON cechy.id = Cechy_id WHERE abonamenty.id = 1;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: opisy ze słowem „zdrowie” albo „opieką”

    Kryteria:

    R.1.6 Wykonane zapytanie 4 wybierające jedynie nazwę i opis abonamentów, w których w opisie w dowolnym miejscu znajdują się słowa „zdrowie” albo „opieką”. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa, opis FROM abonamenty WHERE opis LIKE "%zdrowie%" OR opis LIKE "%opieką%"; oraz wynik działania udokumentowany zrzutem, na którym są widoczne dokładnie 2 rekordy, jedynie pola nazwa i opis, pola nazwy mają wartości Standard, Dziecko

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 4: wybierające jedynie nazwę i opis abonamentów, w których w opisie w dowolnym miejscu znajdują się słowa „zdrowie” albo „opieką”.

    LIKE porównuje ze wzorcem: % zastępuje dowolny ciąg znaków, więc "%zdrowie%" (procent z obu stron) to „zdrowie gdziekolwiek w tekście”. Dwa słowa to dwa warunki, a „albo” to OR - wystarczy, że pasuje jeden. Uwaga: słowo „opieką” z „ą” - wpisujesz je dokładnie tak, jak w arkuszu. Wynik: 2 abonamenty, Standard i Dziecko - jak w kluczu. Zrzut: kw4.jpg. Plik kwerendy.txt ma komplet czterech zapytań.

    Typowe błędy

    AND zamiast OR wymaga obu słów w jednym opisie - wynik byłby pusty. LIKE "zdrowie" bez % dopasuje tylko opis składający się z samego tego słowa.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT nazwa, cena, opis FROM abonamenty;
    
    -- Zapytanie 2
    SELECT ROUND(AVG(cena), 2) AS Srednio_abonament FROM abonamenty;
    
    -- Zapytanie 3
    SELECT nazwa, cecha FROM abonamenty JOIN szczegolyabonamentu ON abonamenty.id = Abonamenty_id JOIN cechy ON cechy.id = Cechy_id WHERE abonamenty.id = 1;
    
    -- Zapytanie 4
    SELECT nazwa, opis FROM abonamenty WHERE opis LIKE "%zdrowie%" OR opis LIKE "%opieką%";
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Grafika i strona HTML

    Kroki 8-22 · po tym etapie masz 16 z 37 kryteriów (pkt)

    Krok 8: Grafika obraz2.png: dokładnie 32 × 32 px

    Kryteria:

    R.2.1 Plik obraz2.png przeskalowany dokładnie do szerokości 32 px i wysokości 32 px

    ok. 2 min
    Polecenie z arkusza

    Plik obraz2.png z czerwonym krzyżem, wypakowany z archiwum, należy przeskalować do dokładnych rozmiarów 32 px na 32 px.

    Ikona z archiwum ma 100 × 100 px. W GIMP-ie: Obraz → Skaluj obraz, szerokość 32, wysokość 32 - obraz jest kwadratowy, więc łańcuch proporcji może zostać zamknięty. Eksportuj jako obraz2.png (nadpisujesz plik; format PNG zachowa przezroczyste tło krzyża). 32 × 32 to standardowy rozmiar favicony - ta sama grafika pojawi się na karcie przeglądarki i w stopce.

    Typowe błędy

    Klucz R.2.1 sprawdza „dokładnie” 32 × 32 - 33 × 32 po niedokładnym wpisaniu to strata punktu. Przeskaluj obraz, nie płótno.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 8
  9. Krok 9: Podstrona opis.html

    Kryteria:

    R.3.3 Odnośnik o treści „Dowiedz się więcej” otwiera podstronę opis.html

    ok. 1 min
    Polecenie z arkusza

    Należy utworzyć plik opis.html i zapisać w nim „Strona w trakcie budowy”.

    Najprostszy plik z samym napisem „Strona w trakcie budowy” - arkusz wymaga tylko tego. Istnieje po to, żeby odnośnik „Dowiedz się więcej” z bloku artykułu miał dokąd prowadzić (klucz R.3.3 sprawdza, czy się otwiera).

    opis.html HTML nowy plik
    Strona w trakcie budowy
    Podgląd strony po tym kroku (opis.html)
  10. Krok 10: Plik medica.php w HTML5

    Kryteria:

    R.2.2 W pliku medica.php zapisana deklaracja HTML5 <!DOCTYPE HTML>, deklaracja języka zawartości: polski np. <html lang="pl">, w sekcji head znajduje się jawnie podany standard kodowania polskich znaków

    ok. 2 min
    Polecenie z arkusza

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

    W edytorze zaznaczającym składnię tworzysz plik medica.php w folderze zdającego, obok grafik i podstrony. Rozszerzenie .php, bo dojdą skrypty PHP - na razie to czysty HTML.

    Pierwsza linia <!DOCTYPE html> mówi przeglądarce, że dokument jest w HTML5. Dalej szkielet każdej strony: <html>, a w nim <head> (informacje o stronie) i <body> (to, co widać w oknie).

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

    Kryteria:

    R.2.2 W pliku medica.php zapisana deklaracja HTML5 <!DOCTYPE HTML>, deklaracja języka zawartości: polski np. <html lang="pl">, w sekcji head znajduje się jawnie podany standard kodowania polskich znaków

    ok. 1 min
    Polecenie z arkusza

    Zadeklarowany polski język zawartości witryny.

    Atrybut lang="pl" w znaczniku <html>. Czytnik ekranu czyta wtedy tekst polską wymową, a przeglądarka dobiera słownik. Na stronie nic się nie zmienia - to informacja dla programów.

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

    Kryteria:

    R.2.2 W pliku medica.php zapisana deklaracja HTML5 <!DOCTYPE HTML>, deklaracja języka zawartości: polski np. <html lang="pl">, w sekcji head znajduje się jawnie podany standard kodowania polskich znaków

    ok. 1 min
    Polecenie z arkusza

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

    <meta charset="UTF-8"> jako pierwsza rzecz w <head>. „Jawnie” znaczy: wpisane w kodzie, nie zdane na domysł przeglądarki. Bez tej linii długie opisy abonamentów z bazy („otoczone szczególną opieką”) mogłyby wyświetlić się jako krzaki. Plik trzeba też zapisać w UTF-8.

    medica.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 (medica.php)
  13. Krok 13: Tytuł strony

    Kryteria:

    R.2.3 Nadany tytuł strony: "Przychodnia Medica"

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony widoczny na karcie przeglądarki: „Przychodnia Medica”.

    <title> w <head> - napis na karcie przeglądarki, nie nagłówek na stronie. Klucz R.2.3 sprawdza tekst „Przychodnia Medica”.

    medica.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przychodnia Medica</title>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (medica.php)
  14. Krok 14: Favicon na karcie przeglądarki

    Kryteria:

    R.2.8 Na karcie przeglądarki jest widoczny favicon w postaci obrazu o nazwie obraz2.png

    ok. 1 min
    Polecenie z arkusza

    Obraz2.png jako favicon widoczny na karcie przeglądarki.

    Favicon to też <link> w <head>, ale z rel="icon" - przeglądarka pokazuje wskazany obraz obok tytułu na karcie. Dlatego arkusz kazał zmniejszyć krzyż do 32 × 32 px. Klucz R.2.8 sprawdza, czy ikona jest widoczna na karcie; w podglądzie karty nie ma, więc efekt zobaczysz dopiero w prawdziwej przeglądarce.

    Typowe błędy

    rel="stylesheet" z obrazem albo <img> w <head> nie zrobią favicony - potrzebny jest dokładnie <link rel="icon"> (dawniej rel="shortcut icon", też działa).

    medica.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przychodnia Medica</title>
        <link rel="icon" href="obraz2.png">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (medica.php)
  15. Krok 15: 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

    ok. 1 min
    Polecenie z arkusza

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

    Drugi <link>, tym razem rel="stylesheet" z plikiem styl.css - sprawdź nazwę w treści zadania, bo w innych arkuszach bywa style.css albo styles.css. Plik napiszesz po HTML.

    Typowe błędy

    Zła nazwa pliku w href to brak stylów i cały rezultat R.4 przepada, bo „formatowanie CSS pochodzi jedynie z tego arkusza”.

    medica.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przychodnia Medica</title>
        <link rel="icon" href="obraz2.png">
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (medica.php)
  16. Krok 16: Bloki semantyczne

    Kryteria:

    R.2.4 W sekcji body zdefiniowany jest podział na bloki za pomocą semantycznych znaczników HTML 5: header, article, main -> section + section + section, footer

    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ą 3. Zawartość bloku głównego: trzy sekcje.

    Z ilustracji 3: blok nagłówkowy, pod nim blok artykułu na całą szerokość, potem blok główny z trzema sekcjami obok siebie, na dole stopka. Klucz R.2.4 wymienia znaczniki wprost: <header>, <article>, <main> z trzema <section>, <footer> (nazwy w tabeli 3 arkusza). Blok artykułu stoi poza <main>, jako osobny blok między nagłówkiem a głównym - tak pokazuje ilustracja 3.

    Trzy sekcje dostają identyczne style, więc nie potrzebują identyfikatorów - CSS sięgnie po nie selektorem section. Nazwy zmiennych piszesz w tym rozwiązaniu po angielsku (arkusz dopuszcza polski lub angielski); nazwy narzucone przez arkusz (medica.php, styl.css, kolumny bazy) zostają.

    Typowe błędy

    <article> wewnątrz <main> nie zgadza się z kluczem: „header, article, main → section + section + section, footer” - cztery bloki na tym samym poziomie.

    medica.php PHP zmienione: 14 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przychodnia Medica</title>
        <link rel="icon" href="obraz2.png">
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
        </header>
        <article>
        </article>
        <main>
            <section>
            </section>
            <section>
            </section>
            <section>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (medica.php)
  17. Krok 17: Blok nagłówkowy: nagłówek pierwszego stopnia

    Kryteria:

    R.2.5 W bloku nagłówkowym zapisany nagłówek <h1>: "Abonamenty w przychodni Medica", w przynajmniej jednej sekcji zapisany nagłówek <h2> "Standardowy" lub "Premium" lub "Dziecko" oraz zapisany w stopce paragraf

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku nagłówkowego: nagłówek pierwszego stopnia o treści „Abonamenty w przychodni Medica”.

    Pierwszego stopnia to <h1>, w <header>. Jedyny <h1> na stronie - sekcje dostaną <h2>, a skrypt 1 wypisze <h3>.

    medica.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przychodnia Medica</title>
        <link rel="icon" href="obraz2.png">
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Abonamenty w przychodni Medica</h1>
        </header>
        <article>
        </article>
        <main>
            <section>
            </section>
            <section>
            </section>
            <section>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (medica.php)
  18. Krok 18: Blok artykułu: odnośnik „Dowiedz się więcej”

    Kryteria:

    R.3.3 Odnośnik o treści „Dowiedz się więcej” otwiera podstronę opis.html

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku artykułu: efekt działania skryptu 1. Odnośnik o treści „Dowiedz się więcej” prowadzący do podstrony opis.html.

    <a href="opis.html"> - ścieżka względna, sama nazwa pliku z tego samego folderu. Skrypt 1 (pakiety z cenami i opisami) dopiszesz nad odnośnikiem, bo arkusz wymienia go pierwszy. Klucz R.3.3 sprawdza, czy kliknięcie otwiera podstronę; w podglądzie kliknięcia są zablokowane.

    medica.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przychodnia Medica</title>
        <link rel="icon" href="obraz2.png">
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Abonamenty w przychodni Medica</h1>
        </header>
        <article>
            <a href="opis.html">Dowiedz się więcej</a>
        </article>
        <main>
            <section>
            </section>
            <section>
            </section>
            <section>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (medica.php)
  19. Krok 19: Pierwsza sekcja: nagłówek „Standardowy” i lista

    Kryteria: ,

    R.2.5 W bloku nagłówkowym zapisany nagłówek <h1>: "Abonamenty w przychodni Medica", w przynajmniej jednej sekcji zapisany nagłówek <h2> "Standardowy" lub "Premium" lub "Dziecko" oraz zapisany w stopce paragraf

    R.2.6 W przynajmniej jednej sekcji umieszczona jedna lista punktowana, zastosowane znaczniki <ul>, <li> (lista wygenerowana skryptem lub w HTML)

    ok. 1 min
    Polecenie z arkusza

    Zawartość pierwszej sekcji: nagłówek drugiego stopnia o treści „Standardowy”. Lista punktowana (nieuporządkowana), a w niej efekt działania skryptu 2.

    <h2> i pod nim pusta lista <ul> - elementy <li> wypisze skrypt 2. Klucz R.2.6 przyjmuje znaczniki listy zarówno w HTML, jak i w skrypcie; sam <ul> w HTML, a <li> w PHP to najczytelniejszy podział.

    medica.php PHP zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przychodnia Medica</title>
        <link rel="icon" href="obraz2.png">
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Abonamenty w przychodni Medica</h1>
        </header>
        <article>
            <a href="opis.html">Dowiedz się więcej</a>
        </article>
        <main>
            <section>
                <h2>Standardowy</h2>
                <ul>
                </ul>
            </section>
            <section>
            </section>
            <section>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (medica.php)
  20. Krok 20: Druga sekcja: nagłówek „Premium” i lista

    Kryteria: ,

    R.2.5 W bloku nagłówkowym zapisany nagłówek <h1>: "Abonamenty w przychodni Medica", w przynajmniej jednej sekcji zapisany nagłówek <h2> "Standardowy" lub "Premium" lub "Dziecko" oraz zapisany w stopce paragraf

    R.2.6 W przynajmniej jednej sekcji umieszczona jedna lista punktowana, zastosowane znaczniki <ul>, <li> (lista wygenerowana skryptem lub w HTML)

    ok. 1 min
    Polecenie z arkusza

    Zawartość drugiej sekcji: nagłówek drugiego stopnia o treści „Premium”. Lista punktowana (nieuporządkowana), a w niej efekt działania zmodyfikowanego skryptu 2 dla id abonamentu równego 2.

    Ta sama budowa: <h2> i pusta <ul>. Listę wypełni kopia skryptu 2 z warunkiem id = 2.

    medica.php PHP zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przychodnia Medica</title>
        <link rel="icon" href="obraz2.png">
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Abonamenty w przychodni Medica</h1>
        </header>
        <article>
            <a href="opis.html">Dowiedz się więcej</a>
        </article>
        <main>
            <section>
                <h2>Standardowy</h2>
                <ul>
                </ul>
            </section>
            <section>
                <h2>Premium</h2>
                <ul>
                </ul>
            </section>
            <section>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (medica.php)
  21. Krok 21: Trzecia sekcja: nagłówek „Dziecko” i lista

    Kryteria: ,

    R.2.5 W bloku nagłówkowym zapisany nagłówek <h1>: "Abonamenty w przychodni Medica", w przynajmniej jednej sekcji zapisany nagłówek <h2> "Standardowy" lub "Premium" lub "Dziecko" oraz zapisany w stopce paragraf

    R.2.6 W przynajmniej jednej sekcji umieszczona jedna lista punktowana, zastosowane znaczniki <ul>, <li> (lista wygenerowana skryptem lub w HTML)

    ok. 1 min
    Polecenie z arkusza

    Zawartość trzeciej sekcji: nagłówek drugiego stopnia o treści „Dziecko”. Lista punktowana (nieuporządkowana), a w niej efekt działania zmodyfikowanego skryptu 2 dla id abonamentu równego 3.

    Trzecia sekcja jak dwie poprzednie; listę wypełni kopia skryptu 2 z warunkiem id = 3. Nagłówki sekcji („Standardowy”, „Premium”, „Dziecko”) to nazwy z arkusza, nie z bazy - w bazie pierwszy pakiet nazywa się „Standard”.

    medica.php PHP zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przychodnia Medica</title>
        <link rel="icon" href="obraz2.png">
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Abonamenty w przychodni Medica</h1>
        </header>
        <article>
            <a href="opis.html">Dowiedz się więcej</a>
        </article>
        <main>
            <section>
                <h2>Standardowy</h2>
                <ul>
                </ul>
            </section>
            <section>
                <h2>Premium</h2>
                <ul>
                </ul>
            </section>
            <section>
                <h2>Dziecko</h2>
                <ul>
                </ul>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (medica.php)
  22. Krok 22: Stopka: paragraf z obrazem i numerem zdającego

    Kryteria: ,

    R.2.5 W bloku nagłówkowym zapisany nagłówek <h1>: "Abonamenty w przychodni Medica", w przynajmniej jednej sekcji zapisany nagłówek <h2> "Standardowy" lub "Premium" lub "Dziecko" oraz zapisany w stopce paragraf

    R.2.7 W stopce umieszczony obraz o nazwie obraz2.png z tekstem alternatywnym "przychodnia"

    ok. 1 min
    Polecenie z arkusza

    Zawartość stopki: paragraf, a w nim obraz o nazwie obraz2.png z tekstem alternatywnym „przychodnia” oraz treść „Stronę przygotował:”, dalej numer zdającego.

    Paragraf <p> w <footer>, a w nim najpierw <img> z przeskalowanym krzyżem i tekstem alternatywnym z arkusza (klucz R.2.7), potem tekst. <img> jest elementem liniowym, więc krzyż i napis stoją w jednej linii, jak na ilustracji 2. W tym rozwiązaniu po dwukropku stoi nazwisko autora - na egzaminie wpisujesz swój numer zdającego (PESEL). Struktura HTML jest kompletna - dalej style.

    medica.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przychodnia Medica</title>
        <link rel="icon" href="obraz2.png">
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Abonamenty w przychodni Medica</h1>
        </header>
        <article>
            <a href="opis.html">Dowiedz się więcej</a>
        </article>
        <main>
            <section>
                <h2>Standardowy</h2>
                <ul>
                </ul>
            </section>
            <section>
                <h2>Premium</h2>
                <ul>
                </ul>
            </section>
            <section>
                <h2>Dziecko</h2>
                <ul>
                </ul>
            </section>
        </main>
        <footer>
            <p><img src="obraz2.png" alt="przychodnia"> Stronę przygotował: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (medica.php)
  23. Etap 3: Styl CSS

    Kroki 23-31 · po tym etapie masz 27 z 37 kryteriów (pkt)

    Krok 23: Plik styl.css: krój czcionki

    Kryteria:

    R.4.1 Ustawione domyślne formatowanie wszystkich selektorów (dla *): krój czcionki Georgia

    ok. 1 min
    Polecenie z arkusza

    Domyślne formatowanie wszystkich selektorów: krój czcionki Georgia.

    Utwórz plik styl.css obok medica.php. Selektor uniwersalny * obejmuje każdy element. Tym razem arkusz podaje jeden krój bez zapasowego - font-family: Georgia (jedno słowo, cudzysłów niepotrzebny). Szeryfowa Georgia od razu zmienia wygląd nagłówka w podglądzie.

    styl.css CSS nowy plik
    * {
        font-family: Georgia;
    }
    Podgląd strony po tym kroku (medica.php)
  24. Krok 24: Nagłówek i stopka: tło, kolor, wyśrodkowanie, margines wewnętrzny

    Kryteria: , ,

    R.4.2 Ustawiony kolor tła #1B5E20 dla bloków: nagłówkowego i stopki, #00E676 dla trzech sekcji oraz tło będące obrazem obraz1.jpg dla bloku artykułu

    R.4.3 Ustawiony biały kolor czcionki dla bloku nagłówkowego i stopki, DimGray dla bloku artykułu, #1B5E20 dla trzech sekcji, rozmiar czcionki 150% dla bloku artykułu

    R.4.7 Ustawione marginesy wewnętrzne (padding) : 5px dla bloku nagłówkowego i stopki, marginesy zewnętrzne (margin) : 2% dla trzech sekcji. Ustawione wyrównanie tekstu do środka dla bloku nagłówkowego i stopki

    ok. 1 min
    Polecenie z arkusza

    Dla bloku nagłówkowego i stopki: kolor tła #1B5E20, biały kolor czcionki, wyrównanie tekstu do środka, marginesy wewnętrzne 5 px.

    Wspólne cechy dwóch bloków piszesz raz - header, footer. Ciemnozielone tło, biały tekst, wyśrodkowanie i padding: 5px (klucz R.4.7 sprawdza margines wewnętrzny i wyrównanie).

    styl.css CSS zmienione: 6 linii
    * {
        font-family: Georgia;
    }
    header, footer {
        background-color: #1B5E20;
        color: white;
        text-align: center;
        padding: 5px;
    }
    Podgląd strony po tym kroku (medica.php)
  25. Krok 25: Blok artykułu: tło z obrazu, kolor, rozmiar, wysokość, paski przewijania

    Kryteria: , , ,

    R.4.2 Ustawiony kolor tła #1B5E20 dla bloków: nagłówkowego i stopki, #00E676 dla trzech sekcji oraz tło będące obrazem obraz1.jpg dla bloku artykułu

    R.4.3 Ustawiony biały kolor czcionki dla bloku nagłówkowego i stopki, DimGray dla bloku artykułu, #1B5E20 dla trzech sekcji, rozmiar czcionki 150% dla bloku artykułu

    R.4.4 Ustawiona wysokość bloku artykułu 350px, trzech sekcji 560px oraz szerokość 28% dla trzech sekcji (width lub grid-template-columns lub flex)

    R.4.8 Ustawione dla bloku artykułu paski przewijania (scroll lub auto)

    ok. 2 min
    Polecenie z arkusza

    Dla bloku artykułu: tłem jest obraz o nazwie obraz1.jpg, kolor czcionki DimGray, rozmiar czcionki 150%, wysokość 350 px, widoczne paski przewijania.

    Selektor article. Tło z obrazu to background-image: url("obraz1.jpg") - ścieżka względem pliku CSS, który leży w tym samym folderze co zdjęcie. „Widoczne paski przewijania” to overflow: scroll - paski są zawsze, nawet gdy treść się mieści (klucz R.4.8 przyjmuje też auto). Trzy pakiety z długimi opisami i czcionką 150% przekroczą 350 px, więc pasek i tak będzie potrzebny. Kolor DimGray to nazwa z palety CSS, jak w arkuszu.

    Typowe błędy

    background: url(obraz1.jpg) też działa, ale background-color: url(...) - nie: kolor i obraz to różne właściwości. overflow: hidden ukryłby część opisów.

    styl.css CSS zmienione: 7 linii
    * {
        font-family: Georgia;
    }
    header, footer {
        background-color: #1B5E20;
        color: white;
        text-align: center;
        padding: 5px;
    }
    article {
        background-image: url("obraz1.jpg");
        color: DimGray;
        font-size: 150%;
        height: 350px;
        overflow: scroll;
    }
    Podgląd strony po tym kroku (medica.php)
  26. Krok 26: Trzy sekcje: tło, kolor, wymiary, margines, zaokrąglenie, cień

    Kryteria: , , , ,

    R.4.2 Ustawiony kolor tła #1B5E20 dla bloków: nagłówkowego i stopki, #00E676 dla trzech sekcji oraz tło będące obrazem obraz1.jpg dla bloku artykułu

    R.4.3 Ustawiony biały kolor czcionki dla bloku nagłówkowego i stopki, DimGray dla bloku artykułu, #1B5E20 dla trzech sekcji, rozmiar czcionki 150% dla bloku artykułu

    R.4.4 Ustawiona wysokość bloku artykułu 350px, trzech sekcji 560px oraz szerokość 28% dla trzech sekcji (width lub grid-template-columns lub flex)

    R.4.6 Ustawione dla trzech sekcji zaokrąglenie rogów 10px oraz cień bloku box-shadow: 8px 8px 6px DimGray;

    R.4.7 Ustawione marginesy wewnętrzne (padding) : 5px dla bloku nagłówkowego i stopki, marginesy zewnętrzne (margin) : 2% dla trzech sekcji. Ustawione wyrównanie tekstu do środka dla bloku nagłówkowego i stopki

    ok. 3 min
    Polecenie z arkusza

    Dla trzech sekcji: kolor tła #00E676, kolor czcionki #1B5E20, szerokość 28%, wysokość 560 px, marginesy zewnętrzne 2%, zaokrąglenie rogów 10 px, cień bloku o przesunięciu 8 px w obu osiach, rozmyciu 6 px i kolorze DimGray.

    Selektor section obejmuje wszystkie trzy sekcje naraz - dlatego nie potrzebowały identyfikatorów. Rachunek szerokości: 3 × 28% plus marginesy 2% z lewej i prawej każdej sekcji (3 × 4%) = 96% - mieści się w wierszu. Cień bloku to box-shadow z tabeli 2 arkusza: h-offset v-offset blur color, czyli 8px 8px 6px DimGray (klucz R.4.6 sprawdza dokładnie ten zapis; spread pomijasz). Sekcje nadal stoją jedna pod drugą - obok siebie ustawi je następny krok.

    Typowe błędy

    text-shadow (druga właściwość z tabeli 2) to cień tekstu, nie bloku. Margines w pikselach zamiast procentów rozjeżdża rachunek szerokości.

    styl.css CSS zmienione: 9 linii
    * {
        font-family: Georgia;
    }
    header, footer {
        background-color: #1B5E20;
        color: white;
        text-align: center;
        padding: 5px;
    }
    article {
        background-image: url("obraz1.jpg");
        color: DimGray;
        font-size: 150%;
        height: 350px;
        overflow: scroll;
    }
    section {
        background-color: #00E676;
        color: #1B5E20;
        width: 28%;
        height: 560px;
        margin: 2%;
        border-radius: 10px;
        box-shadow: 8px 8px 6px DimGray;
    }
    Podgląd strony po tym kroku (medica.php)
  27. Krok 27: Układ bloków zgodny z ilustracją 3

    Kryteria:

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

    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ą 3.

    Sekcje mają szerokość 28%, ale jako bloki stoją jedna pod drugą. display: flex na <main> ustawia jego dzieci w jednym wierszu, obok siebie - a nagłówek, artykuł i stopka, które są poza <main>, zostają na swoich miejscach bez żadnego clear. Klucz R.3.1 przyjmuje też float z clear i grid; flex jest tu najkrótszy. Układ zgadza się z ilustracją 3.

    Typowe błędy

    float: left na sekcjach wymaga clear: both na stopce - inaczej stopka wjeżdża pod pływające sekcje.

    styl.css CSS zmienione: 3 linie
    * {
        font-family: Georgia;
    }
    header, footer {
        background-color: #1B5E20;
        color: white;
        text-align: center;
        padding: 5px;
    }
    article {
        background-image: url("obraz1.jpg");
        color: DimGray;
        font-size: 150%;
        height: 350px;
        overflow: scroll;
    }
    section {
        background-color: #00E676;
        color: #1B5E20;
        width: 28%;
        height: 560px;
        margin: 2%;
        border-radius: 10px;
        box-shadow: 8px 8px 6px DimGray;
    }
    main {
        display: flex;
    }
    Podgląd strony po tym kroku (medica.php)
  28. Krok 28: Sekcja pod kursorem

    Kryteria:

    R.4.10 Gdy kursor znajdzie się na sekcji jej kolor tła zmienia się na #10EF84 oraz gdy kursor znajdzie się na paragrafie jego kolor czcionki zmienia się na czarny

    ok. 1 min
    Polecenie z arkusza

    Gdy kursor znajdzie się na dowolnej sekcji jej kolor tła zmienia się na #10EF84.

    Pseudoklasa :hover na section - jaśniejsza zieleń tylko pod kursorem. Najedź w podglądzie na dowolną sekcję.

    styl.css CSS zmienione: 3 linie
    * {
        font-family: Georgia;
    }
    header, footer {
        background-color: #1B5E20;
        color: white;
        text-align: center;
        padding: 5px;
    }
    article {
        background-image: url("obraz1.jpg");
        color: DimGray;
        font-size: 150%;
        height: 350px;
        overflow: scroll;
    }
    section {
        background-color: #00E676;
        color: #1B5E20;
        width: 28%;
        height: 560px;
        margin: 2%;
        border-radius: 10px;
        box-shadow: 8px 8px 6px DimGray;
    }
    main {
        display: flex;
    }
    section:hover {
        background-color: #10EF84;
    }
    Podgląd strony po tym kroku (medica.php)
  29. Krok 29: Paragraf pod kursorem

    Kryteria:

    R.4.10 Gdy kursor znajdzie się na sekcji jej kolor tła zmienia się na #10EF84 oraz gdy kursor znajdzie się na paragrafie jego kolor czcionki zmienia się na czarny

    ok. 1 min
    Polecenie z arkusza

    Gdy kursor znajdzie się na paragrafie jego kolor czcionki zmienia się na czarny.

    Pseudoklasa :hover na p. Na stronie są paragrafy w stopce (biały napis przy krzyżu) i będą w artykule (opisy pakietów ze skryptu 1, szare) - pod kursorem każdy robi się czarny. Klucz R.4.10 sprawdza obie reguły :hover razem.

    styl.css CSS zmienione: 3 linie
    * {
        font-family: Georgia;
    }
    header, footer {
        background-color: #1B5E20;
        color: white;
        text-align: center;
        padding: 5px;
    }
    article {
        background-image: url("obraz1.jpg");
        color: DimGray;
        font-size: 150%;
        height: 350px;
        overflow: scroll;
    }
    section {
        background-color: #00E676;
        color: #1B5E20;
        width: 28%;
        height: 560px;
        margin: 2%;
        border-radius: 10px;
        box-shadow: 8px 8px 6px DimGray;
    }
    main {
        display: flex;
    }
    section:hover {
        background-color: #10EF84;
    }
    p:hover {
        color: black;
    }
    Podgląd strony po tym kroku (medica.php)
  30. Krok 30: Nagłówki drugiego i trzeciego stopnia

    Kryteria:

    R.4.5 Ustawiony styl nagłówków drugiego i trzeciego stopnia: kolor czcionki #1B5E20, wyrównanie tekstu do środka, odstępy między literami 3px, padding-top: 20px;

    ok. 1 min
    Polecenie z arkusza

    Dla nagłówków drugiego i trzeciego stopnia: kolor czcionki #1B5E20, wyrównanie tekstu do środka, odstępy między literami 3 px, margines wewnętrzny górny 20 px.

    Jedna reguła dla dwóch selektorów: h2, h3. „Odstępy między literami” to letter-spacing: 3px - stąd rozstrzelone napisy „Standardowy” i „Pakiet Standard” na ilustracji 2. „Margines wewnętrzny górny” to padding-top (klucz R.4.5 podaje go wprost). <h3> jeszcze nie ma - wypisze je skrypt 1; reguła na nie czeka.

    Typowe błędy

    margin-top zamiast padding-top to margines zewnętrzny - klucz R.4.5 sprawdza padding-top: 20px.

    styl.css CSS zmienione: 6 linii
    * {
        font-family: Georgia;
    }
    header, footer {
        background-color: #1B5E20;
        color: white;
        text-align: center;
        padding: 5px;
    }
    article {
        background-image: url("obraz1.jpg");
        color: DimGray;
        font-size: 150%;
        height: 350px;
        overflow: scroll;
    }
    section {
        background-color: #00E676;
        color: #1B5E20;
        width: 28%;
        height: 560px;
        margin: 2%;
        border-radius: 10px;
        box-shadow: 8px 8px 6px DimGray;
    }
    main {
        display: flex;
    }
    section:hover {
        background-color: #10EF84;
    }
    p:hover {
        color: black;
    }
    h2, h3 {
        color: #1B5E20;
        text-align: center;
        letter-spacing: 3px;
        padding-top: 20px;
    }
    Podgląd strony po tym kroku (medica.php)
  31. Krok 31: Lista punktowana: punktor z obrazu

    Kryteria:

    R.4.9 Ustawiony styl listy punktowanej: punktorem jest obraz3.png (np. list-style-image)

    ok. 1 min
    Polecenie z arkusza

    Dla listy punktowanej: punktor jest obrazem o nazwie obraz3.png.

    Selektor ul i właściwość list-style-image z adresem obrazu - przeglądarka rysuje zielony „ptaszek” z archiwum zamiast kropki przed każdym <li> (klucz R.4.9 podaje tę właściwość jako przykład). Elementów listy jeszcze nie ma - wypiszą je kopie skryptu 2. Arkusz stylów jest kompletny.

    Typowe błędy

    list-style-type przyjmuje tylko nazwy (disc, square…), nie adres pliku - do obrazu służy list-style-image.

    styl.css CSS zmienione: 3 linie
    * {
        font-family: Georgia;
    }
    header, footer {
        background-color: #1B5E20;
        color: white;
        text-align: center;
        padding: 5px;
    }
    article {
        background-image: url("obraz1.jpg");
        color: DimGray;
        font-size: 150%;
        height: 350px;
        overflow: scroll;
    }
    section {
        background-color: #00E676;
        color: #1B5E20;
        width: 28%;
        height: 560px;
        margin: 2%;
        border-radius: 10px;
        box-shadow: 8px 8px 6px DimGray;
    }
    main {
        display: flex;
    }
    section:hover {
        background-color: #10EF84;
    }
    p:hover {
        color: black;
    }
    h2, h3 {
        color: #1B5E20;
        text-align: center;
        letter-spacing: 3px;
        padding-top: 20px;
    }
    ul {
        list-style-image: url("obraz3.png");
    }
    Podgląd strony po tym kroku (medica.php)
  32. Etap 4: Skrypty PHP

    Kroki 32-40 · po tym etapie masz 37 z 37 kryteriów (pkt)

    Krok 32: Połączenie z bazą danych

    Kryteria: ,

    R.5.1 Skrypt zawiera działające połączenie z serwerem bazy danych i wybór bazy medica

    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

    Napisane w języku PHP. Skrypty łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie medica.

    Kod PHP piszesz między <?php a ?> - serwer wykonuje go, zanim strona trafi do przeglądarki. Połączenie otwierasz raz, na samym początku pliku, i trzymasz w zmiennej $connection (znacząca, angielska nazwa - R.5.10), z której skorzystają wszystkie skrypty. mysqli_connect z tabeli 1 arkusza przyjmuje kolejno: serwer, użytkownika, hasło (puste) i nazwę bazy.

    Od tego kroku podgląd to wynik działania strony po stronie serwera (PHP nie wykonuje się w przeglądarce) - na razie wygląda jak wcześniej.

    Typowe błędy

    Kolejność argumentów ma znaczenie: mysqli_connect('localhost', 'root', 'medica') podaje „medica” jako HASŁO. Nazwa bazy jest czwarta. Plik sprawdzaj przez http://localhost/…, nie otwierając go z dysku.

    medica.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'medica');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przychodnia Medica</title>
        <link rel="icon" href="obraz2.png">
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Abonamenty w przychodni Medica</h1>
        </header>
        <article>
            <a href="opis.html">Dowiedz się więcej</a>
        </article>
        <main>
            <section>
                <h2>Standardowy</h2>
                <ul>
                </ul>
            </section>
            <section>
                <h2>Premium</h2>
                <ul>
                </ul>
            </section>
            <section>
                <h2>Dziecko</h2>
                <ul>
                </ul>
            </section>
        </main>
        <footer>
            <p><img src="obraz2.png" alt="przychodnia"> Stronę przygotował: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (medica.php)
  33. Krok 33: Skrypt 1: zapytanie 1 do bazy

    Kryteria:

    R.5.3 Skrypt 1 wysyła do bazy danych zapytanie 1 lub skrypt 2 wysyła zapytanie 3 (wystarczy jedno zapytanie z dowolnym warunkiem dla id równego 1 lub 2 lub 3)

    ok. 2 min
    Polecenie z arkusza

    Skrypt 1: wysyła do bazy danych zapytanie 1.

    Skrypt 1 piszesz w bloku artykułu, nad odnośnikiem - tam mają trafić pakiety. mysqli_query($connection, ...) wysyła zapytanie i zwraca wynik, który zapisujesz w $plans (klucz R.5.3). Treść to dokładnie zapytanie 1 z kwerendy.txt: nazwa, cena, opis trzech abonamentów.

    medica.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'medica');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przychodnia Medica</title>
        <link rel="icon" href="obraz2.png">
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Abonamenty w przychodni Medica</h1>
        </header>
        <article>
    <?php
    $plans = mysqli_query($connection, 'SELECT nazwa, cena, opis FROM abonamenty;');
    ?>
            <a href="opis.html">Dowiedz się więcej</a>
        </article>
        <main>
            <section>
                <h2>Standardowy</h2>
                <ul>
                </ul>
            </section>
            <section>
                <h2>Premium</h2>
                <ul>
                </ul>
            </section>
            <section>
                <h2>Dziecko</h2>
                <ul>
                </ul>
            </section>
        </main>
        <footer>
            <p><img src="obraz2.png" alt="przychodnia"> Stronę przygotował: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

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

  34. Krok 34: Skrypt 1: nagłówek z nazwą i ceną pakietu

    Kryteria:

    R.5.4 Dla wszystkich zwróconych wierszy skrypt 1 wyświetla tekst: „Pakiet <nazwa> - cena <cena> zł”, gdzie nawiasy <> oznaczają wartość pobraną z bazy danych

    ok. 2 min
    Polecenie z arkusza

    Zwrócone wiersze są wyświetlane według wzoru: w nagłówku trzeciego stopnia: „Pakiet <nazwa> - cena <cena> zł”, gdzie nawiasy <> oznaczają wartość pobraną z bazy danych.

    „Zwrócone wiersze” to pętla: mysqli_fetch_row($plans) (tabela 1) oddaje kolejny wiersz jako tablicę od zera, a gdy wierszy zabraknie - false, co kończy while. <h3> składasz ze słów z arkusza i dwóch kolumn: $row[0] nazwa, $row[1] cena (klucz R.5.4 sprawdza ten format). Reguła h2, h3 z CSS od razu rozstrzela litery i wyśrodkuje nagłówek na tle zdjęcia, jak na ilustracji 2.

    Typowe błędy

    echo '<h3>Pakiet $row[0] ...' w apostrofach wypisze dosłownie „$row[0]” - PHP nie podstawia zmiennych w apostrofach.

    medica.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'medica');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przychodnia Medica</title>
        <link rel="icon" href="obraz2.png">
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Abonamenty w przychodni Medica</h1>
        </header>
        <article>
    <?php
    $plans = mysqli_query($connection, 'SELECT nazwa, cena, opis FROM abonamenty;');
    while ($row = mysqli_fetch_row($plans)) {
        echo "<h3>Pakiet $row[0] - cena $row[1] zł</h3>";
    }
    ?>
            <a href="opis.html">Dowiedz się więcej</a>
        </article>
        <main>
            <section>
                <h2>Standardowy</h2>
                <ul>
                </ul>
            </section>
            <section>
                <h2>Premium</h2>
                <ul>
                </ul>
            </section>
            <section>
                <h2>Dziecko</h2>
                <ul>
                </ul>
            </section>
        </main>
        <footer>
            <p><img src="obraz2.png" alt="przychodnia"> Stronę przygotował: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (medica.php)
  35. Krok 35: Skrypt 1: paragraf z opisem

    Kryteria:

    R.5.5 Dla wszystkich zwróconych wierszy skrypt 1 wyświetla opis pobrany z bazy danych

    ok. 1 min
    Polecenie z arkusza

    W paragrafie opis pobrany z bazy danych.

    Pod każdym nagłówkiem <p> z trzecią kolumną ($row[2]) - długi opis pakietu. Trzy pakiety z opisami przekraczają 350 px artykułu, więc działa pasek przewijania z overflow: scroll; opisy są szare (DimGray) i czernieją pod kursorem (p:hover). Skrypt 1 jest kompletny.

    medica.php PHP zmienione: 1 linia
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'medica');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przychodnia Medica</title>
        <link rel="icon" href="obraz2.png">
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Abonamenty w przychodni Medica</h1>
        </header>
        <article>
    <?php
    $plans = mysqli_query($connection, 'SELECT nazwa, cena, opis FROM abonamenty;');
    while ($row = mysqli_fetch_row($plans)) {
        echo "<h3>Pakiet $row[0] - cena $row[1] zł</h3>";
        echo "<p>$row[2]</p>";
    }
    ?>
            <a href="opis.html">Dowiedz się więcej</a>
        </article>
        <main>
            <section>
                <h2>Standardowy</h2>
                <ul>
                </ul>
            </section>
            <section>
                <h2>Premium</h2>
                <ul>
                </ul>
            </section>
            <section>
                <h2>Dziecko</h2>
                <ul>
                </ul>
            </section>
        </main>
        <footer>
            <p><img src="obraz2.png" alt="przychodnia"> Stronę przygotował: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (medica.php)
  36. Krok 36: Skrypt 2: zapytanie 3 do bazy

    Kryteria:

    R.5.3 Skrypt 1 wysyła do bazy danych zapytanie 1 lub skrypt 2 wysyła zapytanie 3 (wystarczy jedno zapytanie z dowolnym warunkiem dla id równego 1 lub 2 lub 3)

    ok. 1 min
    Polecenie z arkusza

    Skrypt 2: wysyła do bazy danych zapytanie 3.

    Skrypt 2 piszesz wewnątrz <ul> pierwszej sekcji, bo tam mają trafić elementy listy. Zapytanie 3 z kwerendy.txt (sześć cech abonamentu o id 1) trafia do $features. Osobna zmienna, bo $plans ze skryptu 1 jest już przeczytana do końca.

    medica.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'medica');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przychodnia Medica</title>
        <link rel="icon" href="obraz2.png">
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Abonamenty w przychodni Medica</h1>
        </header>
        <article>
    <?php
    $plans = mysqli_query($connection, 'SELECT nazwa, cena, opis FROM abonamenty;');
    while ($row = mysqli_fetch_row($plans)) {
        echo "<h3>Pakiet $row[0] - cena $row[1] zł</h3>";
        echo "<p>$row[2]</p>";
    }
    ?>
            <a href="opis.html">Dowiedz się więcej</a>
        </article>
        <main>
            <section>
                <h2>Standardowy</h2>
                <ul>
    <?php
    $features = mysqli_query($connection, 'SELECT nazwa, cecha FROM abonamenty JOIN szczegolyabonamentu ON abonamenty.id = Abonamenty_id JOIN cechy ON cechy.id = Cechy_id WHERE abonamenty.id = 1;');
    ?>
                </ul>
            </section>
            <section>
                <h2>Premium</h2>
                <ul>
                </ul>
            </section>
            <section>
                <h2>Dziecko</h2>
                <ul>
                </ul>
            </section>
        </main>
        <footer>
            <p><img src="obraz2.png" alt="przychodnia"> Stronę przygotował: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

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

  37. Krok 37: Skrypt 2: cechy jako elementy listy

    Kryteria: ,

    R.5.6 Skrypt 2 wyświetla w pierwszej sekcji wiersze zwrócone zapytaniem dla abonamentu o id równym 1

    R.5.9 Skrypt 2 wyświetla wiersze w elemencie listy (tylko cecha)

    ok. 2 min
    Polecenie z arkusza

    Zwrócone zapytaniem wiersze są wyświetlane jako elementy listy, tylko <cecha>, gdzie nawiasy <> oznaczają wartość pobraną z bazy danych.

    Ta sama pętla co w skrypcie 1, tylko wypisuje <li> - i tylko cechę ($row[1], druga kolumna zapytania), bez nazwy abonamentu, choć zapytanie ją zwraca (klucz R.5.9: „tylko cecha”). Ponieważ <ul> jest już w HTML, każdy <li> ląduje w środku listy i dostaje punktor z obraz3.png. Podgląd: sześć cech Standardu, jak na ilustracji 2.

    Typowe błędy

    $row[0] to nazwa abonamentu („Standard” sześć razy) - cecha jest drugą kolumną zapytania 3.

    medica.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'medica');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przychodnia Medica</title>
        <link rel="icon" href="obraz2.png">
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Abonamenty w przychodni Medica</h1>
        </header>
        <article>
    <?php
    $plans = mysqli_query($connection, 'SELECT nazwa, cena, opis FROM abonamenty;');
    while ($row = mysqli_fetch_row($plans)) {
        echo "<h3>Pakiet $row[0] - cena $row[1] zł</h3>";
        echo "<p>$row[2]</p>";
    }
    ?>
            <a href="opis.html">Dowiedz się więcej</a>
        </article>
        <main>
            <section>
                <h2>Standardowy</h2>
                <ul>
    <?php
    $features = mysqli_query($connection, 'SELECT nazwa, cecha FROM abonamenty JOIN szczegolyabonamentu ON abonamenty.id = Abonamenty_id JOIN cechy ON cechy.id = Cechy_id WHERE abonamenty.id = 1;');
    while ($row = mysqli_fetch_row($features)) {
        echo "<li>$row[1]</li>";
    }
    ?>
                </ul>
            </section>
            <section>
                <h2>Premium</h2>
                <ul>
                </ul>
            </section>
            <section>
                <h2>Dziecko</h2>
                <ul>
                </ul>
            </section>
        </main>
        <footer>
            <p><img src="obraz2.png" alt="przychodnia"> Stronę przygotował: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (medica.php)
  38. Krok 38: Skrypt 2 w drugiej sekcji: id abonamentu 2

    Kryteria:

    R.5.7 Skrypt 2 wyświetla w drugiej sekcji wiersze zwrócone zapytaniem dla abonamentu o id równym 2

    ok. 1 min
    Polecenie z arkusza

    Modyfikacje skryptu 2: w drugiej sekcji należy zastosować zmodyfikowane zapytanie, które sprawdza warunek dla abonamentu o id równym 2. Pozostałe cechy skryptu są identyczne.

    Skopiuj cały skrypt 2 do <ul> drugiej sekcji i zmień jedną cyfrę: WHERE abonamenty.id = 2. Zmienna $features może się nazywać tak samo - poprzednia pętla już ją wyczerpała, a mysqli_query nadpisuje ją nowym wynikiem. Podgląd: jedenaście cech pakietu Premium (klucz R.5.7).

    medica.php PHP zmienione: 6 linii
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'medica');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przychodnia Medica</title>
        <link rel="icon" href="obraz2.png">
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Abonamenty w przychodni Medica</h1>
        </header>
        <article>
    <?php
    $plans = mysqli_query($connection, 'SELECT nazwa, cena, opis FROM abonamenty;');
    while ($row = mysqli_fetch_row($plans)) {
        echo "<h3>Pakiet $row[0] - cena $row[1] zł</h3>";
        echo "<p>$row[2]</p>";
    }
    ?>
            <a href="opis.html">Dowiedz się więcej</a>
        </article>
        <main>
            <section>
                <h2>Standardowy</h2>
                <ul>
    <?php
    $features = mysqli_query($connection, 'SELECT nazwa, cecha FROM abonamenty JOIN szczegolyabonamentu ON abonamenty.id = Abonamenty_id JOIN cechy ON cechy.id = Cechy_id WHERE abonamenty.id = 1;');
    while ($row = mysqli_fetch_row($features)) {
        echo "<li>$row[1]</li>";
    }
    ?>
                </ul>
            </section>
            <section>
                <h2>Premium</h2>
                <ul>
    <?php
    $features = mysqli_query($connection, 'SELECT nazwa, cecha FROM abonamenty JOIN szczegolyabonamentu ON abonamenty.id = Abonamenty_id JOIN cechy ON cechy.id = Cechy_id WHERE abonamenty.id = 2;');
    while ($row = mysqli_fetch_row($features)) {
        echo "<li>$row[1]</li>";
    }
    ?>
                </ul>
            </section>
            <section>
                <h2>Dziecko</h2>
                <ul>
                </ul>
            </section>
        </main>
        <footer>
            <p><img src="obraz2.png" alt="przychodnia"> Stronę przygotował: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (medica.php)
  39. Krok 39: Skrypt 2 w trzeciej sekcji: id abonamentu 3

    Kryteria:

    R.5.8 Skrypt 2 wyświetla w trzeciej sekcji wiersze zwrócone zapytaniem dla abonamentu o id równym 3

    ok. 1 min
    Polecenie z arkusza

    W trzeciej sekcji należy zastosować zmodyfikowane zapytanie, które sprawdza warunek dla abonamentu o id równym 3. Pozostałe cechy skryptu są identyczne.

    Trzecia kopia z WHERE abonamenty.id = 3. Podgląd: pięć cech pakietu Dziecko (klucz R.5.8). Strona wygląda jak ilustracja 2. Trzy niemal identyczne bloki kodu to dokładnie to, czego arkusz wymaga - i dokładnie to, co w prawdziwym projekcie poprawia się jako pierwsze.

    medica.php PHP zmienione: 6 linii
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'medica');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przychodnia Medica</title>
        <link rel="icon" href="obraz2.png">
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Abonamenty w przychodni Medica</h1>
        </header>
        <article>
    <?php
    $plans = mysqli_query($connection, 'SELECT nazwa, cena, opis FROM abonamenty;');
    while ($row = mysqli_fetch_row($plans)) {
        echo "<h3>Pakiet $row[0] - cena $row[1] zł</h3>";
        echo "<p>$row[2]</p>";
    }
    ?>
            <a href="opis.html">Dowiedz się więcej</a>
        </article>
        <main>
            <section>
                <h2>Standardowy</h2>
                <ul>
    <?php
    $features = mysqli_query($connection, 'SELECT nazwa, cecha FROM abonamenty JOIN szczegolyabonamentu ON abonamenty.id = Abonamenty_id JOIN cechy ON cechy.id = Cechy_id WHERE abonamenty.id = 1;');
    while ($row = mysqli_fetch_row($features)) {
        echo "<li>$row[1]</li>";
    }
    ?>
                </ul>
            </section>
            <section>
                <h2>Premium</h2>
                <ul>
    <?php
    $features = mysqli_query($connection, 'SELECT nazwa, cecha FROM abonamenty JOIN szczegolyabonamentu ON abonamenty.id = Abonamenty_id JOIN cechy ON cechy.id = Cechy_id WHERE abonamenty.id = 2;');
    while ($row = mysqli_fetch_row($features)) {
        echo "<li>$row[1]</li>";
    }
    ?>
                </ul>
            </section>
            <section>
                <h2>Dziecko</h2>
                <ul>
    <?php
    $features = mysqli_query($connection, 'SELECT nazwa, cecha FROM abonamenty JOIN szczegolyabonamentu ON abonamenty.id = Abonamenty_id JOIN cechy ON cechy.id = Cechy_id WHERE abonamenty.id = 3;');
    while ($row = mysqli_fetch_row($features)) {
        echo "<li>$row[1]</li>";
    }
    ?>
                </ul>
            </section>
        </main>
        <footer>
            <p><img src="obraz2.png" alt="przychodnia"> Stronę przygotował: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (medica.php)
  40. Krok 40: Zamknięcie połączenia z bazą

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

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

    mysqli_close($connection) na końcu pliku, po wszystkich skryptach. Klucz R.5.2 sprawdza, że zamknięcie jest ostatnią operacją na bazie. Strona spełnia wszystkie kryteria arkusza - dalej trzy kroki „jak w prawdziwym projekcie”.

    Typowe błędy

    mysqli_close po skrypcie 1 sprawia, że trzy kopie skryptu 2 idą po zamkniętym połączeniu - listy cech zostają puste.

    medica.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'medica');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przychodnia Medica</title>
        <link rel="icon" href="obraz2.png">
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Abonamenty w przychodni Medica</h1>
        </header>
        <article>
    <?php
    $plans = mysqli_query($connection, 'SELECT nazwa, cena, opis FROM abonamenty;');
    while ($row = mysqli_fetch_row($plans)) {
        echo "<h3>Pakiet $row[0] - cena $row[1] zł</h3>";
        echo "<p>$row[2]</p>";
    }
    ?>
            <a href="opis.html">Dowiedz się więcej</a>
        </article>
        <main>
            <section>
                <h2>Standardowy</h2>
                <ul>
    <?php
    $features = mysqli_query($connection, 'SELECT nazwa, cecha FROM abonamenty JOIN szczegolyabonamentu ON abonamenty.id = Abonamenty_id JOIN cechy ON cechy.id = Cechy_id WHERE abonamenty.id = 1;');
    while ($row = mysqli_fetch_row($features)) {
        echo "<li>$row[1]</li>";
    }
    ?>
                </ul>
            </section>
            <section>
                <h2>Premium</h2>
                <ul>
    <?php
    $features = mysqli_query($connection, 'SELECT nazwa, cecha FROM abonamenty JOIN szczegolyabonamentu ON abonamenty.id = Abonamenty_id JOIN cechy ON cechy.id = Cechy_id WHERE abonamenty.id = 2;');
    while ($row = mysqli_fetch_row($features)) {
        echo "<li>$row[1]</li>";
    }
    ?>
                </ul>
            </section>
            <section>
                <h2>Dziecko</h2>
                <ul>
    <?php
    $features = mysqli_query($connection, 'SELECT nazwa, cecha FROM abonamenty JOIN szczegolyabonamentu ON abonamenty.id = Abonamenty_id JOIN cechy ON cechy.id = Cechy_id WHERE abonamenty.id = 3;');
    while ($row = mysqli_fetch_row($features)) {
        echo "<li>$row[1]</li>";
    }
    ?>
                </ul>
            </section>
        </main>
        <footer>
            <p><img src="obraz2.png" alt="przychodnia"> Stronę przygotował: Bartosz Bryniarski</p>
        </footer>
    <?php
    mysqli_close($connection);
    ?>
    </body>
    </html>

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

  41. Etap 5: Dobre praktyki (poza kluczem)

    Kroki 41-43 · po tym etapie masz 37 z 37 kryteriów (pkt)

    Krok 41: Refaktor: jedna funkcja zamiast trzech kopii skryptu 2

    Kryteria: , ,

    R.5.6 Skrypt 2 wyświetla w pierwszej sekcji wiersze zwrócone zapytaniem dla abonamentu o id równym 1

    R.5.7 Skrypt 2 wyświetla w drugiej sekcji wiersze zwrócone zapytaniem dla abonamentu o id równym 2

    R.5.8 Skrypt 2 wyświetla w trzeciej sekcji wiersze zwrócone zapytaniem dla abonamentu o id równym 3

    ok. 5 min
    Polecenie z arkusza

    Modyfikacje skryptu 2: w drugiej sekcji należy zastosować zmodyfikowane zapytanie, które sprawdza warunek dla abonamentu o id równym 2. Pozostałe cechy skryptu są identyczne.

    Trzy kopie tego samego kodu różniące się cyfrą to zaproszenie do błędu: poprawka w jednej kopii nie trafia do dwóch pozostałych. W prawdziwym projekcie piszesz funkcję showFeatures($connection, $planId) raz, w bloku na początku pliku, i wywołujesz ją w każdej sekcji z innym numerem. Skoro numer jest parametrem, do zapytania wchodzi przez zapytanie przygotowane (mysqli_prepare z ?, bind_param z typem 'i') - dziś parametr jest stałą w kodzie, ale ta sama funkcja zadziała bezpiecznie, gdy numer przyjdzie kiedyś z adresu albo formularza. Zapytanie w funkcji wybiera tylko cecha (nazwa abonamentu nie była używana), więc tabela abonamenty nie jest już potrzebna - wystarczy złączenie tabeli pośredniej z cechami. W pierwszej sekcji stary skrypt zostaje w komentarzu; w dwóch kolejnych są same wywołania. Podgląd bez zmian.

    Typowe błędy

    Funkcja nie widzi zmiennych z reszty pliku. Bez parametru $connection w środku funkcji jest ostrzeżenie „Undefined variable $connection”, a mysqli_prepare kończy się błędem TypeError (dostaje null zamiast połączenia) - sprawdzone w PHP 8.2.

    medica.php PHP zmienione: 18 linii
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'medica');
    
    function showFeatures($connection, $planId)
    {
        $statement = mysqli_prepare($connection, 'SELECT cecha FROM cechy JOIN szczegolyabonamentu ON cechy.id = Cechy_id WHERE Abonamenty_id = ?');
        mysqli_stmt_bind_param($statement, 'i', $planId);
        mysqli_stmt_execute($statement);
        $features = mysqli_stmt_get_result($statement);
        while ($row = mysqli_fetch_row($features)) {
            echo "<li>$row[0]</li>";
        }
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przychodnia Medica</title>
        <link rel="icon" href="obraz2.png">
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Abonamenty w przychodni Medica</h1>
        </header>
        <article>
    <?php
    $plans = mysqli_query($connection, 'SELECT nazwa, cena, opis FROM abonamenty;');
    while ($row = mysqli_fetch_row($plans)) {
        echo "<h3>Pakiet $row[0] - cena $row[1] zł</h3>";
        echo "<p>$row[2]</p>";
    }
    ?>
            <a href="opis.html">Dowiedz się więcej</a>
        </article>
        <main>
            <section>
                <h2>Standardowy</h2>
                <ul>
    <?php
    // $features = mysqli_query($connection, 'SELECT nazwa, cecha FROM abonamenty JOIN szczegolyabonamentu ON abonamenty.id = Abonamenty_id JOIN cechy ON cechy.id = Cechy_id WHERE abonamenty.id = 1;');
    // while ($row = mysqli_fetch_row($features)) {
    //     echo "<li>$row[1]</li>";
    // }
    showFeatures($connection, 1);
    ?>
                </ul>
            </section>
            <section>
                <h2>Premium</h2>
                <ul>
    <?php
    showFeatures($connection, 2);
    ?>
                </ul>
            </section>
            <section>
                <h2>Dziecko</h2>
                <ul>
    <?php
    showFeatures($connection, 3);
    ?>
                </ul>
            </section>
        </main>
        <footer>
            <p><img src="obraz2.png" alt="przychodnia"> Stronę przygotował: Bartosz Bryniarski</p>
        </footer>
    <?php
    mysqli_close($connection);
    ?>
    </body>
    </html>

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

  42. Krok 42: Refaktor: escapowanie danych z bazy

    Kryteria: , ,

    R.5.4 Dla wszystkich zwróconych wierszy skrypt 1 wyświetla tekst: „Pakiet <nazwa> - cena <cena> zł”, gdzie nawiasy <> oznaczają wartość pobraną z bazy danych

    R.5.5 Dla wszystkich zwróconych wierszy skrypt 1 wyświetla opis pobrany z bazy danych

    R.5.9 Skrypt 2 wyświetla wiersze w elemencie listy (tylko cecha)

    ok. 3 min
    Polecenie z arkusza

    W paragrafie opis pobrany z bazy danych.

    Nazwy pakietów, opisy i cechy ktoś kiedyś wpisał do bazy - a napis <script>…</script> w opisie wykonałby się u każdego, kto otworzy stronę (XSS). htmlspecialchars() zamienia <, >, ", & na encje, więc tekst wyświetla się dosłownie i nie staje się kodem. Napisy przechodzą przez funkcję do zmiennych o angielskich nazwach, a echo używa już tylko ich. Cena (liczba) zostaje bez zmian. Podgląd bez zmian.

    Typowe błędy

    Escapowanie przy zapisie do bazy (zamiast przy wypisywaniu) zniekształca dane - encje &lt; trafiłyby do tabeli. Escapujesz na wyjściu.

    medica.php PHP zmienione: 6 linii
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'medica');
    
    function showFeatures($connection, $planId)
    {
        $statement = mysqli_prepare($connection, 'SELECT cecha FROM cechy JOIN szczegolyabonamentu ON cechy.id = Cechy_id WHERE Abonamenty_id = ?');
        mysqli_stmt_bind_param($statement, 'i', $planId);
        mysqli_stmt_execute($statement);
        $features = mysqli_stmt_get_result($statement);
        while ($row = mysqli_fetch_row($features)) {
            $feature = htmlspecialchars($row[0]);
            echo "<li>$feature</li>";
        }
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przychodnia Medica</title>
        <link rel="icon" href="obraz2.png">
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Abonamenty w przychodni Medica</h1>
        </header>
        <article>
    <?php
    $plans = mysqli_query($connection, 'SELECT nazwa, cena, opis FROM abonamenty;');
    while ($row = mysqli_fetch_row($plans)) {
        $name = htmlspecialchars($row[0]);
        $description = htmlspecialchars($row[2]);
        echo "<h3>Pakiet $name - cena $row[1] zł</h3>";
        echo "<p>$description</p>";
    }
    ?>
            <a href="opis.html">Dowiedz się więcej</a>
        </article>
        <main>
            <section>
                <h2>Standardowy</h2>
                <ul>
    <?php
    // $features = mysqli_query($connection, 'SELECT nazwa, cecha FROM abonamenty JOIN szczegolyabonamentu ON abonamenty.id = Abonamenty_id JOIN cechy ON cechy.id = Cechy_id WHERE abonamenty.id = 1;');
    // while ($row = mysqli_fetch_row($features)) {
    //     echo "<li>$row[1]</li>";
    // }
    showFeatures($connection, 1);
    ?>
                </ul>
            </section>
            <section>
                <h2>Premium</h2>
                <ul>
    <?php
    showFeatures($connection, 2);
    ?>
                </ul>
            </section>
            <section>
                <h2>Dziecko</h2>
                <ul>
    <?php
    showFeatures($connection, 3);
    ?>
                </ul>
            </section>
        </main>
        <footer>
            <p><img src="obraz2.png" alt="przychodnia"> Stronę przygotował: Bartosz Bryniarski</p>
        </footer>
    <?php
    mysqli_close($connection);
    ?>
    </body>
    </html>

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

  43. Krok 43: Refaktor: wiersze po nazwach kolumn

    ok. 3 min
    Polecenie z arkusza

    Zwrócone wiersze są wyświetlane według wzoru: w nagłówku trzeciego stopnia: „Pakiet <nazwa> - cena <cena> zł”.

    $row[2] nic nie mówi czytelnikowi - trzeba zajrzeć do zapytania, żeby wiedzieć, że to opis. mysqli_fetch_assoc oddaje wiersz jako tablicę po nazwach kolumn: $row['nazwa'], $row['opis'], $row['cecha']; w napisie z cudzysłowem taki element obejmujesz klamrami: {$row['cena']}. Kod czyta się jak zdanie, a zmiana kolejności kolumn w SELECT niczego nie psuje.

    Na egzaminie wystarczy mysqli_fetch_row z tabeli 1 - klucz tego nie sprawdza. To już kod gotowy do prawdziwego użycia.

    medica.php PHP zmienione: 6 linii
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'medica');
    
    function showFeatures($connection, $planId)
    {
        $statement = mysqli_prepare($connection, 'SELECT cecha FROM cechy JOIN szczegolyabonamentu ON cechy.id = Cechy_id WHERE Abonamenty_id = ?');
        mysqli_stmt_bind_param($statement, 'i', $planId);
        mysqli_stmt_execute($statement);
        $features = mysqli_stmt_get_result($statement);
        while ($row = mysqli_fetch_assoc($features)) {
            $feature = htmlspecialchars($row['cecha']);
            echo "<li>$feature</li>";
        }
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Przychodnia Medica</title>
        <link rel="icon" href="obraz2.png">
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Abonamenty w przychodni Medica</h1>
        </header>
        <article>
    <?php
    $plans = mysqli_query($connection, 'SELECT nazwa, cena, opis FROM abonamenty;');
    while ($row = mysqli_fetch_assoc($plans)) {
        $name = htmlspecialchars($row['nazwa']);
        $description = htmlspecialchars($row['opis']);
        echo "<h3>Pakiet $name - cena {$row['cena']} zł</h3>";
        echo "<p>$description</p>";
    }
    ?>
            <a href="opis.html">Dowiedz się więcej</a>
        </article>
        <main>
            <section>
                <h2>Standardowy</h2>
                <ul>
    <?php
    // $features = mysqli_query($connection, 'SELECT nazwa, cecha FROM abonamenty JOIN szczegolyabonamentu ON abonamenty.id = Abonamenty_id JOIN cechy ON cechy.id = Cechy_id WHERE abonamenty.id = 1;');
    // while ($row = mysqli_fetch_row($features)) {
    //     echo "<li>$row[1]</li>";
    // }
    showFeatures($connection, 1);
    ?>
                </ul>
            </section>
            <section>
                <h2>Premium</h2>
                <ul>
    <?php
    showFeatures($connection, 2);
    ?>
                </ul>
            </section>
            <section>
                <h2>Dziecko</h2>
                <ul>
    <?php
    showFeatures($connection, 3);
    ?>
                </ul>
            </section>
        </main>
        <footer>
            <p><img src="obraz2.png" alt="przychodnia"> Stronę przygotował: Bartosz Bryniarski</p>
        </footer>
    <?php
    mysqli_close($connection);
    ?>
    </body>
    </html>

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

  44. Etap 6: Oddanie pracy

    Krok 44 · po tym etapie masz 37 z 37 kryteriów (pkt)

    Krok 44: 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. Nagraj płytę z rezultatami pracy.

    W przeglądarka.txt wpisujesz nazwę przeglądarki, w której sprawdzasz stronę przez http://localhost/….

    W folderze z numerem zdającego muszą być: import.png, kw1.jpg … kw4.jpg, kwerendy.txt, medica.php, obraz1.jpg, obraz2.png, obraz3.png, opis.html, przeglądarka.txt, styl.css. Nagraj płytę, sprawdź jej odczyt i opisz numerem zdającego. Pusta lub nieczytelna płyta to zero punktów za wszystkie pięć rezultatów.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

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

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.