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.
Suwak i strzałki obok niego - albo ←→ na klawiaturze - przechodzą krok po kroku; wyjaśnienie, pliki i podgląd zmieniają się razem z krokiem.
Lista „Etap” przenosi od razu do wybranego etapu.
„Rozwiązuję samodzielnie” chowa rozwiązanie kroku - najpierw własna próba, potem odsłonięcie.
„Tryb prezentacji” pokazuje same kroki, bez menu strony - na rzutnik.
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).
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.
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.
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.txtTekstnowy plik
-- Zapytanie 1SELECT nazwa, cena, opis FROM abonamenty;
Podgląd strony po tym kroku
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.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT nazwa, cena, opis FROM abonamenty;-- Zapytanie 2SELECT ROUND(AVG(cena), 2) AS Srednio_abonament FROM abonamenty;
Podgląd strony po tym kroku
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.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT nazwa, cena, opis FROM abonamenty;-- Zapytanie 2SELECT ROUND(AVG(cena), 2) AS Srednio_abonament FROM abonamenty;-- Zapytanie 3SELECT 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
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.txtTekstzmienione: 3 linie
-- Zapytanie 1SELECT nazwa, cena, opis FROM abonamenty;-- Zapytanie 2SELECT ROUND(AVG(cena), 2) AS Srednio_abonament FROM abonamenty;-- Zapytanie 3SELECT nazwa, cecha FROM abonamenty JOIN szczegolyabonamentu ON abonamenty.id = Abonamenty_id JOIN cechy ON cechy.id = Cechy_id WHERE abonamenty.id = 1;-- Zapytanie 4SELECT nazwa, opis FROM abonamenty WHERE opis LIKE "%zdrowie%" OR opis LIKE "%opieką%";
Podgląd strony po tym kroku
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 jakoobraz2.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
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.htmlHTMLnowy plik
Strona w trakcie budowy
Podgląd strony po tym kroku (opis.html)
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).
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.
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.
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).
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”.
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.
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>.
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.
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ł.
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.
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”.
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.
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.cssCSSnowy plik
* {font-family: Georgia;}
Podgląd strony po tym kroku (medica.php)
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).
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.
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.
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.
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ę.
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.
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.
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.
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.
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.
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.
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.
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.phpPHPzmienione: 3 linie
<?php$connection = mysqli_connect('localhost', 'root', '', 'medica');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Przychodnia Medica</title> <linkrel="icon"href="obraz2.png"> <linkrel="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>";}?> <ahref="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><imgsrc="obraz2.png"alt="przychodnia"> Stronę przygotował: Bartosz Bryniarski</p> </footer></body></html>
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.phpPHPzmienione: 3 linie
<?php$connection = mysqli_connect('localhost', 'root', '', 'medica');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Przychodnia Medica</title> <linkrel="icon"href="obraz2.png"> <linkrel="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>";}?> <ahref="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><imgsrc="obraz2.png"alt="przychodnia"> Stronę przygotował: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (medica.php)
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.phpPHPzmienione: 6 linii
<?php$connection = mysqli_connect('localhost', 'root', '', 'medica');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Przychodnia Medica</title> <linkrel="icon"href="obraz2.png"> <linkrel="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>";}?> <ahref="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><imgsrc="obraz2.png"alt="przychodnia"> Stronę przygotował: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (medica.php)
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.phpPHPzmienione: 6 linii
<?php$connection = mysqli_connect('localhost', 'root', '', 'medica');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Przychodnia Medica</title> <linkrel="icon"href="obraz2.png"> <linkrel="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>";}?> <ahref="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><imgsrc="obraz2.png"alt="przychodnia"> Stronę przygotował: Bartosz Bryniarski</p> </footer></body></html>
Podgląd strony po tym kroku (medica.php)
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.phpPHPzmienione: 3 linie
<?php$connection = mysqli_connect('localhost', 'root', '', 'medica');?><!DOCTYPE html><htmllang="pl"><head> <metacharset="UTF-8"> <title>Przychodnia Medica</title> <linkrel="icon"href="obraz2.png"> <linkrel="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>";}?> <ahref="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><imgsrc="obraz2.png"alt="przychodnia"> Stronę przygotował: Bartosz Bryniarski</p> </footer><?phpmysqli_close($connection);?></body></html>
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.phpPHPzmienione: 18 linii
<?php$connection = mysqli_connect('localhost', 'root', '', 'medica');functionshowFeatures($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><htmllang="pl"><head> <metacharset="UTF-8"> <title>Przychodnia Medica</title> <linkrel="icon"href="obraz2.png"> <linkrel="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>";}?> <ahref="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><?phpshowFeatures($connection, 2);?> </ul> </section> <section> <h2>Dziecko</h2> <ul><?phpshowFeatures($connection, 3);?> </ul> </section> </main> <footer> <p><imgsrc="obraz2.png"alt="przychodnia"> Stronę przygotował: Bartosz Bryniarski</p> </footer><?phpmysqli_close($connection);?></body></html>
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 < trafiłyby do tabeli. Escapujesz na wyjściu.
medica.phpPHPzmienione: 6 linii
<?php$connection = mysqli_connect('localhost', 'root', '', 'medica');functionshowFeatures($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><htmllang="pl"><head> <metacharset="UTF-8"> <title>Przychodnia Medica</title> <linkrel="icon"href="obraz2.png"> <linkrel="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>";}?> <ahref="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><?phpshowFeatures($connection, 2);?> </ul> </section> <section> <h2>Dziecko</h2> <ul><?phpshowFeatures($connection, 3);?> </ul> </section> </main> <footer> <p><imgsrc="obraz2.png"alt="przychodnia"> Stronę przygotował: Bartosz Bryniarski</p> </footer><?phpmysqli_close($connection);?></body></html>
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.phpPHPzmienione: 6 linii
<?php$connection = mysqli_connect('localhost', 'root', '', 'medica');functionshowFeatures($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><htmllang="pl"><head> <metacharset="UTF-8"> <title>Przychodnia Medica</title> <linkrel="icon"href="obraz2.png"> <linkrel="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>";}?> <ahref="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><?phpshowFeatures($connection, 2);?> </ul> </section> <section> <h2>Dziecko</h2> <ul><?phpshowFeatures($connection, 3);?> </ul> </section> </main> <footer> <p><imgsrc="obraz2.png"alt="przychodnia"> Stronę przygotował: Bartosz Bryniarski</p> </footer><?phpmysqli_close($connection);?></body></html>
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.
Symulacja ekranu w przeglądarce - w Android Studio / Visual Studio wygląd może się nieco różnić.
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: Bartosz Bryniarski. 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.