Przejdź do głównej treści
INF.03-04-25.06-SGCzerwiec 2025 · 52 kroki · ok. 106 min · 41 kryteriów z klucza

Rozwiązanie krok po kroku: Portal firmy przewozowej (Wersja podstawowa)

Rozwiązanie buduje portal firmy przewozowej: baza przewozy z czterema kwerendami (SELECT, INSERT, DELETE, JOIN), grafika auto.png z przezroczystym tłem i napisem w perspektywie, strona przewozy.php z tabelą zadań, formularzem i listą usług, style w styles.css oraz dwa skrypty PHP: lista zadań z odnośnikami „Usuń” (kasowanie rekordu) i dodawanie zadania z formularza.

Kroki idą zdanie po zdaniu za treścią arkusza - przy każdym jest polecenie 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 przekierowanie po zapisie. W tym arkuszu nie ma JavaScriptu.

Obok jest druga wersja tego rozwiązania - zakładka Nowoczesny kod (grid, PDO): te same punkty, ale sekcje na siatce CSS, PHP na PDO, sprawdzanie id i daty przy wejściu oraz przekierowanie po zapisie od pierwszej linijki.

Grafiki i baza pochodzą z prawdziwego archiwum pliki11 (załącznik do pobrania na stronie arkusza, hasło w treści zadania). Grafika auta jest złożona z plików archiwum tak, jak każe arkusz; zrzuty wyników zapytań są odtworzone z tej bazy - na egzaminie robisz je sam, z całego ekranu.

Jak liczą się punkty. Klucz ma 41 kryteriów (R.1.1 - R.5.9), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 31 punktów - 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-7 · po tym etapie masz 6 z 41 kryteriów (pkt)

    Krok 1: Baza danych przewozy

    Kryteria:

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

    ok. 2 min
    Polecenie z arkusza

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

    W przeglądarce otwierasz http://localhost/phpmyadmin, klikasz Nowa w lewym panelu, wpisujesz nazwę przewozy i z listy metod porównywania wybierasz utf8_unicode_ci (arkusz podaje ją jako przykład; utf8_polish_ci też jest poprawne). Tabele z pliku SQL mają jednak zestaw utf8 i porównywanie utf8_polish_ci zapisane we własnej definicji, więc opisy zadań („Przewóz mebli z ulicy Nawrot…”) zachowają ogonki przy każdym wyborze - ustawienie bazy dziedziczą tylko tabele zakładane później bez własnego ustawienia.

    Typowe błędy

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

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

    Kryteria:

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

    ok. 2 min
    Polecenie z arkusza

    Z rozpakowanego archiwum zaimportuj tabelę z pliku baza.sql do utworzonej bazy.

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

    Po imporcie są dwie tabele z ilustracji 1: osoby (id_osoba, imie, nazwisko, telefon - 6 wierszy) i zadania (id_zadania, zadanie, data, osoba_id - 7 wierszy). Kolumna osoba_id w zadaniach to klucz obcy do id_osoba - jedna osoba ma wiele zadań. Uwaga: klucze nazywają się tu id_osoba i id_zadania, nie po prostu id - w zapytaniach trzeba pisać dokładnie tak.

    Typowe błędy

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

  3. Krok 3: Zrzut ekranu import.png

    Kryteria:

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

    ok. 2 min
    Polecenie z arkusza

    Wykonaj zrzut ekranu po imporcie. Zapisz zrzut w formacie PNG pod nazwą import. Nie kadruj zrzutu. Powinien on obejmować cały ekran monitora, z widocznym paskiem zadań.

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

    Typowe błędy

    Wykadrowany zrzut (samo okno przeglądarki, bez paska zadań) nie jest oceniany - klucz mówi to wprost.

  4. Krok 4: Zapytanie 1: lista zadań

    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 id_zadania, zadanie i datę z tabeli zadania . W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT id_zadania, zadanie, data FROM zadania; oraz wynik działania udokumentowany zrzutem, na którym widocznych jest dokładnie 7 lub 8 rekordów i wyświetlone jedynie kolumny id_zadania, zadanie i data

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 1: wybierające jedynie id_zadania, zadanie i datę z tabeli zadania.

    Zakładka SQL w phpMyAdmin (przy zaznaczonej bazie przewozy). Klauzula po klauzuli:

    • SELECT id_zadania, zadanie, data - dokładnie trzy kolumny z arkusza; SELECT * dołożyłby osoba_id. Kolumna nazywa się data (tak jak w tabeli), choć to słowo kluczowe w wielu językach - w MySQL działa bez problemu.
    • FROM zadania - bez WHERE, bo chcesz wszystkie zadania.

    Wynik: 7 wierszy (wykonujesz zapytania w kolejności z arkusza, więc jeszcze przed wstawieniem i usunięciem - klucz dopuszcza 7 lub 8). Zapytanie kopiujesz do pliku kwerendy.txt i robisz zrzut: kw1.png. To samo zapytanie wyśle skrypt 1.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT id_zadania, zadanie, data FROM zadania;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: nowe zadanie dla osoby o id 1

    Kryteria:

    R.1.4 Wykonane zapytanie 2 wstawiające do tabeli zadania rekord z danymi: „spotkanie firmowe”, dnia „2024-05-10” dla osoby o id = 1 . W pliku z kwerendami lub na zrzucie istnieje zapis np.: INSERT INTO zadania VALUES (NULL, 'Spotkanie firmowe', '2024-05- 10', 1); oraz wynik działania udokumentowany zrzutem, na którym widoczny jest komunikat o poprawnie wstawionym rekordzie lub tabela z dodanym rekordem

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 2: wstawiające do tabeli zadania rekord z danymi: „spotkanie firmowe”, dnia „2024-05-10” dla osoby o id równym 1.

    Wstawianie wiersza to INSERT INTO zadania (...) VALUES (...). W nawiasie po nazwie tabeli wymieniasz kolumny bez id_zadania - klucz główny ma autonumerację, więc baza sama nada mu wartość 8. Wartości w VALUES w tej samej kolejności: opis zadania i data w apostrofach (napisy; data w formacie RRRR-MM-DD), osoba_id jako liczba 1.

    Klucz pokazuje też krótszy wariant INSERT INTO zadania VALUES (NULL, 'spotkanie firmowe', '2024-05-10', 1) - bez listy kolumn trzeba podać wszystkie cztery wartości, a NULL w miejscu klucza oznacza „nadaj sam”. Zrzut kw2.png ma pokazać komunikat o wstawieniu wiersza. Ten sam INSERT (z danymi z formularza) wyśle skrypt 2.

    Typowe błędy

    Data '10-05-2024' zamiast '2024-05-10' - MySQL zapisze 0000-00-00 albo odrzuci wiersz. Daty zawsze RRRR-MM-DD.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT id_zadania, zadanie, data FROM zadania;
    
    -- Zapytanie 2
    INSERT INTO zadania (zadanie, data, osoba_id) VALUES ('spotkanie firmowe', '2024-05-10', 1);
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: usunięcie zadania o id 2

    Kryteria:

    R.1.5 Wykonane zapytanie 3 usuwające rekord o id_zadania = 2 z tabeli zadania. W pliku z kwerendami lub na zrzucie istnieje zapis np.: DELETE FROM zadania WHERE id_zadania = 2; oraz wynik działania udokumentowany zrzutem, na którym widoczny jest komunikat o poprawnie usuniętym rekordzie lub widoczna tabela bez rekordu o id_zadania = 2

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 3: usuwające rekord o id_zadania równym 2 z tabeli zadania.

    DELETE FROM zadania WHERE id_zadania = 2 - usuwanie wierszy to DELETE FROM z nazwą tabeli, a WHERE mówi który. Klucz główny jest liczbą, więc bez apostrofów. phpMyAdmin zapyta o potwierdzenie. Znika „Przewóz telewizora z ulicy Marszałkowskiej…”. Zrzut kw3.png ma pokazać komunikat o usunięciu rekordu.

    Ten sam DELETE (z id z odnośnika) wyśle skrypt 1 po kliknięciu „Usuń”.

    Typowe błędy

    DELETE FROM zadania bez WHERE kasuje wszystkie zadania - i nie ma cofania. Przy DELETE zawsze najpierw uruchom SELECT z tym samym WHERE.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT id_zadania, zadanie, data FROM zadania;
    
    -- Zapytanie 2
    INSERT INTO zadania (zadanie, data, osoba_id) VALUES ('spotkanie firmowe', '2024-05-10', 1);
    
    -- Zapytanie 3
    DELETE FROM zadania WHERE id_zadania = 2;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: kto ma jakie zadanie

    Kryteria:

    R.1.6 Wykonane zapytanie 4 wybierające jedynie imię, nazwisko i numer telefonu osoby oraz odpowiadające im zadanie i datę z tabeli zadania . W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT imie, nazwisko, telefon, zadanie, data FROM zadania INNER JOIN osoby ON id_osoba = osoba_id; (lub samo JOIN, LEFT JOIN, możliwe też porównanie kluczy po WHERE) oraz wynik działania udokumentowany zrzutem, na którym widoczne są rekordy zawierające jedynie pola imie, nazwisko, telefon, zadanie, data

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 4: wybierające jedynie imię, nazwisko i numer telefonu osoby oraz odpowiadające im zadanie i datę z tabeli zadania. Należy posłużyć się relacją.

    Imię, nazwisko i telefon są w osoby, zadanie i data w zadania - „posłużyć się relacją” znaczy złączyć tabele po kluczu.

    • SELECT imie, nazwisko, telefon, zadanie, data - trzy kolumny z jednej tabeli, dwie z drugiej.
    • FROM zadania JOIN osoby ON osoby.id_osoba = zadania.osoba_id - do każdego zadania dokładasz wiersz osoby, której id_osoba równa się osoba_id zadania. ON mówi, po których kolumnach pasują wiersze; nazwy poprzedzasz tabelą dla czytelności.

    Wynik: 7 wierszy (po wstawieniu i usunięciu z poprzednich kroków), w tym nowe „spotkanie firmowe” przypisane Annie. Zrzut: kw4.png. Plik kwerendy.txt ma komplet czterech zapytań.

    Typowe błędy

    JOIN bez ON daje iloczyn kartezjański: 7 · 6 = 42 wiersze z przypadkowymi osobami. Klucz przyjmuje INNER JOIN, LEFT JOIN i starszy zapis FROM zadania, osoby WHERE id_osoba = osoba_id.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT id_zadania, zadanie, data FROM zadania;
    
    -- Zapytanie 2
    INSERT INTO zadania (zadanie, data, osoba_id) VALUES ('spotkanie firmowe', '2024-05-10', 1);
    
    -- Zapytanie 3
    DELETE FROM zadania WHERE id_zadania = 2;
    
    -- Zapytanie 4
    SELECT imie, nazwisko, telefon, zadanie, data FROM zadania JOIN osoby ON osoby.id_osoba = zadania.osoba_id;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Grafika i strona HTML

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

    Krok 8: Grafika auto.png: przezroczyste tło i szerokość 400 px

    Kryteria:

    R.2.1 Grafika auto.png zawiera przezroczyste tło oraz jej szerokość to 400 px, odpowiadająca temu wysokość to 200 px (± 5 px)

    ok. 5 min
    Polecenie z arkusza

    Grafika o nazwie auto w formacie PNG, przygotowana na podstawie grafiki auto1.jpg. Tło przezroczyste. Przeskalowana z zachowaniem proporcji do szerokości 400 px.

    Plik auto1.jpg z archiwum ma 1200 × 600 px i białe tło. W GIMP-ie:

    1. Warstwa → Przezroczystość → Dodaj kanał alfa - JPEG nie ma przezroczystości, trzeba ją najpierw włączyć,
    2. narzędzie Zaznaczenie rozmyte (różdżka) z małym progiem, klik w białe tło, Delete - tło znika (szachownica). Próg musi być mały, bo auto też jest prawie białe,
    3. Obraz → Skaluj obraz, szerokość 400 px z zamkniętym łańcuchem proporcji - wysokość wychodzi 200 px (klucz: 200 ±5),
    4. Plik → Wyeksportuj jako auto.png - tylko PNG zachowa przezroczystość.
    Typowe błędy

    Eksport do .jpg zabija przezroczystość (tło wraca białe) - klucz R.2.1 sprawdza przezroczyste tło. Zbyt duży próg różdżki „zjada” białą karoserię razem z tłem.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 8
  9. Krok 9: Grafika auto.png: napisy na boku auta w perspektywie

    Kryteria:

    R.2.2 Tekst z grafiki napisy.jpg jest nałożony na bok samochodu utworzonej grafiki. Rozmiar tekstu nie przekracza rozmiaru boku samochodu oraz transformowano jego kształt jak na ilustracji 2 w arkuszu egzaminacyjnym

    ok. 6 min
    Polecenie z arkusza

    Na boku samochodu wklejone napisy z pliku napisy.jpg dopasowane do wielkości i położenia samochodu, zgodnie z ilustracją 2 (należy zastosować perspektywę).

    Plik z napisami (w archiwum napisy.png, zielone tło) wczytujesz jako nową warstwę (Plik → Otwórz jako warstwy). Zielone tło usuwasz różdżką (tu próg może być większy - kolor jest jednolity), warstwę skalujesz tak, żeby napis zmieścił się na boku furgonu (ok. 180 px szerokości), i przesuwasz na bok auta.

    Perspektywa: narzędzie Perspektywa (Shift+P) - przeciągasz narożniki warstwy tak, żeby napis „położył się” na boku auta: lewa krawędź nieco wyżej, prawa niżej i mniejsza - zgodnie z kierunkiem, w którym stoi auto na ilustracji 2. Klucz R.2.2 sprawdza, że tekst nie wychodzi poza bok samochodu i że jego kształt został przekształcony. Eksport ponownie do auto.png.

    ⚠️ Rozbieżność arkusz-załącznik: arkusz mówi o pliku napisy.jpg, a w archiwum pliki11 jest napisy.png - to ten sam obraz, użyj pliku z archiwum.

    Typowe błędy

    Napis wklejony „płasko”, bez perspektywy, nie spełnia R.2.2 nawet jeśli leży na boku auta - arkusz mówi wprost „należy zastosować perspektywę”. Napis większy niż bok auta też odpada.

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

  10. Krok 10: Plik przewozy.php w HTML5

    Kryteria:

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

    ok. 2 min
    Polecenie z arkusza

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

    W edytorze zaznaczającym składnię tworzysz plik przewozy.php w folderze zdającego, obok auto.png i zrzutów. 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).

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

    Kryteria:

    R.2.3 W pliku przewozy.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.

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

    Kryteria:

    R.2.3 W pliku przewozy.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 „Półdarmo” i opisy zadań z bazy mogłyby wyświetlić się jako krzaki. Plik trzeba też zapisać w UTF-8.

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

    Kryteria:

    R.2.4 Nadany tytuł stronie "Firma Przewozowa”

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony „Firma Przewozowa” widoczny na karcie przeglądarki.

    <title> w <head> - napis na karcie przeglądarki, nie nagłówek na stronie (ten będzie inny: „Firma przewozowa Półdarmo”). Klucz R.2.4 sprawdza tekst „Firma Przewozowa”.

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

    Kryteria:

    R.3.2 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styles.css formatowanie CSS pochodzi jedynie z tego arkusza

    ok. 1 min
    Polecenie z arkusza

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

    <link rel="stylesheet" href="styles.css"> w <head>. Uwaga na nazwę: styles.css z „s” na końcu - klucz R.3.2 sprawdza dokładnie tę nazwę. Plik napiszesz po HTML.

    Typowe błędy

    href="styl.css" z przyzwyczajenia po innych arkuszach - przeglądarka nie znajdzie pliku i cały rezultat R.4 przepada.

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

    Kryteria:

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

    ok. 2 min
    Polecenie z arkusza

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

    Z ilustracji 3: blok nagłówkowy, pod nim nawigacyjny, potem blok główny z dwiema sekcjami obok siebie (lewa szeroka, prawa wąska), na dole stopka. Klucz R.2.5 wymienia znaczniki wprost: <header>, <nav>, <main> z dwoma <section> w środku, <footer>.

    Sekcje dostają identyfikatory left i right, bo każda będzie stylowana inaczej. Nazwy identyfikatorów, klas i zmiennych piszesz w tym rozwiązaniu po angielsku (arkusz dopuszcza polski lub angielski, wymaga konsekwencji); wyjątkiem są nazwy narzucone przez arkusz (przewozy.php, styles.css, kolumny bazy). To, że sekcje staną obok siebie, zrobi CSS.

    Typowe błędy

    Sekcje poza <main> nie spełniają R.2.5 - klucz mówi „main → section + section”, czyli sekcje wewnątrz bloku głównego.

    przewozy.php PHP zmienione: 12 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Firma Przewozowa</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
        </header>
        <nav>
        </nav>
        <main>
            <section id="left">
            </section>
            <section id="right">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (przewozy.php)
  16. Krok 16: Nagłówek strony

    Kryteria:

    R.2.6 Zapisane w bloku nagłówkowym <h1>: "Firma przewozowa Półdarmo", w sekcji lewej <h2>: "Zadania do wykonania", w sekcji prawej <h3>: "Nasza specjalność" oraz w stopce paragraf z numerem zdającego

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku nagłówkowego: nagłówek pierwszego stopnia o treści: „Firma przewozowa Półdarmo”.

    Pierwszego stopnia to <h1>. Egzaminator sprawdza dokładnie ten znacznik - w sekcjach będą <h2> i <h3>, więc nie mieszaj stopni (klucz R.2.6 wymienia wszystkie trzy).

    przewozy.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Firma Przewozowa</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Firma przewozowa Półdarmo</h1>
        </header>
        <nav>
        </nav>
        <main>
            <section id="left">
            </section>
            <section id="right">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (przewozy.php)
  17. Krok 17: Blok nawigacyjny: odnośniki do zrzutów kwerend

    Kryteria:

    R.3.6 Wybranie przynajmniej jednego odnośnika powoduje wyświetlenie lub pobranie zrzutu ekranu z kwerendą

    ok. 2 min
    Polecenie z arkusza

    Zawartość bloku nawigacyjnego: odnośniki o treści kwerenda1, kwerenda2, kwerenda3, kwerenda4 do plików zawierających zrzuty ekranu kwerend.

    Cztery odnośniki <a> - tekst z arkusza, a w href nazwy plików ze zrzutami z etapu bazy danych (kw1.png … kw4.png), które leżą w tym samym folderze. Kliknięcie otwiera zrzut w przeglądarce (klucz R.3.6 sprawdza przynajmniej jeden). W podglądzie kliknięcia są zablokowane - podgląd pokazuje jedną stronę.

    Typowe błędy

    href="C:\Users\...\kw1.png" działa tylko na komputerze autora. Ścieżka ma być względna, a plik obok przewozy.php.

    przewozy.php PHP zmienione: 4 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Firma Przewozowa</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Firma przewozowa Półdarmo</h1>
        </header>
        <nav>
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <main>
            <section id="left">
            </section>
            <section id="right">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (przewozy.php)
  18. Krok 18: Sekcja lewa: nagłówek „Zadania do wykonania”

    Kryteria:

    R.2.6 Zapisane w bloku nagłówkowym <h1>: "Firma przewozowa Półdarmo", w sekcji lewej <h2>: "Zadania do wykonania", w sekcji prawej <h3>: "Nasza specjalność" oraz w stopce paragraf z numerem zdającego

    ok. 1 min
    Polecenie z arkusza

    Zawartość sekcji lewej: nagłówek drugiego stopnia o treści: „Zadania do wykonania”.

    Drugiego stopnia to <h2>, w sekcji left.

    przewozy.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Firma Przewozowa</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Firma przewozowa Półdarmo</h1>
        </header>
        <nav>
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <main>
            <section id="left">
                <h2>Zadania do wykonania</h2>
            </section>
            <section id="right">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (przewozy.php)
  19. Krok 19: Sekcja lewa: tabela z wierszem nagłówkowym

    Kryteria:

    R.2.8 W sekcji lewej umieszczona tabela o 3 kolumnach i wierszu z komórkami nagłówkowymi, (w skrypcie lub HTML) zastosowane znaczniki <table>, <tr>, <th>, <td>

    ok. 2 min
    Polecenie z arkusza

    Tabela o trzech kolumnach i wierszu z komórkami nagłówkowymi o treści: „Zadanie do wykonania”, „Data realizacji”, „Akcja”. Tabela jest wypełniona i modyfikowana przez skrypt 1. Jest dodatkowo modyfikowana przez skrypt 2.

    <table> z jednym wierszem <tr> i komórkami nagłówkowymi <th> - nie <td>. Kolejne wiersze (zadania z bazy, z odnośnikiem „Usuń” w trzeciej kolumnie) dopisze skrypt 1; skrypt 2 „modyfikuje” tabelę pośrednio - dodaje rekord do bazy, który skrypt 1 od razu wyświetli.

    przewozy.php PHP zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Firma Przewozowa</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Firma przewozowa Półdarmo</h1>
        </header>
        <nav>
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <main>
            <section id="left">
                <h2>Zadania do wykonania</h2>
                <table>
                    <tr><th>Zadanie do wykonania</th><th>Data realizacji</th><th>Akcja</th></tr>
                </table>
            </section>
            <section id="right">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (przewozy.php)
  20. Krok 20: Sekcja lewa: formularz

    Kryteria:

    R.3.3 Formularz wysyła dane metodą POST do tego samego pliku (poprawne również bez atrybutu action)

    ok. 1 min
    Polecenie z arkusza

    Formularz wysyłający dane do tego samego pliku metodą bezpieczną, obsługiwany przez skrypt 2.

    „Metoda bezpieczna” w rozumieniu arkusza to POST - dane nie pojawiają się w adresie strony, jak przy GET. 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="przewozy.php" - klucz R.3.3 przyjmuje oba warianty.

    Typowe błędy

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

    przewozy.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Firma Przewozowa</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Firma przewozowa Półdarmo</h1>
        </header>
        <nav>
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <main>
            <section id="left">
                <h2>Zadania do wykonania</h2>
                <table>
                    <tr><th>Zadanie do wykonania</th><th>Data realizacji</th><th>Akcja</th></tr>
                </table>
                <form method="post">
                </form>
            </section>
            <section id="right">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (przewozy.php)
  21. Krok 21: Formularz: pole zadania z etykietą

    Kryteria:

    R.2.7 W sekcji lewej umieszczony formularz zawierający dwa pola edycyjne i przycisk "Dodaj" oraz zastosowana przynajmniej jedna etykieta <label>

    ok. 2 min
    Polecenie z arkusza

    Pole edycyjne, poprzedzone etykietą „Zadanie do wykonania: ”.

    Etykieta <label> z atrybutem for wskazującym id pola - kliknięcie w napis ustawia kursor w polu (klucz R.2.7 wymaga przynajmniej jednej etykiety). Pole <input type="text"> z name="task" - pod tą nazwą wartość trafi do PHP. „Poprzedzone etykietą” to etykieta przed polem, w tej samej linii, jak na ilustracji 2; <br> po polu przenosi następną parę do nowej linii.

    przewozy.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Firma Przewozowa</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Firma przewozowa Półdarmo</h1>
        </header>
        <nav>
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <main>
            <section id="left">
                <h2>Zadania do wykonania</h2>
                <table>
                    <tr><th>Zadanie do wykonania</th><th>Data realizacji</th><th>Akcja</th></tr>
                </table>
                <form method="post">
                    <label for="task">Zadanie do wykonania: </label>
                    <input type="text" id="task" name="task"><br>
                </form>
            </section>
            <section id="right">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (przewozy.php)
  22. Krok 22: Formularz: pole daty z etykietą

    Kryteria: ,

    R.2.7 W sekcji lewej umieszczony formularz zawierający dwa pola edycyjne i przycisk "Dodaj" oraz zastosowana przynajmniej jedna etykieta <label>

    R.3.5 Pole edycyjne do wpisania daty jest typu date

    ok. 1 min
    Polecenie z arkusza

    Pole edycyjne przeznaczone do wpisania daty, poprzedzone etykietą „Data realizacji: ”.

    „Pole przeznaczone do wpisania daty” to <input type="date"> - przeglądarka pokazuje kalendarz, a wartość wysyła zawsze w formacie RRRR-MM-DD, dokładnie takim, jakiego potrzebuje baza. Klucz R.3.5 sprawdza typ date wprost. Na ilustracji 2 widać pole z maską dd.mm.rrrr - to właśnie type="date".

    Typowe błędy

    type="text" dla daty to strata R.3.5, a użytkownik mógłby wpisać 10.05.2024 - MySQL nie przyjmie takiego formatu.

    przewozy.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Firma Przewozowa</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Firma przewozowa Półdarmo</h1>
        </header>
        <nav>
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <main>
            <section id="left">
                <h2>Zadania do wykonania</h2>
                <table>
                    <tr><th>Zadanie do wykonania</th><th>Data realizacji</th><th>Akcja</th></tr>
                </table>
                <form method="post">
                    <label for="task">Zadanie do wykonania: </label>
                    <input type="text" id="task" name="task"><br>
                    <label for="date">Data realizacji: </label>
                    <input type="date" id="date" name="date"><br>
                </form>
            </section>
            <section id="right">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (przewozy.php)
  23. Krok 23: Formularz: przycisk „Dodaj”

    Kryteria: ,

    R.2.7 W sekcji lewej umieszczony formularz zawierający dwa pola edycyjne i przycisk "Dodaj" oraz zastosowana przynajmniej jedna etykieta <label>

    R.3.4 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 „Dodaj” wysyłający dane z formularza.

    <button type="submit">Dodaj</button> wewnątrz formularza wysyła go po kliknięciu. Klucz R.3.4 przyjmuje też <input type="submit" value="Dodaj">. Sekcja lewa jest kompletna.

    przewozy.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Firma Przewozowa</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Firma przewozowa Półdarmo</h1>
        </header>
        <nav>
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <main>
            <section id="left">
                <h2>Zadania do wykonania</h2>
                <table>
                    <tr><th>Zadanie do wykonania</th><th>Data realizacji</th><th>Akcja</th></tr>
                </table>
                <form method="post">
                    <label for="task">Zadanie do wykonania: </label>
                    <input type="text" id="task" name="task"><br>
                    <label for="date">Data realizacji: </label>
                    <input type="date" id="date" name="date"><br>
                    <button type="submit">Dodaj</button>
                </form>
            </section>
            <section id="right">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (przewozy.php)
  24. Krok 24: Sekcja prawa: obraz auta

    Kryteria:

    R.2.9 W sekcji prawej umieszczony obraz auta z tekstem alternatywnym "auto firmowe"

    ok. 1 min
    Polecenie z arkusza

    Zawartość sekcji prawej: obraz auto.png z tekstem alternatywnym o treści: „auto firmowe”.

    <img src="auto.png" alt="auto firmowe"> - plik PNG złożony w etapie grafiki, leżący obok przewozy.php. Tekst alternatywny dokładnie z arkusza (klucz R.2.9). Dzięki przezroczystemu tłu auto „stoi” na zielonym tle sekcji - to zobaczysz po CSS.

    przewozy.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Firma Przewozowa</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Firma przewozowa Półdarmo</h1>
        </header>
        <nav>
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <main>
            <section id="left">
                <h2>Zadania do wykonania</h2>
                <table>
                    <tr><th>Zadanie do wykonania</th><th>Data realizacji</th><th>Akcja</th></tr>
                </table>
                <form method="post">
                    <label for="task">Zadanie do wykonania: </label>
                    <input type="text" id="task" name="task"><br>
                    <label for="date">Data realizacji: </label>
                    <input type="date" id="date" name="date"><br>
                    <button type="submit">Dodaj</button>
                </form>
            </section>
            <section id="right">
                <img src="auto.png" alt="auto firmowe">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (przewozy.php)
  25. Krok 25: Sekcja prawa: nagłówek „Nasza specjalność”

    Kryteria:

    R.2.6 Zapisane w bloku nagłówkowym <h1>: "Firma przewozowa Półdarmo", w sekcji lewej <h2>: "Zadania do wykonania", w sekcji prawej <h3>: "Nasza specjalność" oraz w stopce paragraf z numerem zdającego

    ok. 1 min
    Polecenie z arkusza

    Nagłówek trzeciego stopnia o treści: „Nasza specjalność”.

    Trzeciego stopnia to <h3>, pod obrazem.

    przewozy.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Firma Przewozowa</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Firma przewozowa Półdarmo</h1>
        </header>
        <nav>
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <main>
            <section id="left">
                <h2>Zadania do wykonania</h2>
                <table>
                    <tr><th>Zadanie do wykonania</th><th>Data realizacji</th><th>Akcja</th></tr>
                </table>
                <form method="post">
                    <label for="task">Zadanie do wykonania: </label>
                    <input type="text" id="task" name="task"><br>
                    <label for="date">Data realizacji: </label>
                    <input type="date" id="date" name="date"><br>
                    <button type="submit">Dodaj</button>
                </form>
            </section>
            <section id="right">
                <img src="auto.png" alt="auto firmowe">
                <h3>Nasza specjalność</h3>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (przewozy.php)
  26. Krok 26: Sekcja prawa: lista usług

    Kryteria:

    R.2.10 W sekcji prawej umieszczona jedna lista punktowana z 5 elementami, zastosowane znaczniki <ul>, <li>

    ok. 2 min
    Polecenie z arkusza

    Lista punktowana (nieuporządkowana) zawierająca elementy: „Przeprowadzki”, „Przewóz mebli”, „Przesyłki gabarytowe”, „Wynajem pojazdów”, „Zakupy towarów”.

    Lista nieuporządkowana <ul> z pięcioma <li> - teksty dokładnie z arkusza, w tej kolejności. Klucz R.2.10 liczy elementy: ma być pięć. Punktory zmienisz w CSS na okręgi.

    Typowe błędy

    <ol> to lista uporządkowana (numerowana). Arkusz mówi „punktowana (nieuporządkowana)”, czyli <ul>.

    przewozy.php PHP zmienione: 7 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Firma Przewozowa</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Firma przewozowa Półdarmo</h1>
        </header>
        <nav>
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <main>
            <section id="left">
                <h2>Zadania do wykonania</h2>
                <table>
                    <tr><th>Zadanie do wykonania</th><th>Data realizacji</th><th>Akcja</th></tr>
                </table>
                <form method="post">
                    <label for="task">Zadanie do wykonania: </label>
                    <input type="text" id="task" name="task"><br>
                    <label for="date">Data realizacji: </label>
                    <input type="date" id="date" name="date"><br>
                    <button type="submit">Dodaj</button>
                </form>
            </section>
            <section id="right">
                <img src="auto.png" alt="auto firmowe">
                <h3>Nasza specjalność</h3>
                <ul>
                    <li>Przeprowadzki</li>
                    <li>Przewóz mebli</li>
                    <li>Przesyłki gabarytowe</li>
                    <li>Wynajem pojazdów</li>
                    <li>Zakupy towarów</li>
                </ul>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (przewozy.php)
  27. Krok 27: Stopka z numerem zdającego

    Kryteria:

    R.2.6 Zapisane w bloku nagłówkowym <h1>: "Firma przewozowa Półdarmo", w sekcji lewej <h2>: "Zadania do wykonania", w sekcji prawej <h3>: "Nasza specjalność" oraz w stopce paragraf z numerem zdającego

    ok. 1 min
    Polecenie z arkusza

    Zawartość stopki: paragraf o treści „Stronę wykonał: ”, dalej wstawiony numer zdającego.

    Paragraf <p> w <footer>. W tym rozwiązaniu po dwukropku stoi nazwisko autora - na egzaminie wpisujesz swój numer zdającego (PESEL), bo tego sprawdza klucz R.2.6. Struktura HTML strony jest kompletna - dalej style.

    przewozy.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Firma Przewozowa</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Firma przewozowa Półdarmo</h1>
        </header>
        <nav>
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <main>
            <section id="left">
                <h2>Zadania do wykonania</h2>
                <table>
                    <tr><th>Zadanie do wykonania</th><th>Data realizacji</th><th>Akcja</th></tr>
                </table>
                <form method="post">
                    <label for="task">Zadanie do wykonania: </label>
                    <input type="text" id="task" name="task"><br>
                    <label for="date">Data realizacji: </label>
                    <input type="date" id="date" name="date"><br>
                    <button type="submit">Dodaj</button>
                </form>
            </section>
            <section id="right">
                <img src="auto.png" alt="auto firmowe">
                <h3>Nasza specjalność</h3>
                <ul>
                    <li>Przeprowadzki</li>
                    <li>Przewóz mebli</li>
                    <li>Przesyłki gabarytowe</li>
                    <li>Wynajem pojazdów</li>
                    <li>Zakupy towarów</li>
                </ul>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (przewozy.php)
  28. Etap 3: Styl CSS

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

    Krok 28: Plik styles.css: ciało strony

    Kryteria:

    R.4.1 Ustawione dla selektora body: kolor tła #FFCC99, krój czcionki Garamond, wyrównanie tekstu do środka

    ok. 2 min
    Polecenie z arkusza

    Dla selektora ciała strony: kolor tła #FFCC99, krój czcionki Garamond, wyrównanie tekstu do środka.

    Utwórz plik styles.css obok przewozy.php. Selektor znacznika body - arkusz zastrzega, że ciało strony stylujesz wyłącznie selektorem znacznika. Kolor szesnastkowy #FFCC99 (brzoskwiniowy) przepisujesz z arkusza, krój Garamond, text-align: center wyśrodkowuje tekst całej strony (dziedziczą go wszystkie bloki). W podglądzie zmienia się tło i czcionka - znak, że <link> zadziałał.

    styles.css CSS nowy plik
    body {
        background-color: #FFCC99;
        font-family: Garamond;
        text-align: center;
    }
    Podgląd strony po tym kroku (przewozy.php)
  29. Krok 29: Nagłówek i stopka: wspólne style

    Kryteria: , , ,

    R.4.2 Ustawione kolory tła: DarkGreen dla bloku nagłówkowego i stopki oraz MediumSeaGreen dla bloku nawigacyjnego i sekcji prawej

    R.4.3 Ustawiony biały kolor czcionki dla bloków nagłówkowego i stopki oraz dla selektora a

    R.4.4 Ustawione wyrównanie tekstu do lewej dla sekcji prawej, odstępy między wyrazami 90px dla bloku nawigacyjnego oraz rozmiar czcionki 140% dla bloku nagłówkowego i stopki

    R.4.7 Ustawione marginesy wewnętrzne (padding) dla bloku nagłówkowego i stopki 30px, dla bloku nawigacyjnego 20px, dla komórek tabeli 5px oraz marginesy zewnętrzne (margin) dla selektora table górny i dolny:30px, pozostałe: auto

    ok. 2 min
    Polecenie z arkusza

    Dla bloku nagłówkowego i stopki: kolor tła DarkGreen, biały kolor czcionki, marginesy wewnętrzne: 30 px, rozmiar czcionki 140%.

    Wspólne cechy dwóch bloków piszesz raz - selektory po przecinku header, footer. Margines wewnętrzny to padding (zewnętrzny byłby margin). font-size: 140% powiększa tekst względem domyślnego; dla <h1> w nagłówku to 140% podstawy, od której nagłówek liczy swoją wielkość - stąd duży napis jak na ilustracji 2.

    Typowe błędy

    margin: 30px zamiast padding: 30px - odstęp będzie na zewnątrz zielonego pola. Kryterium R.4.7 mówi o marginesach wewnętrznych.

    styles.css CSS zmienione: 6 linii
    body {
        background-color: #FFCC99;
        font-family: Garamond;
        text-align: center;
    }
    header, footer {
        background-color: DarkGreen;
        color: white;
        padding: 30px;
        font-size: 140%;
    }
    Podgląd strony po tym kroku (przewozy.php)
  30. Krok 30: Blok nawigacyjny: tło, odstępy między wyrazami, margines wewnętrzny

    Kryteria: , ,

    R.4.2 Ustawione kolory tła: DarkGreen dla bloku nagłówkowego i stopki oraz MediumSeaGreen dla bloku nawigacyjnego i sekcji prawej

    R.4.4 Ustawione wyrównanie tekstu do lewej dla sekcji prawej, odstępy między wyrazami 90px dla bloku nawigacyjnego oraz rozmiar czcionki 140% dla bloku nagłówkowego i stopki

    R.4.7 Ustawione marginesy wewnętrzne (padding) dla bloku nagłówkowego i stopki 30px, dla bloku nawigacyjnego 20px, dla komórek tabeli 5px oraz marginesy zewnętrzne (margin) dla selektora table górny i dolny:30px, pozostałe: auto

    ok. 2 min
    Polecenie z arkusza

    Dla bloku nawigacyjnego: kolor tła MediumSeaGreen, odstępy między wyrazami: 90 px, marginesy wewnętrzne 20 px.

    Selektor nav. „Odstępy między wyrazami” to właściwość word-spacing - rozsuwa odnośniki, bo każdy z nich to jedno słowo („kwerenda1”, „kwerenda2”…) oddzielone spacją. Dzięki temu cztery odnośniki rozkładają się na szerokości paska jak na ilustracji 2, bez żadnych dodatkowych znaczników.

    Typowe błędy

    letter-spacing rozsuwa litery, nie wyrazy - napisy rozciągną się, a odnośniki zostaną obok siebie. Arkusz mówi „między wyrazami”.

    styles.css CSS zmienione: 5 linii
    body {
        background-color: #FFCC99;
        font-family: Garamond;
        text-align: center;
    }
    header, footer {
        background-color: DarkGreen;
        color: white;
        padding: 30px;
        font-size: 140%;
    }
    nav {
        background-color: MediumSeaGreen;
        word-spacing: 90px;
        padding: 20px;
    }
    Podgląd strony po tym kroku (przewozy.php)
  31. Krok 31: Sekcja lewa: szerokość i wysokość

    Kryteria: ,

    R.4.5 Ustawiona wysokość obu sekcji na 500px

    R.4.6 Ustawiona szerokość sekcji lewej na 70% oraz sekcji prawej na 30% (width lub grid- template-columns lub flex)

    ok. 1 min
    Polecenie z arkusza

    Dla sekcji lewej: szerokość 70%, wysokość 500 px.

    Selektor identyfikatora #left. Szerokość w procentach liczy się względem rodzica (<main>), wysokość jest stała. Sekcje stoją jeszcze jedna pod drugą - obok siebie ustawisz je za dwa kroki.

    styles.css CSS zmienione: 4 linie
    body {
        background-color: #FFCC99;
        font-family: Garamond;
        text-align: center;
    }
    header, footer {
        background-color: DarkGreen;
        color: white;
        padding: 30px;
        font-size: 140%;
    }
    nav {
        background-color: MediumSeaGreen;
        word-spacing: 90px;
        padding: 20px;
    }
    #left {
        width: 70%;
        height: 500px;
    }
    Podgląd strony po tym kroku (przewozy.php)
  32. Krok 32: Sekcja prawa: tło, wymiary, wyrównanie

    Kryteria: , , ,

    R.4.2 Ustawione kolory tła: DarkGreen dla bloku nagłówkowego i stopki oraz MediumSeaGreen dla bloku nawigacyjnego i sekcji prawej

    R.4.4 Ustawione wyrównanie tekstu do lewej dla sekcji prawej, odstępy między wyrazami 90px dla bloku nawigacyjnego oraz rozmiar czcionki 140% dla bloku nagłówkowego i stopki

    R.4.5 Ustawiona wysokość obu sekcji na 500px

    R.4.6 Ustawiona szerokość sekcji lewej na 70% oraz sekcji prawej na 30% (width lub grid- template-columns lub flex)

    ok. 1 min
    Polecenie z arkusza

    Dla sekcji prawej: kolor tła MediumSeaGreen, szerokość 30%, wysokość 500 px, wyrównanie tekstu do lewej.

    Selektor #right. text-align: left nadpisuje wyśrodkowanie odziedziczone z body - nagłówek i lista w prawej sekcji są wyrównane do lewej, jak na ilustracji 2. 70% + 30% = 100% - sekcje zmieszczą się obok siebie.

    styles.css CSS zmienione: 6 linii
    body {
        background-color: #FFCC99;
        font-family: Garamond;
        text-align: center;
    }
    header, footer {
        background-color: DarkGreen;
        color: white;
        padding: 30px;
        font-size: 140%;
    }
    nav {
        background-color: MediumSeaGreen;
        word-spacing: 90px;
        padding: 20px;
    }
    #left {
        width: 70%;
        height: 500px;
    }
    #right {
        background-color: MediumSeaGreen;
        width: 30%;
        height: 500px;
        text-align: left;
    }
    Podgląd strony po tym kroku (przewozy.php)
  33. Krok 33: Układ bloków: sekcje obok siebie

    Kryteria:

    R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z ilustracją 3 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. 2 min
    Polecenie z arkusza

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

    Dopisz float: left do obu sekcji - pływają w lewo i ustawiają się obok siebie (70% + 30% w jednym wierszu). Stopka dostaje osobną regułę clear: both, żeby wróciła pod pływające sekcje (wspólna reguła header, footer zostaje). Układ zgadza się z ilustracją 3. Klucz R.3.1 przyjmuje też display: flex albo grid.

    Typowe błędy

    Bez clear: both stopka wjeżdża obok sekcji albo pod nie, a <main> ma zerową wysokość - układ przestaje zgadzać się z ilustracją 3.

    styles.css CSS zmienione: 5 linii
    body {
        background-color: #FFCC99;
        font-family: Garamond;
        text-align: center;
    }
    header, footer {
        background-color: DarkGreen;
        color: white;
        padding: 30px;
        font-size: 140%;
    }
    nav {
        background-color: MediumSeaGreen;
        word-spacing: 90px;
        padding: 20px;
    }
    #left {
        width: 70%;
        height: 500px;
        float: left;
    }
    #right {
        background-color: MediumSeaGreen;
        width: 30%;
        height: 500px;
        text-align: left;
        float: left;
    }
    footer {
        clear: both;
    }
    Podgląd strony po tym kroku (przewozy.php)
  34. Krok 34: Tabela: obramowanie, wyśrodkowanie, marginesy

    Kryteria: ,

    R.4.7 Ustawione marginesy wewnętrzne (padding) dla bloku nagłówkowego i stopki 30px, dla bloku nawigacyjnego 20px, dla komórek tabeli 5px oraz marginesy zewnętrzne (margin) dla selektora table górny i dolny:30px, pozostałe: auto

    R.4.8 Ustawione dla tabeli obramowanie 1px dashed DarkGreen

    ok. 2 min
    Polecenie z arkusza

    Dla selektora tabeli: obramowanie linią kreskowaną o grubości 1 px i kolorze DarkGreen, tabela wyśrodkowana, marginesy zewnętrzne górny i dolny 30 px.

    Selektor znacznika table (arkusz tego wymaga). Skrót border: 1px dashed DarkGreen łączy grubość, styl (kreskowana) i kolor. margin: 30px auto to dwie wartości: 30 px góra i dół, auto lewa i prawa - a auto właśnie wyśrodkowuje tabelę (klucz R.4.7: „górny i dolny 30px, pozostałe: auto”).

    Typowe błędy

    text-align: center z body wyśrodkowuje tekst w komórkach, nie samą tabelę - tabela bez margin: auto stoi przy lewej krawędzi sekcji.

    styles.css CSS zmienione: 4 linie
    body {
        background-color: #FFCC99;
        font-family: Garamond;
        text-align: center;
    }
    header, footer {
        background-color: DarkGreen;
        color: white;
        padding: 30px;
        font-size: 140%;
    }
    nav {
        background-color: MediumSeaGreen;
        word-spacing: 90px;
        padding: 20px;
    }
    #left {
        width: 70%;
        height: 500px;
        float: left;
    }
    #right {
        background-color: MediumSeaGreen;
        width: 30%;
        height: 500px;
        text-align: left;
        float: left;
    }
    footer {
        clear: both;
    }
    table {
        border: 1px dashed DarkGreen;
        margin: 30px auto;
    }
    Podgląd strony po tym kroku (przewozy.php)
  35. Krok 35: Komórki tabeli: margines wewnętrzny

    Kryteria:

    R.4.7 Ustawione marginesy wewnętrzne (padding) dla bloku nagłówkowego i stopki 30px, dla bloku nawigacyjnego 20px, dla komórek tabeli 5px oraz marginesy zewnętrzne (margin) dla selektora table górny i dolny:30px, pozostałe: auto

    ok. 1 min
    Polecenie z arkusza

    Dla komórek tabeli i komórek nagłówkowych: marginesy wewnętrzne 5 px.

    Selektory td, th po przecinku - zwykłe i nagłówkowe komórki dostają padding: 5px, dzięki czemu tekst nie klei się do krawędzi. Efekt zobaczysz w pełni, gdy skrypt wypełni tabelę.

    styles.css CSS zmienione: 3 linie
    body {
        background-color: #FFCC99;
        font-family: Garamond;
        text-align: center;
    }
    header, footer {
        background-color: DarkGreen;
        color: white;
        padding: 30px;
        font-size: 140%;
    }
    nav {
        background-color: MediumSeaGreen;
        word-spacing: 90px;
        padding: 20px;
    }
    #left {
        width: 70%;
        height: 500px;
        float: left;
    }
    #right {
        background-color: MediumSeaGreen;
        width: 30%;
        height: 500px;
        text-align: left;
        float: left;
    }
    footer {
        clear: both;
    }
    table {
        border: 1px dashed DarkGreen;
        margin: 30px auto;
    }
    td, th {
        padding: 5px;
    }
    Podgląd strony po tym kroku (przewozy.php)
  36. Krok 36: Odnośniki: biały kolor

    Kryteria:

    R.4.3 Ustawiony biały kolor czcionki dla bloków nagłówkowego i stopki oraz dla selektora a

    ok. 1 min
    Polecenie z arkusza

    Dla selektora odnośnika: biały kolor czcionki.

    Selektor znacznika a (arkusz tego wymaga). Biały kolor dostają wszystkie odnośniki - te w nawigacji na zielonym pasku i przyszłe „Usuń” w tabeli.

    styles.css CSS zmienione: 3 linie
    body {
        background-color: #FFCC99;
        font-family: Garamond;
        text-align: center;
    }
    header, footer {
        background-color: DarkGreen;
        color: white;
        padding: 30px;
        font-size: 140%;
    }
    nav {
        background-color: MediumSeaGreen;
        word-spacing: 90px;
        padding: 20px;
    }
    #left {
        width: 70%;
        height: 500px;
        float: left;
    }
    #right {
        background-color: MediumSeaGreen;
        width: 30%;
        height: 500px;
        text-align: left;
        float: left;
    }
    footer {
        clear: both;
    }
    table {
        border: 1px dashed DarkGreen;
        margin: 30px auto;
    }
    td, th {
        padding: 5px;
    }
    a {
        color: white;
    }
    Podgląd strony po tym kroku (przewozy.php)
  37. Krok 37: Lista: punktor okrąg

    Kryteria:

    R.4.9 Ustawiony dla selektora ul typ punktora circle

    ok. 1 min
    Polecenie z arkusza

    Dla selektora listy punktowanej: typ elementu okrąg.

    Selektor znacznika ul, list-style-type: circle - pusty okrąg zamiast domyślnej kropki (disc). Klucz R.4.9 podaje wartość circle wprost.

    Typowe błędy

    disc to pełna kropka (domyślna), circle - okrąg, square - kwadrat. Arkusz mówi „okrąg”.

    styles.css CSS zmienione: 3 linie
    body {
        background-color: #FFCC99;
        font-family: Garamond;
        text-align: center;
    }
    header, footer {
        background-color: DarkGreen;
        color: white;
        padding: 30px;
        font-size: 140%;
    }
    nav {
        background-color: MediumSeaGreen;
        word-spacing: 90px;
        padding: 20px;
    }
    #left {
        width: 70%;
        height: 500px;
        float: left;
    }
    #right {
        background-color: MediumSeaGreen;
        width: 30%;
        height: 500px;
        text-align: left;
        float: left;
    }
    footer {
        clear: both;
    }
    table {
        border: 1px dashed DarkGreen;
        margin: 30px auto;
    }
    td, th {
        padding: 5px;
    }
    a {
        color: white;
    }
    ul {
        list-style-type: circle;
    }
    Podgląd strony po tym kroku (przewozy.php)
  38. Krok 38: Wiersz tabeli pod kursorem

    Kryteria:

    R.4.10 W momencie, gdy kursor myszy znajdzie się na wierszu tabeli jego kolor tła zmienia się na DarkGreen, a kolor czcionki na biały

    ok. 1 min
    Polecenie z arkusza

    W momencie, gdy kursor myszy znajdzie się na wierszu tabeli jego kolor tła zmienia się na DarkGreen, a kolor czcionki na biały.

    Pseudoklasa :hover na wierszu tr. Efekt z ilustracji 2 (podświetlony trzeci wiersz) zobaczysz, gdy skrypt wypełni tabelę; wiersz nagłówkowy też reaguje. Arkusz stylów jest kompletny.

    styles.css CSS zmienione: 4 linie
    body {
        background-color: #FFCC99;
        font-family: Garamond;
        text-align: center;
    }
    header, footer {
        background-color: DarkGreen;
        color: white;
        padding: 30px;
        font-size: 140%;
    }
    nav {
        background-color: MediumSeaGreen;
        word-spacing: 90px;
        padding: 20px;
    }
    #left {
        width: 70%;
        height: 500px;
        float: left;
    }
    #right {
        background-color: MediumSeaGreen;
        width: 30%;
        height: 500px;
        text-align: left;
        float: left;
    }
    footer {
        clear: both;
    }
    table {
        border: 1px dashed DarkGreen;
        margin: 30px auto;
    }
    td, th {
        padding: 5px;
    }
    a {
        color: white;
    }
    ul {
        list-style-type: circle;
    }
    tr:hover {
        background-color: DarkGreen;
        color: white;
    }
    Podgląd strony po tym kroku (przewozy.php)
  39. Etap 4: Skrypty PHP

    Kroki 39-48 · po tym etapie masz 41 z 41 kryteriów (pkt)

    Krok 39: Połączenie z bazą danych

    Kryteria: , ,

    R.5.1 Skrypt napisany w języku PHP i zawiera przynajmniej jedną zgodną z treścią zadania instrukcję

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

    R.5.9 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 przewozy.

    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.9), z której skorzystają oba 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, bo skrypt niczego nie wypisuje.

    Typowe błędy

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

    przewozy.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'przewozy');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Firma Przewozowa</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Firma przewozowa Półdarmo</h1>
        </header>
        <nav>
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <main>
            <section id="left">
                <h2>Zadania do wykonania</h2>
                <table>
                    <tr><th>Zadanie do wykonania</th><th>Data realizacji</th><th>Akcja</th></tr>
                </table>
                <form method="post">
                    <label for="task">Zadanie do wykonania: </label>
                    <input type="text" id="task" name="task"><br>
                    <label for="date">Data realizacji: </label>
                    <input type="date" id="date" name="date"><br>
                    <button type="submit">Dodaj</button>
                </form>
            </section>
            <section id="right">
                <img src="auto.png" alt="auto firmowe">
                <h3>Nasza specjalność</h3>
                <ul>
                    <li>Przeprowadzki</li>
                    <li>Przewóz mebli</li>
                    <li>Przesyłki gabarytowe</li>
                    <li>Wynajem pojazdów</li>
                    <li>Zakupy towarów</li>
                </ul>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (przewozy.php)
  40. Krok 40: Skrypt 1: zapytanie 1 do bazy

    Kryteria:

    R.5.4 Skrypty zawierają instrukcje wysyłające do bazy danych co najmniej jedno zapytanie z: zapytanie 1 lub zapytanie 2 zmodyfikowane tak, że zawiera dane z formularza, lub zapytanie 3 zmodyfikowane tak, że usuwany jest rekord o id_zadania odpowiadającym wierszowi tabeli

    ok. 2 min
    Polecenie z arkusza

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

    Skrypt 1 piszesz wewnątrz <table>, pod wierszem nagłówkowym - tam mają trafić wiersze. mysqli_query($connection, ...) wysyła zapytanie i zwraca wynik, który zapisujesz w $tasks. Treść to dokładnie zapytanie 1 z kwerendy.txt. Kolejność kolumn w SELECT zaraz będzie ważna: $row[0] to id_zadania, $row[1] zadanie, $row[2] data.

    przewozy.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'przewozy');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Firma Przewozowa</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Firma przewozowa Półdarmo</h1>
        </header>
        <nav>
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <main>
            <section id="left">
                <h2>Zadania do wykonania</h2>
                <table>
                    <tr><th>Zadanie do wykonania</th><th>Data realizacji</th><th>Akcja</th></tr>
    <?php
    $tasks = mysqli_query($connection, 'SELECT id_zadania, zadanie, data FROM zadania;');
    ?>
                </table>
                <form method="post">
                    <label for="task">Zadanie do wykonania: </label>
                    <input type="text" id="task" name="task"><br>
                    <label for="date">Data realizacji: </label>
                    <input type="date" id="date" name="date"><br>
                    <button type="submit">Dodaj</button>
                </form>
            </section>
            <section id="right">
                <img src="auto.png" alt="auto firmowe">
                <h3>Nasza specjalność</h3>
                <ul>
                    <li>Przeprowadzki</li>
                    <li>Przewóz mebli</li>
                    <li>Przesyłki gabarytowe</li>
                    <li>Wynajem pojazdów</li>
                    <li>Zakupy towarów</li>
                </ul>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

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

  41. Krok 41: Skrypt 1: pętla po zwróconych wierszach

    Kryteria:

    R.5.5 Skrypt 1 wyświetla w wierszach tabeli dane zwrócone zapytaniem, dane są wpisane do pierwszych dwóch kolumn tabeli

    ok. 2 min
    Polecenie z arkusza

    Kolejne zwrócone zapytaniem pola zadanie i data są wyświetlane w kolejnych wierszach tabeli w dwóch pierwszych kolumnach.

    „Kolejne zwrócone wiersze” to pętla. mysqli_fetch_row($tasks) (tabela 1) oddaje kolejny wiersz wyniku jako tablicę numerowaną od zera, a gdy wierszy zabraknie - false. Zapis while ($row = mysqli_fetch_row($tasks)) przypisuje wiersz do $row i od razu sprawdza, czy coś przyszło. Ciało jest jeszcze puste.

    Typowe błędy

    Pojedyncze = w warunku while jest tu celowe (przypisanie); == porównałoby zmienną z wynikiem i pętla nie zadziałałaby poprawnie.

    przewozy.php PHP zmienione: 2 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'przewozy');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Firma Przewozowa</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Firma przewozowa Półdarmo</h1>
        </header>
        <nav>
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <main>
            <section id="left">
                <h2>Zadania do wykonania</h2>
                <table>
                    <tr><th>Zadanie do wykonania</th><th>Data realizacji</th><th>Akcja</th></tr>
    <?php
    $tasks = mysqli_query($connection, 'SELECT id_zadania, zadanie, data FROM zadania;');
    while ($row = mysqli_fetch_row($tasks)) {
    }
    ?>
                </table>
                <form method="post">
                    <label for="task">Zadanie do wykonania: </label>
                    <input type="text" id="task" name="task"><br>
                    <label for="date">Data realizacji: </label>
                    <input type="date" id="date" name="date"><br>
                    <button type="submit">Dodaj</button>
                </form>
            </section>
            <section id="right">
                <img src="auto.png" alt="auto firmowe">
                <h3>Nasza specjalność</h3>
                <ul>
                    <li>Przeprowadzki</li>
                    <li>Przewóz mebli</li>
                    <li>Przesyłki gabarytowe</li>
                    <li>Wynajem pojazdów</li>
                    <li>Zakupy towarów</li>
                </ul>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

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

  42. Krok 42: Skrypt 1: zadanie i data w dwóch pierwszych kolumnach

    Kryteria:

    R.5.5 Skrypt 1 wyświetla w wierszach tabeli dane zwrócone zapytaniem, dane są wpisane do pierwszych dwóch kolumn tabeli

    ok. 2 min
    Polecenie z arkusza

    Kolejne zwrócone zapytaniem pola zadanie i data są wyświetlane w kolejnych wierszach tabeli w dwóch pierwszych kolumnach.

    echo w pętli dokłada jeden <tr> z dwiema komórkami: zadanie ($row[1]) i data ($row[2]) - dokładnie pod nagłówkami „Zadanie do wykonania” i „Data realizacji”. id_zadania ($row[0]) nie wyświetlasz, ale zaraz się przyda. Klucz R.5.5 sprawdza, że dane są w pierwszych dwóch kolumnach.

    Podgląd pokazuje stan bazy po zapytaniach 1-4: 7 zadań, bez „Przewozu telewizora” i ze „spotkaniem firmowym” na końcu - jak na ilustracji 2.

    Typowe błędy

    echo '<tr><td>$row[1]</td>...' w apostrofach wypisze dosłownie „$row[1]” - PHP nie podstawia zmiennych w apostrofach.

    przewozy.php PHP zmienione: 1 linia
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'przewozy');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Firma Przewozowa</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Firma przewozowa Półdarmo</h1>
        </header>
        <nav>
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <main>
            <section id="left">
                <h2>Zadania do wykonania</h2>
                <table>
                    <tr><th>Zadanie do wykonania</th><th>Data realizacji</th><th>Akcja</th></tr>
    <?php
    $tasks = mysqli_query($connection, 'SELECT id_zadania, zadanie, data FROM zadania;');
    while ($row = mysqli_fetch_row($tasks)) {
        echo "<tr><td>$row[1]</td><td>$row[2]</td></tr>";
    }
    ?>
                </table>
                <form method="post">
                    <label for="task">Zadanie do wykonania: </label>
                    <input type="text" id="task" name="task"><br>
                    <label for="date">Data realizacji: </label>
                    <input type="date" id="date" name="date"><br>
                    <button type="submit">Dodaj</button>
                </form>
            </section>
            <section id="right">
                <img src="auto.png" alt="auto firmowe">
                <h3>Nasza specjalność</h3>
                <ul>
                    <li>Przeprowadzki</li>
                    <li>Przewóz mebli</li>
                    <li>Przesyłki gabarytowe</li>
                    <li>Wynajem pojazdów</li>
                    <li>Zakupy towarów</li>
                </ul>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (przewozy.php)
  43. Krok 43: Skrypt 1: odnośnik „Usuń” z id zadania

    Kryteria:

    R.5.5 Skrypt 1 wyświetla w wierszach tabeli dane zwrócone zapytaniem, dane są wpisane do pierwszych dwóch kolumn tabeli

    ok. 3 min
    Polecenie z arkusza

    Trzecia kolumna tabeli zawiera odnośnik o treści Usuń. Jego wybranie powoduje usunięcie rekordu z bazy za pomocą zmodyfikowanego zapytania 3, w ten sposób że odnośnik dotyczy id_zadania dla wiersza tabeli w którym jest umieszczony.

    Trzecia komórka <td> z odnośnikiem <a>. Żeby odnośnik „dotyczył id_zadania swojego wiersza”, wkładasz ten numer do adresu: href='przewozy.php?delete=8' (dla $row[0] równego 8). Część po ? to parametr adresu - po kliknięciu przeglądarka otworzy tę samą stronę, a PHP przeczyta delete=8 z tablicy $_GET. Atrybut w apostrofach, bo cały napis echo jest w cudzysłowie.

    Samo usuwanie dopiszesz w następnym kroku - na razie odnośniki tylko prowadzą na stronę z parametrem.

    przewozy.php PHP zmienione: 1 linia
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'przewozy');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Firma Przewozowa</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Firma przewozowa Półdarmo</h1>
        </header>
        <nav>
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <main>
            <section id="left">
                <h2>Zadania do wykonania</h2>
                <table>
                    <tr><th>Zadanie do wykonania</th><th>Data realizacji</th><th>Akcja</th></tr>
    <?php
    $tasks = mysqli_query($connection, 'SELECT id_zadania, zadanie, data FROM zadania;');
    while ($row = mysqli_fetch_row($tasks)) {
        echo "<tr><td>$row[1]</td><td>$row[2]</td><td><a href='przewozy.php?delete=$row[0]'>Usuń</a></td></tr>";
    }
    ?>
                </table>
                <form method="post">
                    <label for="task">Zadanie do wykonania: </label>
                    <input type="text" id="task" name="task"><br>
                    <label for="date">Data realizacji: </label>
                    <input type="date" id="date" name="date"><br>
                    <button type="submit">Dodaj</button>
                </form>
            </section>
            <section id="right">
                <img src="auto.png" alt="auto firmowe">
                <h3>Nasza specjalność</h3>
                <ul>
                    <li>Przeprowadzki</li>
                    <li>Przewóz mebli</li>
                    <li>Przesyłki gabarytowe</li>
                    <li>Wynajem pojazdów</li>
                    <li>Zakupy towarów</li>
                </ul>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (przewozy.php)
  44. Krok 44: Skrypt 1: reakcja na kliknięcie „Usuń”

    Kryteria:

    R.5.8 Skrypt zawiera instrukcję pobierającą dane z tablicy globalnej odpowiadającej zastosowanej metodzie formularza ($_POST, $_GET, $_REQUEST )

    ok. 2 min
    Polecenie z arkusza

    Jego wybranie powoduje usunięcie rekordu z bazy za pomocą zmodyfikowanego zapytania 3, w ten sposób że odnośnik dotyczy id_zadania dla wiersza tabeli w którym jest umieszczony.

    Usuwanie piszesz na początku pliku, zaraz po połączeniu - przed tabelą, żeby po kliknięciu tabela od razu pokazała stan bez skasowanego zadania. isset($_GET['delete']) (tabela 1) jest prawdziwe tylko wtedy, gdy adres zawiera parametr delete, czyli po kliknięciu „Usuń”. Przy zwykłym wejściu na stronę blok if jest pomijany. Klucz R.5.8 sprawdza użycie tablicy globalnej odpowiadającej metodzie - dla odnośnika to $_GET.

    przewozy.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'przewozy');
    
    if (isset($_GET['delete'])) {
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Firma Przewozowa</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Firma przewozowa Półdarmo</h1>
        </header>
        <nav>
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <main>
            <section id="left">
                <h2>Zadania do wykonania</h2>
                <table>
                    <tr><th>Zadanie do wykonania</th><th>Data realizacji</th><th>Akcja</th></tr>
    <?php
    $tasks = mysqli_query($connection, 'SELECT id_zadania, zadanie, data FROM zadania;');
    while ($row = mysqli_fetch_row($tasks)) {
        echo "<tr><td>$row[1]</td><td>$row[2]</td><td><a href='przewozy.php?delete=$row[0]'>Usuń</a></td></tr>";
    }
    ?>
                </table>
                <form method="post">
                    <label for="task">Zadanie do wykonania: </label>
                    <input type="text" id="task" name="task"><br>
                    <label for="date">Data realizacji: </label>
                    <input type="date" id="date" name="date"><br>
                    <button type="submit">Dodaj</button>
                </form>
            </section>
            <section id="right">
                <img src="auto.png" alt="auto firmowe">
                <h3>Nasza specjalność</h3>
                <ul>
                    <li>Przeprowadzki</li>
                    <li>Przewóz mebli</li>
                    <li>Przesyłki gabarytowe</li>
                    <li>Wynajem pojazdów</li>
                    <li>Zakupy towarów</li>
                </ul>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

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

  45. Krok 45: Skrypt 1: zmodyfikowane zapytanie 3

    Kryteria: ,

    R.5.4 Skrypty zawierają instrukcje wysyłające do bazy danych co najmniej jedno zapytanie z: zapytanie 1 lub zapytanie 2 zmodyfikowane tak, że zawiera dane z formularza, lub zapytanie 3 zmodyfikowane tak, że usuwany jest rekord o id_zadania odpowiadającym wierszowi tabeli

    R.5.6 Skrypt 1 usuwa z bazy danych rekord którego id_zadania odpowiada wierszowi odnośnika (należy sprawdzić w phpMyAdmin)

    ok. 3 min
    Polecenie z arkusza

    Jego wybranie powoduje usunięcie rekordu z bazy za pomocą zmodyfikowanego zapytania 3, w ten sposób że odnośnik dotyczy id_zadania dla wiersza tabeli w którym jest umieszczony.

    Numer z adresu trafia do $task_id, a zapytanie 3 z kwerendy.txt dostaje go w miejsce 2: DELETE FROM zadania WHERE id_zadania = $task_id. Napis w cudzysłowie, żeby PHP podstawił zmienną. mysqli_query wykonuje DELETE - nic nie wypisuje, efekt widać w tabeli poniżej i w phpMyAdmin (klucz R.5.6 sprawdza właśnie tam).

    W podglądzie kliknięcia są zablokowane, ale na żywym serwerze „Usuń” kasuje wiersz i strona przeładowuje się bez niego.

    Typowe błędy

    Wartość z adresu wstawiona prosto do DELETE to podatność - ktoś może wpisać w adres delete=1 OR 1=1 i skasować wszystko. Na egzaminie wystarcza, w refaktorze to naprawisz.

    przewozy.php PHP zmienione: 2 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'przewozy');
    
    if (isset($_GET['delete'])) {
        $task_id = $_GET['delete'];
        mysqli_query($connection, "DELETE FROM zadania WHERE id_zadania = $task_id;");
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Firma Przewozowa</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Firma przewozowa Półdarmo</h1>
        </header>
        <nav>
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <main>
            <section id="left">
                <h2>Zadania do wykonania</h2>
                <table>
                    <tr><th>Zadanie do wykonania</th><th>Data realizacji</th><th>Akcja</th></tr>
    <?php
    $tasks = mysqli_query($connection, 'SELECT id_zadania, zadanie, data FROM zadania;');
    while ($row = mysqli_fetch_row($tasks)) {
        echo "<tr><td>$row[1]</td><td>$row[2]</td><td><a href='przewozy.php?delete=$row[0]'>Usuń</a></td></tr>";
    }
    ?>
                </table>
                <form method="post">
                    <label for="task">Zadanie do wykonania: </label>
                    <input type="text" id="task" name="task"><br>
                    <label for="date">Data realizacji: </label>
                    <input type="date" id="date" name="date"><br>
                    <button type="submit">Dodaj</button>
                </form>
            </section>
            <section id="right">
                <img src="auto.png" alt="auto firmowe">
                <h3>Nasza specjalność</h3>
                <ul>
                    <li>Przeprowadzki</li>
                    <li>Przewóz mebli</li>
                    <li>Przesyłki gabarytowe</li>
                    <li>Wynajem pojazdów</li>
                    <li>Zakupy towarów</li>
                </ul>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

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

  46. Krok 46: Skrypt 2: tylko po wysłaniu formularza

    Kryteria:

    R.5.8 Skrypt zawiera instrukcję pobierającą dane z tablicy globalnej odpowiadającej zastosowanej metodzie formularza ($_POST, $_GET, $_REQUEST )

    ok. 2 min
    Polecenie z arkusza

    Skrypt 2: dane pobrane z formularza wstawia do bazy za pomocą zmodyfikowanego zapytania 2.

    Skrypt 2 też piszesz na początku pliku, pod usuwaniem - dodany rekord ma być widoczny w tabeli od razu po kliknięciu „Dodaj”. Warunek ma dwie części: isset($_POST['task']) (czy formularz został wysłany) i $_POST['task'] != '' (czy pole nie jest puste - bez tego kliknięcie „Dodaj” z pustym polem wstawiałoby puste zadanie). Formularz ma method="post", więc dane są w $_POST (R.5.8).

    przewozy.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'przewozy');
    
    if (isset($_GET['delete'])) {
        $task_id = $_GET['delete'];
        mysqli_query($connection, "DELETE FROM zadania WHERE id_zadania = $task_id;");
    }
    
    if (isset($_POST['task']) && $_POST['task'] != '') {
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Firma Przewozowa</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Firma przewozowa Półdarmo</h1>
        </header>
        <nav>
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <main>
            <section id="left">
                <h2>Zadania do wykonania</h2>
                <table>
                    <tr><th>Zadanie do wykonania</th><th>Data realizacji</th><th>Akcja</th></tr>
    <?php
    $tasks = mysqli_query($connection, 'SELECT id_zadania, zadanie, data FROM zadania;');
    while ($row = mysqli_fetch_row($tasks)) {
        echo "<tr><td>$row[1]</td><td>$row[2]</td><td><a href='przewozy.php?delete=$row[0]'>Usuń</a></td></tr>";
    }
    ?>
                </table>
                <form method="post">
                    <label for="task">Zadanie do wykonania: </label>
                    <input type="text" id="task" name="task"><br>
                    <label for="date">Data realizacji: </label>
                    <input type="date" id="date" name="date"><br>
                    <button type="submit">Dodaj</button>
                </form>
            </section>
            <section id="right">
                <img src="auto.png" alt="auto firmowe">
                <h3>Nasza specjalność</h3>
                <ul>
                    <li>Przeprowadzki</li>
                    <li>Przewóz mebli</li>
                    <li>Przesyłki gabarytowe</li>
                    <li>Wynajem pojazdów</li>
                    <li>Zakupy towarów</li>
                </ul>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

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

  47. Krok 47: Skrypt 2: zmodyfikowane zapytanie 2

    Kryteria: ,

    R.5.4 Skrypty zawierają instrukcje wysyłające do bazy danych co najmniej jedno zapytanie z: zapytanie 1 lub zapytanie 2 zmodyfikowane tak, że zawiera dane z formularza, lub zapytanie 3 zmodyfikowane tak, że usuwany jest rekord o id_zadania odpowiadającym wierszowi tabeli

    R.5.7 Skrypt 2 wstawia do bazy danych przewozy nowy rekord z danymi z formularza. (należy sprawdzić w phpMyAdmin)

    ok. 3 min
    Polecenie z arkusza

    Dane pobrane z formularza wstawia do bazy za pomocą zmodyfikowanego zapytania 2. Zadanie jest przypisane do osoby o id równym 1.

    Dwie wartości z $_POST (klucze = atrybuty name pól) do zmiennych $task i $date. Zapytanie 2 z kwerendy.txt dostaje je w miejsce „spotkanie firmowe” i daty, a osoba_id zostaje stałą 1, jak każe arkusz. Napisy w apostrofach wewnątrz cudzysłowu, żeby PHP podstawił zmienne. Pole type="date" wysyła datę w formacie RRRR-MM-DD, więc baza ją przyjmie.

    Klucz R.5.7 sprawdza wstawiony rekord w phpMyAdmin; na stronie nowe zadanie pojawia się na końcu tabeli.

    Typowe błędy

    Zadanie z apostrofem (np. „Przewóz z ul. O'Connora”) rozrywa napis SQL i wstawianie kończy się błędem - objaw tej samej podatności co przy usuwaniu.

    przewozy.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'przewozy');
    
    if (isset($_GET['delete'])) {
        $task_id = $_GET['delete'];
        mysqli_query($connection, "DELETE FROM zadania WHERE id_zadania = $task_id;");
    }
    
    if (isset($_POST['task']) && $_POST['task'] != '') {
        $task = $_POST['task'];
        $date = $_POST['date'];
        mysqli_query($connection, "INSERT INTO zadania (zadanie, data, osoba_id) VALUES ('$task', '$date', 1);");
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Firma Przewozowa</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Firma przewozowa Półdarmo</h1>
        </header>
        <nav>
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <main>
            <section id="left">
                <h2>Zadania do wykonania</h2>
                <table>
                    <tr><th>Zadanie do wykonania</th><th>Data realizacji</th><th>Akcja</th></tr>
    <?php
    $tasks = mysqli_query($connection, 'SELECT id_zadania, zadanie, data FROM zadania;');
    while ($row = mysqli_fetch_row($tasks)) {
        echo "<tr><td>$row[1]</td><td>$row[2]</td><td><a href='przewozy.php?delete=$row[0]'>Usuń</a></td></tr>";
    }
    ?>
                </table>
                <form method="post">
                    <label for="task">Zadanie do wykonania: </label>
                    <input type="text" id="task" name="task"><br>
                    <label for="date">Data realizacji: </label>
                    <input type="date" id="date" name="date"><br>
                    <button type="submit">Dodaj</button>
                </form>
            </section>
            <section id="right">
                <img src="auto.png" alt="auto firmowe">
                <h3>Nasza specjalność</h3>
                <ul>
                    <li>Przeprowadzki</li>
                    <li>Przewóz mebli</li>
                    <li>Przesyłki gabarytowe</li>
                    <li>Wynajem pojazdów</li>
                    <li>Zakupy towarów</li>
                </ul>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    </body>
    </html>

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

  48. Krok 48: Zamknięcie połączenia z bazą

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

    Na końcu działania skrypt zamyka połączenie z serwerem.

    mysqli_close($connection) na końcu pliku, po wszystkich operacjach na bazie (usuwanie, wstawianie, pobranie listy). Klucz R.5.3 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 tuż po skryptach na górze pliku sprawia, że zapytanie 1 w tabeli idzie po zamkniętym połączeniu - tabela zostaje pusta.

    przewozy.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'przewozy');
    
    if (isset($_GET['delete'])) {
        $task_id = $_GET['delete'];
        mysqli_query($connection, "DELETE FROM zadania WHERE id_zadania = $task_id;");
    }
    
    if (isset($_POST['task']) && $_POST['task'] != '') {
        $task = $_POST['task'];
        $date = $_POST['date'];
        mysqli_query($connection, "INSERT INTO zadania (zadanie, data, osoba_id) VALUES ('$task', '$date', 1);");
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Firma Przewozowa</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Firma przewozowa Półdarmo</h1>
        </header>
        <nav>
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <main>
            <section id="left">
                <h2>Zadania do wykonania</h2>
                <table>
                    <tr><th>Zadanie do wykonania</th><th>Data realizacji</th><th>Akcja</th></tr>
    <?php
    $tasks = mysqli_query($connection, 'SELECT id_zadania, zadanie, data FROM zadania;');
    while ($row = mysqli_fetch_row($tasks)) {
        echo "<tr><td>$row[1]</td><td>$row[2]</td><td><a href='przewozy.php?delete=$row[0]'>Usuń</a></td></tr>";
    }
    ?>
                </table>
                <form method="post">
                    <label for="task">Zadanie do wykonania: </label>
                    <input type="text" id="task" name="task"><br>
                    <label for="date">Data realizacji: </label>
                    <input type="date" id="date" name="date"><br>
                    <button type="submit">Dodaj</button>
                </form>
            </section>
            <section id="right">
                <img src="auto.png" alt="auto firmowe">
                <h3>Nasza specjalność</h3>
                <ul>
                    <li>Przeprowadzki</li>
                    <li>Przewóz mebli</li>
                    <li>Przesyłki gabarytowe</li>
                    <li>Wynajem pojazdów</li>
                    <li>Zakupy towarów</li>
                </ul>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    <?php
    mysqli_close($connection);
    ?>
    </body>
    </html>

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

  49. Etap 5: Dobre praktyki (poza kluczem)

    Kroki 49-51 · po tym etapie masz 41 z 41 kryteriów (pkt)

    Krok 49: Refaktor: zapytania przygotowane dla usuwania i wstawiania

    Kryteria: , ,

    R.5.4 Skrypty zawierają instrukcje wysyłające do bazy danych co najmniej jedno zapytanie z: zapytanie 1 lub zapytanie 2 zmodyfikowane tak, że zawiera dane z formularza, lub zapytanie 3 zmodyfikowane tak, że usuwany jest rekord o id_zadania odpowiadającym wierszowi tabeli

    R.5.6 Skrypt 1 usuwa z bazy danych rekord którego id_zadania odpowiada wierszowi odnośnika (należy sprawdzić w phpMyAdmin)

    R.5.7 Skrypt 2 wstawia do bazy danych przewozy nowy rekord z danymi z formularza. (należy sprawdzić w phpMyAdmin)

    ok. 5 min
    Polecenie z arkusza

    Jego wybranie powoduje usunięcie rekordu z bazy za pomocą zmodyfikowanego zapytania 3. Dane pobrane z formularza wstawia do bazy za pomocą zmodyfikowanego zapytania 2.

    Oba skrypty sklejały zapytanie z tekstem od użytkownika - z adresu (delete=) i z formularza. To SQL injection: delete=1 OR 1=1 kasuje całą tabelę. W prawdziwych projektach dane nigdy nie trafiają do zapytania jako tekst, tylko przez zapytanie przygotowane:

    • mysqli_prepare dostaje zapytanie z ? w miejscu wartości - baza zna jego strukturę, zanim zobaczy dane,
    • mysqli_stmt_bind_param podpina zmienne pod ? z typami: 'i' dla id (liczba całkowita), 'ss' dla zadania i daty (dwa napisy); stała 1 dla osoba_id zostaje w zapytaniu,
    • mysqli_stmt_execute wykonuje.

    Stare linie zostają w komentarzu // do porównania. Zadanie z apostrofem teraz zapisze się poprawnie. Podgląd bez zmian.

    Typowe błędy

    Liczba liter w 'ss' musi równać się liczbie zmiennych, a liczba zmiennych - liczbie ? w zapytaniu. Inaczej mysqli_stmt_bind_param kończy się błędem ArgumentCountError i strona się urywa (sprawdzone w PHP 8.2).

    przewozy.php PHP zmienione: 8 linii
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'przewozy');
    
    if (isset($_GET['delete'])) {
        $task_id = $_GET['delete'];
        // mysqli_query($connection, "DELETE FROM zadania WHERE id_zadania = $task_id;");
        $statement = mysqli_prepare($connection, 'DELETE FROM zadania WHERE id_zadania = ?');
        mysqli_stmt_bind_param($statement, 'i', $task_id);
        mysqli_stmt_execute($statement);
    }
    
    if (isset($_POST['task']) && $_POST['task'] != '') {
        $task = $_POST['task'];
        $date = $_POST['date'];
        // mysqli_query($connection, "INSERT INTO zadania (zadanie, data, osoba_id) VALUES ('$task', '$date', 1);");
        $statement = mysqli_prepare($connection, 'INSERT INTO zadania (zadanie, data, osoba_id) VALUES (?, ?, 1)');
        mysqli_stmt_bind_param($statement, 'ss', $task, $date);
        mysqli_stmt_execute($statement);
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Firma Przewozowa</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Firma przewozowa Półdarmo</h1>
        </header>
        <nav>
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <main>
            <section id="left">
                <h2>Zadania do wykonania</h2>
                <table>
                    <tr><th>Zadanie do wykonania</th><th>Data realizacji</th><th>Akcja</th></tr>
    <?php
    $tasks = mysqli_query($connection, 'SELECT id_zadania, zadanie, data FROM zadania;');
    while ($row = mysqli_fetch_row($tasks)) {
        echo "<tr><td>$row[1]</td><td>$row[2]</td><td><a href='przewozy.php?delete=$row[0]'>Usuń</a></td></tr>";
    }
    ?>
                </table>
                <form method="post">
                    <label for="task">Zadanie do wykonania: </label>
                    <input type="text" id="task" name="task"><br>
                    <label for="date">Data realizacji: </label>
                    <input type="date" id="date" name="date"><br>
                    <button type="submit">Dodaj</button>
                </form>
            </section>
            <section id="right">
                <img src="auto.png" alt="auto firmowe">
                <h3>Nasza specjalność</h3>
                <ul>
                    <li>Przeprowadzki</li>
                    <li>Przewóz mebli</li>
                    <li>Przesyłki gabarytowe</li>
                    <li>Wynajem pojazdów</li>
                    <li>Zakupy towarów</li>
                </ul>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    <?php
    mysqli_close($connection);
    ?>
    </body>
    </html>

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

  50. Krok 50: Refaktor: escapowanie danych przed wypisaniem

    Kryteria:

    R.5.5 Skrypt 1 wyświetla w wierszach tabeli dane zwrócone zapytaniem, dane są wpisane do pierwszych dwóch kolumn tabeli

    ok. 3 min
    Polecenie z arkusza

    Kolejne zwrócone zapytaniem pola zadanie i data są wyświetlane w kolejnych wierszach tabeli w dwóch pierwszych kolumnach.

    Druga strona tego samego problemu: skrypt 1 wypisuje do HTML to, co ktoś wpisał w formularzu. Zadanie <script>alert(1)</script> wykonałoby się w przeglądarce każdego, kto otworzy stronę (XSS). htmlspecialchars() zamienia <, >, ", & na encje, więc tekst wyświetla się dosłownie i nie staje się kodem.

    Opis zadania przechodzi przez htmlspecialchars do zmiennej $task; data i id to wartości z bazy w ustalonym formacie, nie wymagają tego. Stare echo zostaje w komentarzu. Podgląd bez zmian.

    Typowe błędy

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

    przewozy.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'przewozy');
    
    if (isset($_GET['delete'])) {
        $task_id = $_GET['delete'];
        // mysqli_query($connection, "DELETE FROM zadania WHERE id_zadania = $task_id;");
        $statement = mysqli_prepare($connection, 'DELETE FROM zadania WHERE id_zadania = ?');
        mysqli_stmt_bind_param($statement, 'i', $task_id);
        mysqli_stmt_execute($statement);
    }
    
    if (isset($_POST['task']) && $_POST['task'] != '') {
        $task = $_POST['task'];
        $date = $_POST['date'];
        // mysqli_query($connection, "INSERT INTO zadania (zadanie, data, osoba_id) VALUES ('$task', '$date', 1);");
        $statement = mysqli_prepare($connection, 'INSERT INTO zadania (zadanie, data, osoba_id) VALUES (?, ?, 1)');
        mysqli_stmt_bind_param($statement, 'ss', $task, $date);
        mysqli_stmt_execute($statement);
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Firma Przewozowa</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Firma przewozowa Półdarmo</h1>
        </header>
        <nav>
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <main>
            <section id="left">
                <h2>Zadania do wykonania</h2>
                <table>
                    <tr><th>Zadanie do wykonania</th><th>Data realizacji</th><th>Akcja</th></tr>
    <?php
    $tasks = mysqli_query($connection, 'SELECT id_zadania, zadanie, data FROM zadania;');
    while ($row = mysqli_fetch_row($tasks)) {
        // echo "<tr><td>$row[1]</td><td>$row[2]</td><td><a href='przewozy.php?delete=$row[0]'>Usuń</a></td></tr>";
        $task = htmlspecialchars($row[1]);
        echo "<tr><td>$task</td><td>$row[2]</td><td><a href='przewozy.php?delete=$row[0]'>Usuń</a></td></tr>";
    }
    ?>
                </table>
                <form method="post">
                    <label for="task">Zadanie do wykonania: </label>
                    <input type="text" id="task" name="task"><br>
                    <label for="date">Data realizacji: </label>
                    <input type="date" id="date" name="date"><br>
                    <button type="submit">Dodaj</button>
                </form>
            </section>
            <section id="right">
                <img src="auto.png" alt="auto firmowe">
                <h3>Nasza specjalność</h3>
                <ul>
                    <li>Przeprowadzki</li>
                    <li>Przewóz mebli</li>
                    <li>Przesyłki gabarytowe</li>
                    <li>Wynajem pojazdów</li>
                    <li>Zakupy towarów</li>
                </ul>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    <?php
    mysqli_close($connection);
    ?>
    </body>
    </html>

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

  51. Krok 51: Refaktor: przekierowanie po zapisie

    ok. 3 min
    Polecenie z arkusza

    Na końcu działania skrypt zamyka połączenie z serwerem.

    Dwa drobne, ale realne problemy wersji egzaminacyjnej: po kliknięciu „Usuń” adres strony zostaje z ?delete=8, więc odświeżenie (F5) próbuje usunąć jeszcze raz; po „Dodaj” odświeżenie wysyła formularz ponownie i dubluje zadanie. Rozwiązanie z prawdziwych projektów to wzorzec „zapisz i przekieruj”: po udanym DELETE albo INSERT wysyłasz header('Location: przewozy.php') - przeglądarka od razu otwiera czysty adres metodą GET - i exit, żeby nie wypisywać już nic więcej.

    header() musi stać przed jakimkolwiek HTML-em - w tym rozwiązaniu skrypty są na początku pliku, więc warunek jest spełniony. Podgląd bez zmian, ale F5 jest teraz bezpieczne.

    Typowe błędy

    header() po wypisaniu choćby jednej spacji przed <?php daje błąd „headers already sent” - to częsty powód, dla którego przekierowanie „nie działa”.

    przewozy.php PHP zmienione: 4 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'przewozy');
    
    if (isset($_GET['delete'])) {
        $task_id = $_GET['delete'];
        // mysqli_query($connection, "DELETE FROM zadania WHERE id_zadania = $task_id;");
        $statement = mysqli_prepare($connection, 'DELETE FROM zadania WHERE id_zadania = ?');
        mysqli_stmt_bind_param($statement, 'i', $task_id);
        mysqli_stmt_execute($statement);
        header('Location: przewozy.php');
        exit;
    }
    
    if (isset($_POST['task']) && $_POST['task'] != '') {
        $task = $_POST['task'];
        $date = $_POST['date'];
        // mysqli_query($connection, "INSERT INTO zadania (zadanie, data, osoba_id) VALUES ('$task', '$date', 1);");
        $statement = mysqli_prepare($connection, 'INSERT INTO zadania (zadanie, data, osoba_id) VALUES (?, ?, 1)');
        mysqli_stmt_bind_param($statement, 'ss', $task, $date);
        mysqli_stmt_execute($statement);
        header('Location: przewozy.php');
        exit;
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Firma Przewozowa</title>
        <link rel="stylesheet" href="styles.css">
    </head>
    <body>
        <header>
            <h1>Firma przewozowa Półdarmo</h1>
        </header>
        <nav>
            <a href="kw1.png">kwerenda1</a>
            <a href="kw2.png">kwerenda2</a>
            <a href="kw3.png">kwerenda3</a>
            <a href="kw4.png">kwerenda4</a>
        </nav>
        <main>
            <section id="left">
                <h2>Zadania do wykonania</h2>
                <table>
                    <tr><th>Zadanie do wykonania</th><th>Data realizacji</th><th>Akcja</th></tr>
    <?php
    $tasks = mysqli_query($connection, 'SELECT id_zadania, zadanie, data FROM zadania;');
    while ($row = mysqli_fetch_row($tasks)) {
        // echo "<tr><td>$row[1]</td><td>$row[2]</td><td><a href='przewozy.php?delete=$row[0]'>Usuń</a></td></tr>";
        $task = htmlspecialchars($row[1]);
        echo "<tr><td>$task</td><td>$row[2]</td><td><a href='przewozy.php?delete=$row[0]'>Usuń</a></td></tr>";
    }
    ?>
                </table>
                <form method="post">
                    <label for="task">Zadanie do wykonania: </label>
                    <input type="text" id="task" name="task"><br>
                    <label for="date">Data realizacji: </label>
                    <input type="date" id="date" name="date"><br>
                    <button type="submit">Dodaj</button>
                </form>
            </section>
            <section id="right">
                <img src="auto.png" alt="auto firmowe">
                <h3>Nasza specjalność</h3>
                <ul>
                    <li>Przeprowadzki</li>
                    <li>Przewóz mebli</li>
                    <li>Przesyłki gabarytowe</li>
                    <li>Wynajem pojazdów</li>
                    <li>Zakupy towarów</li>
                </ul>
            </section>
        </main>
        <footer>
            <p>Stronę wykonał: Bartosz Bryniarski</p>
        </footer>
    <?php
    mysqli_close($connection);
    ?>
    </body>
    </html>

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

  52. Etap 6: Oddanie pracy

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

    Krok 52: 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ć: auto.png, import.png, kw1.png, kw2.png, kw3.png, kw4.png, kwerendy.txt, przeglądarka.txt, przewozy.php, styles.css. Nagraj płytę, sprawdź jej odczyt i opisz numerem zdającego. Pusta lub nieczytelna płyta to zero punktów za wszystkie pięć rezultatów.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

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

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.