Przejdź do głównej treści
INF.03-09-25.06-SGCzerwiec 2025 · 59 kroków · ok. 105 min · 42 kryteria z klucza

Rozwiązanie krok po kroku: Portal remontowy z bazą (Wersja podstawowa)

Rozwiązanie buduje portal dla remontujących mieszkania: baza remonty z czterema tabelami i pięcioma zapytaniami (funkcje grupujące z aliasami, WHERE z porównaniem, DISTINCT z sortowaniem, złączenie dwóch i czterech tabel), grafika wycięta z tapety i jej lustrzane odbicie, strona zlecenia.php z dwoma formularzami (pole liczbowe oraz lista rozwijana z polami opcji), style w styl.css z tłem obrazkowym i układem na pływających blokach oraz trzy skrypty PHP: lista firm od podanej liczby pracowników, lista miast do listy rozwijanej i lista zleceń dla wybranego miasta i rodzaju remontu.

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), a na końcu zostaje przerobiony na wersję z prawdziwych projektów: zapytania przygotowane z bindowaniem, escapowanie danych i walidacja tego, co przychodzi z formularza. W tym arkuszu nie ma JavaScriptu.

To jest wersja podstawowa. W zakładce „Nowoczesny kod (grid, PDO)” jest drugi sposób rozwiązania tego samego arkusza: układ na siatce CSS i PHP na PDO z zapytaniami przygotowanymi od pierwszej linii - obie wersje są zgodne z kluczem.

Baza i tapeta pochodzą z prawdziwego archiwum pliki5 (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 42 kryteria (R.1.1 - R.5.10), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 32 punkty - można stracić 10 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-8 · po tym etapie masz 7 z 42 kryteriów (pkt)

    Krok 1: Baza danych remonty

    Kryteria:

    R.1.1 Wykonany import tabeli do bazy danych remonty , czynność udokumentowana w pliku import w formacie JPEG

    ok. 2 min
    Polecenie z arkusza

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

    W przeglądarce otwierasz http://localhost/phpmyadmin, klikasz Nowa w lewym panelu, wpisujesz nazwę remonty i z listy metod porównywania wybierasz utf8_unicode_ci (albo utf8_polish_ci - plik remonty.sql i tak ustawia polskie porównywanie na tabelach). Ma to znaczenie w zapytaniu 3: sortowanie miast ma ustawić „Łeba” i „Łódź” po „Lesko”, nie na końcu listy.

    Typowe błędy

    Nazwa bazy musi być dokładnie remonty - 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 remonty.sql

    Kryteria:

    R.1.1 Wykonany import tabeli do bazy danych remonty , czynność udokumentowana w pliku import w formacie JPEG

    ok. 2 min
    Polecenie z arkusza

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

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

    Po imporcie są cztery tabele z ilustracji 1: klienci (id_klienta, imie, miasto - 32 wiersze), wykonawcy (id_wykonawcy, nazwa_firmy, liczba_pracownikow - 18 firm), zlecenia (id_zlecenia, id_klienta, cena, rodzaj - 46 zleceń; rodzaj to malowanie albo gipsowanie) i wykonanie (id_wykonawcy, id_zlecenia - 52 pary). Tabela wykonanie jest tabelą łączącą: jedno zlecenie może wykonywać kilka firm, dlatego nie ma własnego identyfikatora, a jej klucz to para dwóch kluczy obcych.

    Typowe błędy

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

  3. Krok 3: Zrzut ekranu import.jpg

    Kryteria:

    R.1.1 Wykonany import tabeli do bazy danych remonty , czynność udokumentowana w pliku import w formacie JPEG

    ok. 2 min
    Polecenie z arkusza

    Wykonaj zrzut ekranu po imporcie. Zapisz zrzut w formacie JPEG 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.jpg w folderze z numerem zdającego. Uwaga na formaty w tym arkuszu: zrzut importu w JPEG, zrzuty kwerend w PNG. Na zrzucie ma być widać komunikat phpMyAdmin o pomyślnym imporcie albo cztery 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 (PNG zamiast JPEG) to też strata punktu.

  4. Krok 4: Zapytanie 1: średnia, minimum i maksimum pracownikó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 średnią liczbę pracowników w firmach, minimalną liczbę pracowników i maksymalną liczbę pracowników. Kolumnom nadano aliasy, kolejno: „średnia”, „najmniej”, „najwięcej”. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT AVG(liczba_pracownikow) AS "średnia", MIN(liczba_pracownikow) AS "najmniej", MAX(liczba_pracownikow) AS "najwięcej" FROM wykonawcy; (AS opcjonalnie) oraz wynik działania zapytania udokumentowany zrzutem, na którym widoczne są wartości: 27.3333 (dowolne zaokrąglenie) , 11, 55

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 1: wybierające jedynie średnią liczbę pracowników w firmach, minimalną liczbę pracowników i maksymalną liczbę pracowników. Kolumnom należy nadać nazwy (aliasy), kolejno: „średnia”, „najmniej”, „najwięcej”.

    Zakładka SQL w phpMyAdmin (przy zaznaczonej bazie remonty). Trzy funkcje grupujące liczone na całej tabeli, więc bez GROUP BY - wynik to jeden wiersz:

    • AVG(liczba_pracownikow) AS "średnia" - średnia; AS nadaje kolumnie wyniku nazwę (alias). Cudzysłów przy aliasie z polską literą jest najbezpieczniejszy, choć MySQL przyjąłby też AS średnia bez niego - litery spoza ASCII są dozwolone w nazwach.
    • MIN(...) AS "najmniej", MAX(...) AS "najwięcej" - najmniejsza i największa wartość.
    • FROM wykonawcy - liczba pracowników jest w tabeli firm.

    Wynik: 27.3333, 11, 55 - dokładnie wartości z klucza. Zapytanie kopiujesz do pliku kwerendy.txt i robisz zrzut: kw1.png.

    Typowe błędy

    Kolejność aliasów jest sprawdzana: średnia, najmniej, najwięcej. Zamiana miejscami kolumn to niespełnione kryterium R.1.3.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT AVG(liczba_pracownikow) AS "średnia", MIN(liczba_pracownikow) AS "najmniej", MAX(liczba_pracownikow) AS "najwięcej" FROM wykonawcy;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: firmy z co najmniej 40 pracownikami

    Kryteria:

    R.1.4 Wykonane zapytanie 2 wybierające jedynie nazwy firm i liczbę pracowników dla firm, w których liczba pracowników wynosi 40 i więcej. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa_firmy, liczba_pracownikow FROM wykonawcy WHERE liczba_pracownikow >= 40; oraz wynik działania zapytania udokumentowany zrzutem, na którym widocznych jest dokładnie 5 rekordów. Nazwy firm: BBud, GBud, KBud, RBud, SBud

    ok. 1 min
    Polecenie z arkusza

    Zapytanie 2: wybierające jedynie nazwy firm i liczbę pracowników dla firm, w których liczba pracowników wynosi 40 i więcej.

    SELECT nazwa_firmy, liczba_pracownikow FROM wykonawcy WHERE liczba_pracownikow >= 40 - dwie kolumny i warunek „40 i więcej”, czyli >=, nie >. Wynik: dokładnie 5 firm: BBud, GBud, KBud, RBud, SBud. Zrzut: kw2.png. To zapytanie wyśle skrypt 1, podstawiając w miejsce 40 liczbę z formularza.

    Typowe błędy

    > 40 pominie firmę z dokładnie 40 pracownikami. „40 i więcej” to >= 40.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT AVG(liczba_pracownikow) AS "średnia", MIN(liczba_pracownikow) AS "najmniej", MAX(liczba_pracownikow) AS "najwięcej" FROM wykonawcy;
    
    -- Zapytanie 2
    SELECT nazwa_firmy, liczba_pracownikow FROM wykonawcy WHERE liczba_pracownikow >= 40;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: miasta klientów bez powtórzeń

    Kryteria:

    R.1.5 Wykonane zapytanie 3 wybierające bez powtórzeń jedynie nazwy miast, z których pochodzą klienci, posortowane rosnąco według nazwy miasta. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT DISTINCT miasto FROM klienci ORDER BY miasto; (opcjonalnie ASC; możliwe również GROUP BY, wtedy ORDER BY opcjonalne) oraz wynik działania zapytania udokumentowany zrzutem, na którym widocznych jest dokładnie 16 rekordów, pierwszy rekord to Darłowo, ostatni - Wrocław

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 3: wybierające bez powtórzeń jedynie nazwy miast, z których pochodzą klienci, posortowane rosnąco według nazwy miasta.

    • SELECT DISTINCT miasto - „bez powtórzeń” to słowo DISTINCT: każde miasto raz, choć w Gdańsku mieszka trzech klientów.
    • FROM klienci - miasto jest w tabeli klientów.
    • ORDER BY miasto - alfabetycznie, rosnąco (domyślne ASC).

    Wynik: dokładnie 16 miast, od Darłowa do Wrocławia (klucz podaje pierwszy i ostatni rekord). Zrzut: kw3.png. To zapytanie wyśle skrypt 2 do listy rozwijanej.

    Typowe błędy

    Bez DISTINCT wynik ma 32 wiersze z powtórzeniami - a lista rozwijana w skrypcie 2 pokazałaby Gdańsk trzy razy.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT AVG(liczba_pracownikow) AS "średnia", MIN(liczba_pracownikow) AS "najmniej", MAX(liczba_pracownikow) AS "najwięcej" FROM wykonawcy;
    
    -- Zapytanie 2
    SELECT nazwa_firmy, liczba_pracownikow FROM wykonawcy WHERE liczba_pracownikow >= 40;
    
    -- Zapytanie 3
    SELECT DISTINCT miasto FROM klienci ORDER BY miasto;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: malowanie u klientów z Poznania

    Kryteria:

    R.1.6 Wykonane zapytanie 4 wybierające jedynie imiona klientów oraz ceny ich zleceń tylko dla klientów z Poznania, którzy zamówili malowanie. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT imie, cena FROM klienci JOIN zlecenia USING(id_klienta) WHERE miasto = "Poznań" AND rodzaj = "malowanie"; (możliwe również INNER JOIN lub porównanie kluczy po ON lub WHERE) oraz wynik działania zapytania udokumentowany zrzutem, na którym są widoczne dokładnie 4 rekordy: Paweł, 2500, Paweł 6500, Adam 777, Paulina 1200

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 4: wybierające jedynie imiona klientów oraz ceny ich zleceń tylko dla klientów z Poznania, którzy zamówili malowanie. Należy posłużyć się relacją.

    Imię jest w klienci, cena i rodzaj w zlecenia - „posłużyć się relacją” znaczy złączyć tabele po id_klienta.

    • SELECT imie, cena - jedynie te dwie kolumny.
    • FROM klienci JOIN zlecenia ON klienci.id_klienta = zlecenia.id_klienta - do każdego zlecenia dokładasz jego klienta; kolumna nazywa się tak samo w obu tabelach, więc poprzedzasz ją nazwą tabeli (klucz CKE pokazuje też skrót USING(id_klienta)).
    • WHERE miasto = "Poznań" AND rodzaj = "malowanie" - dwa warunki naraz, więc AND.

    Wynik: dokładnie 4 wiersze: Paweł 2500, Paweł 6500, Adam 777, Paulina 1200. Zrzut: kw4.png. To zapytanie wyśle skrypt 3 z miastem i rodzajem z formularza.

    Typowe błędy

    OR zamiast AND zwróci wszystkie zlecenia z Poznania ORAZ wszystkie malowania z całej Polski - kilkanaście wierszy zamiast czterech.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT AVG(liczba_pracownikow) AS "średnia", MIN(liczba_pracownikow) AS "najmniej", MAX(liczba_pracownikow) AS "najwięcej" FROM wykonawcy;
    
    -- Zapytanie 2
    SELECT nazwa_firmy, liczba_pracownikow FROM wykonawcy WHERE liczba_pracownikow >= 40;
    
    -- Zapytanie 3
    SELECT DISTINCT miasto FROM klienci ORDER BY miasto;
    
    -- Zapytanie 4
    SELECT imie, cena FROM klienci JOIN zlecenia ON klienci.id_klienta = zlecenia.id_klienta WHERE miasto = "Poznań" AND rodzaj = "malowanie";
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Krok 8: Zapytanie 5: klienci i firmy wykonujące ich zlecenia

    Kryteria:

    R.1.7 Wykonane zapytanie 5 wybierające jedynie imiona klientów oraz nazwy firm wykonujących dla nich zlecenia. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT imie, nazwa_firmy FROM klienci JOIN zlecenia USING (id_klienta) JOIN wykonanie USING (id_zlecenia)JOIN wykonawcy USING (id_wykonawcy); (możliwe również INNER JOIN lub porównanie kluczy po ON lub WHERE) oraz wynik działania zapytania udokumentowany zrzutem informującym o wybranych 52 rekordach

    ok. 4 min
    Polecenie z arkusza

    Zapytanie 5: wybierające jedynie imiona klientów oraz nazwy firm wykonujących dla nich zlecenia. Należy posłużyć się relacją.

    Od imienia klienta do nazwy firmy prowadzi łańcuch czterech tabel z ilustracji 1: klienci → zlecenia (po id_klienta) → wykonanie (po id_zlecenia) → wykonawcy (po id_wykonawcy). Każdy JOIN ... ON dokłada jedno ogniwo; warunek złączenia zawsze zrównuje klucz główny jednej tabeli z kluczem obcym drugiej.

    Wynik: 52 wiersze - tyle, ile par w tabeli wykonanie, bo to ona decyduje, która firma robiła które zlecenie (klient z dwoma firmami przy jednym zleceniu pojawia się dwa razy). Zrzut: kw5.png - w phpMyAdmin wystarczy komunikat „Pokazano 52 rekordów”. Plik kwerendy.txt ma komplet pięciu zapytań.

    Typowe błędy

    Pominięcie tabeli wykonanie (złączenie zlecenia bezpośrednio z wykonawcy) nie ma po czym złączyć - te tabele nie mają wspólnej kolumny. Łańcuch musi przejść przez tabelę łączącą.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT AVG(liczba_pracownikow) AS "średnia", MIN(liczba_pracownikow) AS "najmniej", MAX(liczba_pracownikow) AS "najwięcej" FROM wykonawcy;
    
    -- Zapytanie 2
    SELECT nazwa_firmy, liczba_pracownikow FROM wykonawcy WHERE liczba_pracownikow >= 40;
    
    -- Zapytanie 3
    SELECT DISTINCT miasto FROM klienci ORDER BY miasto;
    
    -- Zapytanie 4
    SELECT imie, cena FROM klienci JOIN zlecenia ON klienci.id_klienta = zlecenia.id_klienta WHERE miasto = "Poznań" AND rodzaj = "malowanie";
    
    -- Zapytanie 5
    SELECT imie, nazwa_firmy FROM klienci JOIN zlecenia ON klienci.id_klienta = zlecenia.id_klienta JOIN wykonanie ON zlecenia.id_zlecenia = wykonanie.id_zlecenia JOIN wykonawcy ON wykonanie.id_wykonawcy = wykonawcy.id_wykonawcy;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 8
  9. Etap 2: Grafika i strona HTML

    Kroki 9-34 · po tym etapie masz 21 z 42 kryteriów (pkt)

    Krok 9: Grafika cegla.jpg: cała cegła z pierwszego wiersza, wysokość 60 px

    Kryteria:

    R.2.1 Grafika cegla.jpg zawiera jedną całą cegłę. Wysokość grafiki to 60 px. Grafika tapeta_prawa.png jest odbiciem poziomym grafiki tapeta_lewa.png

    ok. 4 min
    Polecenie z arkusza

    Cechy grafiki cegla.jpg: wycięta z pliku tapeta_lewa.png, zawiera całą cegłę z pierwszego od góry wiersza cegieł. Po wycięciu cegły należy ją przeskalować z zachowaniem proporcji do wysokości 60 px.

    Otwórz tapeta_lewa.png (330 × 380 px) w GIMP-ie. W pierwszym wierszu są trzy cegły, ale tylko środkowa jest cała - lewa i prawa są ucięte krawędzią obrazu. Zaznacz ją prostokątem po fugach (ok. 154 × 54 px), Obraz → Przytnij do zaznaczenia, potem Obraz → Skaluj obraz z wysokością 60 px i zamkniętym łańcuchem proporcji - szerokość wychodzi sama (ok. 171 px). Eksportuj jako cegla.jpg. Ta grafika będzie tłem nagłówka i stopki o wysokości 60 px, dlatego arkusz wymaga dokładnie takiej wysokości.

    Typowe błędy

    Cegła ucięta krawędzią tapety albo wycinek z fragmentem sąsiedniej cegły nie spełnia „jedna cała cegła”. Skalowanie bez proporcji rozciąga cegłę.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 9
  10. Krok 10: Grafika tapeta_prawa.png: odbicie poziome

    Kryteria:

    R.2.1 Grafika cegla.jpg zawiera jedną całą cegłę. Wysokość grafiki to 60 px. Grafika tapeta_prawa.png jest odbiciem poziomym grafiki tapeta_lewa.png

    ok. 2 min
    Polecenie z arkusza

    Cechy grafiki tapeta_prawa.png: grafika jest odbitą poziomo wersją grafiki tapeta_lewa.png.

    Ponownie otwórz oryginalną tapeta_lewa.png, Obraz → Przekształć → Odbij poziomo (lustro: czerwona cegła i czerwone kółko przechodzą na drugą stronę) i wyeksportuj jako tapeta_prawa.png. Obie tapety trafią do bloku bocznego, jedna pod drugą.

    Typowe błędy

    „Odbij pionowo” (góra-dół) to nie to samo co „odbij poziomo” (lustro). Arkusz mówi „poziomo”.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 10
  11. Krok 11: Podstrona kontakt.html

    Kryteria:

    R.3.3 Wybranie odnośnika "Kontakt" otwiera podstronę kontakt.html , wybranie odnośnika "Partnerzy" inicjuje otwarcie strony https://remonty.pl w nowym oknie

    ok. 1 min
    Polecenie z arkusza

    Składa się ze stron o nazwie zlecenia.php, kontakt.html. W pliku kontakt.html należy umieścić tekst „Strona w budowie”.

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

    kontakt.html HTML nowy plik
    Strona w budowie
    Podgląd strony po tym kroku (kontakt.html)
  12. Krok 12: Plik zlecenia.php w HTML5

    Kryteria:

    R.2.2 W pliku zlecenia.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

    Zapisana w języku HTML5.

    W edytorze zaznaczającym składnię tworzysz plik zlecenia.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).

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

    Kryteria:

    R.2.2 W pliku zlecenia.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.

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

    Kryteria:

    R.2.2 W pliku zlecenia.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ź” z bazy i „Stronę wykonał” ze stopki mogłyby wyświetlić się jako krzaki. Plik trzeba też zapisać w UTF-8.

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

    Kryteria:

    R.2.3 Stronie nadany tytuł "Remonty”

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony „Remonty”.

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

    zlecenia.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (zlecenia.php)
  16. Krok 16: 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.

    <link rel="stylesheet" href="styl.css"> w <head>. W tym arkuszu nazwa to styl.css - sprawdź ją 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”.

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

    Kryteria:

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

    ok. 3 min
    Polecenie z arkusza

    Podział strony na bloki zrealizowany za pomocą semantycznych znaczników bloków języka HTML5 tak, aby po uruchomieniu w przeglądarce układ bloków na stronie był zgodny z ilustracją 4. Zawartość bloku głównego: bloki nawigacyjny, dwie sekcje, boczny.

    Z ilustracji 4: blok nagłówkowy na górze, pod nim blok główny, a w nim nawigacja, dwie sekcje i blok boczny, który zajmuje prawą kolumnę na wysokości nawigacji i sekcji razem; na dole stopka. Klucz R.2.4 wymienia znaczniki wprost: <header>, <main> z <nav>, <aside>, <section>, <section>, oraz <footer> (nazwy w tabeli 2 arkusza).

    Blok boczny wpisujesz przed sekcjami, choć arkusz wymienia go na końcu: układ zrobisz na pływających blokach (float), a pływający element nigdy nie wjeżdża wyżej niż elementy przed nim w kodzie. Blok boczny ma sięgać od góry nawigacji, więc w kodzie musi stać zaraz po niej. Klucz podaje tę samą kolejność: nav, aside, section, section.

    Dwie sekcje nie potrzebują identyfikatorów - dostaną te same wymiary. Nazwy identyfikatorów i zmiennych piszesz w tym rozwiązaniu po angielsku (arkusz dopuszcza polski lub angielski); nazwy narzucone przez arkusz (styl.css, zlecenia.php, kolumny bazy) zostają.

    Typowe błędy

    <nav> albo <aside> poza <main> nie spełnia R.2.4 - klucz mówi „main → nav+aside+section+section”.

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

    Kryteria:

    R.2.5 Umieszczone w bloku nagłówkowym <h1>: „Malowanie i gipsowanie”, w sekcji lewej <h2> „Dla klientów", w sekcji środkowej <h2>: „Dla wykonawców” oraz paragraf z numerem zdającego w stopce

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku nagłówkowego: nagłówek pierwszego stopnia o treści: „Malowanie i gipsowanie”.

    Pierwszego stopnia to <h1>, w <header>. Jedyny <h1> na stronie - sekcje dostaną <h2>.

    zlecenia.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
            </nav>
            <aside>
            </aside>
            <section>
            </section>
            <section>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zlecenia.php)
  19. Krok 19: Blok nawigacyjny: odnośnik „Kontakt”

    Kryteria:

    R.3.3 Wybranie odnośnika "Kontakt" otwiera podstronę kontakt.html , wybranie odnośnika "Partnerzy" inicjuje otwarcie strony https://remonty.pl w nowym oknie

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku nawigacyjnego: odnośnik do podstrony kontakt.html o treści „Kontakt”.

    <a href="kontakt.html">Kontakt</a> - ścieżka względna, sama nazwa pliku z tego samego folderu. Klucz R.3.3 sprawdza, czy kliknięcie otwiera podstronę. W podglądzie kliknięcia są zablokowane.

    zlecenia.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
            </nav>
            <aside>
            </aside>
            <section>
            </section>
            <section>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zlecenia.php)
  20. Krok 20: Blok nawigacyjny: odnośnik „Partnerzy” w nowej karcie

    Kryteria:

    R.3.3 Wybranie odnośnika "Kontakt" otwiera podstronę kontakt.html , wybranie odnośnika "Partnerzy" inicjuje otwarcie strony https://remonty.pl w nowym oknie

    ok. 1 min
    Polecenie z arkusza

    Odnośnik do strony o adresie „https://remonty.pl” i treści „Partnerzy”. Odnośnik otwiera się w nowej karcie przeglądarki.

    Adres zewnętrzny wpisujesz w całości, z https://. „W nowej karcie” to atrybut target="_blank" - bez niego strona partnerów zastąpiłaby portal w tej samej karcie. Klucz R.3.3 sprawdza oba odnośniki.

    Typowe błędy

    href="remonty.pl" bez https:// to ścieżka względna - przeglądarka szuka pliku remonty.pl w folderze strony.

    zlecenia.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
            </aside>
            <section>
            </section>
            <section>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zlecenia.php)
  21. Krok 21: Blok boczny: pierwsza tapeta

    Kryteria:

    R.2.6 W bloku bocznym umieszczona przynajmniej jedna grafika z tapetą, z tekstem alternatywnym "usługi"

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku bocznego: obraz tapeta_lewa.png z tekstem alternatywnym "usługi".

    <img> ze src wskazującym plik obok strony i tekstem alternatywnym z arkusza. alt jest sprawdzany w kluczu R.2.6 - czytnik ekranu i wyszukiwarka widzą tylko ten tekst. Obraz jest na razie w pełnym rozmiarze (330 px) - skaluje go dopiero CSS.

    zlecenia.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
                <img src="tapeta_lewa.png" alt="usługi">
            </aside>
            <section>
            </section>
            <section>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zlecenia.php)
  22. Krok 22: Blok boczny: tapeta prawa i znów lewa

    Kryteria:

    R.2.6 W bloku bocznym umieszczona przynajmniej jedna grafika z tapetą, z tekstem alternatywnym "usługi"

    ok. 1 min
    Polecenie z arkusza

    Poniżej obraz tapeta_prawa.png z tekstem alternatywnym "usługi". Poniżej obraz tapeta_lewa.png z tekstem alternatywnym "usługi".

    Dwa kolejne obrazy, każdy z tym samym alt. <img> to element liniowy, ale trzy obrazy o szerokości 100% bloku (ustawisz ją w CSS) i tak ustawią się jeden pod drugim - stąd „poniżej” bez żadnych <br>. Naprzemienne lustro daje ciągły wzór muru, jak na ilustracji 3.

    zlecenia.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
                <img src="tapeta_lewa.png" alt="usługi">
                <img src="tapeta_prawa.png" alt="usługi">
                <img src="tapeta_lewa.png" alt="usługi">
            </aside>
            <section>
            </section>
            <section>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zlecenia.php)
  23. Krok 23: Sekcja lewa: nagłówek „Dla klientów”

    Kryteria:

    R.2.5 Umieszczone w bloku nagłówkowym <h1>: „Malowanie i gipsowanie”, w sekcji lewej <h2> „Dla klientów", w sekcji środkowej <h2>: „Dla wykonawców” oraz paragraf z numerem zdającego w stopce

    ok. 1 min
    Polecenie z arkusza

    Zawartość sekcji lewej: nagłówek drugiego stopnia o treści „Dla klientów”.

    Drugiego stopnia to <h2>, w pierwszej <section>. Wyśrodkuje go reguła h2 w CSS - arkusz każe ją napisać właśnie selektorem znacznika.

    zlecenia.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
                <img src="tapeta_lewa.png" alt="usługi">
                <img src="tapeta_prawa.png" alt="usługi">
                <img src="tapeta_lewa.png" alt="usługi">
            </aside>
            <section>
                <h2>Dla klientów</h2>
            </section>
            <section>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zlecenia.php)
  24. Krok 24: Sekcja lewa: formularz

    Kryteria: ,

    R.2.8 Na stronie umieszczony przynajmniej jeden formularz

    R.3.4 Przynajmniej jeden formularz wysyła dane metodą POST do tego samego pliku (poprawne również bez atrybutu action) oraz przynajmniej jeden przycisk realizuje funkcję wysłania danych z formularza (input lub button z typem submit lub button bez typu)

    ok. 1 min
    Polecenie z arkusza

    Formularz wysyłający dane do tego samego pliku metodą bezpieczną, z elementami: etykieta, pole edycyjne, przycisk.

    „Metoda bezpieczna” w rozumieniu arkusza to POST - dane nie pojawiają się w adresie strony. W specyfikacji HTTP „bezpieczne” są co prawda metody, które niczego nie zmieniają na serwerze (GET i HEAD), ale arkusz używa tego słowa w innym sensie, a klucz wymaga POST. „Do tego samego pliku” to brak atrybutu action (przeglądarka wysyła formularz na adres bieżącej strony) albo action="zlecenia.php" - klucz R.3.4 przyjmuje oba warianty. Na stronie będą dwa formularze; każdy wysyła tylko własne pola.

    Typowe błędy

    method="get" (albo brak method) wyśle dane w adresie - arkusz i klucz wymagają tu POST, a skrypt czytający $_POST nic nie dostanie.

    zlecenia.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
                <img src="tapeta_lewa.png" alt="usługi">
                <img src="tapeta_prawa.png" alt="usługi">
                <img src="tapeta_lewa.png" alt="usługi">
            </aside>
            <section>
                <h2>Dla klientów</h2>
                <form method="post">
                </form>
            </section>
            <section>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zlecenia.php)
  25. Krok 25: Formularz 1: etykieta pola

    Kryteria: ,

    R.2.9 Umieszczone kontrolki: pole edycyjne, przycisk "Szukaj firm" lub "Szukaj klientów", lista rozwijana, dwa pola opcji (radio)

    R.3.5 Etykieta jest powiązana z polem edycyjnym, pole edycyjne jest typu number, pierwsze pole radio zawiera atrybut checked oraz w jednym momencie można wybrać tylko jedno pole radio

    ok. 1 min
    Polecenie z arkusza

    Etykieta dla pola edycyjnego o treści „Ilu co najmniej pracowników potrzebujesz?”.

    <label for="workers"> - atrybut for wskazuje id pola, które dopiszesz za chwilę; to jest „powiązanie” sprawdzane w kluczu R.3.5 (kliknięcie etykiety ustawia kursor w polu). Na ilustracji 5 pole stoi pod etykietą - stąd <br>.

    zlecenia.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
                <img src="tapeta_lewa.png" alt="usługi">
                <img src="tapeta_prawa.png" alt="usługi">
                <img src="tapeta_lewa.png" alt="usługi">
            </aside>
            <section>
                <h2>Dla klientów</h2>
                <form method="post">
                    <label for="workers">Ilu co najmniej pracowników potrzebujesz?</label><br>
                </form>
            </section>
            <section>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zlecenia.php)
  26. Krok 26: Formularz 1: pole liczbowe

    Kryteria: ,

    R.2.9 Umieszczone kontrolki: pole edycyjne, przycisk "Szukaj firm" lub "Szukaj klientów", lista rozwijana, dwa pola opcji (radio)

    R.3.5 Etykieta jest powiązana z polem edycyjnym, pole edycyjne jest typu number, pierwsze pole radio zawiera atrybut checked oraz w jednym momencie można wybrać tylko jedno pole radio

    ok. 1 min
    Polecenie z arkusza

    Poniżej pole edycyjne przeznaczone do wpisywania jedynie liczb całkowitych.

    „Jedynie liczby całkowite” to type="number" - przeglądarka nie pozwoli wpisać liter i pokazuje strzałki jak na ilustracji 5 (klucz R.3.5: pole typu number). id="workers" łączy pole z etykietą, name="workers" to klucz, pod którym wartość trafi do $_POST w PHP.

    Typowe błędy

    Pole bez name nie jest wysyłane z formularzem - skrypt 1 nie dostanie liczby, choć pole wygląda poprawnie.

    zlecenia.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
                <img src="tapeta_lewa.png" alt="usługi">
                <img src="tapeta_prawa.png" alt="usługi">
                <img src="tapeta_lewa.png" alt="usługi">
            </aside>
            <section>
                <h2>Dla klientów</h2>
                <form method="post">
                    <label for="workers">Ilu co najmniej pracowników potrzebujesz?</label><br>
                    <input type="number" id="workers" name="workers">
                </form>
            </section>
            <section>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zlecenia.php)
  27. Krok 27: Formularz 1: przycisk „Szukaj firm”

    Kryteria: ,

    R.2.9 Umieszczone kontrolki: pole edycyjne, przycisk "Szukaj firm" lub "Szukaj klientów", lista rozwijana, dwa pola opcji (radio)

    R.3.4 Przynajmniej jeden formularz wysyła dane metodą POST do tego samego pliku (poprawne również bez atrybutu action) oraz przynajmniej jeden przycisk realizuje funkcję wysłania danych z formularza (input lub button z typem submit lub button bez typu)

    ok. 1 min
    Polecenie z arkusza

    Przycisk „Szukaj firm” wysyłający dane z formularza do skryptu 1.

    <button type="submit">Szukaj firm</button> wewnątrz formularza wysyła go po kliknięciu. Klucz R.3.4 przyjmuje też <input type="submit" value="Szukaj firm">. „Efekt działania skryptu 1” (lista firm) pojawi się pod formularzem - dopisze go PHP.

    zlecenia.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
                <img src="tapeta_lewa.png" alt="usługi">
                <img src="tapeta_prawa.png" alt="usługi">
                <img src="tapeta_lewa.png" alt="usługi">
            </aside>
            <section>
                <h2>Dla klientów</h2>
                <form method="post">
                    <label for="workers">Ilu co najmniej pracowników potrzebujesz?</label><br>
                    <input type="number" id="workers" name="workers">
                    <button type="submit">Szukaj firm</button>
                </form>
            </section>
            <section>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zlecenia.php)
  28. Krok 28: Sekcja środkowa: nagłówek „Dla wykonawców”

    Kryteria:

    R.2.5 Umieszczone w bloku nagłówkowym <h1>: „Malowanie i gipsowanie”, w sekcji lewej <h2> „Dla klientów", w sekcji środkowej <h2>: „Dla wykonawców” oraz paragraf z numerem zdającego w stopce

    ok. 1 min
    Polecenie z arkusza

    Zawartość sekcji środkowej: nagłówek drugiego stopnia o treści „Dla wykonawców”.

    Drugi <h2>, w drugiej <section>. Klucz R.2.5 sprawdza oba nagłówki drugiego stopnia razem z <h1> i paragrafem stopki.

    zlecenia.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
                <img src="tapeta_lewa.png" alt="usługi">
                <img src="tapeta_prawa.png" alt="usługi">
                <img src="tapeta_lewa.png" alt="usługi">
            </aside>
            <section>
                <h2>Dla klientów</h2>
                <form method="post">
                    <label for="workers">Ilu co najmniej pracowników potrzebujesz?</label><br>
                    <input type="number" id="workers" name="workers">
                    <button type="submit">Szukaj firm</button>
                </form>
            </section>
            <section>
                <h2>Dla wykonawców</h2>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zlecenia.php)
  29. Krok 29: Formularz 2: lista rozwijana

    Kryteria: , ,

    R.2.8 Na stronie umieszczony przynajmniej jeden formularz

    R.2.9 Umieszczone kontrolki: pole edycyjne, przycisk "Szukaj firm" lub "Szukaj klientów", lista rozwijana, dwa pola opcji (radio)

    R.3.4 Przynajmniej jeden formularz wysyła dane metodą POST do tego samego pliku (poprawne również bez atrybutu action) oraz przynajmniej jeden przycisk realizuje funkcję wysłania danych z formularza (input lub button z typem submit lub button bez typu)

    ok. 2 min
    Polecenie z arkusza

    Formularz wysyłający dane do tego samego pliku metodą bezpieczną, z elementami: lista rozwijana wypełniona skryptem 2.

    Drugi formularz, też method="post" bez action. <select name="city"> to lista rozwijana; pod nazwą city wybrane miasto trafi do $_POST. Opcje zostawiasz puste - wstawi je skrypt 2, w pętli po wyniku zapytania 3. Na ilustracji 6 pola opcji są pod listą, stąd <br>.

    zlecenia.php PHP zmienione: 4 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
                <img src="tapeta_lewa.png" alt="usługi">
                <img src="tapeta_prawa.png" alt="usługi">
                <img src="tapeta_lewa.png" alt="usługi">
            </aside>
            <section>
                <h2>Dla klientów</h2>
                <form method="post">
                    <label for="workers">Ilu co najmniej pracowników potrzebujesz?</label><br>
                    <input type="number" id="workers" name="workers">
                    <button type="submit">Szukaj firm</button>
                </form>
            </section>
            <section>
                <h2>Dla wykonawców</h2>
                <form method="post">
                    <select name="city">
                    </select><br>
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zlecenia.php)
  30. Krok 30: Formularz 2: pole opcji „malowanie”, domyślnie zaznaczone

    Kryteria: ,

    R.2.9 Umieszczone kontrolki: pole edycyjne, przycisk "Szukaj firm" lub "Szukaj klientów", lista rozwijana, dwa pola opcji (radio)

    R.3.5 Etykieta jest powiązana z polem edycyjnym, pole edycyjne jest typu number, pierwsze pole radio zawiera atrybut checked oraz w jednym momencie można wybrać tylko jedno pole radio

    ok. 2 min
    Polecenie z arkusza

    Poniżej pole opcji o treści „malowanie”, pole jest domyślnie zaznaczone.

    Pole opcji to type="radio". Trzy atrybuty robią tu robotę: name="kind" (pod tą nazwą PHP dostanie wybór), value="malowanie" (co dokładnie zostanie wysłane - tę wartość wstawisz do zapytania, więc musi być równa zawartości kolumny rodzaj) i checked - „domyślnie zaznaczone” (klucz R.3.5 sprawdza checked na pierwszym polu). Treść „malowanie” to osobna etykieta powiązana przez for.

    Typowe błędy

    Pole radio bez value wysyła napis „on” - skrypt 3 szukałby zleceń o rodzaju „on” i nic nie znajdzie.

    zlecenia.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
                <img src="tapeta_lewa.png" alt="usługi">
                <img src="tapeta_prawa.png" alt="usługi">
                <img src="tapeta_lewa.png" alt="usługi">
            </aside>
            <section>
                <h2>Dla klientów</h2>
                <form method="post">
                    <label for="workers">Ilu co najmniej pracowników potrzebujesz?</label><br>
                    <input type="number" id="workers" name="workers">
                    <button type="submit">Szukaj firm</button>
                </form>
            </section>
            <section>
                <h2>Dla wykonawców</h2>
                <form method="post">
                    <select name="city">
                    </select><br>
                    <input type="radio" id="painting" name="kind" value="malowanie" checked>
                    <label for="painting">malowanie</label><br>
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zlecenia.php)
  31. Krok 31: Formularz 2: pole opcji „gipsowanie”, jeden wybór naraz

    Kryteria: ,

    R.2.9 Umieszczone kontrolki: pole edycyjne, przycisk "Szukaj firm" lub "Szukaj klientów", lista rozwijana, dwa pola opcji (radio)

    R.3.5 Etykieta jest powiązana z polem edycyjnym, pole edycyjne jest typu number, pierwsze pole radio zawiera atrybut checked oraz w jednym momencie można wybrać tylko jedno pole radio

    ok. 1 min
    Polecenie z arkusza

    Poniżej pole opcji o treści „gipsowanie”. W jednym momencie można wybrać tylko jedno pole opcji.

    Drugie pole radio z tym samym name="kind" - i to właśnie wspólna nazwa sprawia, że „w jednym momencie można wybrać tylko jedno”: przeglądarka traktuje pola o tej samej nazwie jako jedną grupę i zaznaczenie jednego odznacza drugie (klucz R.3.5). Różne id i value, bo to różne pola. Sprawdź w podglądzie: klik na „gipsowanie” zdejmuje zaznaczenie z „malowanie”.

    Typowe błędy

    Różne name w dwóch polach radio (name="painting", name="plastering") pozwalają zaznaczyć oba naraz - kryterium „tylko jedno” pada.

    zlecenia.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
                <img src="tapeta_lewa.png" alt="usługi">
                <img src="tapeta_prawa.png" alt="usługi">
                <img src="tapeta_lewa.png" alt="usługi">
            </aside>
            <section>
                <h2>Dla klientów</h2>
                <form method="post">
                    <label for="workers">Ilu co najmniej pracowników potrzebujesz?</label><br>
                    <input type="number" id="workers" name="workers">
                    <button type="submit">Szukaj firm</button>
                </form>
            </section>
            <section>
                <h2>Dla wykonawców</h2>
                <form method="post">
                    <select name="city">
                    </select><br>
                    <input type="radio" id="painting" name="kind" value="malowanie" checked>
                    <label for="painting">malowanie</label><br>
                    <input type="radio" id="plastering" name="kind" value="gipsowanie">
                    <label for="plastering">gipsowanie</label><br>
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zlecenia.php)
  32. Krok 32: Formularz 2: przycisk „Szukaj klientów”

    Kryteria:

    R.2.9 Umieszczone kontrolki: pole edycyjne, przycisk "Szukaj firm" lub "Szukaj klientów", lista rozwijana, dwa pola opcji (radio)

    ok. 1 min
    Polecenie z arkusza

    Przycisk „Szukaj klientów” wysyłający dane z formularza do skryptu 3.

    Drugi przycisk type="submit", wewnątrz drugiego formularza - wysyła miasto i rodzaj remontu. Każdy formularz ma własny przycisk, bo wysyła się tylko ten formularz, w którym przycisk stoi.

    zlecenia.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
                <img src="tapeta_lewa.png" alt="usługi">
                <img src="tapeta_prawa.png" alt="usługi">
                <img src="tapeta_lewa.png" alt="usługi">
            </aside>
            <section>
                <h2>Dla klientów</h2>
                <form method="post">
                    <label for="workers">Ilu co najmniej pracowników potrzebujesz?</label><br>
                    <input type="number" id="workers" name="workers">
                    <button type="submit">Szukaj firm</button>
                </form>
            </section>
            <section>
                <h2>Dla wykonawców</h2>
                <form method="post">
                    <select name="city">
                    </select><br>
                    <input type="radio" id="painting" name="kind" value="malowanie" checked>
                    <label for="painting">malowanie</label><br>
                    <input type="radio" id="plastering" name="kind" value="gipsowanie">
                    <label for="plastering">gipsowanie</label><br>
                    <button type="submit">Szukaj klientów</button>
                </form>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zlecenia.php)
  33. Krok 33: Sekcja środkowa: lista punktowana

    Kryteria:

    R.2.7 W sekcji środkowej umieszczona jedna lista punktowana z elementami, zastosowane znaczniki <ul>, <li> (w skrypcie lub HTML)

    ok. 1 min
    Polecenie z arkusza

    Lista punktowana (nieuporządkowana) wypełniona za pomocą skryptu 3.

    <ul> pod formularzem, na razie pusta - elementy <li> wypisze skrypt 3. Klucz R.2.7 przyjmuje znaczniki listy zarówno w HTML, jak i w skrypcie; sam <ul> w HTML, a <li> w PHP to najczytelniejszy podział.

    zlecenia.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
                <img src="tapeta_lewa.png" alt="usługi">
                <img src="tapeta_prawa.png" alt="usługi">
                <img src="tapeta_lewa.png" alt="usługi">
            </aside>
            <section>
                <h2>Dla klientów</h2>
                <form method="post">
                    <label for="workers">Ilu co najmniej pracowników potrzebujesz?</label><br>
                    <input type="number" id="workers" name="workers">
                    <button type="submit">Szukaj firm</button>
                </form>
            </section>
            <section>
                <h2>Dla wykonawców</h2>
                <form method="post">
                    <select name="city">
                    </select><br>
                    <input type="radio" id="painting" name="kind" value="malowanie" checked>
                    <label for="painting">malowanie</label><br>
                    <input type="radio" id="plastering" name="kind" value="gipsowanie">
                    <label for="plastering">gipsowanie</label><br>
                    <button type="submit">Szukaj klientów</button>
                </form>
                <ul>
                </ul>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zlecenia.php)
  34. Krok 34: Stopka: paragraf z tekstem ważnym

    Kryteria: ,

    R.2.5 Umieszczone w bloku nagłówkowym <h1>: „Malowanie i gipsowanie”, w sekcji lewej <h2> „Dla klientów", w sekcji środkowej <h2>: „Dla wykonawców” oraz paragraf z numerem zdającego w stopce

    R.2.10 Zastosowany znacznik <strong> do treści w stopce

    ok. 1 min
    Polecenie z arkusza

    Zawartość stopki: paragraf o treści „Stronę wykonał: ”, dalej wstawiony numer zdającego. Tekst w paragrafie zapisany jest za pomocą znacznika semantycznego oznaczającego tekst ważny, formatowany domyślnie jako pogrubiony.

    Paragraf <p> w <footer>, a w nim <strong> - semantyczny znacznik tekstu ważnego, który przeglądarka domyślnie pogrubia (klucz R.2.10 sprawdza właśnie strong). <b> daje ten sam wygląd, ale nie niesie znaczenia i nie spełnia kryterium. W tym rozwiązaniu po dwukropku stoi nazwisko autora - na egzaminie wpisujesz swój numer zdającego (PESEL). Struktura HTML jest kompletna - dalej style.

    Typowe błędy

    <b> zamiast <strong> wygląda tak samo, ale klucz R.2.10 wymaga znacznika semantycznego.

    zlecenia.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
                <img src="tapeta_lewa.png" alt="usługi">
                <img src="tapeta_prawa.png" alt="usługi">
                <img src="tapeta_lewa.png" alt="usługi">
            </aside>
            <section>
                <h2>Dla klientów</h2>
                <form method="post">
                    <label for="workers">Ilu co najmniej pracowników potrzebujesz?</label><br>
                    <input type="number" id="workers" name="workers">
                    <button type="submit">Szukaj firm</button>
                </form>
            </section>
            <section>
                <h2>Dla wykonawców</h2>
                <form method="post">
                    <select name="city">
                    </select><br>
                    <input type="radio" id="painting" name="kind" value="malowanie" checked>
                    <label for="painting">malowanie</label><br>
                    <input type="radio" id="plastering" name="kind" value="gipsowanie">
                    <label for="plastering">gipsowanie</label><br>
                    <button type="submit">Szukaj klientów</button>
                </form>
                <ul>
                </ul>
            </section>
        </main>
        <footer>
            <p><strong>Stronę wykonał: Bartosz Bryniarski</strong></p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zlecenia.php)
  35. Etap 3: Styl CSS

    Kroki 35-45 · po tym etapie masz 32 z 42 kryteriów (pkt)

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

    Kryteria:

    R.4.1 Ustawione domyślnie dla wszystkich selektorów (*): krój czcionki "Century Gothic", Verdana (tylko taka kolejność)

    ok. 1 min
    Polecenie z arkusza

    Domyślnie dla wszystkich selektorów: krój czcionki Century Gothic, w przypadku braku Verdana.

    Utwórz plik styl.css obok zlecenia.php. Selektor uniwersalny * obejmuje każdy element. font-family to lista: nazwa z dwóch słów w cudzysłowie ("Century Gothic"), potem zapasowa Verdana - klucz R.4.1 mówi: tylko taka kolejność. Podgląd od razu zmienia krój.

    styl.css CSS nowy plik
    * {
        font-family: "Century Gothic", Verdana;
    }
    Podgląd strony po tym kroku (zlecenia.php)
  36. Krok 36: Ciało strony: kolor tła

    Kryteria:

    R.4.2 Ustawiony kolor tła dla bloku nawigacyjnego #824555 oraz dla selektora body #F0EAE2, dla bloku nagłówkowego i stopki tło w postaci obrazu z cegłą

    ok. 1 min
    Polecenie z arkusza

    Dla selektora ciała strony: kolor tła #F0EAE2.

    Selektor body - arkusz podkreśla w uwadze, że ciało strony, h2 i a formatujesz wyłącznie selektorem znacznika (bez klas i identyfikatorów), bo witryna ma być później rozbudowywana. Kolor szesnastkowy przepisujesz z arkusza: jasny beż.

    styl.css CSS zmienione: 3 linie
    * {
        font-family: "Century Gothic", Verdana;
    }
    body {
        background-color: #F0EAE2;
    }
    Podgląd strony po tym kroku (zlecenia.php)
  37. Krok 37: Nagłówek i stopka: tło z cegły, biały tekst, wysokość, wyśrodkowanie

    Kryteria: , , ,

    R.4.2 Ustawiony kolor tła dla bloku nawigacyjnego #824555 oraz dla selektora body #F0EAE2, dla bloku nagłówkowego i stopki tło w postaci obrazu z cegłą

    R.4.3 Ustawiony kolor czcionki: dla bloku nagłówkowego i stopki biały oraz dla selektora a #F0EAE2

    R.4.4 Ustawiona wysokość bloku nagłówkowego, nawigacyjnego i stopki 60px, bocznego 560px, obu sekcji 500px

    R.4.6 Ustawione wyrównanie tekstu do środka dla bloku nagłówkowego, stopki i selektora h2 oraz do prawej strony dla bloku bocznego

    ok. 2 min
    Polecenie z arkusza

    Dla bloku nagłówkowego i stopki: tło, które stanowi obraz cegla.jpg, biały kolor czcionki, wysokość 60 px, wyrównanie tekstu do środka.

    Wspólne cechy dwóch bloków piszesz raz - header, footer. Tło z obrazu to background-image: url("cegla.jpg") - ścieżka względem pliku CSS, który leży w tym samym folderze co grafika. Obraz jest mniejszy niż blok, więc przeglądarka domyślnie go powtarza w obu kierunkach - stąd mur z cegieł na ilustracji 3; wysokość bloku 60 px równa wysokości cegły daje dokładnie jeden rząd. Biały tekst (color: white) i wyśrodkowanie dopełniają regułę.

    Typowe błędy

    background: url(cegla.jpg) też działa, ale background-color: url(...) - nie: kolor i obraz to różne właściwości.

    styl.css CSS zmienione: 6 linii
    * {
        font-family: "Century Gothic", Verdana;
    }
    body {
        background-color: #F0EAE2;
    }
    header, footer {
        background-image: url("cegla.jpg");
        color: white;
        height: 60px;
        text-align: center;
    }
    Podgląd strony po tym kroku (zlecenia.php)
  38. Krok 38: Blok nawigacyjny: tło, szerokość, wysokość

    Kryteria: , ,

    R.4.2 Ustawiony kolor tła dla bloku nawigacyjnego #824555 oraz dla selektora body #F0EAE2, dla bloku nagłówkowego i stopki tło w postaci obrazu z cegłą

    R.4.4 Ustawiona wysokość bloku nagłówkowego, nawigacyjnego i stopki 60px, bocznego 560px, obu sekcji 500px

    R.4.5 Ustawiona szerokość bloku nawigacyjnego 80%, bocznego 20%, obu sekcji 40% (width lub grid-template-columns lub flex) , oraz jedynie dla obrazów w bloku bocznym 100%

    ok. 1 min
    Polecenie z arkusza

    Dla bloku nawigacyjnego: kolor tła #824555, szerokość 80%, wysokość 60 px.

    Selektor znacznika nav. Bordowe tło z arkusza, szerokość 80% strony (pozostałe 20% zajmie blok boczny) i wysokość 60 px, taka sama jak nagłówka.

    styl.css CSS zmienione: 5 linii
    * {
        font-family: "Century Gothic", Verdana;
    }
    body {
        background-color: #F0EAE2;
    }
    header, footer {
        background-image: url("cegla.jpg");
        color: white;
        height: 60px;
        text-align: center;
    }
    nav {
        background-color: #824555;
        width: 80%;
        height: 60px;
    }
    Podgląd strony po tym kroku (zlecenia.php)
  39. Krok 39: Blok boczny: wymiary, wyrównanie, ukryte przepełnienie

    Kryteria: , , ,

    R.4.4 Ustawiona wysokość bloku nagłówkowego, nawigacyjnego i stopki 60px, bocznego 560px, obu sekcji 500px

    R.4.5 Ustawiona szerokość bloku nawigacyjnego 80%, bocznego 20%, obu sekcji 40% (width lub grid-template-columns lub flex) , oraz jedynie dla obrazów w bloku bocznym 100%

    R.4.6 Ustawione wyrównanie tekstu do środka dla bloku nagłówkowego, stopki i selektora h2 oraz do prawej strony dla bloku bocznego

    R.4.9 Ustawiona właściwość overflow dla bloku bocznego - hidden, dla obu sekcji - auto

    ok. 2 min
    Polecenie z arkusza

    Dla bloku bocznego: szerokość 20%, wysokość 560 px, wyrównanie tekstu do prawej strony, w przypadku przepełnienia bloku zawartość poza blokiem jest ukryta.

    Selektor aside. Wysokość 560 px to dokładnie nawigacja (60 px) plus sekcje (500 px) - blok boczny ma sięgać od góry nawigacji do dołu sekcji. „Zawartość poza blokiem jest ukryta” to overflow: hidden (klucz R.4.9): trzy tapety jedna pod drugą są wyższe niż 560 px i dolna zostanie po prostu obcięta, bez paska przewijania. text-align: right dla wyrównania zawartości do prawej.

    styl.css CSS zmienione: 6 linii
    * {
        font-family: "Century Gothic", Verdana;
    }
    body {
        background-color: #F0EAE2;
    }
    header, footer {
        background-image: url("cegla.jpg");
        color: white;
        height: 60px;
        text-align: center;
    }
    nav {
        background-color: #824555;
        width: 80%;
        height: 60px;
    }
    aside {
        width: 20%;
        height: 560px;
        text-align: right;
        overflow: hidden;
    }
    Podgląd strony po tym kroku (zlecenia.php)
  40. Krok 40: Obrazy w bloku bocznym: szerokość 100%

    Kryteria:

    R.4.5 Ustawiona szerokość bloku nawigacyjnego 80%, bocznego 20%, obu sekcji 40% (width lub grid-template-columns lub flex) , oraz jedynie dla obrazów w bloku bocznym 100%

    ok. 1 min
    Polecenie z arkusza

    Jedynie dla obrazów w bloku bocznym: szerokość 100%.

    „Jedynie dla obrazów w bloku bocznym” to selektor potomka aside img - obraz, który leży wewnątrz aside. Sam selektor img objąłby też ewentualne obrazy w innych blokach, a tego arkusz nie chce. Tapeta 330 px rozciąga się na całe 20% szerokości strony, a wysokość skaluje się proporcjonalnie.

    Typowe błędy

    Sam selektor img nie spełnia „jedynie dla obrazów w bloku bocznym” - klucz R.4.5 sprawdza zawężenie.

    styl.css CSS zmienione: 3 linie
    * {
        font-family: "Century Gothic", Verdana;
    }
    body {
        background-color: #F0EAE2;
    }
    header, footer {
        background-image: url("cegla.jpg");
        color: white;
        height: 60px;
        text-align: center;
    }
    nav {
        background-color: #824555;
        width: 80%;
        height: 60px;
    }
    aside {
        width: 20%;
        height: 560px;
        text-align: right;
        overflow: hidden;
    }
    aside img {
        width: 100%;
    }
    Podgląd strony po tym kroku (zlecenia.php)
  41. Krok 41: Obie sekcje: wymiary i paski przewijania

    Kryteria: , ,

    R.4.4 Ustawiona wysokość bloku nagłówkowego, nawigacyjnego i stopki 60px, bocznego 560px, obu sekcji 500px

    R.4.5 Ustawiona szerokość bloku nawigacyjnego 80%, bocznego 20%, obu sekcji 40% (width lub grid-template-columns lub flex) , oraz jedynie dla obrazów w bloku bocznym 100%

    R.4.9 Ustawiona właściwość overflow dla bloku bocznego - hidden, dla obu sekcji - auto

    ok. 1 min
    Polecenie z arkusza

    Dla obu sekcji: szerokość 40%, wysokość 500 px, paski przewijania tylko w przypadku przepełnienia bloku.

    Selektor section obejmuje obie sekcje naraz. Dwa razy 40% plus 20% bloku bocznego daje 100%. „Paski tylko przy przepełnieniu” to overflow: auto (klucz R.4.9: hidden dla bocznego, auto dla sekcji) - gdy skrypt 1 wypisze długą listę firm, pojawi się pasek; przy pustej sekcji go nie ma.

    styl.css CSS zmienione: 5 linii
    * {
        font-family: "Century Gothic", Verdana;
    }
    body {
        background-color: #F0EAE2;
    }
    header, footer {
        background-image: url("cegla.jpg");
        color: white;
        height: 60px;
        text-align: center;
    }
    nav {
        background-color: #824555;
        width: 80%;
        height: 60px;
    }
    aside {
        width: 20%;
        height: 560px;
        text-align: right;
        overflow: hidden;
    }
    aside img {
        width: 100%;
    }
    section {
        width: 40%;
        height: 500px;
        overflow: auto;
    }
    Podgląd strony po tym kroku (zlecenia.php)
  42. Krok 42: Układ bloków zgodny z ilustracją 4

    Kryteria:

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

    ok. 3 min
    Polecenie z arkusza

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

    Cztery dopiski robią układ z ilustracji 4:

    • nav dostaje float: left - pływa w lewo i zajmuje 80% szerokości,
    • aside dostaje float: right - pływa w prawo, w 20% obok nawigacji, i dzięki wysokości 560 px schodzi aż do dołu sekcji,
    • section dostaje float: left - sekcje nie mieszczą się obok nawigacji (zostało 20%, zajęte przez aside), więc spadają pod nią i ustawiają się obok siebie, na lewo od bloku bocznego,
    • footer dostaje osobną regułę clear: both, żeby wrócił pod wszystkie pływające bloki (wspólna reguła header, footer zostaje).

    Klucz R.3.1 przyjmuje też flex i grid - float jest najprostszy przy tej ilustracji.

    Typowe błędy

    Bez clear: both stopka wjeżdża pod pływające bloki, a <main> ma zerową wysokość. Blok boczny z float: left wylądowałby na lewo od sekcji.

    styl.css CSS zmienione: 6 linii
    * {
        font-family: "Century Gothic", Verdana;
    }
    body {
        background-color: #F0EAE2;
    }
    header, footer {
        background-image: url("cegla.jpg");
        color: white;
        height: 60px;
        text-align: center;
    }
    nav {
        float: left;
        background-color: #824555;
        width: 80%;
        height: 60px;
    }
    aside {
        float: right;
        width: 20%;
        height: 560px;
        text-align: right;
        overflow: hidden;
    }
    aside img {
        width: 100%;
    }
    section {
        float: left;
        width: 40%;
        height: 500px;
        overflow: auto;
    }
    footer {
        clear: both;
    }
    Podgląd strony po tym kroku (zlecenia.php)
  43. Krok 43: Nagłówki drugiego stopnia: wyśrodkowanie

    Kryteria:

    R.4.6 Ustawione wyrównanie tekstu do środka dla bloku nagłówkowego, stopki i selektora h2 oraz do prawej strony dla bloku bocznego

    ok. 1 min
    Polecenie z arkusza

    Dla selektora nagłówka drugiego stopnia: wyrównanie tekstu do środka.

    Selektor h2 - zgodnie z uwagą arkusza bez klas i identyfikatorów. Oba nagłówki sekcji stają na środku swojej sekcji.

    styl.css CSS zmienione: 3 linie
    * {
        font-family: "Century Gothic", Verdana;
    }
    body {
        background-color: #F0EAE2;
    }
    header, footer {
        background-image: url("cegla.jpg");
        color: white;
        height: 60px;
        text-align: center;
    }
    nav {
        float: left;
        background-color: #824555;
        width: 80%;
        height: 60px;
    }
    aside {
        float: right;
        width: 20%;
        height: 560px;
        text-align: right;
        overflow: hidden;
    }
    aside img {
        width: 100%;
    }
    section {
        float: left;
        width: 40%;
        height: 500px;
        overflow: auto;
    }
    footer {
        clear: both;
    }
    h2 {
        text-align: center;
    }
    Podgląd strony po tym kroku (zlecenia.php)
  44. Krok 44: Odnośniki: kolor, lewy margines wewnętrzny, rozmiar czcionki

    Kryteria: , ,

    R.4.3 Ustawiony kolor czcionki: dla bloku nagłówkowego i stopki biały oraz dla selektora a #F0EAE2

    R.4.7 Ustawiony jedynie lewy margines wewnętrzny (padding-left) dla odnośników 100 px

    R.4.8 Ustawiony rozmiar czcionki dla odnośników 180%

    ok. 1 min
    Polecenie z arkusza

    Dla selektora odnośnika: kolor tekstu #F0EAE2, tylko lewy margines wewnętrzny 100 px, rozmiar czcionki 180%.

    Selektor a. „Tylko lewy margines wewnętrzny” to padding-left: 100px (klucz R.4.7 sprawdza właśnie tę właściwość, nie skrót padding), a nie margin-left - margines zewnętrzny to inna rzecz. Rozmiar 180% liczy się względem czcionki rodzica, więc odnośniki są prawie dwa razy większe niż zwykły tekst, jak na ilustracji 3. Kolor tekstu taki jak tło strony.

    Typowe błędy

    margin-left zamiast padding-left nie spełnia R.4.7 - arkusz mówi o marginesie wewnętrznym.

    styl.css CSS zmienione: 5 linii
    * {
        font-family: "Century Gothic", Verdana;
    }
    body {
        background-color: #F0EAE2;
    }
    header, footer {
        background-image: url("cegla.jpg");
        color: white;
        height: 60px;
        text-align: center;
    }
    nav {
        float: left;
        background-color: #824555;
        width: 80%;
        height: 60px;
    }
    aside {
        float: right;
        width: 20%;
        height: 560px;
        text-align: right;
        overflow: hidden;
    }
    aside img {
        width: 100%;
    }
    section {
        float: left;
        width: 40%;
        height: 500px;
        overflow: auto;
    }
    footer {
        clear: both;
    }
    h2 {
        text-align: center;
    }
    a {
        color: #F0EAE2;
        padding-left: 100px;
        font-size: 180%;
    }
    Podgląd strony po tym kroku (zlecenia.php)
  45. Krok 45: Odnośnik pod kursorem

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

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

    Pseudoklasa :hover na a: biały kolor tylko pod kursorem. Najedź w podglądzie na „Kontakt”. Arkusz stylów jest kompletny.

    styl.css CSS zmienione: 3 linie
    * {
        font-family: "Century Gothic", Verdana;
    }
    body {
        background-color: #F0EAE2;
    }
    header, footer {
        background-image: url("cegla.jpg");
        color: white;
        height: 60px;
        text-align: center;
    }
    nav {
        float: left;
        background-color: #824555;
        width: 80%;
        height: 60px;
    }
    aside {
        float: right;
        width: 20%;
        height: 560px;
        text-align: right;
        overflow: hidden;
    }
    aside img {
        width: 100%;
    }
    section {
        float: left;
        width: 40%;
        height: 500px;
        overflow: auto;
    }
    footer {
        clear: both;
    }
    h2 {
        text-align: center;
    }
    a {
        color: #F0EAE2;
        padding-left: 100px;
        font-size: 180%;
    }
    a:hover {
        color: white;
    }
    Podgląd strony po tym kroku (zlecenia.php)
  46. Etap 4: Skrypty PHP

    Kroki 46-55 · po tym etapie masz 42 z 42 kryteriów (pkt)

    Krok 46: Połączenie z bazą danych

    Kryteria: ,

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

    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. Łączą się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie remonty.

    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ą trzy 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', 'remonty') podaje „remonty” jako HASŁO. Nazwa bazy jest czwarta. Plik sprawdzaj przez http://localhost/…, nie otwierając go z dysku.

    zlecenia.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'remonty');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
                <img src="tapeta_lewa.png" alt="usługi">
                <img src="tapeta_prawa.png" alt="usługi">
                <img src="tapeta_lewa.png" alt="usługi">
            </aside>
            <section>
                <h2>Dla klientów</h2>
                <form method="post">
                    <label for="workers">Ilu co najmniej pracowników potrzebujesz?</label><br>
                    <input type="number" id="workers" name="workers">
                    <button type="submit">Szukaj firm</button>
                </form>
            </section>
            <section>
                <h2>Dla wykonawców</h2>
                <form method="post">
                    <select name="city">
                    </select><br>
                    <input type="radio" id="painting" name="kind" value="malowanie" checked>
                    <label for="painting">malowanie</label><br>
                    <input type="radio" id="plastering" name="kind" value="gipsowanie">
                    <label for="plastering">gipsowanie</label><br>
                    <button type="submit">Szukaj klientów</button>
                </form>
                <ul>
                </ul>
            </section>
        </main>
        <footer>
            <p><strong>Stronę wykonał: Bartosz Bryniarski</strong></p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zlecenia.php)
  47. Krok 47: Skrypt 1: tylko po wysłaniu formularza

    Kryteria:

    R.5.9 Przynajmniej jeden skrypt obsługujący formularz realizuje czynności tylko wtedy, gdy formularz wypełniono

    ok. 2 min
    Polecenie z arkusza

    Skrypt 1, wykonywany tylko gdy wypełniono formularz.

    Skrypt 1 piszesz w sekcji lewej, pod formularzem - tam ma się pojawić lista firm. !empty($_POST['workers']) jest prawdziwe tylko wtedy, gdy wysłano pierwszy formularz z wpisaną liczbą: przy wejściu na stronę tablica $_POST jest pusta, a po wysłaniu drugiego formularza nie ma w niej klucza workers. Cały blok if jest wtedy pomijany (klucz R.5.9: „tylko gdy wypełniono formularz”).

    Samo isset z tabeli 1 tu nie wystarczy: po kliknięciu „Szukaj firm” z pustym polem klucz workers istnieje (z pustym napisem), zapytanie miałoby postać ... >= ;, a PHP 8 przerwałby stronę błędem SQL.

    Typowe błędy

    isset($_POST['workers']) przepuszcza puste pole - zapytanie z pustym warunkiem kończy się wyjątkiem mysqli_sql_exception i zamiast strony widać komunikat błędu.

    zlecenia.php PHP zmienione: 4 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'remonty');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
                <img src="tapeta_lewa.png" alt="usługi">
                <img src="tapeta_prawa.png" alt="usługi">
                <img src="tapeta_lewa.png" alt="usługi">
            </aside>
            <section>
                <h2>Dla klientów</h2>
                <form method="post">
                    <label for="workers">Ilu co najmniej pracowników potrzebujesz?</label><br>
                    <input type="number" id="workers" name="workers">
                    <button type="submit">Szukaj firm</button>
                </form>
    <?php
    if (!empty($_POST['workers'])) {
    }
    ?>
            </section>
            <section>
                <h2>Dla wykonawców</h2>
                <form method="post">
                    <select name="city">
                    </select><br>
                    <input type="radio" id="painting" name="kind" value="malowanie" checked>
                    <label for="painting">malowanie</label><br>
                    <input type="radio" id="plastering" name="kind" value="gipsowanie">
                    <label for="plastering">gipsowanie</label><br>
                    <button type="submit">Szukaj klientów</button>
                </form>
                <ul>
                </ul>
            </section>
        </main>
        <footer>
            <p><strong>Stronę wykonał: Bartosz Bryniarski</strong></p>
        </footer>
    </body>
    </html>

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

  48. Krok 48: Skrypt 1: zapytanie 2 z liczbą z formularza

    Kryteria: ,

    R.5.3 Przynajmniej jeden skrypt zawiera instrukcję wysyłającą do bazy danych zapytanie

    R.5.4 Skrypt 1 wyświetla każdy wiersz zwrócony zmodyfikowanym zapytaniem 2, tak że w warunku podana jest wartość wysłana z formularza

    ok. 2 min
    Polecenie z arkusza

    Wysyła do bazy danych zapytanie 2, zmodyfikowane tak, że w warunku podana jest wartość wysłana z formularza.

    Liczbę z pola workers przepisujesz do zmiennej $workers, a zapytanie 2 z kwerendy.txt dostaje ją w miejsce 40 - cały napis w cudzysłowie, żeby PHP podstawił zmienną. mysqli_query($connection, ...) wysyła zapytanie i zwraca wynik, który zapisujesz w $companies (klucz R.5.3).

    zlecenia.php PHP zmienione: 2 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'remonty');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
                <img src="tapeta_lewa.png" alt="usługi">
                <img src="tapeta_prawa.png" alt="usługi">
                <img src="tapeta_lewa.png" alt="usługi">
            </aside>
            <section>
                <h2>Dla klientów</h2>
                <form method="post">
                    <label for="workers">Ilu co najmniej pracowników potrzebujesz?</label><br>
                    <input type="number" id="workers" name="workers">
                    <button type="submit">Szukaj firm</button>
                </form>
    <?php
    if (!empty($_POST['workers'])) {
        $workers = $_POST['workers'];
        $companies = mysqli_query($connection, "SELECT nazwa_firmy, liczba_pracownikow FROM wykonawcy WHERE liczba_pracownikow >= $workers;");
    }
    ?>
            </section>
            <section>
                <h2>Dla wykonawców</h2>
                <form method="post">
                    <select name="city">
                    </select><br>
                    <input type="radio" id="painting" name="kind" value="malowanie" checked>
                    <label for="painting">malowanie</label><br>
                    <input type="radio" id="plastering" name="kind" value="gipsowanie">
                    <label for="plastering">gipsowanie</label><br>
                    <button type="submit">Szukaj klientów</button>
                </form>
                <ul>
                </ul>
            </section>
        </main>
        <footer>
            <p><strong>Stronę wykonał: Bartosz Bryniarski</strong></p>
        </footer>
    </body>
    </html>

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

  49. Krok 49: Skrypt 1: paragraf dla każdej firmy

    Kryteria: ,

    R.5.4 Skrypt 1 wyświetla każdy wiersz zwrócony zmodyfikowanym zapytaniem 2, tak że w warunku podana jest wartość wysłana z formularza

    R.5.5 Dane są wyświetlone w postaci „<firma>, <liczbaPracownikow> pracowników”

    ok. 2 min
    Polecenie z arkusza

    Dla każdego zwróconego wiersza wypisuje paragraf o treści <firma>, <liczbaPracownikow> pracowników, gdzie pola <> są pobrane z bazy.

    „Dla każdego wiersza” to pętla. mysqli_fetch_row($companies) (tabela 1) oddaje kolejny wiersz jako tablicę od zera, a gdy wierszy zabraknie - false, co kończy while. Kolejność kolumn jak w SELECT: $row[0] nazwa firmy, $row[1] liczba pracowników. echo składa paragraf dokładnie w formacie z arkusza: nazwa, przecinek, liczba, słowo „pracowników” (klucz R.5.5 sprawdza ten format). Podgląd pokazuje stan po wpisaniu 30 - siedem firm, jak na ilustracji 5.

    Typowe błędy

    echo '<p>$row[0], $row[1] pracowników</p>' w apostrofach wypisze dosłownie „$row[0]” - PHP nie podstawia zmiennych w apostrofach.

    zlecenia.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'remonty');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
                <img src="tapeta_lewa.png" alt="usługi">
                <img src="tapeta_prawa.png" alt="usługi">
                <img src="tapeta_lewa.png" alt="usługi">
            </aside>
            <section>
                <h2>Dla klientów</h2>
                <form method="post">
                    <label for="workers">Ilu co najmniej pracowników potrzebujesz?</label><br>
                    <input type="number" id="workers" name="workers">
                    <button type="submit">Szukaj firm</button>
                </form>
    <?php
    if (!empty($_POST['workers'])) {
        $workers = $_POST['workers'];
        $companies = mysqli_query($connection, "SELECT nazwa_firmy, liczba_pracownikow FROM wykonawcy WHERE liczba_pracownikow >= $workers;");
        while ($row = mysqli_fetch_row($companies)) {
            echo "<p>$row[0], $row[1] pracowników</p>";
        }
    }
    ?>
            </section>
            <section>
                <h2>Dla wykonawców</h2>
                <form method="post">
                    <select name="city">
                    </select><br>
                    <input type="radio" id="painting" name="kind" value="malowanie" checked>
                    <label for="painting">malowanie</label><br>
                    <input type="radio" id="plastering" name="kind" value="gipsowanie">
                    <label for="plastering">gipsowanie</label><br>
                    <button type="submit">Szukaj klientów</button>
                </form>
                <ul>
                </ul>
            </section>
        </main>
        <footer>
            <p><strong>Stronę wykonał: Bartosz Bryniarski</strong></p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zlecenia.php)
  50. Krok 50: Skrypt 2: zapytanie 3 do bazy

    Kryteria:

    R.5.3 Przynajmniej jeden skrypt zawiera instrukcję wysyłającą do bazy danych zapytanie

    ok. 1 min
    Polecenie z arkusza

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

    Skrypt 2 piszesz wewnątrz <select>, bo tam mają trafić opcje. Działa zawsze, bez if - lista miast ma być widoczna od pierwszego wejścia na stronę. Treść to dokładnie zapytanie 3 z kwerendy.txt: miasta bez powtórzeń, alfabetycznie. Wynik w $cities.

    zlecenia.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'remonty');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
                <img src="tapeta_lewa.png" alt="usługi">
                <img src="tapeta_prawa.png" alt="usługi">
                <img src="tapeta_lewa.png" alt="usługi">
            </aside>
            <section>
                <h2>Dla klientów</h2>
                <form method="post">
                    <label for="workers">Ilu co najmniej pracowników potrzebujesz?</label><br>
                    <input type="number" id="workers" name="workers">
                    <button type="submit">Szukaj firm</button>
                </form>
    <?php
    if (!empty($_POST['workers'])) {
        $workers = $_POST['workers'];
        $companies = mysqli_query($connection, "SELECT nazwa_firmy, liczba_pracownikow FROM wykonawcy WHERE liczba_pracownikow >= $workers;");
        while ($row = mysqli_fetch_row($companies)) {
            echo "<p>$row[0], $row[1] pracowników</p>";
        }
    }
    ?>
            </section>
            <section>
                <h2>Dla wykonawców</h2>
                <form method="post">
                    <select name="city">
    <?php
    $cities = mysqli_query($connection, 'SELECT DISTINCT miasto FROM klienci ORDER BY miasto;');
    ?>
                    </select><br>
                    <input type="radio" id="painting" name="kind" value="malowanie" checked>
                    <label for="painting">malowanie</label><br>
                    <input type="radio" id="plastering" name="kind" value="gipsowanie">
                    <label for="plastering">gipsowanie</label><br>
                    <button type="submit">Szukaj klientów</button>
                </form>
                <ul>
                </ul>
            </section>
        </main>
        <footer>
            <p><strong>Stronę wykonał: Bartosz Bryniarski</strong></p>
        </footer>
    </body>
    </html>

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

  51. Krok 51: Skrypt 2: miasta jako opcje listy

    Kryteria:

    R.5.6 Skrypt 2 wyświetla nazwy miast zwrócone zapytaniem 3 jako opcje listy rozwijanej

    ok. 2 min
    Polecenie z arkusza

    Każde zwrócone zapytaniem miasto jest treścią opcji listy rozwijanej.

    Ta sama pętla co w skrypcie 1, tylko wypisuje <option>. Bez atrybutu value formularz wysyła treść opcji, czyli nazwę miasta - dokładnie to, czego skrypt 3 potrzebuje w warunku miasto = .... Podgląd: lista ma 16 miast, od Darłowa, jak na ilustracji 6.

    zlecenia.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'remonty');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
                <img src="tapeta_lewa.png" alt="usługi">
                <img src="tapeta_prawa.png" alt="usługi">
                <img src="tapeta_lewa.png" alt="usługi">
            </aside>
            <section>
                <h2>Dla klientów</h2>
                <form method="post">
                    <label for="workers">Ilu co najmniej pracowników potrzebujesz?</label><br>
                    <input type="number" id="workers" name="workers">
                    <button type="submit">Szukaj firm</button>
                </form>
    <?php
    if (!empty($_POST['workers'])) {
        $workers = $_POST['workers'];
        $companies = mysqli_query($connection, "SELECT nazwa_firmy, liczba_pracownikow FROM wykonawcy WHERE liczba_pracownikow >= $workers;");
        while ($row = mysqli_fetch_row($companies)) {
            echo "<p>$row[0], $row[1] pracowników</p>";
        }
    }
    ?>
            </section>
            <section>
                <h2>Dla wykonawców</h2>
                <form method="post">
                    <select name="city">
    <?php
    $cities = mysqli_query($connection, 'SELECT DISTINCT miasto FROM klienci ORDER BY miasto;');
    while ($row = mysqli_fetch_row($cities)) {
        echo "<option>$row[0]</option>";
    }
    ?>
                    </select><br>
                    <input type="radio" id="painting" name="kind" value="malowanie" checked>
                    <label for="painting">malowanie</label><br>
                    <input type="radio" id="plastering" name="kind" value="gipsowanie">
                    <label for="plastering">gipsowanie</label><br>
                    <button type="submit">Szukaj klientów</button>
                </form>
                <ul>
                </ul>
            </section>
        </main>
        <footer>
            <p><strong>Stronę wykonał: Bartosz Bryniarski</strong></p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zlecenia.php)
  52. Krok 52: Skrypt 3: tylko po wysłaniu drugiego formularza

    Kryteria:

    R.5.9 Przynajmniej jeden skrypt obsługujący formularz realizuje czynności tylko wtedy, gdy formularz wypełniono

    ok. 2 min
    Polecenie z arkusza

    Skrypt 3, wykonywany tylko gdy wypełniono formularz.

    Skrypt 3 piszesz wewnątrz <ul> - elementy listy mają trafić do środka. Warunek sprawdza klucz city, który wysyła tylko drugi formularz (lista rozwijana zawsze ma jakąś wartość wybraną, więc klucz jest, gdy formularz poszedł). Od tego kroku podgląd pokazuje stan po wysłaniu drugiego formularza (Wrocław, malowanie), więc lista firm z pierwszego formularza znika - oba formularze nie wysyłają się naraz.

    zlecenia.php PHP zmienione: 4 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'remonty');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
                <img src="tapeta_lewa.png" alt="usługi">
                <img src="tapeta_prawa.png" alt="usługi">
                <img src="tapeta_lewa.png" alt="usługi">
            </aside>
            <section>
                <h2>Dla klientów</h2>
                <form method="post">
                    <label for="workers">Ilu co najmniej pracowników potrzebujesz?</label><br>
                    <input type="number" id="workers" name="workers">
                    <button type="submit">Szukaj firm</button>
                </form>
    <?php
    if (!empty($_POST['workers'])) {
        $workers = $_POST['workers'];
        $companies = mysqli_query($connection, "SELECT nazwa_firmy, liczba_pracownikow FROM wykonawcy WHERE liczba_pracownikow >= $workers;");
        while ($row = mysqli_fetch_row($companies)) {
            echo "<p>$row[0], $row[1] pracowników</p>";
        }
    }
    ?>
            </section>
            <section>
                <h2>Dla wykonawców</h2>
                <form method="post">
                    <select name="city">
    <?php
    $cities = mysqli_query($connection, 'SELECT DISTINCT miasto FROM klienci ORDER BY miasto;');
    while ($row = mysqli_fetch_row($cities)) {
        echo "<option>$row[0]</option>";
    }
    ?>
                    </select><br>
                    <input type="radio" id="painting" name="kind" value="malowanie" checked>
                    <label for="painting">malowanie</label><br>
                    <input type="radio" id="plastering" name="kind" value="gipsowanie">
                    <label for="plastering">gipsowanie</label><br>
                    <button type="submit">Szukaj klientów</button>
                </form>
                <ul>
    <?php
    if (isset($_POST['city'])) {
    }
    ?>
                </ul>
            </section>
        </main>
        <footer>
            <p><strong>Stronę wykonał: Bartosz Bryniarski</strong></p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zlecenia.php)
  53. Krok 53: Skrypt 3: zapytanie 4 z miastem i rodzajem z formularza

    Kryteria:

    R.5.7 Skrypt 3 wyświetla każdy wiersz zwrócony zmodyfikowanym zapytaniem 4 , tak że w warunku podane jest miasto i rodzaj remontu wysłane z formularza

    ok. 3 min
    Polecenie z arkusza

    Wysyła do bazy danych zapytanie 4, zmodyfikowane tak, że w warunku podane są nazwa miasta i rodzaj remontu wysłane z formularza.

    Dwie wartości z $_POST: city z listy rozwijanej i kind z zaznaczonego pola radio (jego value, czyli „malowanie” albo „gipsowanie”). Zapytanie 4 z kwerendy.txt dostaje je w miejsce „Poznań” i „malowanie”; wartości w SQL w apostrofach, cały napis PHP w cudzysłowie. Wynik w $orders.

    zlecenia.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'remonty');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
                <img src="tapeta_lewa.png" alt="usługi">
                <img src="tapeta_prawa.png" alt="usługi">
                <img src="tapeta_lewa.png" alt="usługi">
            </aside>
            <section>
                <h2>Dla klientów</h2>
                <form method="post">
                    <label for="workers">Ilu co najmniej pracowników potrzebujesz?</label><br>
                    <input type="number" id="workers" name="workers">
                    <button type="submit">Szukaj firm</button>
                </form>
    <?php
    if (!empty($_POST['workers'])) {
        $workers = $_POST['workers'];
        $companies = mysqli_query($connection, "SELECT nazwa_firmy, liczba_pracownikow FROM wykonawcy WHERE liczba_pracownikow >= $workers;");
        while ($row = mysqli_fetch_row($companies)) {
            echo "<p>$row[0], $row[1] pracowników</p>";
        }
    }
    ?>
            </section>
            <section>
                <h2>Dla wykonawców</h2>
                <form method="post">
                    <select name="city">
    <?php
    $cities = mysqli_query($connection, 'SELECT DISTINCT miasto FROM klienci ORDER BY miasto;');
    while ($row = mysqli_fetch_row($cities)) {
        echo "<option>$row[0]</option>";
    }
    ?>
                    </select><br>
                    <input type="radio" id="painting" name="kind" value="malowanie" checked>
                    <label for="painting">malowanie</label><br>
                    <input type="radio" id="plastering" name="kind" value="gipsowanie">
                    <label for="plastering">gipsowanie</label><br>
                    <button type="submit">Szukaj klientów</button>
                </form>
                <ul>
    <?php
    if (isset($_POST['city'])) {
        $city = $_POST['city'];
        $kind = $_POST['kind'];
        $orders = mysqli_query($connection, "SELECT imie, cena FROM klienci JOIN zlecenia ON klienci.id_klienta = zlecenia.id_klienta WHERE miasto = '$city' AND rodzaj = '$kind';");
    }
    ?>
                </ul>
            </section>
        </main>
        <footer>
            <p><strong>Stronę wykonał: Bartosz Bryniarski</strong></p>
        </footer>
    </body>
    </html>

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

  54. Krok 54: Skrypt 3: imię i cena w elementach listy

    Kryteria: ,

    R.5.7 Skrypt 3 wyświetla każdy wiersz zwrócony zmodyfikowanym zapytaniem 4 , tak że w warunku podane jest miasto i rodzaj remontu wysłane z formularza

    R.5.8 Dane są wyświetlane w postaci elementów listy

    ok. 2 min
    Polecenie z arkusza

    Każde zwrócone zapytaniem imię i cenę wypisuje w elementach listy punktowanej, oddzielone od siebie znakiem myślnika.

    Pętla po wyniku wypisuje <li> z imieniem ($row[0]), myślnikiem i ceną ($row[1]). Ponieważ <ul> jest już w HTML, każdy <li> ląduje w środku listy (klucz R.5.8: dane jako elementy listy). Podgląd: siedem zleceń malowania we Wrocławiu, dokładnie jak na ilustracji 6, z cenami z dwoma miejscami po kropce - tak oddaje je kolumna decimal(10,2).

    zlecenia.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'remonty');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
                <img src="tapeta_lewa.png" alt="usługi">
                <img src="tapeta_prawa.png" alt="usługi">
                <img src="tapeta_lewa.png" alt="usługi">
            </aside>
            <section>
                <h2>Dla klientów</h2>
                <form method="post">
                    <label for="workers">Ilu co najmniej pracowników potrzebujesz?</label><br>
                    <input type="number" id="workers" name="workers">
                    <button type="submit">Szukaj firm</button>
                </form>
    <?php
    if (!empty($_POST['workers'])) {
        $workers = $_POST['workers'];
        $companies = mysqli_query($connection, "SELECT nazwa_firmy, liczba_pracownikow FROM wykonawcy WHERE liczba_pracownikow >= $workers;");
        while ($row = mysqli_fetch_row($companies)) {
            echo "<p>$row[0], $row[1] pracowników</p>";
        }
    }
    ?>
            </section>
            <section>
                <h2>Dla wykonawców</h2>
                <form method="post">
                    <select name="city">
    <?php
    $cities = mysqli_query($connection, 'SELECT DISTINCT miasto FROM klienci ORDER BY miasto;');
    while ($row = mysqli_fetch_row($cities)) {
        echo "<option>$row[0]</option>";
    }
    ?>
                    </select><br>
                    <input type="radio" id="painting" name="kind" value="malowanie" checked>
                    <label for="painting">malowanie</label><br>
                    <input type="radio" id="plastering" name="kind" value="gipsowanie">
                    <label for="plastering">gipsowanie</label><br>
                    <button type="submit">Szukaj klientów</button>
                </form>
                <ul>
    <?php
    if (isset($_POST['city'])) {
        $city = $_POST['city'];
        $kind = $_POST['kind'];
        $orders = mysqli_query($connection, "SELECT imie, cena FROM klienci JOIN zlecenia ON klienci.id_klienta = zlecenia.id_klienta WHERE miasto = '$city' AND rodzaj = '$kind';");
        while ($row = mysqli_fetch_row($orders)) {
            echo "<li>$row[0] - $row[1]</li>";
        }
    }
    ?>
                </ul>
            </section>
        </main>
        <footer>
            <p><strong>Stronę wykonał: Bartosz Bryniarski</strong></p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (zlecenia.php)
  55. Krok 55: Zamknięcie połączenia z bazą

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

    Na końcu działania zamykane jest połączenie z bazą.

    mysqli_close($connection) na końcu pliku, po wszystkich trzech 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 skrypty 2 i 3 idą po zamkniętym połączeniu - lista miast zostaje pusta.

    zlecenia.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'remonty');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
                <img src="tapeta_lewa.png" alt="usługi">
                <img src="tapeta_prawa.png" alt="usługi">
                <img src="tapeta_lewa.png" alt="usługi">
            </aside>
            <section>
                <h2>Dla klientów</h2>
                <form method="post">
                    <label for="workers">Ilu co najmniej pracowników potrzebujesz?</label><br>
                    <input type="number" id="workers" name="workers">
                    <button type="submit">Szukaj firm</button>
                </form>
    <?php
    if (!empty($_POST['workers'])) {
        $workers = $_POST['workers'];
        $companies = mysqli_query($connection, "SELECT nazwa_firmy, liczba_pracownikow FROM wykonawcy WHERE liczba_pracownikow >= $workers;");
        while ($row = mysqli_fetch_row($companies)) {
            echo "<p>$row[0], $row[1] pracowników</p>";
        }
    }
    ?>
            </section>
            <section>
                <h2>Dla wykonawców</h2>
                <form method="post">
                    <select name="city">
    <?php
    $cities = mysqli_query($connection, 'SELECT DISTINCT miasto FROM klienci ORDER BY miasto;');
    while ($row = mysqli_fetch_row($cities)) {
        echo "<option>$row[0]</option>";
    }
    ?>
                    </select><br>
                    <input type="radio" id="painting" name="kind" value="malowanie" checked>
                    <label for="painting">malowanie</label><br>
                    <input type="radio" id="plastering" name="kind" value="gipsowanie">
                    <label for="plastering">gipsowanie</label><br>
                    <button type="submit">Szukaj klientów</button>
                </form>
                <ul>
    <?php
    if (isset($_POST['city'])) {
        $city = $_POST['city'];
        $kind = $_POST['kind'];
        $orders = mysqli_query($connection, "SELECT imie, cena FROM klienci JOIN zlecenia ON klienci.id_klienta = zlecenia.id_klienta WHERE miasto = '$city' AND rodzaj = '$kind';");
        while ($row = mysqli_fetch_row($orders)) {
            echo "<li>$row[0] - $row[1]</li>";
        }
    }
    ?>
                </ul>
            </section>
        </main>
        <footer>
            <p><strong>Stronę wykonał: Bartosz Bryniarski</strong></p>
        </footer>
    <?php
    mysqli_close($connection);
    ?>
    </body>
    </html>

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

  56. Etap 5: Dobre praktyki (poza kluczem)

    Kroki 56-58 · po tym etapie masz 42 z 42 kryteriów (pkt)

    Krok 56: Refaktor: zapytania przygotowane w skryptach 1 i 3

    Kryteria: ,

    R.5.4 Skrypt 1 wyświetla każdy wiersz zwrócony zmodyfikowanym zapytaniem 2, tak że w warunku podana jest wartość wysłana z formularza

    R.5.7 Skrypt 3 wyświetla każdy wiersz zwrócony zmodyfikowanym zapytaniem 4 , tak że w warunku podane jest miasto i rodzaj remontu wysłane z formularza

    ok. 5 min
    Polecenie z arkusza

    Wysyła do bazy danych zapytanie 2, zmodyfikowane tak, że w warunku podana jest wartość wysłana z formularza.

    Wartości z formularzy szły prosto do zapytań - to SQL injection: lista rozwijana i pola radio tylko podpowiadają, w żądaniu POST można wysłać dowolny napis, np. x' OR '1'='1. W prawdziwych projektach dane trafiają do zapytania przez zapytanie przygotowane: mysqli_prepare z ? w miejscu wartości, mysqli_stmt_bind_param z typami ('i' - liczba całkowita w skrypcie 1, 'ss' - dwa napisy w skrypcie 3), mysqli_stmt_execute i mysqli_stmt_get_result, z którego czytasz pętlą jak dotąd. Stare linie zostają w komentarzu. Podgląd bez zmian.

    Typowe błędy

    Znaki ? w zapytaniu przygotowanym stoją bez apostrofów (miasto = ?, nie miasto = '?') - w apostrofach byłyby zwykłym napisem ze znakiem zapytania i mysqli_stmt_bind_param zgłosiłby błąd liczby parametrów.

    zlecenia.php PHP zmienione: 10 linii
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'remonty');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
                <img src="tapeta_lewa.png" alt="usługi">
                <img src="tapeta_prawa.png" alt="usługi">
                <img src="tapeta_lewa.png" alt="usługi">
            </aside>
            <section>
                <h2>Dla klientów</h2>
                <form method="post">
                    <label for="workers">Ilu co najmniej pracowników potrzebujesz?</label><br>
                    <input type="number" id="workers" name="workers">
                    <button type="submit">Szukaj firm</button>
                </form>
    <?php
    if (!empty($_POST['workers'])) {
        $workers = $_POST['workers'];
        // $companies = mysqli_query($connection, "SELECT nazwa_firmy, liczba_pracownikow FROM wykonawcy WHERE liczba_pracownikow >= $workers;");
        $statement = mysqli_prepare($connection, 'SELECT nazwa_firmy, liczba_pracownikow FROM wykonawcy WHERE liczba_pracownikow >= ?');
        mysqli_stmt_bind_param($statement, 'i', $workers);
        mysqli_stmt_execute($statement);
        $companies = mysqli_stmt_get_result($statement);
        while ($row = mysqli_fetch_row($companies)) {
            echo "<p>$row[0], $row[1] pracowników</p>";
        }
    }
    ?>
            </section>
            <section>
                <h2>Dla wykonawców</h2>
                <form method="post">
                    <select name="city">
    <?php
    $cities = mysqli_query($connection, 'SELECT DISTINCT miasto FROM klienci ORDER BY miasto;');
    while ($row = mysqli_fetch_row($cities)) {
        echo "<option>$row[0]</option>";
    }
    ?>
                    </select><br>
                    <input type="radio" id="painting" name="kind" value="malowanie" checked>
                    <label for="painting">malowanie</label><br>
                    <input type="radio" id="plastering" name="kind" value="gipsowanie">
                    <label for="plastering">gipsowanie</label><br>
                    <button type="submit">Szukaj klientów</button>
                </form>
                <ul>
    <?php
    if (isset($_POST['city'])) {
        $city = $_POST['city'];
        $kind = $_POST['kind'];
        // $orders = mysqli_query($connection, "SELECT imie, cena FROM klienci JOIN zlecenia ON klienci.id_klienta = zlecenia.id_klienta WHERE miasto = '$city' AND rodzaj = '$kind';");
        $statement = mysqli_prepare($connection, 'SELECT imie, cena FROM klienci JOIN zlecenia ON klienci.id_klienta = zlecenia.id_klienta WHERE miasto = ? AND rodzaj = ?');
        mysqli_stmt_bind_param($statement, 'ss', $city, $kind);
        mysqli_stmt_execute($statement);
        $orders = mysqli_stmt_get_result($statement);
        while ($row = mysqli_fetch_row($orders)) {
            echo "<li>$row[0] - $row[1]</li>";
        }
    }
    ?>
                </ul>
            </section>
        </main>
        <footer>
            <p><strong>Stronę wykonał: Bartosz Bryniarski</strong></p>
        </footer>
    <?php
    mysqli_close($connection);
    ?>
    </body>
    </html>

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

  57. Krok 57: Refaktor: escapowanie danych przed wypisaniem

    Kryteria: , ,

    R.5.5 Dane są wyświetlone w postaci „<firma>, <liczbaPracownikow> pracowników”

    R.5.6 Skrypt 2 wyświetla nazwy miast zwrócone zapytaniem 3 jako opcje listy rozwijanej

    R.5.8 Dane są wyświetlane w postaci elementów listy

    ok. 3 min
    Polecenie z arkusza

    Dla każdego zwróconego wiersza wypisuje paragraf o treści <firma>, <liczbaPracownikow> pracowników, gdzie pola <> są pobrane z bazy.

    Wszystkie trzy skrypty wypisują do HTML napisy z bazy: nazwy firm, miasta, imiona. Dziś są niewinne, ale wpisuje je kiedyś człowiek - a napis <script>…</script> w nazwie firmy 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. Wartości przechodzą przez funkcję do zmiennych o angielskich nazwach, a echo używa już tylko ich. Liczby ($row[1]) zostają - nie niosą znaczników. Podgląd bez zmian.

    Typowe błędy

    htmlspecialchars przy zapisie do bazy zamiast przy wypisywaniu to częsty błąd - baza dostaje zniekształcone dane. Escapujesz na wyjściu.

    zlecenia.php PHP zmienione: 6 linii
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'remonty');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
                <img src="tapeta_lewa.png" alt="usługi">
                <img src="tapeta_prawa.png" alt="usługi">
                <img src="tapeta_lewa.png" alt="usługi">
            </aside>
            <section>
                <h2>Dla klientów</h2>
                <form method="post">
                    <label for="workers">Ilu co najmniej pracowników potrzebujesz?</label><br>
                    <input type="number" id="workers" name="workers">
                    <button type="submit">Szukaj firm</button>
                </form>
    <?php
    if (!empty($_POST['workers'])) {
        $workers = $_POST['workers'];
        // $companies = mysqli_query($connection, "SELECT nazwa_firmy, liczba_pracownikow FROM wykonawcy WHERE liczba_pracownikow >= $workers;");
        $statement = mysqli_prepare($connection, 'SELECT nazwa_firmy, liczba_pracownikow FROM wykonawcy WHERE liczba_pracownikow >= ?');
        mysqli_stmt_bind_param($statement, 'i', $workers);
        mysqli_stmt_execute($statement);
        $companies = mysqli_stmt_get_result($statement);
        while ($row = mysqli_fetch_row($companies)) {
            $company = htmlspecialchars($row[0]);
            echo "<p>$company, $row[1] pracowników</p>";
        }
    }
    ?>
            </section>
            <section>
                <h2>Dla wykonawców</h2>
                <form method="post">
                    <select name="city">
    <?php
    $cities = mysqli_query($connection, 'SELECT DISTINCT miasto FROM klienci ORDER BY miasto;');
    while ($row = mysqli_fetch_row($cities)) {
        $city = htmlspecialchars($row[0]);
        echo "<option>$city</option>";
    }
    ?>
                    </select><br>
                    <input type="radio" id="painting" name="kind" value="malowanie" checked>
                    <label for="painting">malowanie</label><br>
                    <input type="radio" id="plastering" name="kind" value="gipsowanie">
                    <label for="plastering">gipsowanie</label><br>
                    <button type="submit">Szukaj klientów</button>
                </form>
                <ul>
    <?php
    if (isset($_POST['city'])) {
        $city = $_POST['city'];
        $kind = $_POST['kind'];
        // $orders = mysqli_query($connection, "SELECT imie, cena FROM klienci JOIN zlecenia ON klienci.id_klienta = zlecenia.id_klienta WHERE miasto = '$city' AND rodzaj = '$kind';");
        $statement = mysqli_prepare($connection, 'SELECT imie, cena FROM klienci JOIN zlecenia ON klienci.id_klienta = zlecenia.id_klienta WHERE miasto = ? AND rodzaj = ?');
        mysqli_stmt_bind_param($statement, 'ss', $city, $kind);
        mysqli_stmt_execute($statement);
        $orders = mysqli_stmt_get_result($statement);
        while ($row = mysqli_fetch_row($orders)) {
            $name = htmlspecialchars($row[0]);
            echo "<li>$name - $row[1]</li>";
        }
    }
    ?>
                </ul>
            </section>
        </main>
        <footer>
            <p><strong>Stronę wykonał: Bartosz Bryniarski</strong></p>
        </footer>
    <?php
    mysqli_close($connection);
    ?>
    </body>
    </html>

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

  58. Krok 58: Refaktor: walidacja liczby i rodzaju remontu

    ok. 3 min
    Polecenie z arkusza

    Wysyła do bazy danych zapytanie 4, zmodyfikowane tak, że w warunku podane są nazwa miasta i rodzaj remontu wysłane z formularza.

    Dwie poprawki na to, co przychodzi z formularzy:

    • skrypt 1: (int) rzutuje wartość na liczbę całkowitą (puste pole albo litery dają 0, nie błąd), a max(0, …) odcina liczby ujemne - formularz z type="number" to tylko wygoda w przeglądarce, nie gwarancja,
    • skrypt 3: rodzaj remontu może mieć tylko dwie wartości, więc sprawdzasz go białą listą in_array($kind, ['malowanie', 'gipsowanie']) i każdą inną wartość zamieniasz na domyślne „malowanie”. Miasta nie walidujesz listą - jest ich 16 i mogą dojść nowe; przed wstrzyknięciem chroni je już zapytanie przygotowane.

    Na egzaminie wystarczy wersja z kroków wcześniejszych - klucz tego nie sprawdza. To już kod gotowy do prawdziwego użycia.

    Typowe błędy

    Walidacja po stronie przeglądarki (type="number", pola radio) nie zastępuje walidacji w PHP - żądanie POST można wysłać bez przeglądarki.

    zlecenia.php PHP zmienione: 4 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'remonty');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Remonty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Malowanie i gipsowanie</h1>
        </header>
        <main>
            <nav>
                <a href="kontakt.html">Kontakt</a>
                <a href="https://remonty.pl" target="_blank">Partnerzy</a>
            </nav>
            <aside>
                <img src="tapeta_lewa.png" alt="usługi">
                <img src="tapeta_prawa.png" alt="usługi">
                <img src="tapeta_lewa.png" alt="usługi">
            </aside>
            <section>
                <h2>Dla klientów</h2>
                <form method="post">
                    <label for="workers">Ilu co najmniej pracowników potrzebujesz?</label><br>
                    <input type="number" id="workers" name="workers">
                    <button type="submit">Szukaj firm</button>
                </form>
    <?php
    if (!empty($_POST['workers'])) {
        $workers = max(0, (int) $_POST['workers']);
        // $companies = mysqli_query($connection, "SELECT nazwa_firmy, liczba_pracownikow FROM wykonawcy WHERE liczba_pracownikow >= $workers;");
        $statement = mysqli_prepare($connection, 'SELECT nazwa_firmy, liczba_pracownikow FROM wykonawcy WHERE liczba_pracownikow >= ?');
        mysqli_stmt_bind_param($statement, 'i', $workers);
        mysqli_stmt_execute($statement);
        $companies = mysqli_stmt_get_result($statement);
        while ($row = mysqli_fetch_row($companies)) {
            $company = htmlspecialchars($row[0]);
            echo "<p>$company, $row[1] pracowników</p>";
        }
    }
    ?>
            </section>
            <section>
                <h2>Dla wykonawców</h2>
                <form method="post">
                    <select name="city">
    <?php
    $cities = mysqli_query($connection, 'SELECT DISTINCT miasto FROM klienci ORDER BY miasto;');
    while ($row = mysqli_fetch_row($cities)) {
        $city = htmlspecialchars($row[0]);
        echo "<option>$city</option>";
    }
    ?>
                    </select><br>
                    <input type="radio" id="painting" name="kind" value="malowanie" checked>
                    <label for="painting">malowanie</label><br>
                    <input type="radio" id="plastering" name="kind" value="gipsowanie">
                    <label for="plastering">gipsowanie</label><br>
                    <button type="submit">Szukaj klientów</button>
                </form>
                <ul>
    <?php
    if (isset($_POST['city'])) {
        $city = $_POST['city'];
        $kind = $_POST['kind'];
        if (!in_array($kind, ['malowanie', 'gipsowanie'])) {
            $kind = 'malowanie';
        }
        // $orders = mysqli_query($connection, "SELECT imie, cena FROM klienci JOIN zlecenia ON klienci.id_klienta = zlecenia.id_klienta WHERE miasto = '$city' AND rodzaj = '$kind';");
        $statement = mysqli_prepare($connection, 'SELECT imie, cena FROM klienci JOIN zlecenia ON klienci.id_klienta = zlecenia.id_klienta WHERE miasto = ? AND rodzaj = ?');
        mysqli_stmt_bind_param($statement, 'ss', $city, $kind);
        mysqli_stmt_execute($statement);
        $orders = mysqli_stmt_get_result($statement);
        while ($row = mysqli_fetch_row($orders)) {
            $name = htmlspecialchars($row[0]);
            echo "<li>$name - $row[1]</li>";
        }
    }
    ?>
                </ul>
            </section>
        </main>
        <footer>
            <p><strong>Stronę wykonał: Bartosz Bryniarski</strong></p>
        </footer>
    <?php
    mysqli_close($connection);
    ?>
    </body>
    </html>

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

  59. Etap 6: Oddanie pracy

    Krok 59 · po tym etapie masz 42 z 42 kryteriów (pkt)

    Krok 59: 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ć: cegla.jpg, import.jpg, kontakt.html, kw1.png … kw5.png, kwerendy.txt, przeglądarka.txt, styl.css, tapeta_lewa.png, tapeta_prawa.png, zlecenia.php. 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 54.

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.