Przejdź do głównej treści
INF.03-02-23.06-SGCzerwiec 2023 · 51 kroków · ok. 76 min · 39 kryteriów z klucza

Rozwiązanie krok po kroku: Portal hurtowni papierniczej (Wersja podstawowa)

Portal hurtowni papierniczej: baza sklep (dostawcy i towary w relacji) i cztery zapytania - LIMIT ograniczający wynik do czterech rekordów, cena jednego towaru, UPDATE zmieniający nazwę dostawcy i COUNT z GROUP BY z aliasem, potem grafika przeskalowana z zachowaniem przezroczystości, strona index.php z banerem, trzema panelami obok siebie i stopką, style w styl.css i dwa skrypty PHP: tabela czterech towarów z cenami z bazy oraz formularz, po którego wysłaniu skrypt pobiera cenę wybranego towaru i mnoży ją przez liczbę sztuk.

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ą. Skrypty piszę najpierw najprościej, funkcjami z tabeli 1 arkusza (mysqli_query, mysqli_fetch_row) i warunkiem isset, a na końcu przerabiam je na wersję z prawdziwych projektów: walidacja liczby sztuk, zapytanie przygotowane, htmlspecialchars i zapamiętany wybór w formularzu.

To jest wersja podstawowa. W zakładce „Nowoczesny kod (flex, PDO)” jest drugi sposób rozwiązania tego samego arkusza: panele ustawione flexem w znaczniku <main>, kolory w zmiennych CSS, dane pobierane przez PDO na górze pliku, liczba sztuk sprawdzana filter_var, a nazwa towaru - z listą i zapytaniem przygotowanym - obie wersje są zgodne z kluczem.

Baza i grafika pochodzą z prawdziwego archiwum materialy2 (załącznik do arkusza, hasło w treści zadania). Zapytania i zrzuty są policzone na MariaDB 10.4 - tej samej, która jest w XAMPP. Zrzuty są odtworzone z tej bazy; na egzaminie robisz je sam, z całego ekranu i z widocznym zapytaniem. Podgląd strony od kroków z PHP to wynik prawdziwego uruchomienia kodu z danego kroku w PHP 8.2; od kroku z formularzem pokazuje stan z obrazu 2, czyli po wybraniu „Zeszyt 32 kartki”, wpisaniu 2 i kliknięciu „OBLICZ”.

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

  1. Etap 1: Baza danych

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

    Krok 1: Baza danych sklep

    ok. 2 min
    Polecenie z arkusza

    Utwórz nową bazę danych o nazwie sklep

    Najpierw archiwum: materialy2.7z z pulpitu, hasło Zad@n!e#2 (wielkie Z, małpa zamiast „a”, wykrzyknik zamiast „i”). Rozpakowane pliki sklep.sql i zakupy.png trafiają do folderu nazwanego numerem zdającego. W phpMyAdmin: Nowa, nazwa sklep, porównywanie np. utf8_polish_ci - arkusz go nie narzuca.

    Typowe błędy

    Nazwa bazy inna niż sklep psuje połączenie w skrypcie - tracisz wtedy także punkty za działanie PHP (R.5.1).

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

    ok. 2 min
    Polecenie z arkusza

    Do bazy zaimportuj tabele z pliku sklep.sql, z wcześniej rozpakowanego archiwum

    Przy zaznaczonej bazie sklep: Import → sklep.sql → Wykonaj. Powstają dwie tabele z obrazu 1: dostawcy (3 rekordy) i towary (10 rekordów). Pole promocja przyjmuje 0 albo 1, a idDostawcy wskazuje id dostawcy - to relacja opisana w arkuszu (klucz obcy w dumpie). Cena ma typ float, dlatego w wynikach zobaczysz 4.5, a nie 4.50.

  3. Krok 3: Zrzut ekranu import.jpg

    Kryteria:

    R.1.1 Wykonano import tabel do bazy danych o nazwie sklep , czynność udokumentowano plikiem o nazwie import w formacie JPEG

    ok. 2 min
    Polecenie z arkusza

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

    Print Screen, wklejenie do edytora grafiki i zapis jako import.jpg - w tym arkuszu wszystkie zrzuty są w JPEG. Na zrzucie ma być widać komunikat o zakończonym imporcie albo obie tabele w drzewie bazy sklep.

    Typowe błędy

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

  4. Krok 4: Plik kwerendy.txt i zasady zrzutów

    ok. 1 min
    Polecenie z arkusza

    Wykonaj zapytania SQL działające na bazie danych sklep. Zapytania zapisz w pliku kwerendy.txt. Wykonaj zrzuty ekranu przedstawiające wyniki działania kwerend. Zrzuty zapisz w formacie JPEG i nadaj im nazwy kwerenda1, kwerenda2, kwerenda3, kwerenda4. Zrzuty powinny obejmować cały ekran monitora z widocznym paskiem zadań oraz zawierać wykonane zapytanie

    Każde zapytanie piszę w zakładce SQL bazy sklep, a po wykonaniu kopiuję je do kwerendy.txt (zwykły plik tekstowy w folderze zdającego). phpMyAdmin pokazuje treść wykonanego zapytania nad wynikiem, więc zrzut całego ekranu od razu ją zawiera. Nazwy plików: kwerenda1.jpg - kwerenda4.jpg, a nie kw1 jak w wielu innych arkuszach.

  5. Krok 5: Zapytanie 1: cztery pierwsze towary

    Kryteria: ,

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

    R.1.3 Zapisano zapytanie 1 wybierające jedynie pola nazwa i cena z tabeli towary i wyświetlające jedynie cztery pierwsze zwrócone zapytaniem rekordy. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa, cena FROM towary LIMIT 4; oraz wynik działania zapytania udokumentowano zrzutem ekranu, na którym widoczne są dokładnie 4 rekordy: Zeszyt 60 kartek, 4.5; Zeszyt 32 kartki, 1.2; Cyrkiel, 12.4; Linijka 30 cm, 7.2

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 1: wybierające jedynie pola nazwa i cena z tabeli towary i wyświetlające jedynie cztery pierwsze zwrócone zapytaniem rekordy

    • SELECT nazwa, cena - tylko dwa pola,
    • FROM towary,
    • LIMIT 4 - z wyniku zostają cztery pierwsze rekordy.

    Wynik: Zeszyt 60 kartek 4.5, Zeszyt 32 kartki 1.2, Cyrkiel 12.4, Linijka 30 cm 7.2 - dokładnie jak w kluczu. Zapytanie zapisuję w kwerendy.txt, zrzut jako kwerenda1.jpg. To zapytanie wyśle Skrypt 1 - stąd te cztery wiersze tabeli na obrazie 2.

    Typowe błędy

    WHERE id <= 4 daje ten sam wynik na tej bazie, ale nie jest „czterema pierwszymi rekordami” - klucz wymaga zapytania uniwersalnego dla każdego zestawu danych.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT nazwa, cena FROM towary LIMIT 4;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 2: cena ekierki

    Kryteria:

    R.1.4 Zapisano zapytanie 2 wybierające jedynie pole cena z tabeli towary dla towaru Ekierka. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT cena FROM towary WHERE nazwa = 'Ekierka'; oraz wynik działania zapytania udokumentowano zrzutem ekranu, na którym widoczna jest jedynie cena 5.5

    ok. 1 min
    Polecenie z arkusza

    Zapytanie 2: wybierające jedynie pole cena z tabeli towary dla towaru Ekierka

    • SELECT cena FROM towary - jedno pole,
    • WHERE nazwa = 'Ekierka' - tekst w apostrofach, porównany z całą nazwą towaru.

    Wynik: 5.5, jak w kluczu. Zrzut: kwerenda2.jpg. Skrypt 2 użyje tego zapytania, wstawiając w miejsce „Ekierka” nazwę wybraną z listy.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT nazwa, cena FROM towary LIMIT 4;
    
    -- Zapytanie 2
    SELECT cena FROM towary WHERE nazwa = 'Ekierka';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 3: nowa nazwa dostawcy

    Kryteria:

    R.1.5 Zapisano zapytanie 3 aktualizujące dane w tabeli dostawcy , rekord o id równym 2 w polu nazwa przyjmuje nową wartość „Artykuly szkolne”. W pliku z kwerendami lub na zrzucie istnieje zapis np.: UPDATE dostawcy SET nazwa = "Artykuly szkolne" WHERE id = 2; oraz wynik działania zapytania udokumentowano zrzutem wskazującym na poprawne wykonanie zapytania

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 3: aktualizujące dane w tabeli dostawcy, rekord o id równym 2 w polu nazwa przyjmuje nową wartość „Artykuly szkolne”

    • UPDATE dostawcy - zmieniam dane w tej tabeli,
    • SET nazwa = 'Artykuly szkolne' - nowa wartość pola, bez polskiej litery, dokładnie jak w arkuszu,
    • WHERE id = 2 - tylko rekord o identyfikatorze 2 (dotąd „Art. szkolne”).

    phpMyAdmin odpowiada komunikatem „1 wiersz zmieniony”. Na zrzucie kwerenda3.jpg widać zapytanie, komunikat i tabelę po zmianie.

    Typowe błędy

    UPDATE dostawcy SET nazwa = ... bez WHERE zmienia nazwę wszystkich trzech dostawców.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT nazwa, cena FROM towary LIMIT 4;
    
    -- Zapytanie 2
    SELECT cena FROM towary WHERE nazwa = 'Ekierka';
    
    -- Zapytanie 3
    UPDATE dostawcy SET nazwa = 'Artykuly szkolne' WHERE id = 2;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Krok 8: Zapytanie 4: liczba towarów w promocji i poza nią

    Kryteria:

    R.1.6 Zapisano zapytanie 4 wybierające jedynie pole promocja z tabeli towary oraz zliczające liczbę towarów objętych i towarów nieobjętych promocją. Nazwa kolumny (alias) zliczającej podsumowanie. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT promocja, COUNT(*) AS podsumowanie FROM towary GROUP BY promocja; (w COUNT możliwa również nazwa dowolnego pola tabeli) oraz wynik działania zapytania udokumentowano zrzutem ekranu, na którym widoczne są jedynie dwa rekordy z danymi: 0 i 6 oraz 1 i 4

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 4: wybierające jedynie pole promocja dla wszystkich rekordów z tabeli towary oraz zliczające liczbę towarów objętych i towarów nieobjętych promocją. Nazwa kolumny (alias) dla zliczenia towarów: podsumowanie.

    • SELECT promocja, COUNT(*) AS podsumowanie - pole promocja i liczba wierszy pod aliasem z arkusza,
    • FROM towary,
    • GROUP BY promocja - towary z tą samą wartością pola tworzą jedną grupę, a COUNT(*) liczy jej wiersze.

    Wynik: dwa wiersze - 0 i 6 oraz 1 i 4, jak w kluczu (klucz uznaje też COUNT z nazwą dowolnego pola, sprawdziłem COUNT(id) - ten sam wynik). Zrzut: kwerenda4.jpg.

    Typowe błędy

    Bez GROUP BY wychodzi jeden wiersz z liczbą 10 - nie widać podziału na towary w promocji i poza nią.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT nazwa, cena FROM towary LIMIT 4;
    
    -- Zapytanie 2
    SELECT cena FROM towary WHERE nazwa = 'Ekierka';
    
    -- Zapytanie 3
    UPDATE dostawcy SET nazwa = 'Artykuly szkolne' WHERE id = 2;
    
    -- Zapytanie 4
    SELECT promocja, COUNT(*) AS podsumowanie FROM towary GROUP BY promocja;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 8
  9. Etap 2: Grafika i strona HTML

    Kroki 9-28 · po tym etapie masz 19 z 39 kryteriów (pkt)

    Krok 9: zakupy.png: 180 px szerokości z przezroczystością

    Kryteria:

    R.2.1 Obraz z pliku zakupy.png przeskalowano do szerokości 180 px, odpowiadająca temu wysokość wynosi 162 px (± 5 px) oraz obraz ma tło przezroczyste

    ok. 2 min
    Polecenie z arkusza

    Plik zakupy.png, wypakowany z archiwum, należy przeskalować z zachowaniem proporcji tak, aby jego szerokość wynosiła dokładnie 180 px. Plik należy zapisać z zachowaniem przezroczystości pod tą samą nazwą

    Obraz ma 377 × 340 px i przezroczyste tło. W GIMP-ie: Obraz → Skaluj obraz, łańcuch proporcji zamknięty, szerokość 180 - wysokość wyliczy się sama: 340 · 180 / 377 ≈ 162 px (klucz: 162 ± 5). Plik → Nadpisz zakupy.png albo Eksportuj jako zakupy.png: PNG zachowuje kanał alfa, więc tło zostaje przezroczyste i na stronie prześwieca przez nie szary kolor panelu.

    Typowe błędy

    Eksport do JPEG albo spłaszczenie obrazu zamienia przezroczyste tło na białe - klucz R.2.1 wymaga przezroczystości.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 9
  10. Krok 10: Plik index.php w HTML5

    ok. 1 min
    Polecenie z arkusza

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

    Deklaracja <!DOCTYPE html> mówi przeglądarce, że to HTML5. Rozszerzenie .php, bo dojdą skrypty; plik leży w htdocs XAMPP-a i otwieram go przez http://localhost/..., nie z dysku. Dopóki nie ma w nim kodu PHP, podgląd pokazuje go wprost.

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

    ok. 1 min
    Polecenie z arkusza

    Ustawiony język zawartości strony na polski

    Atrybut lang="pl" w znaczniku <html>.

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

    Kryteria:

    R.2.2 Witrynę internetową zapisano w pliku index.php, zastosowano deklarację języka HTML5 <!DOCTYPE html>, zadeklarowano język witryny polski np. <html lang = "pl"> oraz zastosowano właściwy 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 pierwszy element <head>. Z deklaracją HTML5 i językiem to jedno kryterium klucza (R.2.2).

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

    Kryteria:

    R.2.3 Nadano stronie tytuł "Hurtownia szkolna”

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony widoczny na karcie przeglądarki: „Hurtownia szkolna”

    <title> w <head> - tekst widać na karcie przeglądarki, nie na samej stronie.

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

    Kryteria:

    R.3.6 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl.css , formatowanie pochodzi jedynie z tego arkusza Uwaga: kryterium to nie jest spełnione również, gdy zdający źle przypisał co najmniej jeden selektor do bloku (np. kropka zamiast #)

    ok. 1 min
    Polecenie z arkusza

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

    <link rel="stylesheet" href="styl.css">. Całe formatowanie ma pochodzić z tego pliku - żadnych atrybutów style w HTML-u ani znacznika <style> (R.3.6).

    Typowe błędy

    Klucz R.3.6 odbiera punkt także wtedy, gdy choć jeden selektor bloku jest źle przypisany, np. kropka zamiast #.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  15. Krok 15: Bloki: baner, trzy panele, stopka

    Kryteria:

    R.2.4 Wygląd strony zdefiniowano dzieląc ją na bloki: baner, panele lewy, środkowy, prawy oraz stopkę. Zastosowano znaczniki sekcji

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki: na górze baner, poniżej bloki: lewy, środkowy i prawy oraz na dole blok stopki. Podział zrealizowany za pomocą znaczników sekcji tak, aby wygląd układu bloków strony po uruchomieniu w przeglądarce, był zgodny z Obrazem 2

    Baner to <header>, stopka <footer>, a trzy panele - trzy <section> z identyfikatorami left, middle i right. Panele boczne mają wspólny wygląd, a środkowy własny, więc CSS musi je rozróżniać - po identyfikatorach. Obok siebie postawię je w arkuszu stylów (R.2.4).

    index.php PHP zmienione: 10 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
        </header>
        <section id="left">
        </section>
        <section id="middle">
        </section>
        <section id="right">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  16. Krok 16: Baner: nagłówek pierwszego stopnia

    ok. 1 min
    Polecenie z arkusza

    Zawartość banera: nagłówek pierwszego stopnia o treści „Hurtownia z najlepszymi cenami”

    <h1> w <header>.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia z najlepszymi cenami</h1>
        </header>
        <section id="left">
        </section>
        <section id="middle">
        </section>
        <section id="right">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  17. Krok 17: Panel lewy: nagłówek

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku lewego: Nagłówek drugiego stopnia o treści „Nasze ceny”

    <h2> na początku lewego panelu.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia z najlepszymi cenami</h1>
        </header>
        <section id="left">
            <h2>Nasze ceny</h2>
        </section>
        <section id="middle">
        </section>
        <section id="right">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  18. Krok 18: Panel lewy: tabela na ceny

    ok. 1 min
    Polecenie z arkusza

    Tabela składająca się z czterech wierszy i dwóch kolumn o zawartości generowanej skryptem 1

    Na razie sam znacznik <table>. Wiersze <tr> z dwiema komórkami <td> (nazwa i cena) wypisze Skrypt 1 - tyle wierszy, ile zwróci zapytanie 1, czyli cztery. Tabela nie ma wiersza nagłówkowego - na obrazie 2 są tylko dane.

    index.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia z najlepszymi cenami</h1>
        </header>
        <section id="left">
            <h2>Nasze ceny</h2>
            <table>
            </table>
        </section>
        <section id="middle">
        </section>
        <section id="right">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  19. Krok 19: Panel środkowy: nagłówek

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku środkowego: Nagłówek drugiego stopnia o treści „Koszt zakupów”

    Drugi <h2>, tym razem w środkowym panelu.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia z najlepszymi cenami</h1>
        </header>
        <section id="left">
            <h2>Nasze ceny</h2>
            <table>
            </table>
        </section>
        <section id="middle">
            <h2>Koszt zakupów</h2>
        </section>
        <section id="right">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  20. Krok 20: Formularz wysyłany metodą POST

    Kryteria:

    R.3.2 Dane z formularza przesyłane są za pomocą metody POST do tego samego pliku (poprawne również bez atrybutu action)

    ok. 1 min
    Polecenie z arkusza

    Formularz zgodny z obrazem 2, wysyłający dane do tego samego pliku metodą bezpieczną.

    Metoda bezpieczna w rozumieniu arkusza to method="post": dane jadą w treści żądania, a nie w adresie strony. W specyfikacji HTTP „bezpieczne” są co prawda metody, które niczego nie zmieniają na serwerze (GET i HEAD), ale arkusz używa tego słowa w innym sensie, a klucz wymaga POST. Atrybut action pomijam - formularz bez niego wysyła dane do bieżącego pliku, a klucz wprost to dopuszcza („poprawne również bez atrybutu action”).

    Typowe błędy

    method="get" też wysyła dane, ale wtedy trafiają do adresu i do $_GET - klucz R.3.2 wymaga POST.

    index.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia z najlepszymi cenami</h1>
        </header>
        <section id="left">
            <h2>Nasze ceny</h2>
            <table>
            </table>
        </section>
        <section id="middle">
            <h2>Koszt zakupów</h2>
            <form method="post">
            </form>
        </section>
        <section id="right">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  21. Krok 21: Napis „wybierz artykuł: ”

    ok. 1 min
    Polecenie z arkusza

    Napis „wybierz artykuł: ”

    Napis zapisuję jako etykietę <label>: atrybut for wskaże id listy, którą dodam w następnym kroku, więc kliknięcie napisu rozwinie listę. Spacja po dwukropku należy do treści, jak w arkuszu.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia z najlepszymi cenami</h1>
        </header>
        <section id="left">
            <h2>Nasze ceny</h2>
            <table>
            </table>
        </section>
        <section id="middle">
            <h2>Koszt zakupów</h2>
            <form method="post">
                <label for="product">wybierz artykuł: </label>
            </form>
        </section>
        <section id="right">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  22. Krok 22: Lista rozwijalna z czterema towarami

    Kryteria:

    R.2.7 W bloku środkowym umieszczono jedną listę rozwijaną, zawierającą nazwy produktów: Zeszyt 60 kartek, Zeszyt 32 kartki, Cyrkiel, Linijka 30 cm. Zastosowano znaczniki <select>, <option>

    ok. 2 min
    Polecenie z arkusza

    Listę rozwijalną zawierającą nazwy towarów z tabeli: Zeszyt 60 kartek, Zeszyt 32 kartki, Cyrkiel, Linijka 30 cm.

    Lista rozwijalna to <select> z czterema <option>. Atrybut name="product" to klucz, pod którym wybór trafi do $_POST. Opcje nie mają atrybutu value, więc formularz wysyła ich tekst - a Skrypt 2 potrzebuje właśnie nazwy towaru do warunku zapytania. Nazwy przepisuję dokładnie, z wielkością liter i spacjami, bo porównanie w SQL dotyczy całego napisu. <br> przenosi kolejne pole do nowej linii, jak na obrazie 2.

    Typowe błędy

    Literówka w nazwie („Linijka 30cm” bez spacji) sprawi, że zapytanie w Skrypcie 2 nic nie znajdzie dla tego towaru.

    index.php PHP zmienione: 7 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia z najlepszymi cenami</h1>
        </header>
        <section id="left">
            <h2>Nasze ceny</h2>
            <table>
            </table>
        </section>
        <section id="middle">
            <h2>Koszt zakupów</h2>
            <form method="post">
                <label for="product">wybierz artykuł: </label>
                <select id="product" name="product">
                    <option>Zeszyt 60 kartek</option>
                    <option>Zeszyt 32 kartki</option>
                    <option>Cyrkiel</option>
                    <option>Linijka 30 cm</option>
                </select>
                <br>
            </form>
        </section>
        <section id="right">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  23. Krok 23: Pole liczbowe z napisem „liczba sztuk: ”

    Kryteria:

    R.3.4 Pole edycyjne jest typu numerycznego

    ok. 1 min
    Polecenie z arkusza

    Pole edycyjne typu numerycznego poprzedzone napisem „liczba sztuk: ”

    type="number" - przeglądarka przyjmuje tylko liczby i pokazuje strzałki do zmiany wartości. Napis to znowu <label> powiązany przez for/id, a name="amount" to klucz w $_POST. Kolejny <br> przenosi przycisk pod pole.

    index.php PHP zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia z najlepszymi cenami</h1>
        </header>
        <section id="left">
            <h2>Nasze ceny</h2>
            <table>
            </table>
        </section>
        <section id="middle">
            <h2>Koszt zakupów</h2>
            <form method="post">
                <label for="product">wybierz artykuł: </label>
                <select id="product" name="product">
                    <option>Zeszyt 60 kartek</option>
                    <option>Zeszyt 32 kartki</option>
                    <option>Cyrkiel</option>
                    <option>Linijka 30 cm</option>
                </select>
                <br>
                <label for="amount">liczba sztuk: </label>
                <input type="number" id="amount" name="amount">
                <br>
            </form>
        </section>
        <section id="right">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  24. Krok 24: Przycisk „OBLICZ”

    Kryteria: ,

    R.2.8 W bloku środkowym umieszczono pole edycyjne oraz przycisk OBLICZ

    R.3.3 Przycisk w formularzu realizuje wysyłanie danych do skryptu (input lub button z typem submit lub button bez typu)

    ok. 1 min
    Polecenie z arkusza

    Przycisk „OBLICZ”, którego wybranie powoduje przesłanie danych do skryptu 2

    <button type="submit"> wysyła formularz; po przeładowaniu strony Skrypt 2 odczyta przesłane dane. Używam <button>, a nie <input type="submit">, z prostego powodu: arkusz każe nadać margines selektorowi pola edycyjnego input, a przycisk zbudowany z input dostałby go razem z polem.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia z najlepszymi cenami</h1>
        </header>
        <section id="left">
            <h2>Nasze ceny</h2>
            <table>
            </table>
        </section>
        <section id="middle">
            <h2>Koszt zakupów</h2>
            <form method="post">
                <label for="product">wybierz artykuł: </label>
                <select id="product" name="product">
                    <option>Zeszyt 60 kartek</option>
                    <option>Zeszyt 32 kartki</option>
                    <option>Cyrkiel</option>
                    <option>Linijka 30 cm</option>
                </select>
                <br>
                <label for="amount">liczba sztuk: </label>
                <input type="number" id="amount" name="amount">
                <br>
                <button type="submit">OBLICZ</button>
            </form>
        </section>
        <section id="right">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  25. Krok 25: Panel prawy: nagłówek

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku prawego: Nagłówek drugiego stopnia o treści „Kontakt”

    Trzeci <h2> - w prawym panelu.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia z najlepszymi cenami</h1>
        </header>
        <section id="left">
            <h2>Nasze ceny</h2>
            <table>
            </table>
        </section>
        <section id="middle">
            <h2>Koszt zakupów</h2>
            <form method="post">
                <label for="product">wybierz artykuł: </label>
                <select id="product" name="product">
                    <option>Zeszyt 60 kartek</option>
                    <option>Zeszyt 32 kartki</option>
                    <option>Cyrkiel</option>
                    <option>Linijka 30 cm</option>
                </select>
                <br>
                <label for="amount">liczba sztuk: </label>
                <input type="number" id="amount" name="amount">
                <br>
                <button type="submit">OBLICZ</button>
            </form>
        </section>
        <section id="right">
            <h2>Kontakt</h2>
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  26. Krok 26: Obraz z tekstem alternatywnym

    Kryteria:

    R.2.9 W bloku prawym umieszczono obraz zakupy.png z tekstem alternatywnym "hurtownia"

    ok. 1 min
    Polecenie z arkusza

    Obraz zakupy.png z tekstem alternatywnym „hurtownia”

    <img> z przeskalowanym obrazem. Tekst alternatywny w atrybucie alt odczyta czytnik ekranu i pokaże przeglądarka, gdy obrazu zabraknie. Na szarym tle panelu widać, że tło obrazu jest przezroczyste.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia z najlepszymi cenami</h1>
        </header>
        <section id="left">
            <h2>Nasze ceny</h2>
            <table>
            </table>
        </section>
        <section id="middle">
            <h2>Koszt zakupów</h2>
            <form method="post">
                <label for="product">wybierz artykuł: </label>
                <select id="product" name="product">
                    <option>Zeszyt 60 kartek</option>
                    <option>Zeszyt 32 kartki</option>
                    <option>Cyrkiel</option>
                    <option>Linijka 30 cm</option>
                </select>
                <br>
                <label for="amount">liczba sztuk: </label>
                <input type="number" id="amount" name="amount">
                <br>
                <button type="submit">OBLICZ</button>
            </form>
        </section>
        <section id="right">
            <h2>Kontakt</h2>
            <img src="zakupy.png" alt="hurtownia">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  27. Krok 27: Akapit z odsyłaczem pocztowym

    Kryteria:

    R.3.5 Napis [email protected] jest odsyłaczem pocztowym, w definicji zastosowano "mailto:"

    ok. 1 min
    Polecenie z arkusza

    Paragraf (akapit) „e-mail: [email protected]”, gdzie [email protected] jest odsyłaczem pocztowym łączącym z tym adresem

    Odsyłacz pocztowy to zwykły <a>, ale adres zaczyna się od mailto: - kliknięcie otwiera program pocztowy z wpisanym odbiorcą. Odnośnikiem jest sam adres, słowo „e-mail:” zostaje zwykłym tekstem akapitu.

    Typowe błędy

    href="[email protected]" bez mailto: przeglądarka potraktuje jak plik w bieżącym folderze.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia z najlepszymi cenami</h1>
        </header>
        <section id="left">
            <h2>Nasze ceny</h2>
            <table>
            </table>
        </section>
        <section id="middle">
            <h2>Koszt zakupów</h2>
            <form method="post">
                <label for="product">wybierz artykuł: </label>
                <select id="product" name="product">
                    <option>Zeszyt 60 kartek</option>
                    <option>Zeszyt 32 kartki</option>
                    <option>Cyrkiel</option>
                    <option>Linijka 30 cm</option>
                </select>
                <br>
                <label for="amount">liczba sztuk: </label>
                <input type="number" id="amount" name="amount">
                <br>
                <button type="submit">OBLICZ</button>
            </form>
        </section>
        <section id="right">
            <h2>Kontakt</h2>
            <img src="zakupy.png" alt="hurtownia">
            <p>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  28. Krok 28: Stopka: nagłówek czwartego stopnia

    Kryteria:

    R.2.5 Zapisano w banerze nagłówek h1 "Hurtownia z najlepszymi cenami”, w panelach przynajmniej jeden z nagłówków h2: "Nasze ceny”, "Koszt zakupów”, "Kontakt" i nagłówek h4 z numerem zdającego w stopce oraz w panelu prawym zapisano paragraf

    ok. 1 min
    Polecenie z arkusza

    Zawartość stopki: nagłówek czwartego stopnia o treści „Witrynę wykonał: ”, dalej wstawiony numer zdającego

    <h4> w <footer> - w tym arkuszu stopka ma nagłówek, nie akapit. Na egzaminie po dwukropku wpisujesz swój numer zdającego (PESEL) - tutaj stoi imię i nazwisko autora rozwiązania. Z tym krokiem strona ma wszystkie nagłówki i akapit z kryterium R.2.5: <h1>, trzy <h2>, <h4> i akapit w prawym panelu.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia z najlepszymi cenami</h1>
        </header>
        <section id="left">
            <h2>Nasze ceny</h2>
            <table>
            </table>
        </section>
        <section id="middle">
            <h2>Koszt zakupów</h2>
            <form method="post">
                <label for="product">wybierz artykuł: </label>
                <select id="product" name="product">
                    <option>Zeszyt 60 kartek</option>
                    <option>Zeszyt 32 kartki</option>
                    <option>Cyrkiel</option>
                    <option>Linijka 30 cm</option>
                </select>
                <br>
                <label for="amount">liczba sztuk: </label>
                <input type="number" id="amount" name="amount">
                <br>
                <button type="submit">OBLICZ</button>
            </form>
        </section>
        <section id="right">
            <h2>Kontakt</h2>
            <img src="zakupy.png" alt="hurtownia">
            <p>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
        </section>
        <footer>
            <h4>Witrynę wykonał: Bartosz Bryniarski</h4>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  29. Etap 3: Styl CSS

    Kroki 29-37 · po tym etapie masz 30 z 39 kryteriów (pkt)

    Krok 29: Plik styl.css: krój Georgia i wyśrodkowanie

    Kryteria:

    R.4.1 Ustawiono domyślne formatowanie wszystkich selektorów (*) krój czcionki Georgia i wyrównanie tekstu do środka

    ok. 1 min
    Polecenie z arkusza

    Domyślne formatowanie wszystkich selektorów: krój czcionki Georgia i wyrównanie tekstu do środka

    Selektor uniwersalny * - formatowanie dostaje każdy element, także lista, pole i przycisk, które domyślnie mają krój systemowy.

    styl.css CSS nowy plik
    * {
        font-family: Georgia;
        text-align: center;
    }
    Podgląd strony po tym kroku (index.php)
  30. Krok 30: Baner i stopka

    Kryteria: ,

    R.4.3 Ustawiono biały kolor czcionki dla banera i stopki

    R.4.6 Ustawiono rozmiar czcionki dla banera i stopki na 120%

    ok. 1 min
    Polecenie z arkusza

    Dla banera i stopki: kolor tła #4BA3C7, biały kolor czcionki, wysokość 80 px, rozmiar czcionki 120%

    Jedna reguła dla dwóch selektorów rozdzielonych przecinkiem. Kolor czcionki to color, a rozmiar font-size - 120% rozmiaru odziedziczonego, więc nagłówki w banerze i stopce też urosną o 20%.

    styl.css CSS zmienione: 6 linii
    * {
        font-family: Georgia;
        text-align: center;
    }
    header, footer {
        background-color: #4BA3C7;
        color: white;
        height: 80px;
        font-size: 120%;
    }
    Podgląd strony po tym kroku (index.php)
  31. Krok 31: Panele lewy i prawy

    ok. 1 min
    Polecenie z arkusza

    Dla panelu lewego i prawego: kolor tła #EFEFEF wysokość 400 px, szerokość 25%

    Selektory identyfikatorów zaczynają się od # i tu przydają się identyfikatory nadane panelom w HTML-u.

    Typowe błędy

    .left (kropka) zamiast #left nie trafia w element - klucz przy R.3.6 wprost wymienia ten błąd.

    styl.css CSS zmienione: 5 linii
    * {
        font-family: Georgia;
        text-align: center;
    }
    header, footer {
        background-color: #4BA3C7;
        color: white;
        height: 80px;
        font-size: 120%;
    }
    #left, #right {
        background-color: #EFEFEF;
        height: 400px;
        width: 25%;
    }
    Podgląd strony po tym kroku (index.php)
  32. Krok 32: Panel środkowy

    Kryteria: , ,

    R.4.2 Ustawiono kolor tła dla banera i stopki na #4BA3C7, panelu lewego i prawego na #EFEFEF, panelu środkowego na #90CAF9

    R.4.4 Ustawiono wysokość banera i stopki na 80px oraz trzech paneli na 400px

    R.4.5 Ustawiono szerokość panelu lewego i prawego na 25%, panelu środkowego na 50%

    ok. 1 min
    Polecenie z arkusza

    Dla panelu środkowego: kolor tła #90CAF9, wysokość 400 px, szerokość 50%

    Środkowy panel jest dwa razy szerszy od bocznych: 25% + 50% + 25% = 100%. Z tą regułą wszystkie kolory tła, wysokości i szerokości bloków są już ustawione.

    styl.css CSS zmienione: 5 linii
    * {
        font-family: Georgia;
        text-align: center;
    }
    header, footer {
        background-color: #4BA3C7;
        color: white;
        height: 80px;
        font-size: 120%;
    }
    #left, #right {
        background-color: #EFEFEF;
        height: 400px;
        width: 25%;
    }
    #middle {
        background-color: #90CAF9;
        height: 400px;
        width: 50%;
    }
    Podgląd strony po tym kroku (index.php)
  33. Krok 33: Tabela: margines lewy, szerokość, obramowanie

    ok. 1 min
    Polecenie z arkusza

    Dla selektora tabeli: margines zewnętrzny lewy 10 px, szerokość 90%, obramowanie linią ciągłą o szerokości 1 px i kolorze #90CAF9

    „Margines zewnętrzny lewy” to margin-left, a nie margin ze wszystkich stron. Skrót border łączy grubość, styl (solid - linia ciągła) i kolor. Selektor znacznika table, jak każe uwaga pod listą stylów.

    styl.css CSS zmienione: 5 linii
    * {
        font-family: Georgia;
        text-align: center;
    }
    header, footer {
        background-color: #4BA3C7;
        color: white;
        height: 80px;
        font-size: 120%;
    }
    #left, #right {
        background-color: #EFEFEF;
        height: 400px;
        width: 25%;
    }
    #middle {
        background-color: #90CAF9;
        height: 400px;
        width: 50%;
    }
    table {
        margin-left: 10px;
        width: 90%;
        border: 1px solid #90CAF9;
    }
    Podgląd strony po tym kroku (index.php)
  34. Krok 34: Komórki tabeli

    Kryteria: ,

    R.4.7 Ustawiono styl selektora table: margines zewnętrzny lewy (np. margin-left) 10px, szerokość 90% oraz ustawiono dla selektora komórki tabeli marginesy wewnętrzne (padding) 5px

    R.4.8 Ustawiono obramowanie tabeli oraz komórki tabeli na 1px solid #90CAF9

    ok. 1 min
    Polecenie z arkusza

    Dla selektora komórek tabeli: marginesy wewnętrzne 5 px, obramowanie komórek tabeli linią ciągłą o szerokości 1 px i kolorze #90CAF9

    Komórka to td; marginesy wewnętrzne to padding. Bez border-collapse każda komórka ma osobną ramkę z odstępem od ramki tabeli - tak wygląda tabela na obrazie 2. Komórki wypisze dopiero Skrypt 1, więc efekt zobaczę po nim.

    styl.css CSS zmienione: 4 linie
    * {
        font-family: Georgia;
        text-align: center;
    }
    header, footer {
        background-color: #4BA3C7;
        color: white;
        height: 80px;
        font-size: 120%;
    }
    #left, #right {
        background-color: #EFEFEF;
        height: 400px;
        width: 25%;
    }
    #middle {
        background-color: #90CAF9;
        height: 400px;
        width: 50%;
    }
    table {
        margin-left: 10px;
        width: 90%;
        border: 1px solid #90CAF9;
    }
    td {
        padding: 5px;
        border: 1px solid #90CAF9;
    }
    Podgląd strony po tym kroku (index.php)
  35. Krok 35: Wiersz tabeli pod kursorem

    Kryteria:

    R.4.9 Po wskazaniu kursorem myszy dowolnego wiersza tabeli zmienia się kolor tekstu w tym wierszu na #90CAF9 (np. za pomocą tr:hover)

    ok. 1 min
    Polecenie z arkusza

    Po wskazaniu kursorem myszy dowolnego wiersza tabeli zostaje zmieniony kolor tekstu w tym wierszu na #90CAF9

    Pseudoklasa :hover na wierszu tr - kolor czcionki dziedziczą obie komórki wiersza.

    styl.css CSS zmienione: 3 linie
    * {
        font-family: Georgia;
        text-align: center;
    }
    header, footer {
        background-color: #4BA3C7;
        color: white;
        height: 80px;
        font-size: 120%;
    }
    #left, #right {
        background-color: #EFEFEF;
        height: 400px;
        width: 25%;
    }
    #middle {
        background-color: #90CAF9;
        height: 400px;
        width: 50%;
    }
    table {
        margin-left: 10px;
        width: 90%;
        border: 1px solid #90CAF9;
    }
    td {
        padding: 5px;
        border: 1px solid #90CAF9;
    }
    tr:hover {
        color: #90CAF9;
    }
    Podgląd strony po tym kroku (index.php)
  36. Krok 36: Pole edycyjne: marginesy górny i dolny

    Kryteria:

    R.4.10 Ustawiono margines zewnętrzny górny i dolny (np. margin-top i margin-bottom) 15px dla selektora input

    ok. 1 min
    Polecenie z arkusza

    Dla selektora pola edycyjnego: margines zewnętrzny górny i dolny 15 px

    Selektor znacznika input, a w nim tylko dwa marginesy - margin-top i margin-bottom (zapis margin: 15px 0 też jest poprawny). Pole odsuwa się od listy i od przycisku, jak na obrazie 2.

    styl.css CSS zmienione: 4 linie
    * {
        font-family: Georgia;
        text-align: center;
    }
    header, footer {
        background-color: #4BA3C7;
        color: white;
        height: 80px;
        font-size: 120%;
    }
    #left, #right {
        background-color: #EFEFEF;
        height: 400px;
        width: 25%;
    }
    #middle {
        background-color: #90CAF9;
        height: 400px;
        width: 50%;
    }
    table {
        margin-left: 10px;
        width: 90%;
        border: 1px solid #90CAF9;
    }
    td {
        padding: 5px;
        border: 1px solid #90CAF9;
    }
    tr:hover {
        color: #90CAF9;
    }
    input {
        margin-top: 15px;
        margin-bottom: 15px;
    }
    Podgląd strony po tym kroku (index.php)
  37. Krok 37: Układ bloków zgodny z obrazem 2

    Kryteria:

    R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z obrazem 2 w arkuszu (prawidłowo zastosowano właściwości CSS układające bloki na stronie, np. float i clear albo display z wartością flex lub grid albo position)

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki: na górze baner, poniżej bloki: lewy, środkowy i prawy oraz na dole blok stopki. Podział zrealizowany za pomocą znaczników sekcji tak, aby wygląd układu bloków strony po uruchomieniu w przeglądarce, był zgodny z Obrazem 2

    Arkusz nie mówi, jak postawić panele obok siebie - wybieram float: left dla wszystkich sekcji i clear: both dla stopki, żeby wróciła pod panele zamiast wjechać pod nie od góry. Szerokości sumują się dokładnie do 100%, więc panelom nie wolno dodawać ramek ani marginesów poziomych - prawy spadłby wtedy pod lewy. Klucz uznaje też display: flex (R.3.1).

    Typowe błędy

    Bez clear: both stopka wsuwa się pod pływające panele i jej tło znika za nimi - układ niezgodny z obrazem 2.

    styl.css CSS zmienione: 6 linii
    * {
        font-family: Georgia;
        text-align: center;
    }
    header, footer {
        background-color: #4BA3C7;
        color: white;
        height: 80px;
        font-size: 120%;
    }
    section {
        float: left;
    }
    #left, #right {
        background-color: #EFEFEF;
        height: 400px;
        width: 25%;
    }
    #middle {
        background-color: #90CAF9;
        height: 400px;
        width: 50%;
    }
    table {
        margin-left: 10px;
        width: 90%;
        border: 1px solid #90CAF9;
    }
    td {
        padding: 5px;
        border: 1px solid #90CAF9;
    }
    tr:hover {
        color: #90CAF9;
    }
    input {
        margin-top: 15px;
        margin-bottom: 15px;
    }
    footer {
        clear: both;
    }
    Podgląd strony po tym kroku (index.php)
  38. Etap 4: Skrypty PHP

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

    Krok 38: Połączenie z bazą sklep

    Kryteria:

    R.5.1 Zastosowano funkcję realizującą połączenie z serwerem bazy danych na localhost, użytkownik root, bez hasła, baza danych sklep

    ok. 2 min
    Polecenie z arkusza

    Napisany w języku PHP, umieszczony w pliku index.php. Skrypt łączy się z serwerem bazodanowym na localhost, użytkownik root bez hasła, baza danych o nazwie sklep

    Pierwszy skrypt na stronie wypełnia tabelę, więc stawiam go wewnątrz <table> i tam otwieram połączenie: mysqli_connect z czterema argumentami (serwer, użytkownik, puste hasło, baza) łączy się z serwerem i od razu wybiera bazę. Uchwyt w zmiennej $connection posłuży obu skryptom. Od tego kroku podgląd to wynik prawdziwego uruchomienia strony w PHP.

    index.php PHP zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia z najlepszymi cenami</h1>
        </header>
        <section id="left">
            <h2>Nasze ceny</h2>
            <table>
                <?php
                    $connection = mysqli_connect("localhost", "root", "", "sklep");
                ?>
            </table>
        </section>
        <section id="middle">
            <h2>Koszt zakupów</h2>
            <form method="post">
                <label for="product">wybierz artykuł: </label>
                <select id="product" name="product">
                    <option>Zeszyt 60 kartek</option>
                    <option>Zeszyt 32 kartki</option>
                    <option>Cyrkiel</option>
                    <option>Linijka 30 cm</option>
                </select>
                <br>
                <label for="amount">liczba sztuk: </label>
                <input type="number" id="amount" name="amount">
                <br>
                <button type="submit">OBLICZ</button>
            </form>
        </section>
        <section id="right">
            <h2>Kontakt</h2>
            <img src="zakupy.png" alt="hurtownia">
            <p>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
        </section>
        <footer>
            <h4>Witrynę wykonał: Bartosz Bryniarski</h4>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  39. Krok 39: Skrypt 1: zapytanie 1

    Kryteria:

    R.5.3 Skrypt 1 zawiera instrukcję wysyłającą do bazy danych zapytanie 1

    ok. 1 min
    Polecenie z arkusza

    Wysyła do bazy danych zapytanie 1

    Zapytanie 1 bez zmian w zmiennej $query; mysqli_query wysyła je do bazy, a w $result zostaje wynik do odczytania wiersz po wierszu. Strona wygląda na razie tak samo - nic jeszcze nie jest wypisywane.

    index.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia z najlepszymi cenami</h1>
        </header>
        <section id="left">
            <h2>Nasze ceny</h2>
            <table>
                <?php
                    $connection = mysqli_connect("localhost", "root", "", "sklep");
                    $query = "SELECT nazwa, cena FROM towary LIMIT 4;";
                    $result = mysqli_query($connection, $query);
                ?>
            </table>
        </section>
        <section id="middle">
            <h2>Koszt zakupów</h2>
            <form method="post">
                <label for="product">wybierz artykuł: </label>
                <select id="product" name="product">
                    <option>Zeszyt 60 kartek</option>
                    <option>Zeszyt 32 kartki</option>
                    <option>Cyrkiel</option>
                    <option>Linijka 30 cm</option>
                </select>
                <br>
                <label for="amount">liczba sztuk: </label>
                <input type="number" id="amount" name="amount">
                <br>
                <button type="submit">OBLICZ</button>
            </form>
        </section>
        <section id="right">
            <h2>Kontakt</h2>
            <img src="zakupy.png" alt="hurtownia">
            <p>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
        </section>
        <footer>
            <h4>Witrynę wykonał: Bartosz Bryniarski</h4>
        </footer>
    </body>
    </html>

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

  40. Krok 40: Skrypt 1: wiersz tabeli z nazwą towaru

    ok. 2 min
    Polecenie z arkusza

    Wyświetla dane pobrane zapytaniem wewnątrz komórek tabeli. W pierwszej kolumnie tabeli wypisywane są nazwy czterech towarów, w drugiej kolumnie odpowiadające im ceny (zgodnie z obrazem 2)

    mysqli_fetch_row oddaje kolejny wiersz wyniku jako tablicę numerowaną od zera ($row[0] - nazwa, $row[1] - cena), a po ostatnim zwraca null i pętla while się kończy. Każdy rekord to osobny wiersz <tr>. Na razie jedna komórka - w podglądzie cztery nazwy jedna pod drugą.

    index.php PHP zmienione: 5 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia z najlepszymi cenami</h1>
        </header>
        <section id="left">
            <h2>Nasze ceny</h2>
            <table>
                <?php
                    $connection = mysqli_connect("localhost", "root", "", "sklep");
                    $query = "SELECT nazwa, cena FROM towary LIMIT 4;";
                    $result = mysqli_query($connection, $query);
                    while ($row = mysqli_fetch_row($result)) {
                        echo "<tr>";
                        echo "<td>$row[0]</td>";
                        echo "</tr>";
                    }
                ?>
            </table>
        </section>
        <section id="middle">
            <h2>Koszt zakupów</h2>
            <form method="post">
                <label for="product">wybierz artykuł: </label>
                <select id="product" name="product">
                    <option>Zeszyt 60 kartek</option>
                    <option>Zeszyt 32 kartki</option>
                    <option>Cyrkiel</option>
                    <option>Linijka 30 cm</option>
                </select>
                <br>
                <label for="amount">liczba sztuk: </label>
                <input type="number" id="amount" name="amount">
                <br>
                <button type="submit">OBLICZ</button>
            </form>
        </section>
        <section id="right">
            <h2>Kontakt</h2>
            <img src="zakupy.png" alt="hurtownia">
            <p>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
        </section>
        <footer>
            <h4>Witrynę wykonał: Bartosz Bryniarski</h4>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  41. Krok 41: Skrypt 1: druga kolumna z ceną

    Kryteria: ,

    R.5.4 Skrypt 1 wyświetla dane zwrócone zapytaniem w tabeli, każdy rekord w osobnym wierszu

    R.2.6 W bloku lewym umieszczono tabelę o 2 kolumnach (w kodzie HTML lub skrypcie), zastosowano znaczniki <table>, <tr>, <td>

    ok. 1 min
    Polecenie z arkusza

    Wyświetla dane pobrane zapytaniem wewnątrz komórek tabeli. W pierwszej kolumnie tabeli wypisywane są nazwy czterech towarów, w drugiej kolumnie odpowiadające im ceny (zgodnie z obrazem 2)

    Druga komórka z ceną. Tabela ma teraz cztery wiersze i dwie kolumny, jak na obrazie 2: Zeszyt 60 kartek 4.5, Zeszyt 32 kartki 1.2, Cyrkiel 12.4, Linijka 30 cm 7.2. Po najechaniu kursorem wiersz zmienia kolor tekstu.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia z najlepszymi cenami</h1>
        </header>
        <section id="left">
            <h2>Nasze ceny</h2>
            <table>
                <?php
                    $connection = mysqli_connect("localhost", "root", "", "sklep");
                    $query = "SELECT nazwa, cena FROM towary LIMIT 4;";
                    $result = mysqli_query($connection, $query);
                    while ($row = mysqli_fetch_row($result)) {
                        echo "<tr>";
                        echo "<td>$row[0]</td>";
                        echo "<td>$row[1]</td>";
                        echo "</tr>";
                    }
                ?>
            </table>
        </section>
        <section id="middle">
            <h2>Koszt zakupów</h2>
            <form method="post">
                <label for="product">wybierz artykuł: </label>
                <select id="product" name="product">
                    <option>Zeszyt 60 kartek</option>
                    <option>Zeszyt 32 kartki</option>
                    <option>Cyrkiel</option>
                    <option>Linijka 30 cm</option>
                </select>
                <br>
                <label for="amount">liczba sztuk: </label>
                <input type="number" id="amount" name="amount">
                <br>
                <button type="submit">OBLICZ</button>
            </form>
        </section>
        <section id="right">
            <h2>Kontakt</h2>
            <img src="zakupy.png" alt="hurtownia">
            <p>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
        </section>
        <footer>
            <h4>Witrynę wykonał: Bartosz Bryniarski</h4>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  42. Krok 42: Skrypt 2 pod formularzem i zamknięcie połączenia

    Kryteria:

    R.5.2 Ostatnią operacją na bazie jest zamknięcie połączenia z serwerem bazy danych

    ok. 2 min
    Polecenie z arkusza

    Wyniki działania skryptu 2

    Wynik Skryptu 2 ma się pojawić pod formularzem, więc jego blok PHP stawiam zaraz po </form>. Warunek isset($_POST["product"]) sprawdza, czy formularz został wysłany - przy pierwszym wejściu na stronę tej wartości nie ma i skrypt nic nie robi. Od razu dopisuję też ostatni blok PHP pod stopką, który zamyka połączenie: mysqli_close($connection) to ostatnia operacja na bazie, wykonywana zawsze, także gdy formularza nie wysłano.

    Typowe błędy

    mysqli_close wewnątrz if zamyka połączenie tylko po wysłaniu formularza - przy pierwszym wejściu zostaje otwarte.

    index.php PHP zmienione: 7 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia z najlepszymi cenami</h1>
        </header>
        <section id="left">
            <h2>Nasze ceny</h2>
            <table>
                <?php
                    $connection = mysqli_connect("localhost", "root", "", "sklep");
                    $query = "SELECT nazwa, cena FROM towary LIMIT 4;";
                    $result = mysqli_query($connection, $query);
                    while ($row = mysqli_fetch_row($result)) {
                        echo "<tr>";
                        echo "<td>$row[0]</td>";
                        echo "<td>$row[1]</td>";
                        echo "</tr>";
                    }
                ?>
            </table>
        </section>
        <section id="middle">
            <h2>Koszt zakupów</h2>
            <form method="post">
                <label for="product">wybierz artykuł: </label>
                <select id="product" name="product">
                    <option>Zeszyt 60 kartek</option>
                    <option>Zeszyt 32 kartki</option>
                    <option>Cyrkiel</option>
                    <option>Linijka 30 cm</option>
                </select>
                <br>
                <label for="amount">liczba sztuk: </label>
                <input type="number" id="amount" name="amount">
                <br>
                <button type="submit">OBLICZ</button>
            </form>
            <?php
                if (isset($_POST["product"])) {
                }
            ?>
        </section>
        <section id="right">
            <h2>Kontakt</h2>
            <img src="zakupy.png" alt="hurtownia">
            <p>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
        </section>
        <footer>
            <h4>Witrynę wykonał: Bartosz Bryniarski</h4>
        </footer>
        <?php
            mysqli_close($connection);
        ?>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  43. Krok 43: Skrypt 2: nazwa towaru z listy

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

    Pobiera z formularza nazwę towaru wybraną w liście rozwijalnej

    Dane wysłane metodą POST są w tablicy globalnej $_POST, pod nazwą pola z formularza - name="product". Od tego kroku podgląd pokazuje stronę po wysłaniu formularza z obrazu 2 (Zeszyt 32 kartki, 2 sztuki); niczego jeszcze nie widać, bo skrypt tylko zapamiętuje nazwę.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia z najlepszymi cenami</h1>
        </header>
        <section id="left">
            <h2>Nasze ceny</h2>
            <table>
                <?php
                    $connection = mysqli_connect("localhost", "root", "", "sklep");
                    $query = "SELECT nazwa, cena FROM towary LIMIT 4;";
                    $result = mysqli_query($connection, $query);
                    while ($row = mysqli_fetch_row($result)) {
                        echo "<tr>";
                        echo "<td>$row[0]</td>";
                        echo "<td>$row[1]</td>";
                        echo "</tr>";
                    }
                ?>
            </table>
        </section>
        <section id="middle">
            <h2>Koszt zakupów</h2>
            <form method="post">
                <label for="product">wybierz artykuł: </label>
                <select id="product" name="product">
                    <option>Zeszyt 60 kartek</option>
                    <option>Zeszyt 32 kartki</option>
                    <option>Cyrkiel</option>
                    <option>Linijka 30 cm</option>
                </select>
                <br>
                <label for="amount">liczba sztuk: </label>
                <input type="number" id="amount" name="amount">
                <br>
                <button type="submit">OBLICZ</button>
            </form>
            <?php
                if (isset($_POST["product"])) {
                    $product = $_POST["product"];
                }
            ?>
        </section>
        <section id="right">
            <h2>Kontakt</h2>
            <img src="zakupy.png" alt="hurtownia">
            <p>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
        </section>
        <footer>
            <h4>Witrynę wykonał: Bartosz Bryniarski</h4>
        </footer>
        <?php
            mysqli_close($connection);
        ?>
    </body>
    </html>

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

  44. Krok 44: Skrypt 2: liczba sztuk z pola

    ok. 1 min
    Polecenie z arkusza

    Pobiera z formularza liczbę wpisaną w polu edycyjnym

    Tak samo dla pola liczbowego: $_POST["amount"]. Wartość przychodzi jako napis ("2"), ale PHP przy mnożeniu sam zamieni go na liczbę.

    Typowe błędy

    Puste pole liczby sztuk kończy się w PHP 8 błędem krytycznym „Unsupported operand types: string * string” przy mnożeniu ceny - strona urywa się w połowie (sprawdzone w PHP 8.2). Klucz tego nie sprawdza - zabezpieczenie jest w etapie „Dobre praktyki”.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia z najlepszymi cenami</h1>
        </header>
        <section id="left">
            <h2>Nasze ceny</h2>
            <table>
                <?php
                    $connection = mysqli_connect("localhost", "root", "", "sklep");
                    $query = "SELECT nazwa, cena FROM towary LIMIT 4;";
                    $result = mysqli_query($connection, $query);
                    while ($row = mysqli_fetch_row($result)) {
                        echo "<tr>";
                        echo "<td>$row[0]</td>";
                        echo "<td>$row[1]</td>";
                        echo "</tr>";
                    }
                ?>
            </table>
        </section>
        <section id="middle">
            <h2>Koszt zakupów</h2>
            <form method="post">
                <label for="product">wybierz artykuł: </label>
                <select id="product" name="product">
                    <option>Zeszyt 60 kartek</option>
                    <option>Zeszyt 32 kartki</option>
                    <option>Cyrkiel</option>
                    <option>Linijka 30 cm</option>
                </select>
                <br>
                <label for="amount">liczba sztuk: </label>
                <input type="number" id="amount" name="amount">
                <br>
                <button type="submit">OBLICZ</button>
            </form>
            <?php
                if (isset($_POST["product"])) {
                    $product = $_POST["product"];
                    $amount = $_POST["amount"];
                }
            ?>
        </section>
        <section id="right">
            <h2>Kontakt</h2>
            <img src="zakupy.png" alt="hurtownia">
            <p>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
        </section>
        <footer>
            <h4>Witrynę wykonał: Bartosz Bryniarski</h4>
        </footer>
        <?php
            mysqli_close($connection);
        ?>
    </body>
    </html>

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

  45. Krok 45: Skrypt 2: zapytanie 2 z wybraną nazwą

    Kryteria:

    R.5.6 Skrypt 2 zawiera instrukcję wysyłającą do bazy danych zapytanie 2 zmodyfikowane tak, że w sekcji warunku nazwa towaru jest zgodna z wyborem w liście rozwijanej

    ok. 2 min
    Polecenie z arkusza

    Wysyła do bazy danych zapytanie 2, zmodyfikowane w ten sposób, że w sekcji warunku nazwa towaru jest zgodna z wyborem w liście rozwijalnej

    Zapytanie 2, w którym zamiast „Ekierka” stoi zmienna $product. Napis PHP jest w cudzysłowach, więc PHP wstawi do niego wartość zmiennej, a apostrofy wokół zostają - w SQL nazwa to nadal tekst. Zapytanie zwraca jeden wiersz, więc wystarczy jedno wywołanie mysqli_fetch_row; cena jest w $row[0]. Zmienne $query i $result używam ponownie - tabela Skryptu 1 jest już wypisana.

    index.php PHP zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia z najlepszymi cenami</h1>
        </header>
        <section id="left">
            <h2>Nasze ceny</h2>
            <table>
                <?php
                    $connection = mysqli_connect("localhost", "root", "", "sklep");
                    $query = "SELECT nazwa, cena FROM towary LIMIT 4;";
                    $result = mysqli_query($connection, $query);
                    while ($row = mysqli_fetch_row($result)) {
                        echo "<tr>";
                        echo "<td>$row[0]</td>";
                        echo "<td>$row[1]</td>";
                        echo "</tr>";
                    }
                ?>
            </table>
        </section>
        <section id="middle">
            <h2>Koszt zakupów</h2>
            <form method="post">
                <label for="product">wybierz artykuł: </label>
                <select id="product" name="product">
                    <option>Zeszyt 60 kartek</option>
                    <option>Zeszyt 32 kartki</option>
                    <option>Cyrkiel</option>
                    <option>Linijka 30 cm</option>
                </select>
                <br>
                <label for="amount">liczba sztuk: </label>
                <input type="number" id="amount" name="amount">
                <br>
                <button type="submit">OBLICZ</button>
            </form>
            <?php
                if (isset($_POST["product"])) {
                    $product = $_POST["product"];
                    $amount = $_POST["amount"];
                    $query = "SELECT cena FROM towary WHERE nazwa = '$product';";
                    $result = mysqli_query($connection, $query);
                    $row = mysqli_fetch_row($result);
                }
            ?>
        </section>
        <section id="right">
            <h2>Kontakt</h2>
            <img src="zakupy.png" alt="hurtownia">
            <p>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
        </section>
        <footer>
            <h4>Witrynę wykonał: Bartosz Bryniarski</h4>
        </footer>
        <?php
            mysqli_close($connection);
        ?>
    </body>
    </html>

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

  46. Krok 46: Skrypt 2: wartość zakupów

    Kryteria:

    R.5.7 Skrypt 2 oblicza wartość zakupów przez pomnożenie ceny zwróconej zapytaniem z liczbą pobraną z pola edycyjnego

    ok. 1 min
    Polecenie z arkusza

    Oblicza wartość zakupów na podstawie ceny towaru zwróconej zapytaniem i liczby sztuk tego towaru pobranej z pola edycyjnego

    Wartość zakupów to cena razy liczba sztuk: $row[0] * $amount. Dla zeszytu za 1.2 i 2 sztuk wychodzi 2.4.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia z najlepszymi cenami</h1>
        </header>
        <section id="left">
            <h2>Nasze ceny</h2>
            <table>
                <?php
                    $connection = mysqli_connect("localhost", "root", "", "sklep");
                    $query = "SELECT nazwa, cena FROM towary LIMIT 4;";
                    $result = mysqli_query($connection, $query);
                    while ($row = mysqli_fetch_row($result)) {
                        echo "<tr>";
                        echo "<td>$row[0]</td>";
                        echo "<td>$row[1]</td>";
                        echo "</tr>";
                    }
                ?>
            </table>
        </section>
        <section id="middle">
            <h2>Koszt zakupów</h2>
            <form method="post">
                <label for="product">wybierz artykuł: </label>
                <select id="product" name="product">
                    <option>Zeszyt 60 kartek</option>
                    <option>Zeszyt 32 kartki</option>
                    <option>Cyrkiel</option>
                    <option>Linijka 30 cm</option>
                </select>
                <br>
                <label for="amount">liczba sztuk: </label>
                <input type="number" id="amount" name="amount">
                <br>
                <button type="submit">OBLICZ</button>
            </form>
            <?php
                if (isset($_POST["product"])) {
                    $product = $_POST["product"];
                    $amount = $_POST["amount"];
                    $query = "SELECT cena FROM towary WHERE nazwa = '$product';";
                    $result = mysqli_query($connection, $query);
                    $row = mysqli_fetch_row($result);
                    $value = $row[0] * $amount;
                }
            ?>
        </section>
        <section id="right">
            <h2>Kontakt</h2>
            <img src="zakupy.png" alt="hurtownia">
            <p>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
        </section>
        <footer>
            <h4>Witrynę wykonał: Bartosz Bryniarski</h4>
        </footer>
        <?php
            mysqli_close($connection);
        ?>
    </body>
    </html>

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

  47. Krok 47: Skrypt 2: napis z kwotą pod formularzem

    Kryteria:

    R.5.8 Kwota wyświetlona jest pod formularzem w formacie: „wartość zakupów: <obliczona_kwota>”

    ok. 1 min
    Polecenie z arkusza

    Obliczona kwota wyświetlana jest pod formularzem w formacie: „wartość zakupów: <obliczona_kwota>”, zgodnie z obrazem 2.

    Jedno echo z napisem z arkusza i zmienną w środku. Podgląd wygląda jak środkowy panel z obrazu 2: pod przyciskiem „wartość zakupów: 2.4”. Strona spełnia już wszystkie kryteria klucza.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia z najlepszymi cenami</h1>
        </header>
        <section id="left">
            <h2>Nasze ceny</h2>
            <table>
                <?php
                    $connection = mysqli_connect("localhost", "root", "", "sklep");
                    $query = "SELECT nazwa, cena FROM towary LIMIT 4;";
                    $result = mysqli_query($connection, $query);
                    while ($row = mysqli_fetch_row($result)) {
                        echo "<tr>";
                        echo "<td>$row[0]</td>";
                        echo "<td>$row[1]</td>";
                        echo "</tr>";
                    }
                ?>
            </table>
        </section>
        <section id="middle">
            <h2>Koszt zakupów</h2>
            <form method="post">
                <label for="product">wybierz artykuł: </label>
                <select id="product" name="product">
                    <option>Zeszyt 60 kartek</option>
                    <option>Zeszyt 32 kartki</option>
                    <option>Cyrkiel</option>
                    <option>Linijka 30 cm</option>
                </select>
                <br>
                <label for="amount">liczba sztuk: </label>
                <input type="number" id="amount" name="amount">
                <br>
                <button type="submit">OBLICZ</button>
            </form>
            <?php
                if (isset($_POST["product"])) {
                    $product = $_POST["product"];
                    $amount = $_POST["amount"];
                    $query = "SELECT cena FROM towary WHERE nazwa = '$product';";
                    $result = mysqli_query($connection, $query);
                    $row = mysqli_fetch_row($result);
                    $value = $row[0] * $amount;
                    echo "wartość zakupów: $value";
                }
            ?>
        </section>
        <section id="right">
            <h2>Kontakt</h2>
            <img src="zakupy.png" alt="hurtownia">
            <p>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
        </section>
        <footer>
            <h4>Witrynę wykonał: Bartosz Bryniarski</h4>
        </footer>
        <?php
            mysqli_close($connection);
        ?>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  48. Etap 5: Dobre praktyki (poza kluczem)

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

    Krok 48: Skrypt 2: sprawdzenie liczby sztuk i zapytanie przygotowane

    ok. 4 min
    Polecenie z arkusza

    Wysyła do bazy danych zapytanie 2, zmodyfikowane w ten sposób, że w sekcji warunku nazwa towaru jest zgodna z wyborem w liście rozwijalnej

    Wersję z egzaminu sprawdziłem trzema „złośliwymi” wysłaniami formularza, uruchamiając ją naprawdę w PHP 8.2:

    • puste pole liczby sztuk - mnożenie ceny przez pusty napis kończy się błędem krytycznym „Unsupported operand types: string * string” i strona urywa się w środkowym panelu, bez prawego panelu i stopki,
    • nazwa towaru spoza listy (np. „Ekierka 2”) - zapytanie nic nie zwraca, PHP ostrzega „Trying to access array offset on value of type null” i wypisuje „wartość zakupów: 0”,
    • wartość ' OR '1'='1 zamiast nazwy (listę da się obejść, wysyłając żądanie z pominięciem formularza) - warunek staje się zawsze prawdziwy i strona liczy cenę pierwszego towaru w tabeli, czyli zeszytu za 4.5.

    Poprawki: is_numeric i warunek > 0 dla liczby sztuk, zapytanie przygotowane (mysqli_prepare ze znakiem ?, mysqli_stmt_bind_param z typem "s") - wartość z formularza nigdy nie staje się częścią SQL - i sprawdzenie, czy zapytanie coś zwróciło. Stara wersja zostaje w komentarzu. Na egzaminie wystarczy wersja wcześniejsza - klucz tego nie sprawdza.

    index.php PHP zmienione: 18 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia z najlepszymi cenami</h1>
        </header>
        <section id="left">
            <h2>Nasze ceny</h2>
            <table>
                <?php
                    $connection = mysqli_connect("localhost", "root", "", "sklep");
                    $query = "SELECT nazwa, cena FROM towary LIMIT 4;";
                    $result = mysqli_query($connection, $query);
                    while ($row = mysqli_fetch_row($result)) {
                        echo "<tr>";
                        echo "<td>$row[0]</td>";
                        echo "<td>$row[1]</td>";
                        echo "</tr>";
                    }
                ?>
            </table>
        </section>
        <section id="middle">
            <h2>Koszt zakupów</h2>
            <form method="post">
                <label for="product">wybierz artykuł: </label>
                <select id="product" name="product">
                    <option>Zeszyt 60 kartek</option>
                    <option>Zeszyt 32 kartki</option>
                    <option>Cyrkiel</option>
                    <option>Linijka 30 cm</option>
                </select>
                <br>
                <label for="amount">liczba sztuk: </label>
                <input type="number" id="amount" name="amount">
                <br>
                <button type="submit">OBLICZ</button>
            </form>
            <?php
                if (isset($_POST["product"]) && is_numeric($_POST["amount"] ?? "") && $_POST["amount"] > 0) {
                    $product = $_POST["product"];
                    $amount = (int) $_POST["amount"];
                    $statement = mysqli_prepare($connection, "SELECT cena FROM towary WHERE nazwa = ?");
                    mysqli_stmt_bind_param($statement, "s", $product);
                    mysqli_stmt_execute($statement);
                    $row = mysqli_fetch_row(mysqli_stmt_get_result($statement));
                    if ($row) {
                        $value = $row[0] * $amount;
                        echo "wartość zakupów: $value";
                    } else {
                        echo "Nie ma takiego towaru.";
                    }
                } elseif (isset($_POST["product"])) {
                    echo "Podaj liczbę sztuk większą od zera.";
                }
                /* Wersja z egzaminu:
                if (isset($_POST["product"])) {
                    $product = $_POST["product"];
                    $amount = $_POST["amount"];
                    $query = "SELECT cena FROM towary WHERE nazwa = '$product';";
                    $result = mysqli_query($connection, $query);
                    $row = mysqli_fetch_row($result);
                    $value = $row[0] * $amount;
                    echo "wartość zakupów: $value";
                }
                */
            ?>
        </section>
        <section id="right">
            <h2>Kontakt</h2>
            <img src="zakupy.png" alt="hurtownia">
            <p>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
        </section>
        <footer>
            <h4>Witrynę wykonał: Bartosz Bryniarski</h4>
        </footer>
        <?php
            mysqli_close($connection);
        ?>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  49. Krok 49: Skrypt 1: kolumny po nazwach i ceny po polsku

    ok. 3 min
    Polecenie z arkusza

    Wyświetla dane pobrane zapytaniem wewnątrz komórek tabeli. W pierwszej kolumnie tabeli wypisywane są nazwy czterech towarów, w drugiej kolumnie odpowiadające im ceny (zgodnie z obrazem 2)

    mysqli_fetch_assoc oddaje wiersz z kluczami takimi jak nazwy kolumn - $product["cena"] mówi więcej niż $row[1]. Nazwę towaru wypisuję przez htmlspecialchars: gdyby w bazie znalazł się znak < albo &, przeglądarka nie potraktuje go jak początku znacznika. Cenę formatuje number_format($cena, 2, ",", " ") - dwa miejsca po przecinku dziesiętnym i „zł”, czyli 4,50 zł zamiast 4.5. Na egzaminie wystarczy wersja wcześniejsza - obraz 2 pokazuje ceny tak, jak są w bazie, i klucz tego nie sprawdza.

    index.php PHP zmienione: 4 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia z najlepszymi cenami</h1>
        </header>
        <section id="left">
            <h2>Nasze ceny</h2>
            <table>
                <?php
                    $connection = mysqli_connect("localhost", "root", "", "sklep");
                    $query = "SELECT nazwa, cena FROM towary LIMIT 4;";
                    $result = mysqli_query($connection, $query);
                    while ($product = mysqli_fetch_assoc($result)) {
                        echo "<tr>";
                        echo "<td>" . htmlspecialchars($product["nazwa"]) . "</td>";
                        echo "<td>" . number_format($product["cena"], 2, ",", " ") . " zł</td>";
                        echo "</tr>";
                    }
                    // while ($row = mysqli_fetch_row($result)) { echo "<tr><td>$row[0]</td><td>$row[1]</td></tr>"; }
                ?>
            </table>
        </section>
        <section id="middle">
            <h2>Koszt zakupów</h2>
            <form method="post">
                <label for="product">wybierz artykuł: </label>
                <select id="product" name="product">
                    <option>Zeszyt 60 kartek</option>
                    <option>Zeszyt 32 kartki</option>
                    <option>Cyrkiel</option>
                    <option>Linijka 30 cm</option>
                </select>
                <br>
                <label for="amount">liczba sztuk: </label>
                <input type="number" id="amount" name="amount">
                <br>
                <button type="submit">OBLICZ</button>
            </form>
            <?php
                if (isset($_POST["product"]) && is_numeric($_POST["amount"] ?? "") && $_POST["amount"] > 0) {
                    $product = $_POST["product"];
                    $amount = (int) $_POST["amount"];
                    $statement = mysqli_prepare($connection, "SELECT cena FROM towary WHERE nazwa = ?");
                    mysqli_stmt_bind_param($statement, "s", $product);
                    mysqli_stmt_execute($statement);
                    $row = mysqli_fetch_row(mysqli_stmt_get_result($statement));
                    if ($row) {
                        $value = $row[0] * $amount;
                        echo "wartość zakupów: $value";
                    } else {
                        echo "Nie ma takiego towaru.";
                    }
                } elseif (isset($_POST["product"])) {
                    echo "Podaj liczbę sztuk większą od zera.";
                }
                /* Wersja z egzaminu:
                if (isset($_POST["product"])) {
                    $product = $_POST["product"];
                    $amount = $_POST["amount"];
                    $query = "SELECT cena FROM towary WHERE nazwa = '$product';";
                    $result = mysqli_query($connection, $query);
                    $row = mysqli_fetch_row($result);
                    $value = $row[0] * $amount;
                    echo "wartość zakupów: $value";
                }
                */
            ?>
        </section>
        <section id="right">
            <h2>Kontakt</h2>
            <img src="zakupy.png" alt="hurtownia">
            <p>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
        </section>
        <footer>
            <h4>Witrynę wykonał: Bartosz Bryniarski</h4>
        </footer>
        <?php
            mysqli_close($connection);
        ?>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  50. Krok 50: Formularz pamięta wybór po wysłaniu

    ok. 3 min
    Polecenie z arkusza

    Formularz zgodny z obrazem 2, wysyłający dane do tego samego pliku metodą bezpieczną.

    Po kliknięciu „OBLICZ” strona ładuje się od nowa i formularz wraca do stanu początkowego - lista pokazuje pierwszy zeszyt, pole jest puste, a pod spodem stoi wynik dla czegoś innego. Na obrazie 2 lista i pole pokazują to, co wysłano, więc tak też robię: opcje wypisuje krótka pętla po tablicy nazw, a opcja równa wysłanej dostaje atrybut selected; pole liczbowe dostaje value z wysłaną liczbą (przez htmlspecialchars, bo to tekst od użytkownika) i min="1", które blokuje zero i liczby ujemne już w przeglądarce. Stare opcje zostają w komentarzu HTML. Na egzaminie wystarczy wersja wcześniejsza - klucz tego nie sprawdza.

    index.php PHP zmienione: 12 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Hurtownia szkolna</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Hurtownia z najlepszymi cenami</h1>
        </header>
        <section id="left">
            <h2>Nasze ceny</h2>
            <table>
                <?php
                    $connection = mysqli_connect("localhost", "root", "", "sklep");
                    $query = "SELECT nazwa, cena FROM towary LIMIT 4;";
                    $result = mysqli_query($connection, $query);
                    while ($product = mysqli_fetch_assoc($result)) {
                        echo "<tr>";
                        echo "<td>" . htmlspecialchars($product["nazwa"]) . "</td>";
                        echo "<td>" . number_format($product["cena"], 2, ",", " ") . " zł</td>";
                        echo "</tr>";
                    }
                    // while ($row = mysqli_fetch_row($result)) { echo "<tr><td>$row[0]</td><td>$row[1]</td></tr>"; }
                ?>
            </table>
        </section>
        <section id="middle">
            <h2>Koszt zakupów</h2>
            <form method="post">
                <label for="product">wybierz artykuł: </label>
                <select id="product" name="product">
                    <?php
                        $products = ["Zeszyt 60 kartek", "Zeszyt 32 kartki", "Cyrkiel", "Linijka 30 cm"];
                        foreach ($products as $name) {
                            $selected = ($_POST["product"] ?? "") == $name ? " selected" : "";
                            echo "<option$selected>$name</option>";
                        }
                    ?>
                    <!-- <option>Zeszyt 60 kartek</option> -->
                    <!-- <option>Zeszyt 32 kartki</option> -->
                    <!-- <option>Cyrkiel</option> -->
                    <!-- <option>Linijka 30 cm</option> -->
                </select>
                <br>
                <label for="amount">liczba sztuk: </label>
                <input type="number" id="amount" name="amount" min="1" value="<?php echo htmlspecialchars($_POST["amount"] ?? ""); ?>">
                <br>
                <button type="submit">OBLICZ</button>
            </form>
            <?php
                if (isset($_POST["product"]) && is_numeric($_POST["amount"] ?? "") && $_POST["amount"] > 0) {
                    $product = $_POST["product"];
                    $amount = (int) $_POST["amount"];
                    $statement = mysqli_prepare($connection, "SELECT cena FROM towary WHERE nazwa = ?");
                    mysqli_stmt_bind_param($statement, "s", $product);
                    mysqli_stmt_execute($statement);
                    $row = mysqli_fetch_row(mysqli_stmt_get_result($statement));
                    if ($row) {
                        $value = $row[0] * $amount;
                        echo "wartość zakupów: $value";
                    } else {
                        echo "Nie ma takiego towaru.";
                    }
                } elseif (isset($_POST["product"])) {
                    echo "Podaj liczbę sztuk większą od zera.";
                }
                /* Wersja z egzaminu:
                if (isset($_POST["product"])) {
                    $product = $_POST["product"];
                    $amount = $_POST["amount"];
                    $query = "SELECT cena FROM towary WHERE nazwa = '$product';";
                    $result = mysqli_query($connection, $query);
                    $row = mysqli_fetch_row($result);
                    $value = $row[0] * $amount;
                    echo "wartość zakupów: $value";
                }
                */
            ?>
        </section>
        <section id="right">
            <h2>Kontakt</h2>
            <img src="zakupy.png" alt="hurtownia">
            <p>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
        </section>
        <footer>
            <h4>Witrynę wykonał: Bartosz Bryniarski</h4>
        </footer>
        <?php
            mysqli_close($connection);
        ?>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  51. Etap 6: Oddanie pracy

    Krok 51 · po tym etapie masz 39 z 39 kryteriów (pkt)

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

    ok. 5 min
    Polecenie z arkusza

    Po zakończeniu pracy utwórz w folderze z numerem zdającego 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 folderze z numerem zdającego powinny się znajdować następujące pliki: import.jpg, index.php, kwerenda1.jpg, kwerenda2.jpg, kwerenda3.jpg, kwerenda4.jpg, kwerendy.txt, przeglądarka.txt, styl.css, zakupy.png oraz ewentualnie inne przygotowane pliki.

    W przeglądarka.txt wpisuję nazwę przeglądarki, w której sprawdzałem stronę przez http://localhost/.... Potem porównuję zawartość folderu z listą z arkusza (sklep.sql z archiwum może zostać), nagrywam płytę, sprawdzam jej odczyt i opisuję ją numerem zdającego.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

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

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.