Przejdź do głównej treści
E.14-02-21.06-SGCzerwiec 2021 · 25 kroków · ok. 62 min · 39 kryteriów z klucza

Rozwiązanie krok po kroku: Hurtownia artykułów biurowych i kalkulator zakupów (Nowoczesny kod (flex, PDO))

To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza sklep zaimportowana z pliku sklep.sql, cztery zapytania, grafika zakupy2.png o szerokości 150 px z przezroczystością i strona index.php z cennikiem w tabeli i kalkulatorem wartości zakupów - i tak samo spełnia każde kryterium klucza. Inna jest droga:

  • flex zamiast float: trzy panele stoją we wspólnym znaczniku <main>, który układa je obok siebie, a stopka nie potrzebuje clear. Szerokości 35%, 30% i 35% zostają na panelach, bo tam sprawdza je klucz,
  • zmienne CSS dla kolorów z arkusza, napisy przy polach formularza jako etykiety <label>,
  • PHP na PDO na górze pliku, przed HTML-em - wszystkie operacje na bazie odbywają się, zanim zacznie się strona, więc kolejność skryptów w panelach przestaje mieć znaczenie,
  • lista artykułów w jednej tablicy: z niej powstają opcje listy rozwijanej i z nią porównywany jest wybór z formularza (biała lista),
  • sprawdzona liczba sztuk (liczba całkowita, nie mniejsza od zera), cena pobierana zapytaniem przygotowanym, dane przez htmlspecialchars, a po wysłaniu formularz pamięta wybór.

To arkusz E.14 z czerwca 2021 - kwalifikacji formuły 2012, którą zastąpiła INF.03. Materiały pochodzą z oryginalnego archiwum CKE do pokrewnego zadania E.14-02-19.06 (szczegóły we wstępie wersji podstawowej, archiwum do pobrania na stronie arkusza). Zapytania są wykonane na MariaDB 10.4, a podgląd od kroków z PHP to wynik prawdziwego uruchomienia kodu w PHP 8.2.

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. Przy każdym kroku jest napisane, które kryteria spełnia; klucz uznaje zarówno MySQLi, jak i PDO.

  1. Etap 1: Baza danych

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

    Krok 1: Folder zdającego, archiwum i import bazy sklep

    Kryteria:

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

    ok. 4 min
    Polecenie z arkusza

    Do bazy zaimportuj tabele z pliku sklep.sql, z wcześniej rozpakowanego archiwum. Wykonaj zrzut ekranu po imporcie. Zrzut zapisz w folderze z Twoim numerem PESEL, 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

    Folder na pulpicie nazwany numerem PESEL (w tym rozwiązaniu w jego miejscu stoi Bartosz Bryniarski), do niego archiwum pliki2.zip rozpakowane z hasłem Zad@n!E_14. W phpMyAdmin nowa baza sklep, karta Import, plik sklep.sql - powstają tabele towary (10 wierszy) i dostawcy (3 wiersze). Zrzut całego ekranu z komunikatem o udanym imporcie zapisz jako import.jpg (R.1.1).

    sklep.sql SQL nowy plik
    -- phpMyAdmin SQL Dump
    -- version 4.5.1
    -- http://www.phpmyadmin.net
    --
    -- Host: 127.0.0.1
    -- Czas generowania: 14 Lis 2017, 11:08
    -- Wersja serwera: 10.1.10-MariaDB
    -- Wersja PHP: 5.6.15
    
    SET SQL_MODE = "NO_AUTO_VALUE_ON_ZERO";
    SET time_zone = "+00:00";
    
    
    /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
    /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
    /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
    /*!40101 SET NAMES utf8mb4 */;
    
    --
    -- Baza danych: `sklep2`
    --
    
    -- --------------------------------------------------------
    
    --
    -- Struktura tabeli dla tabeli `dostawcy`
    --
    
    CREATE TABLE `dostawcy` (
      `id` int(11) NOT NULL,
      `nazwa` varchar(50) NOT NULL,
      `adres` varchar(50) NOT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
    
    --
    -- Zrzut danych tabeli `dostawcy`
    --
    
    INSERT INTO `dostawcy` (`id`, `nazwa`, `adres`) VALUES
    (1, 'Papiernictwo', 'ul. Boczna 12, 23-355 Bukowina'),
    (2, 'Art. szkolne', 'ul. Dworcowa 20, 34-565 Warszawa'),
    (3, 'Wszystko dla ucznia', 'ul. Towarowa 35, 88-123 Zakopane');
    
    -- --------------------------------------------------------
    
    --
    -- Struktura tabeli dla tabeli `towary`
    --
    
    CREATE TABLE `towary` (
      `id` int(11) NOT NULL,
      `nazwa` varchar(20) NOT NULL,
      `cena` float NOT NULL,
      `promocja` tinyint(1) NOT NULL,
      `idDostawcy` int(11) NOT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1;
    
    --
    -- Zrzut danych tabeli `towary`
    --
    
    INSERT INTO `towary` (`id`, `nazwa`, `cena`, `promocja`, `idDostawcy`) VALUES
    (1, 'Zeszyt 60 kartek', 4.5, 0, 1),
    (2, 'Zeszyt 32 kartki', 1.2, 0, 2),
    (3, 'Cyrkiel', 12.4, 0, 1),
    (4, 'Linijka 30 cm', 7.2, 0, 3),
    (5, 'Ekierka', 5.5, 0, 3),
    (6, 'Linijka 50 cm', 8.2, 0, 2),
    (7, 'Gumka do mazania', 3.2, 1, 1),
    (8, 'Cienkopis', 2.5, 1, 2),
    (9, 'Pisaki 60 szt.', 55, 1, 2),
    (10, 'Markery 4 szt.', 22.4, 1, 1);
    
    --
    -- Indeksy dla zrzutów tabel
    --
    
    --
    -- Indexes for table `dostawcy`
    --
    ALTER TABLE `dostawcy`
      ADD PRIMARY KEY (`id`);
    
    --
    -- Indexes for table `towary`
    --
    ALTER TABLE `towary`
      ADD PRIMARY KEY (`id`);
    
    --
    -- AUTO_INCREMENT for dumped tables
    --
    
    --
    -- AUTO_INCREMENT dla tabeli `dostawcy`
    --
    ALTER TABLE `dostawcy`
      MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=4;
    --
    -- AUTO_INCREMENT dla tabeli `towary`
    --
    ALTER TABLE `towary`
      MODIFY `id` int(11) NOT NULL AUTO_INCREMENT, AUTO_INCREMENT=11;
    /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
    /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
    /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 1
  2. Krok 2: 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 dla czterech pierwszych rekordów w tabeli towary. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa, cena FROM towary LIMIT 4; oraz na zrzucie widoczne są jedynie nazwy i ceny 4 produktów: Zeszyt 60 kartek - 4.5, Zeszyt 32 kartki - 1.2, Cyrkiel - 12.4, Linijka 30 cm - 7.2 (w zapytaniu zamiast klauzuli LIMIT można zastosować również WHERE określającą wartości w polu id, np.: id<=4, lub id=1 OR id=2 OR...)

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 1: wybierające jedynie pola nazwa i cena dla czterech pierwszych rekordów w tabeli towary

    SELECT nazwa, cena FROM towary ORDER BY id LIMIT 4; - ORDER BY id mówi wprost, które rekordy są „pierwsze”. Bez sortowania baza może oddać wiersze w dowolnej kolejności - dziś jest to kolejność id, ale po zmianach w tabeli nie musi. Wynik: Zeszyt 60 kartek 4.5, Zeszyt 32 kartki 1.2, Cyrkiel 12.4, Linijka 30 cm 7.2 (R.1.3), zapytanie w nowym pliku kwerendy.txt (R.1.2), zrzut kw1.jpg.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT nazwa, cena FROM towary ORDER BY id LIMIT 4;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 2
  3. Krok 3: Zapytanie 2: cena cyrkla

    Kryteria:

    R.1.4 Zapisano zapytanie 2 wybierające jedynie pole cena dla produktu cyrkiel. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT cena FROM towary WHERE nazwa='cyrkiel'; (alternatywnie id) oraz na zrzucie widoczna jest jedynie cena 12.4

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 2: wybierające jedynie pole cena dla produktu cyrkiel z tabeli towary

    Wynik: 12.4 (R.1.4), zrzut kw2.jpg. W skrypcie w miejscu 'Cyrkiel' stanie parametr :name zapytania przygotowanego.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT nazwa, cena FROM towary ORDER BY id LIMIT 4;
    
    -- Zapytanie 2
    SELECT cena FROM towary WHERE nazwa = 'Cyrkiel';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 3
  4. Krok 4: Zapytanie 3: nazwy wszystkich towarów

    Kryteria:

    R.1.5 Zapisano zapytanie 3 wybierające jedynie pole nazwa dla wszystkich rekordów w tabeli towary. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa FROM towary; oraz na zrzucie widoczne są jedynie nazwy wszystkich produktów (10 nazw)

    ok. 1 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie pole nazwa dla wszystkich rekordów w tabeli towary

    SELECT nazwa FROM towary; - wszystkie 10 nazw (R.1.5), zrzut kw3.jpg. Strona z niego nie korzysta.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT nazwa, cena FROM towary ORDER BY id LIMIT 4;
    
    -- Zapytanie 2
    SELECT cena FROM towary WHERE nazwa = 'Cyrkiel';
    
    -- Zapytanie 3
    SELECT nazwa FROM towary;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: 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 ilość towarów objętych i towarów nieobjętych promocją. Pole zliczające nazwano ilosc. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT promocja, COUNT(nazwa) AS ilosc FROM towary GROUP BY promocja; oraz Na zrzucie widoczne są jedynie następujące dane: 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 ilość towarów objętych i towarów nieobjętych promocją. Pole zliczające należy nazwać ilosc.

    GROUP BY promocja dzieli towary na dwie grupy (0 i 1), a COUNT(*) liczy wiersze w każdej - nie zależy od tego, czy jakieś pole jest puste, więc liczy po prostu towary. AS ilosc - nazwa z arkusza. Wynik: 0 - 6 i 1 - 4 (R.1.6), zrzut kw4.jpg.

    kwerendy.txt Tekst zmienione: 5 linii
    -- Zapytanie 1
    SELECT nazwa, cena FROM towary ORDER BY id LIMIT 4;
    
    -- Zapytanie 2
    SELECT cena FROM towary WHERE nazwa = 'Cyrkiel';
    
    -- Zapytanie 3
    SELECT nazwa FROM towary;
    
    -- Zapytanie 4
    SELECT promocja, COUNT(*) AS ilosc
    FROM towary
    GROUP BY promocja;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Etap 2: Grafika

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

    Krok 6: zakupy2.png: 150 px szerokości, przezroczyste tło

    Kryteria:

    R.2.1 Obraz przeskalowano z zachowaniem proporcji tak, aby szerokość wynosiła 150 px, odpowiadająca temu wysokość wynosi 135 px oraz zapisano w pliku z rozszerzeniem png z zachowaniem przezroczystości

    ok. 3 min
    Polecenie z arkusza

    Plik zakupy.png, wypakowany z archiwum, należy przeskalować z zachowaniem proporcji tak, aby jego szerokość wynosiła dokładnie 150 px. Plik po przeskalowaniu należy zapisać jako zakupy2.png, z zachowaniem pozostałych atrybutów, w tym przezroczystości

    W GIMP-ie Obraz → Skaluj obraz z zamkniętym łańcuchem: szerokość 150 px, wysokość przeliczy się sama na 135 px (377 × 340 → 150 × 135). Plik → Eksportuj jako zakupy2.png z kanałem alfa - tło zostaje przezroczyste (R.2.1). Podgląd: grafika na tle panelu środkowego.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Etap 3: Strona index.php

    Kroki 7-11 · po tym etapie masz 18 z 39 kryteriów (pkt)

    Krok 7: Szkielet index.php: kodowanie, tytuł i arkusz stylów

    Kryteria: , ,

    R.2.2 Witrynę internetową zapisano w pliku index.php oraz zastosowano właściwy standard kodowania polskich znaków

    R.2.3 Nadano tytuł stronie "Artykuły biurowe”, jest on widoczny na karcie przeglądarki

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

    ok. 2 min
    Polecenie z arkusza

    Cechy witryny: Składa się ze strony o nazwie index.php. Zastosowany właściwy standard kodowania polskich znaków. Tytuł strony widoczny na karcie przeglądarki: „Artykuły biurowe”. Arkusz stylów w pliku o nazwie styl.css prawidłowo połączony z kodem strony

    • <meta charset="UTF-8"> - kodowanie polskich znaków (R.2.2),
    • <meta name="viewport"> - na telefonie strona nie udaje szerokiego ekranu,
    • <title> - napis na karcie (R.2.3),
    • <link rel="stylesheet" href="styl.css"> - całe formatowanie z pliku CSS (R.3.5).
    index.php PHP nowy plik
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Artykuły biurowe</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  8. Krok 8: Baner, trzy panele w znaczniku main i stopka

    Kryteria:

    R.2.4 Układ strony zdefiniowano dzieląc ją na 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 po uruchomieniu w przeglądarce wygląd układu bloków był zgodny z obrazem 2

    <header> z nagłówkiem <h1>, trzy <section> z identyfikatorami left, middle, right i <footer> z nagłówkiem <h2> (R.2.4). Panele są objęte znacznikiem <main> - to główna treść strony i zarazem pojemnik, który w CSS ułoży je w rzędzie. Na egzaminie w stopce po napisie „Witrynę wykonał” stoi numer PESEL, tu imię i nazwisko autora - Bartosz Bryniarski.

    index.php PHP zmienione: 14 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Artykuły biurowe</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszej hurtowni kupisz najtaniej</h1>
        </header>
        <main>
            <section id="left">
            </section>
            <section id="middle">
            </section>
            <section id="right">
            </section>
        </main>
        <footer>
            <h2>Witrynę wykonał Bartosz Bryniarski</h2>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  9. Krok 9: Panel lewy: formularz z etykietami

    Kryteria: , ,

    R.2.8 Na stronie umieszczono pole edycyjne typu numerycznego z wartością początkową ustawioną na 0 oraz przycisk OBLICZ

    R.3.2 Dane z formularza przesyłane są za pomocą metody POST

    R.3.3 Przycisk wysyła dane z formularza (input lub button z typem submit lub button bez typu)

    ok. 3 min
    Polecenie z arkusza

    Formularz zgodny z obrazem 4, zawierający: Napis „wybierz artykuł”. Listę rozwijalną zgodną z obrazem 5, zawierającą nazwy wszystkich towarów: Zeszyt 60 kartek, Zeszyt 32 kartki, Cyrkiel, Linijka 30 cm, Ekierka, Linijka 50 cm. Pole edycyjne typu numerycznego z wartością domyślną 0 poprzedzone napisem „liczba sztuk:”. Przycisk „OBLICZ” - realizujący skrypt 2. Formularz wysyła dane do serwera za pomocą przycisku OBLICZ, metodą post

    • <h4> i formularz z method="post" (R.3.2),
    • napisy „wybierz artykuł” i „liczba sztuk:” jako <label for="…"> - wyglądają jak zwykły tekst, ale kliknięcie napisu ustawia kursor w polu, a czytnik ekranu odczyta napis razem z polem,
    • lista <select> jest na razie pusta - sześć opcji wygeneruje PHP z tablicy artykułów,
    • type="number" z value="0", min="0" i step="1" - przeglądarka nie pozwoli wpisać liczby ujemnej ani ułamka, a required - wysłać pustego pola,
    • <button type="submit">OBLICZ</button> - przycisk wysyłający formularz (R.2.8 razem z polem liczbowym, R.3.3).

    min i required pomagają tylko uczciwemu użytkownikowi - skrypt i tak sprawdzi liczbę sam.

    index.php PHP zmienione: 9 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Artykuły biurowe</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszej hurtowni kupisz najtaniej</h1>
        </header>
        <main>
            <section id="left">
                <h4>Ile będą kosztować Twoje zakupy?</h4>
                <form method="post">
                    <label for="product">wybierz artykuł</label>
                    <select name="product" id="product">
                    </select><br>
                    <label for="quantity">liczba sztuk:</label>
                    <input type="number" name="quantity" id="quantity" value="0" min="0" step="1" required><br>
                    <button type="submit">OBLICZ</button>
                </form>
            </section>
            <section id="middle">
            </section>
            <section id="right">
            </section>
        </main>
        <footer>
            <h2>Witrynę wykonał Bartosz Bryniarski</h2>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  10. Krok 10: Panel środkowy: grafika i kontakt

    Kryteria: , ,

    R.2.9 Na stronie umieszczono obraz z rozszerzeniem png z tekstem alternatywnym "hurtownia"

    R.2.10 Na stronie umieszczono akapit lub 2 akapity z tekstem „telefon: 888999777 e-mail: [email protected]” oraz tekst jest podzielony wyłącznie na dwa wiersze (zgodnie z obrazem 2 w arkuszu

    R.3.4 Napis [email protected] jest odsyłaczem pocztowym mającym postać np.: <a href= "mailto:[email protected]" >…

    ok. 2 min
    Polecenie z arkusza

    Zawartość bloku środkowego: Obraz zakupy2.png z tekstem alternatywnym „hurtownia”. Nagłówek czwartego stopnia o treści „Kontakt”. Umieszczony w akapicie (paragrafie) tekst „telefon: 888999777 e-mail: [email protected]” podzielony na dwa wiersze (zgodnie z obrazem 2). Napis [email protected] jest odsyłaczem pocztowym łączącym z tym samym adresem

    • <img> z tekstem alternatywnym (R.2.9) i atrybutami width/height - przeglądarka rezerwuje miejsce na obraz, zanim go pobierze, więc treść pod nim nie podskakuje,
    • <h4>Kontakt</h4> i jeden akapit z <br> - dwa wiersze bez odstępu (R.2.10),
    • href="mailto:[email protected]" - odsyłacz pocztowy (R.3.4).
    index.php PHP zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Artykuły biurowe</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszej hurtowni kupisz najtaniej</h1>
        </header>
        <main>
            <section id="left">
                <h4>Ile będą kosztować Twoje zakupy?</h4>
                <form method="post">
                    <label for="product">wybierz artykuł</label>
                    <select name="product" id="product">
                    </select><br>
                    <label for="quantity">liczba sztuk:</label>
                    <input type="number" name="quantity" id="quantity" value="0" min="0" step="1" required><br>
                    <button type="submit">OBLICZ</button>
                </form>
            </section>
            <section id="middle">
                <img src="zakupy2.png" alt="hurtownia" width="150" height="135">
                <h4>Kontakt</h4>
                <p>telefon: 888999777<br>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
            </section>
            <section id="right">
            </section>
        </main>
        <footer>
            <h2>Witrynę wykonał Bartosz Bryniarski</h2>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  11. Krok 11: Panel prawy: nagłówek i tabela

    Kryteria:

    R.2.5 Zapisano wszystkie nagłówki: w banerze - h1 "W naszej hurtowni kupisz najtaniej”, w bloku lewym - h4 "Ile będą kosztować Twoje zakupy?”, w bloku środkowym - h4 "Kontakt", w bloku prawym - h4 "Ceny wybranych artykułów w hurtowni:”,, w stopce - h2 "Witrynę wykonał" i dalej wstawiony numer PESEL zdającego

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku prawego: Nagłówek czwartego stopnia o treści „Ceny wybranych artykułów w hurtowni:”. Tabela zgodna z obrazem 3, składająca się z czterech wierszy i dwóch kolumn. Zawartość tabeli generowana jest skryptem 1

    <h4> i pusta tabela - cztery wiersze wypisze pętla z wyniku zapytania 1. Komplet nagłówków: h1, trzy h4 i h2 w stopce (R.2.5).

    index.php PHP zmienione: 3 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Artykuły biurowe</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszej hurtowni kupisz najtaniej</h1>
        </header>
        <main>
            <section id="left">
                <h4>Ile będą kosztować Twoje zakupy?</h4>
                <form method="post">
                    <label for="product">wybierz artykuł</label>
                    <select name="product" id="product">
                    </select><br>
                    <label for="quantity">liczba sztuk:</label>
                    <input type="number" name="quantity" id="quantity" value="0" min="0" step="1" required><br>
                    <button type="submit">OBLICZ</button>
                </form>
            </section>
            <section id="middle">
                <img src="zakupy2.png" alt="hurtownia" width="150" height="135">
                <h4>Kontakt</h4>
                <p>telefon: 888999777<br>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
            </section>
            <section id="right">
                <h4>Ceny wybranych artykułów w hurtowni:</h4>
                <table>
                </table>
            </section>
        </main>
        <footer>
            <h2>Witrynę wykonał Bartosz Bryniarski</h2>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  12. Etap 4: Styl CSS

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

    Krok 12: Zmienne z kolorami i baner

    ok. 2 min
    Polecenie z arkusza

    Format banera: kolor tła #A61C2E, wysokość 70 px, wielkość czcionki 110%, wyrównanie tekstu do środka

    Plik styl.css zaczyna się od zmiennych CSS w selektorze :root: czerwień #A61C2E występuje w arkuszu cztery razy, szarość raz. Reguły odczytują je funkcją var(--red) - zmiana odcienia to jeden wpis. Baner: tło ze zmiennej, wysokość, czcionka i wyśrodkowanie.

    styl.css CSS nowy plik
    :root {
        --red: #A61C2E;
        --gray: #4D524A;
    }
    
    header {
        background-color: var(--red);
        height: 70px;
        font-size: 110%;
        text-align: center;
    }
    Podgląd strony po tym kroku (index.php)
  13. Krok 13: Panele obok siebie: flex w main

    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 po uruchomieniu w przeglądarce wygląd układu bloków był zgodny z obrazem 2

    display: flex na <main> ustawia trzy sekcje w jednym wierszu. Nie ma float, więc stopka sama staje pod <main>, bez clear: both.

    Siatka (grid-template-columns: 35% 30% 35%) byłaby równie dobra, ale klucz ocenia szerokość na panelach (R.4.4) - dlatego pojemnik flex tylko układa, a szerokości zostają w regułach paneli.

    styl.css CSS zmienione: 4 linie
    :root {
        --red: #A61C2E;
        --gray: #4D524A;
    }
    
    header {
        background-color: var(--red);
        height: 70px;
        font-size: 110%;
        text-align: center;
    }
    
    main {
        display: flex;
    }
    Podgląd strony po tym kroku (index.php)
  14. Krok 14: Trzy panele

    Kryteria: ,

    R.3.1 Układ bloków po uruchomieniu strony w przeglądarce jest zgodny z obrazem 2 w arkuszu

    R.4.4 Ustawiono szerokość panelu lewego i prawego na 35% oraz panelu środkowego na 30%

    ok. 3 min
    Polecenie z arkusza

    Format panelu lewego: kolor tła #A61C2E, wysokość 550 px, szerokość 35%, wielkość czcionki 110%, wyrównanie tekstu do środka. Format panelu środkowego: kolor tła #4D524A, wysokość 550 px, szerokość 30%, wyrównanie tekstu do środka. Format panelu prawego: kolor tła #A61C2E, wysokość 550 px, szerokość 35%, wielkość czcionki 110%, wyrównanie tekstu do prawej

    Panele boczne różnią się tylko wyrównaniem tekstu, więc ich wspólne cechy (czerwone tło, 35%, czcionka 110%) stoją w jednej regule #left, #right, a wysokość 550 px - w regule dla wszystkich trzech. Selektory z krzyżykiem, bo panele mają id.

    Szerokości 35% + 30% + 35% (R.4.4), panele w rzędzie flex - układ jak na obrazie 2 (R.3.1).

    styl.css CSS zmienione: 27 linii
    :root {
        --red: #A61C2E;
        --gray: #4D524A;
    }
    
    header {
        background-color: var(--red);
        height: 70px;
        font-size: 110%;
        text-align: center;
    }
    
    main {
        display: flex;
    }
    
    #left,
    #middle,
    #right {
        height: 550px;
    }
    
    #left,
    #right {
        background-color: var(--red);
        width: 35%;
        font-size: 110%;
    }
    
    #left {
        text-align: center;
    }
    
    #middle {
        background-color: var(--gray);
        width: 30%;
        text-align: center;
    }
    
    #right {
        text-align: right;
    }
    Podgląd strony po tym kroku (index.php)
  15. Krok 15: Stopka

    Kryteria: ,

    R.4.1 Ustawiono kolor tła dla banera, stopki, panelu lewego i prawego na #A61C2E oraz panelu środkowego na #4D524A

    R.4.2 Ustawiono wysokość banera i stopki na 70px oraz paneli: lewego, prawego i środkowego na 550px

    ok. 2 min
    Polecenie z arkusza

    Format stopki: kolor tła #A61C2E, wysokość 70 px, wyrównanie tekstu do prawej

    Kolory tła wszystkich bloków (R.4.1) i wysokości 70/550/70 px (R.4.2) są ustawione. display: flow-root zamyka marginesy nagłówka <h2> wewnątrz stopki - bez tego margines „wypływa” nad stopkę i między panelami a stopką zostaje biały pasek (w wersji podstawowej ten sam efekt usuwa clear: both).

    styl.css CSS zmienione: 7 linii
    :root {
        --red: #A61C2E;
        --gray: #4D524A;
    }
    
    header {
        background-color: var(--red);
        height: 70px;
        font-size: 110%;
        text-align: center;
    }
    
    main {
        display: flex;
    }
    
    #left,
    #middle,
    #right {
        height: 550px;
    }
    
    #left,
    #right {
        background-color: var(--red);
        width: 35%;
        font-size: 110%;
    }
    
    #left {
        text-align: center;
    }
    
    #middle {
        background-color: var(--gray);
        width: 30%;
        text-align: center;
    }
    
    #right {
        text-align: right;
    }
    
    footer {
        background-color: var(--red);
        height: 70px;
        text-align: right;
        display: flow-root;
    }
    Podgląd strony po tym kroku (index.php)
  16. Krok 16: Tabela, komórki i wiersz pod kursorem

    Kryteria: , , , ,

    R.4.3 Ustawiono wyrównanie tekstu do środka dla: banera, panelu lewego i środkowego, komórek tabeli oraz wyrównanie tekstu do prawej dla panelu prawego i stopki

    R.4.5 Ustawiono wielkość czcionki dla banera, paneli lewego i prawego oraz komórek tabeli na 110%

    R.4.6 Ustawiono marginesy wewnętrzne (padding) dla komórek tabeli na 10px

    R.4.8 Ustawiono obramowanie komórek tabeli linią pojedynczą kropkowaną niebieską (dowolny odcień) o grubości 2px (właściwość border)

    R.4.9 Ustawiono styl dla wierszy tabeli z pseudoklasą hover: po najechaniu kursorem na wybrany wiersz tekst zmienia kolor na niebieski (dowolny odcień)

    ok. 3 min
    Polecenie z arkusza

    Format tabeli: margines zewnętrzny lewy 150 px, obramowanie komórek tabeli linią kropkowaną (brak odstępów pomiędzy komórkami). Format komórek tabeli: obramowanie linią kropkowaną niebieską o szerokości 2 px, marginesy wewnętrzne 10 px, wyrównanie tekstu do środka, wielkość czcionki 110%. Po wskazaniu kursorem myszki wybranego wiersza tabeli zmienia się kolor tekstu w tym wierszu na niebieski

    • table - margin-left: 150px i border-collapse: collapse, czyli wspólne krawędzie komórek bez odstępów,
    • td - border: 2px dotted blue (R.4.8), padding: 10px (R.4.6), wyśrodkowanie i czcionka 110%,
    • tr:hover - cały wiersz pod kursorem zmienia kolor tekstu na niebieski (R.4.9).

    Wyrównania (R.4.3) i rozmiary czcionki 110% w banerze, panelach bocznych i komórkach (R.4.5) są kompletne.

    styl.css CSS zmienione: 16 linii
    :root {
        --red: #A61C2E;
        --gray: #4D524A;
    }
    
    header {
        background-color: var(--red);
        height: 70px;
        font-size: 110%;
        text-align: center;
    }
    
    main {
        display: flex;
    }
    
    #left,
    #middle,
    #right {
        height: 550px;
    }
    
    #left,
    #right {
        background-color: var(--red);
        width: 35%;
        font-size: 110%;
    }
    
    #left {
        text-align: center;
    }
    
    #middle {
        background-color: var(--gray);
        width: 30%;
        text-align: center;
    }
    
    #right {
        text-align: right;
    }
    
    footer {
        background-color: var(--red);
        height: 70px;
        text-align: right;
        display: flow-root;
    }
    
    table {
        margin-left: 150px;
        border-collapse: collapse;
    }
    
    td {
        border: 2px dotted blue;
        padding: 10px;
        text-align: center;
        font-size: 110%;
    }
    
    tr:hover {
        color: blue;
    }
    Podgląd strony po tym kroku (index.php)
  17. Krok 17: Obraz, nagłówki h4 i pole liczby sztuk

    Kryteria: ,

    R.4.7 Ustawiono wyłącznie margines zewnętrzny górny (margin-top) dla pola edycyjnego na 5px oraz wyłącznie margines zewnętrzny górny (margin-top) dla obrazu i nagłówka h4 na 80px oraz wyłącznie margines zewnętrzny lewy (margin-left) dla tabeli na 150px

    R.4.10 Ustawiono biały kolor tekstu dla nagłówka h4

    ok. 1 min
    Polecenie z arkusza

    Formatowanie obrazu: margines zewnętrzny górny 80 px. Formatowanie nagłówka czwartego stopnia: kolor tekstu biały, margines zewnętrzny górny 80 px. Formatowanie pola edycyjnego: margines zewnętrzny górny 5 px

    Same marginesy górne - img i h4 po 80 px, pole #quantity 5 px (R.4.7). Nagłówki h4 są białe (R.4.10).

    styl.css CSS zmienione: 13 linii
    :root {
        --red: #A61C2E;
        --gray: #4D524A;
    }
    
    header {
        background-color: var(--red);
        height: 70px;
        font-size: 110%;
        text-align: center;
    }
    
    main {
        display: flex;
    }
    
    #left,
    #middle,
    #right {
        height: 550px;
    }
    
    #left,
    #right {
        background-color: var(--red);
        width: 35%;
        font-size: 110%;
    }
    
    #left {
        text-align: center;
    }
    
    #middle {
        background-color: var(--gray);
        width: 30%;
        text-align: center;
    }
    
    #right {
        text-align: right;
    }
    
    footer {
        background-color: var(--red);
        height: 70px;
        text-align: right;
        display: flow-root;
    }
    
    table {
        margin-left: 150px;
        border-collapse: collapse;
    }
    
    td {
        border: 2px dotted blue;
        padding: 10px;
        text-align: center;
        font-size: 110%;
    }
    
    tr:hover {
        color: blue;
    }
    
    img {
        margin-top: 80px;
    }
    
    h4 {
        color: white;
        margin-top: 80px;
    }
    
    #quantity {
        margin-top: 5px;
    }
    Podgląd strony po tym kroku (index.php)
  18. Etap 5: Skrypty PHP

    Kroki 18-23 · po tym etapie masz 39 z 39 kryteriów (pkt)

    Krok 18: Skrypt na górze pliku, PDO i lista artykułów

    Kryteria: , ,

    R.2.7 Na stronie umieszczono listę rozwijalną, zawierającą nazwy produktów (Zeszyt 60 kartek, Zeszyt 32 kartki, Cyrkiel, Linijka 30 cm., Ekierka, Linijka 50 cm.) użyto znaczników <select>, <option> oraz ich zamknięcia

    R.5.1 Skrypt napisano w języku PHP i umieszczono w pliku index.php

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

    ok. 4 min
    Polecenie z arkusza

    Skrypt połączenia z bazą. 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

    Cały kod PHP stoi na samej górze index.php, przed <!DOCTYPE html>: najpierw przygotowuje dane, a HTML tylko je wypisuje (R.5.1).

    • e() - krótka funkcja z htmlspecialchars, przez którą przejdzie każdy tekst wypisany na stronie,
    • $articles - sześć artykułów z obrazu 5 w jednej tablicy. Pętla foreach w liście rozwijanej robi z nich opcje (R.2.7), a za chwilę ta sama tablica posłuży do sprawdzenia wyboru,
    • new PDO(...) - serwer localhost, baza sklep, kodowanie utf8mb4, użytkownik root i puste hasło (R.5.2); try / catch zamienia ewentualny błąd połączenia na zwykłe zdanie.

    Klucz uznaje zarówno MySQLi, jak i PDO. Od tego kroku podgląd to wynik prawdziwego uruchomienia w PHP 8.2 - lista rozwijana ma już sześć pozycji.

    index.php PHP zmienione: 21 linii
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $articles = ['Zeszyt 60 kartek', 'Zeszyt 32 kartki', 'Cyrkiel', 'Linijka 30 cm', 'Ekierka', 'Linijka 50 cm'];
    $prices = [];
    $message = '';
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=sklep;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $exception) {
        $message = 'Nie udało się połączyć z bazą danych, spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Artykuły biurowe</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszej hurtowni kupisz najtaniej</h1>
        </header>
        <main>
            <section id="left">
                <h4>Ile będą kosztować Twoje zakupy?</h4>
                <form method="post">
                    <label for="product">wybierz artykuł</label>
                    <select name="product" id="product">
                        <?php foreach ($articles as $name): ?>
                        <option><?= e($name) ?></option>
                        <?php endforeach; ?>
                    </select><br>
                    <label for="quantity">liczba sztuk:</label>
                    <input type="number" name="quantity" id="quantity" value="0" min="0" step="1" required><br>
                    <button type="submit">OBLICZ</button>
                </form>
            </section>
            <section id="middle">
                <img src="zakupy2.png" alt="hurtownia" width="150" height="135">
                <h4>Kontakt</h4>
                <p>telefon: 888999777<br>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
            </section>
            <section id="right">
                <h4>Ceny wybranych artykułów w hurtowni:</h4>
                <table>
                </table>
            </section>
        </main>
        <footer>
            <h2>Witrynę wykonał Bartosz Bryniarski</h2>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  19. Krok 19: Zapytanie 1 i tabela cen

    Kryteria: , , ,

    R.2.6 W bloku prawym umieszczono tabelę z 4 wierszami i 2 kolumnami. W kodzie HTML lub PHP użyto znaczników <table>, <tr>, <td> oraz ich zamknięcia

    R.5.4 Skrypt 1 wysyła do bazy danych zapytanie 1

    R.5.5 Skrypt 1 wyświetla dane zwrócone zapytaniem (Zeszyt 60 kartek - 4.5, Zeszyt 32 kartki - 1.2, Cyrkiel - 12.4, Linijka 30 cm - 7.2)

    R.5.6 Dane wyświetlane przez skrypt 1 umieszczone są wewnątrz komórek tabeli (zawartość tabeli jest zgodna z obrazem 3, symbol waluty nie jest wymagany)

    ok. 3 min
    Polecenie z arkusza

    Skrypt 1: Wysyła do bazy danych zapytanie 1 (z pliku kwerendy.txt). Wyświetla dane pobrane zapytaniem wewnątrz komórek tabeli. W lewej kolumnie tabeli wypisywane są nazwy czterech towarów, w prawej kolumnie tabeli – odpowiadające im ceny (zgodnie z obrazem 3)

    • $pdo->query(...) - zapytanie 1 bez danych z zewnątrz, więc nie musi być przygotowane (R.5.4),
    • fetchAll() - wszystkie cztery wiersze jako tablice z nazwami kolumn: $item['nazwa'], $item['cena'].

    W tabeli pętla foreach składa wiersz <tr> z dwiema komórkami <td>. Nazwy kolumn zamiast numerów [0], [1] - kod nie zależy od kolejności pól w SELECT. Zapis <?= … ?> to skrót od <?php echo … ?>.

    Podgląd: cztery wiersze cennika (R.2.6, R.5.5, R.5.6).

    index.php PHP zmienione: 4 linie
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $articles = ['Zeszyt 60 kartek', 'Zeszyt 32 kartki', 'Cyrkiel', 'Linijka 30 cm', 'Ekierka', 'Linijka 50 cm'];
    $prices = [];
    $message = '';
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=sklep;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $prices = $pdo->query('SELECT nazwa, cena FROM towary ORDER BY id LIMIT 4;')->fetchAll();
    } catch (PDOException $exception) {
        $message = 'Nie udało się połączyć z bazą danych, spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Artykuły biurowe</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszej hurtowni kupisz najtaniej</h1>
        </header>
        <main>
            <section id="left">
                <h4>Ile będą kosztować Twoje zakupy?</h4>
                <form method="post">
                    <label for="product">wybierz artykuł</label>
                    <select name="product" id="product">
                        <?php foreach ($articles as $name): ?>
                        <option><?= e($name) ?></option>
                        <?php endforeach; ?>
                    </select><br>
                    <label for="quantity">liczba sztuk:</label>
                    <input type="number" name="quantity" id="quantity" value="0" min="0" step="1" required><br>
                    <button type="submit">OBLICZ</button>
                </form>
            </section>
            <section id="middle">
                <img src="zakupy2.png" alt="hurtownia" width="150" height="135">
                <h4>Kontakt</h4>
                <p>telefon: 888999777<br>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
            </section>
            <section id="right">
                <h4>Ceny wybranych artykułów w hurtowni:</h4>
                <table>
                    <?php foreach ($prices as $item): ?>
                    <tr><td><?= e($item['nazwa']) ?></td><td><?= e($item['cena']) ?></td></tr>
                    <?php endforeach; ?>
                </table>
            </section>
        </main>
        <footer>
            <h2>Witrynę wykonał Bartosz Bryniarski</h2>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  20. Krok 20: Artykuł i liczba sztuk z formularza

    ok. 3 min
    Polecenie z arkusza

    Skrypt 2: Pobiera z formularza nazwę produktu wybraną w liście rozwijalnej. Pobiera z formularza ilość wpisaną w polu edycyjnym

    • filter_input(INPUT_POST, 'product') - wybrany artykuł albo null przy pierwszym wejściu, bez ostrzeżenia; (string) zamienia null na pusty napis,
    • FILTER_VALIDATE_INT z min_range 0 - liczba sztuk tylko wtedy, gdy wpis jest liczbą całkowitą nie mniejszą od zera. Puste pole, „-5”, „2,5” czy „abc” dają false, a brak pola - null,
    • w polu value="<?= $quantity ?? 0 ?>" - przy pierwszym wejściu 0, jak wymaga arkusz, a po wysłaniu wpisana liczba zostaje w polu; zaznaczona zostaje też wybrana pozycja listy.

    Podgląd: wysłane - cyrkiel, 3 sztuki.

    index.php PHP zmienione: 5 linii
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $articles = ['Zeszyt 60 kartek', 'Zeszyt 32 kartki', 'Cyrkiel', 'Linijka 30 cm', 'Ekierka', 'Linijka 50 cm'];
    $prices = [];
    $product = (string) filter_input(INPUT_POST, 'product');
    $quantity = filter_input(INPUT_POST, 'quantity', FILTER_VALIDATE_INT, ['options' => ['min_range' => 0]]);
    $total = null;
    $message = '';
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=sklep;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $prices = $pdo->query('SELECT nazwa, cena FROM towary ORDER BY id LIMIT 4;')->fetchAll();
    } catch (PDOException $exception) {
        $message = 'Nie udało się połączyć z bazą danych, spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Artykuły biurowe</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszej hurtowni kupisz najtaniej</h1>
        </header>
        <main>
            <section id="left">
                <h4>Ile będą kosztować Twoje zakupy?</h4>
                <form method="post">
                    <label for="product">wybierz artykuł</label>
                    <select name="product" id="product">
                        <?php foreach ($articles as $name): ?>
                        <option<?= $name === $product ? ' selected' : '' ?>><?= e($name) ?></option>
                        <?php endforeach; ?>
                    </select><br>
                    <label for="quantity">liczba sztuk:</label>
                    <input type="number" name="quantity" id="quantity" value="<?= $quantity ?? 0 ?>" min="0" step="1" required><br>
                    <button type="submit">OBLICZ</button>
                </form>
            </section>
            <section id="middle">
                <img src="zakupy2.png" alt="hurtownia" width="150" height="135">
                <h4>Kontakt</h4>
                <p>telefon: 888999777<br>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
            </section>
            <section id="right">
                <h4>Ceny wybranych artykułów w hurtowni:</h4>
                <table>
                    <?php foreach ($prices as $item): ?>
                    <tr><td><?= e($item['nazwa']) ?></td><td><?= e($item['cena']) ?></td></tr>
                    <?php endforeach; ?>
                </table>
            </section>
        </main>
        <footer>
            <h2>Witrynę wykonał Bartosz Bryniarski</h2>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  21. Krok 21: Zapytanie 2 dla artykułu z białej listy

    Kryteria:

    R.5.7 Skrypt 2 wysyła do bazy danych zapytanie 2 zmodyfikowane tak, że w sekcji warunku sprawdzana nazwa produktu jest zgodna z wyborem

    ok. 3 min
    Polecenie z arkusza

    Wysyła do bazy danych zapytanie 2 (z pliku kwerendy.txt), zmodyfikowane w ten sposób, że w sekcji warunku nazwa produktu jest zgodna z wyborem w liście rozwijalnej

    • in_array($product, $articles, true) - zapytanie idzie do bazy tylko dla artykułu z listy. To biała lista: formularz spreparowany w narzędziach przeglądarki nie wyśle do bazy niczego innego,
    • is_int($quantity) - i tylko przy poprawnej liczbie sztuk,
    • prepare z parametrem :name i execute(['name' => $product]) - zapytanie 2, w którym nazwę baza dostaje osobno od treści zapytania (R.5.7),
    • fetchColumn() - cena z jedynego wiersza, pomnożona przez liczbę sztuk i zaokrąglona round(…, 1) do jednego miejsca po przecinku.
    index.php PHP zmienione: 5 linii
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $articles = ['Zeszyt 60 kartek', 'Zeszyt 32 kartki', 'Cyrkiel', 'Linijka 30 cm', 'Ekierka', 'Linijka 50 cm'];
    $prices = [];
    $product = (string) filter_input(INPUT_POST, 'product');
    $quantity = filter_input(INPUT_POST, 'quantity', FILTER_VALIDATE_INT, ['options' => ['min_range' => 0]]);
    $total = null;
    $message = '';
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=sklep;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $prices = $pdo->query('SELECT nazwa, cena FROM towary ORDER BY id LIMIT 4;')->fetchAll();
        if (in_array($product, $articles, true) && is_int($quantity)) {
            $statement = $pdo->prepare('SELECT cena FROM towary WHERE nazwa = :name;');
            $statement->execute(['name' => $product]);
            $total = round($statement->fetchColumn() * $quantity, 1);
        }
    } catch (PDOException $exception) {
        $message = 'Nie udało się połączyć z bazą danych, spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Artykuły biurowe</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszej hurtowni kupisz najtaniej</h1>
        </header>
        <main>
            <section id="left">
                <h4>Ile będą kosztować Twoje zakupy?</h4>
                <form method="post">
                    <label for="product">wybierz artykuł</label>
                    <select name="product" id="product">
                        <?php foreach ($articles as $name): ?>
                        <option<?= $name === $product ? ' selected' : '' ?>><?= e($name) ?></option>
                        <?php endforeach; ?>
                    </select><br>
                    <label for="quantity">liczba sztuk:</label>
                    <input type="number" name="quantity" id="quantity" value="<?= $quantity ?? 0 ?>" min="0" step="1" required><br>
                    <button type="submit">OBLICZ</button>
                </form>
            </section>
            <section id="middle">
                <img src="zakupy2.png" alt="hurtownia" width="150" height="135">
                <h4>Kontakt</h4>
                <p>telefon: 888999777<br>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
            </section>
            <section id="right">
                <h4>Ceny wybranych artykułów w hurtowni:</h4>
                <table>
                    <?php foreach ($prices as $item): ?>
                    <tr><td><?= e($item['nazwa']) ?></td><td><?= e($item['cena']) ?></td></tr>
                    <?php endforeach; ?>
                </table>
            </section>
        </main>
        <footer>
            <h2>Witrynę wykonał Bartosz Bryniarski</h2>
        </footer>
    </body>
    </html>

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

  22. Krok 22: Wartość zakupów pod formularzem

    Kryteria:

    R.5.8 Skrypt 2 oblicza oraz wyświetla poniżej formularza kwotę powstałą po pomnożeniu ceny pobranej zapytaniem przez ilość pobraną z pola edycyjnego

    ok. 2 min
    Polecenie z arkusza

    Oblicza kwotę zakupów mnożąc cenę pobraną zapytaniem 2 przez ilość pobraną z pola edycyjnego. Obliczona kwota, zaokrąglona do jednego miejsca po przecinku, wyświetlana jest poniżej formularza

    Pod formularzem akapit tylko wtedy, gdy kwota jest policzona, a w nim number_format($total, 1, ',', ' ') - jedno miejsce po przecinku, przecinek dziesiętny i spacja między tysiącami, jak w polskich cenach.

    Podgląd: 3 cyrkle po 12,40 zł - Wartość zakupów: 37,2 zł (R.5.8). Trzy zeszyty po 1,20 zł dają 3,6 zł, a nie 3.5999999999999996 - to zasługa round.

    index.php PHP zmienione: 3 linie
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $articles = ['Zeszyt 60 kartek', 'Zeszyt 32 kartki', 'Cyrkiel', 'Linijka 30 cm', 'Ekierka', 'Linijka 50 cm'];
    $prices = [];
    $product = (string) filter_input(INPUT_POST, 'product');
    $quantity = filter_input(INPUT_POST, 'quantity', FILTER_VALIDATE_INT, ['options' => ['min_range' => 0]]);
    $total = null;
    $message = '';
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=sklep;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $prices = $pdo->query('SELECT nazwa, cena FROM towary ORDER BY id LIMIT 4;')->fetchAll();
        if (in_array($product, $articles, true) && is_int($quantity)) {
            $statement = $pdo->prepare('SELECT cena FROM towary WHERE nazwa = :name;');
            $statement->execute(['name' => $product]);
            $total = round($statement->fetchColumn() * $quantity, 1);
        }
    } catch (PDOException $exception) {
        $message = 'Nie udało się połączyć z bazą danych, spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Artykuły biurowe</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszej hurtowni kupisz najtaniej</h1>
        </header>
        <main>
            <section id="left">
                <h4>Ile będą kosztować Twoje zakupy?</h4>
                <form method="post">
                    <label for="product">wybierz artykuł</label>
                    <select name="product" id="product">
                        <?php foreach ($articles as $name): ?>
                        <option<?= $name === $product ? ' selected' : '' ?>><?= e($name) ?></option>
                        <?php endforeach; ?>
                    </select><br>
                    <label for="quantity">liczba sztuk:</label>
                    <input type="number" name="quantity" id="quantity" value="<?= $quantity ?? 0 ?>" min="0" step="1" required><br>
                    <button type="submit">OBLICZ</button>
                </form>
                <?php if ($total !== null): ?>
                <p>Wartość zakupów: <?= number_format($total, 1, ',', ' ') ?> zł</p>
                <?php endif; ?>
            </section>
            <section id="middle">
                <img src="zakupy2.png" alt="hurtownia" width="150" height="135">
                <h4>Kontakt</h4>
                <p>telefon: 888999777<br>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
            </section>
            <section id="right">
                <h4>Ceny wybranych artykułów w hurtowni:</h4>
                <table>
                    <?php foreach ($prices as $item): ?>
                    <tr><td><?= e($item['nazwa']) ?></td><td><?= e($item['cena']) ?></td></tr>
                    <?php endforeach; ?>
                </table>
            </section>
        </main>
        <footer>
            <h2>Witrynę wykonał Bartosz Bryniarski</h2>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  23. Krok 23: Zamknięcie połączenia

    Kryteria:

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

    ok. 2 min
    Polecenie z arkusza

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

    PDO nie ma funkcji close - połączenie zamyka się, gdy znika ostatnie odwołanie do obiektu, czyli po $statement = null i $pdo = null. Stoją w bloku finally, który wykona się zawsze po try (R.5.3).

    W wersji podstawowej skrypt 2 stoi w panelu lewym, a skrypt 1 w prawym, więc zamknięcie trzeba było postawić w skrypcie 1. Tutaj obie operacje na bazie odbywają się na górze pliku, a HTML korzysta już tylko z tablicy $prices i liczby $total - kolejność paneli nie ma znaczenia. Pod formularzem jest też miejsce na komunikat, gdy serwer bazy nie działa.

    index.php PHP zmienione: 6 linii
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $articles = ['Zeszyt 60 kartek', 'Zeszyt 32 kartki', 'Cyrkiel', 'Linijka 30 cm', 'Ekierka', 'Linijka 50 cm'];
    $prices = [];
    $product = (string) filter_input(INPUT_POST, 'product');
    $quantity = filter_input(INPUT_POST, 'quantity', FILTER_VALIDATE_INT, ['options' => ['min_range' => 0]]);
    $total = null;
    $message = '';
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=sklep;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
        $prices = $pdo->query('SELECT nazwa, cena FROM towary ORDER BY id LIMIT 4;')->fetchAll();
        if (in_array($product, $articles, true) && is_int($quantity)) {
            $statement = $pdo->prepare('SELECT cena FROM towary WHERE nazwa = :name;');
            $statement->execute(['name' => $product]);
            $total = round($statement->fetchColumn() * $quantity, 1);
        }
    } catch (PDOException $exception) {
        $message = 'Nie udało się połączyć z bazą danych, spróbuj ponownie za chwilę.';
    } finally {
        $statement = null;
        $pdo = null;
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Artykuły biurowe</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszej hurtowni kupisz najtaniej</h1>
        </header>
        <main>
            <section id="left">
                <h4>Ile będą kosztować Twoje zakupy?</h4>
                <form method="post">
                    <label for="product">wybierz artykuł</label>
                    <select name="product" id="product">
                        <?php foreach ($articles as $name): ?>
                        <option<?= $name === $product ? ' selected' : '' ?>><?= e($name) ?></option>
                        <?php endforeach; ?>
                    </select><br>
                    <label for="quantity">liczba sztuk:</label>
                    <input type="number" name="quantity" id="quantity" value="<?= $quantity ?? 0 ?>" min="0" step="1" required><br>
                    <button type="submit">OBLICZ</button>
                </form>
                <?php if ($total !== null): ?>
                <p>Wartość zakupów: <?= number_format($total, 1, ',', ' ') ?> zł</p>
                <?php endif; ?>
                <?php if ($message !== ''): ?>
                <p><?= e($message) ?></p>
                <?php endif; ?>
            </section>
            <section id="middle">
                <img src="zakupy2.png" alt="hurtownia" width="150" height="135">
                <h4>Kontakt</h4>
                <p>telefon: 888999777<br>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
            </section>
            <section id="right">
                <h4>Ceny wybranych artykułów w hurtowni:</h4>
                <table>
                    <?php foreach ($prices as $item): ?>
                    <tr><td><?= e($item['nazwa']) ?></td><td><?= e($item['cena']) ?></td></tr>
                    <?php endforeach; ?>
                </table>
            </section>
        </main>
        <footer>
            <h2>Witrynę wykonał Bartosz Bryniarski</h2>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  24. Etap 6: Porównanie z wersją podstawową

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

    Krok 24: Co jest inaczej i którą wersję wybrać na egzaminie

    ok. 3 min

    Obie wersje dają komplet 39 punktów. Różnice:

    wersja podstawowawersja nowoczesna
    układfloat: left sekcji, clear stopki<main> z display: flex
    szerokości 35/30/35%width na panelachwidth na panelach (tak ocenia klucz)
    kolorywpisane w każdej regulezmienne --red, --gray
    napisy przy polachzwykły tekst<label for>
    lista rozwijanasześć opcji wpisanych w HTMLopcje z tablicy $articles
    bibliotekamysqli (tabela 1 arkusza)PDO z wyjątkami
    gdzie jest kod PHPw dwóch panelach, zamknięcie w skrypcie 1na górze pliku, w HTML-u tylko pętle
    artykuł w zapytaniuwklejony w treść zapytaniabiała lista i parametr :name
    liczba sztukbez sprawdzaniaFILTER_VALIDATE_INT, min="0"
    wypisywanieecho wprost z bazye() z htmlspecialchars
    kwota37.237,2 zł, 1 230,0 zł
    po wysłaniupole wraca do 0, lista do pierwszej pozycjiformularz pamięta wybór i liczbę

    Sprawdzone na tej wersji: artykuł x' OR '1'='1, artykuł spoza listy, puste pole, liczba ujemna i ułamek nie dają kwoty ani błędu - w wersji podstawowej puste pole kończy się „Fatal error”, a -5 cyrkli kosztuje -62 zł.

    Na egzaminie wersja podstawowa trzyma się funkcji z tabeli 1 i łatwiej ją napisać bez dokumentacji. Nowoczesna nie wywraca się na nietypowych danych z formularza - a klucz przyjmuje obie.

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

  25. Etap 7: Oddanie pracy

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

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

    ok. 5 min
    Polecenie z arkusza

    Po zakończeniu pracy utwórz plik tekstowy. Zapisz w nim nazwę przeglądarki internetowej, w której weryfikowałeś poprawność działania witryny. Nazwij plik przeglądarka.txt i zapisz go w folderze z numerem PESEL. Nagraj płytę z rezultatami pracy.

    W przeglądarka.txt nazwa przeglądarki. W folderze z numerem zdającego: import.jpg, kw1.jpg - kw4.jpg, kwerendy.txt, index.php, styl.css, zakupy.png, zakupy2.png, przeglądarka.txt (i rozpakowany sklep.sql). Nagraj płytę, sprawdź jej odczyt i opisz ją numerem zdającego.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

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

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.

Potwierdzenie

Informacja