Przejdź do głównej treści
E.14-01-21.06-SGCzerwiec 2021 · 45 kroków · ok. 79 min · 37 kryteriów z klucza

Rozwiązanie krok po kroku: Sklep papierniczy z ceną promocyjną (Wersja podstawowa)

Strona sklepu z artykułami papierniczymi: import gotowej bazy sklep z pliku sklep.sql, cztery zapytania - towary w promocji, cena jednego towaru, liczba towarów każdego dostawcy (COUNT z GROUP BY na dwóch połączonych tabelach) i dodanie kolumny poleceniem ALTER TABLE - oraz grafika przeskalowana do szerokości 150 px z zachowaniem przezroczystości. Strona index.php ma baner, trzy panele i stopkę, style w styl.css i dwa skrypty PHP: pierwszy wypisuje towary w promocji jako listę punktowaną, drugi po wyborze towaru z listy rozwijanej liczy jego cenę po 10% rabatu.

To arkusz E.14 z czerwca 2021 - kwalifikacji formuły 2012, którą zastąpiła INF.03. Arkusz jest pokrewny zadaniu E.14-01-19.06 (ta sama baza i grafika), ale ma inne polecenia, kolory i układ. 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 są najpierw napisane najprościej, funkcjami mysqli z tabeli 1 arkusza, a na końcu sprawdzone na prawdziwym wstrzyknięciu SQL i zabezpieczone.

To jest wersja podstawowa. W zakładce „Nowoczesny kod (flex, PDO)” jest drugi sposób rozwiązania tego samego arkusza: układ na flex we wspólnym pojemniku, semantyczne znaczniki, PDO z zapytaniami przygotowanymi i zabezpieczonym wypisywaniem danych - obie wersje są zgodne z kluczem.

Materiały. Archiwum pliki1.zip z arkusza nie zachowało się w publicznych źródłach. Pliki sklep.sql i promocja.png pochodzą z oryginalnego archiwum CKE do zadania E.14-01-19.06 - mają te same nazwy, struktura bazy jest identyczna z obrazem 1 tego arkusza, a zapytania z klucza dają na nich dokładnie wyniki podane w kluczu. Archiwum odtworzone z tych plików (hasło jak w arkuszu) jest do pobrania na stronie arkusza. Zapytania wykonano na MariaDB 10.4 - tej samej, która jest w XAMPP. Podgląd od kroków z PHP to wynik prawdziwego uruchomienia kodu w PHP 8.2.

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

  1. Etap 1: Baza danych

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

    Krok 1: Folder zdającego i archiwum pliki1.zip

    ok. 2 min
    Polecenie z arkusza

    Aby wykonać zadanie, zaloguj się na konto Egzamin bez hasła. Na pulpicie znajdziesz archiwum ZIP o nazwie pliki1.zip zabezpieczone hasłem: Z@daniE_14 Archiwum należy rozpakować. Na pulpicie konta Egzamin utwórz folder. Jako nazwy folderu użyj swojego numeru PESEL. Rozpakowane pliki umieść w tym folderze. Wyniki swojej pracy również zapisz w tym folderze.

    Na pulpicie tworzysz folder nazwany numerem PESEL - w tym rozwiązaniu w jego miejscu stoi imię i nazwisko autora, Bartosz Bryniarski. Archiwum pliki1.zip otwierasz programem 7-Zip i rozpakowujesz do tego folderu, podając hasło Z@daniE_14 - wielkość liter, @ i podkreślenie mają znaczenie. W środku są dwa pliki: sklep.sql (tabele do zaimportowania) i promocja.png (grafika do przeskalowania).

    Do tego samego folderu trafi wszystko, co powstanie: zrzuty, kwerendy.txt, grafika, strona i styl.

    Typowe błędy

    Pliki zapisane poza folderem zdającego (np. tylko w htdocs) nie trafią na płytę - a ocenia się wyłącznie to, co jest na płycie.

    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 */;
  2. Krok 2: XAMPP i nowa baza sklep

    ok. 2 min
    Polecenie z arkusza

    Uruchom usługi MySQL i Apache z XAMPP Control Panel i przejdź do narzędzia phpMyAdmin. Następnie wykonaj operacje na bazie danych: Utwórz nową bazę danych o nazwie sklep

    W XAMPP Control Panel klikasz Start przy Apache i MySQL, potem w przeglądarce otwierasz http://localhost/phpmyadmin. W lewym panelu Nowa, nazwa bazy sklep, Utwórz. Porównywania napisów nie trzeba ustawiać - tabele z pliku sklep.sql mają własne kodowanie zapisane w poleceniach CREATE TABLE.

    Typowe błędy

    Nazwa bazy musi brzmieć dokładnie sklep - skrypt PHP łączy się z bazą po nazwie (R.5.2). Komentarz w pliku sklep.sql wspomina bazę sklep2, ale to tylko opis, z którego eksportowano tabele; importujesz do bazy sklep.

  3. Krok 3: Import tabel z pliku sklep.sql i zrzut baza.jpg

    Kryteria:

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

    ok. 3 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 baza. 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

    Zaznacz bazę sklep, karta Import, Wybierz plik - sklep.sql z folderu zdającego, Wykonaj (albo Importuj). Plik tworzy dwie tabele z obrazu 1: towary (10 wierszy) i dostawcy (3 wiersze), ustawia klucze główne i autonumerowanie.

    Po imporcie phpMyAdmin pokazuje zielony komunikat o sukcesie, a w lewym panelu pod bazą sklep pojawiają się obie tabele - to są „elementy wskazujące na poprawnie wykonany import”. Wtedy Print Screen, wklejenie do edytora grafiki i zapis jako baza.jpg (R.1.1). Podgląd: karta Struktura bazy po imporcie, narysowana z tej samej bazy.

    Typowe błędy

    Zrzut ma być w JPEG, mieć nazwę baza i obejmować cały ekran z paskiem zadań - wykadrowanego zrzutu egzaminator nie ocenia (uwaga przy R.1 w kluczu).

    Podgląd strony po tym kroku
    Podgląd strony po kroku 3
  4. Krok 4: Zapytanie 1: towary objęte promocją

    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 pole nazwa dla produktów objętych promocją z tabeli towary. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa FROM towary WHERE promocja=1; oraz na zrzucie widoczne są jedynie nazwy towarów: Gumka do mazania, Cienkopis, Pisaki 60 szt., Markery 4 szt.

    ok. 3 min
    Polecenie z arkusza

    Zapisz i wykonaj w phpMyAdmin podane niżej zapytania SQL działające na bazie danych. Wykonaj zrzuty ekranu przedstawiające wyniki działania zapytań. Zrzuty zapisz w formacie JPEG i nadaj im nazwy kw1, kw2, kw3, kw4. Zapytania zapisz w pliku kwerendy.txt. Zapytanie 1: wybierające jedynie pole nazwa dla produktów objętych promocją z tabeli towary

    Karta SQL bazy sklep. Zapytanie klauzula po klauzuli:

    • SELECT nazwa - „jedynie” jedno pole, bez *,
    • FROM towary,
    • WHERE promocja = 1 - pole promocja przechowuje 1 dla towarów w promocji i 0 dla pozostałych.

    Wynik: cztery nazwy - Gumka do mazania, Cienkopis, Pisaki 60 szt., Markery 4 szt. (R.1.3). Zapytanie kopiujesz do nowego pliku kwerendy.txt w folderze zdającego (R.1.2), a cały ekran z wynikiem zapisujesz jako kw1.jpg. To samo zapytanie wykona później skrypt 1.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT nazwa FROM towary WHERE promocja = 1;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: cena cienkopisu

    Kryteria:

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

    ok. 2 min
    Polecenie z arkusza

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

    • SELECT cena - samo pole ceny,
    • FROM towary,
    • WHERE nazwa = 'Cienkopis' - napis w apostrofach. Porównywanie w tabeli latin1_swedish_ci nie rozróżnia wielkości liter, więc 'cienkopis' też zadziała.

    Wynik: 2.5 (R.1.4). Zrzut: kw2.jpg. W skrypcie 2 w miejsce 'Cienkopis' stanie towar wybrany w formularzu.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT nazwa FROM towary WHERE promocja = 1;
    
    -- Zapytanie 2
    SELECT cena FROM towary WHERE nazwa = 'Cienkopis';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: liczba towarów każdego dostawcy

    Kryteria:

    R.1.5 Zapisano zapytanie 3 wybierające jedynie pole nazwa z tabeli dostawcy oraz zliczające liczbę towarów dostarczanych przez każdego z dostawców. Pole zliczające nazwano ilosc. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT dostawcy.nazwa, COUNT(towary.nazwa) FROM dostawcy INNER JOIN towary ON dostawcy.id=towary.idDostawcy GROUP BY dostawcy.nazwa; oraz na zrzucie widoczne jest jedynie pole nazwa z tabeli dostawcy oraz odpowiadająca każdemu dostawcy liczba: Art. szkolne - 4, Papiernictwo - 4, Wszystko dla ucznia - 2 (widoczne są dokładnie trzy rekordy)

    ok. 4 min
    Polecenie z arkusza

    Zapytanie 3: wybierające jedynie pole nazwa dla wszystkich rekordów z tabeli dostawcy oraz zliczające ilość towarów dostarczanych przez każdego z dostawców. Pole zliczające należy nazwać ilosc

    Nazwa dostawcy jest w tabeli dostawcy, a towary w tabeli towary - zapytanie łączy obie:

    • SELECT dostawcy.nazwa, COUNT(towary.id) AS ilosc - nazwa dostawcy i liczba jego towarów; AS ilosc nadaje kolumnie zliczającej nazwę z arkusza. Nazwa pola jest poprzedzona nazwą tabeli, bo kolumna nazwa jest w obu tabelach,
    • FROM dostawcy JOIN towary ON towary.idDostawcy = dostawcy.id - relacja z obrazu 1: towar wskazuje dostawcę polem idDostawcy,
    • GROUP BY dostawcy.nazwa - wiersze jednego dostawcy tworzą grupę, a COUNT liczy towary w każdej grupie osobno.

    Wynik: trzy wiersze - Art. szkolne 4, Papiernictwo 4, Wszystko dla ucznia 2 (R.1.5). Zrzut: kw3.jpg.

    Typowe błędy

    Bez GROUP BY COUNT liczy wszystkie towary naraz i zwraca jeden wiersz z liczbą 10 - a „dla każdego z dostawców” wymaga podziału na grupy. Bez AS ilosc kolumna nazywa się COUNT(towary.id), a arkusz wymaga nazwy ilosc.

    kwerendy.txt Tekst zmienione: 5 linii
    -- Zapytanie 1
    SELECT nazwa FROM towary WHERE promocja = 1;
    
    -- Zapytanie 2
    SELECT cena FROM towary WHERE nazwa = 'Cienkopis';
    
    -- Zapytanie 3
    SELECT dostawcy.nazwa, COUNT(towary.id) AS ilosc FROM dostawcy
    JOIN towary ON towary.idDostawcy = dostawcy.id
    GROUP BY dostawcy.nazwa;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: nowe pole e_mail w tabeli dostawcy

    Kryteria:

    R.1.6 Zapisano zapytanie 4 modyfikujące strukturę tabeli dostawcy poprzez dodanie pola o nazwie e_mail i typie TEXT. W pliku z kwerendami lub na zrzucie istnieje zapis np.: ALTER TABLE dostawcy ADD e_mail TEXT; (możliwa jest również postać ADD COLUMN) oraz na zrzucie ekranu widoczny jest komunikat o poprawnie wykonanej kwerendzie lub zmieniona struktura tabeli

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 4: modyfikujące strukturę tabeli dostawcy poprzez dodanie pola o nazwie e_mail i typie TEXT

    Zmiana struktury tabeli to polecenie ALTER TABLE:

    • ALTER TABLE dostawcy - która tabela,
    • ADD e_mail TEXT - dodaj kolumnę e_mail typu TEXT (można też napisać ADD COLUMN).

    Zapytanie nie zwraca wierszy - phpMyAdmin pokazuje komunikat o wykonaniu, a w karcie Struktura tabeli dostawcy pojawia się czwarta kolumna, e_mail typu text. Zrzut z komunikatem albo ze zmienioną strukturą: kw4.jpg (R.1.6). Plik kwerendy.txt ma komplet czterech zapytań. Nowa kolumna jest pusta (NULL) i nie przeszkadza skryptom strony.

    Typowe błędy

    Drugie wykonanie tego samego zapytania kończy się błędem „Duplicate column name” - kolumna już istnieje. Zrzut rób przy pierwszym, udanym wykonaniu.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT nazwa FROM towary WHERE promocja = 1;
    
    -- Zapytanie 2
    SELECT cena FROM towary WHERE nazwa = 'Cienkopis';
    
    -- Zapytanie 3
    SELECT dostawcy.nazwa, COUNT(towary.id) AS ilosc FROM dostawcy
    JOIN towary ON towary.idDostawcy = dostawcy.id
    GROUP BY dostawcy.nazwa;
    
    -- Zapytanie 4
    ALTER TABLE dostawcy ADD e_mail TEXT;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Grafika

    Kroki 8-9 · po tym etapie masz 8 z 37 kryteriów (pkt)

    Krok 8: Przeskalowanie promocja.png do szerokości 150 px

    Kryteria:

    R.2.1 Obraz przeskalowano z zachowaniem proporcji do szerokości 150 px, odpowiadająca temu wysokość wynosi 135 px (± 5 px)

    ok. 2 min
    Polecenie z arkusza

    Plik promocja.png, wypakowany z archiwum, należy przeskalować z zachowaniem proporcji tak, aby jego szerokość wynosiła dokładnie 150 px

    promocja.png ma 378 × 340 px. W GIMP-ie: Obraz → Skaluj obraz, łańcuch przy szerokości i wysokości zamknięty (to jest „zachowanie proporcji”), szerokość 150 px - wysokość przeliczy się sama: 340 · 150 / 378 ≈ 135 px. Klucz wymaga właśnie 135 px (± 5 px) (R.2.1).

    Typowe błędy

    Rozpięty łańcuch i wpisana ręcznie wysokość (np. 150 × 150) zniekształcą etykietę - kryterium wymaga zachowania proporcji.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 8
  9. Krok 9: Zapis jako promocja2.png z przezroczystością

    Kryteria:

    R.2.2 Zachowano przezroczystość tła w obrazie zapisanym w pliku promocja2.png

    ok. 2 min
    Polecenie z arkusza

    Plik po przeskalowaniu należy zapisać jako promocja2.png, z zachowaniem pozostałych atrybutów, w tym przezroczystości

    Plik → Eksportuj jako, nazwa promocja2.png, Eksportuj - w oknie opcji PNG zostaw zaznaczone zapisywanie kanału alfa. Format PNG obsługuje przezroczystość, więc tło wokół etykiety zostaje przezroczyste i na stronie przez nie prześwituje kolor panelu (R.2.2). Plik promocja.png zostaje w folderze bez zmian - arkusz wymienia oba pliki na liście do oddania.

    Podgląd: promocja2.png położony na zielonym tle panelu - widać, że wokół etykiety nie ma białego prostokąta.

    Typowe błędy

    Eksport do JPEG albo spłaszczenie obrazu z białym tłem usuwa przezroczystość - na zielonym panelu pojawia się biały prostokąt, a R.2.2 przepada.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 9
  10. Etap 3: Strona index.php

    Kroki 10-26 · po tym etapie masz 19 z 37 kryteriów (pkt)

    Krok 10: Plik index.php

    ok. 1 min
    Polecenie z arkusza

    Cechy witryny: Składa się ze strony o nazwie index.php

    Witryna ma jedną stronę - index.php. Rozszerzenie .php, bo na stronie staną dwa skrypty; dopóki nie ma w niej kodu PHP, serwer odda ją przeglądarce jak zwykły HTML. <!DOCTYPE html> oznacza HTML5, lang="pl" mówi przeglądarce, że treść jest po polsku.

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

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

    Zastosowany właściwy standard kodowania polskich znaków

    <meta charset="UTF-8"> jako pierwsza rzecz w <head>; plik zapisz w edytorze w kodowaniu UTF-8. Strona index.php z właściwym kodowaniem daje R.2.3.

    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)
  12. Krok 12: Tytuł strony

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony widoczny na karcie przeglądarki „Artykuły papiernicze”

    <title> w <head> - napis na karcie przeglądarki (R.2.4).

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

    Kryteria:

    R.3.5 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl.css formatowanie pochodzi jedynie od pliku CSS Uwaga: kryterium to nie jest spełnione również, gdy źle przypisano 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"> w <head> (R.3.5). Od tej chwili żadnych atrybutów style w HTML-u - klucz wymaga, żeby formatowanie pochodziło wyłącznie z pliku CSS.

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

    Kryteria:

    R.2.5 Wygląd 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

    Baner to znacznik <header>, stopka <footer>, a trzy panele - znaczniki <section> z identyfikatorami left, middle i right (R.2.5). Identyfikatory przydadzą się w CSS: panele różnią się szerokością i wyrównaniem tekstu, więc każdy dostaje własną regułę. Ułożenie paneli obok siebie ustawi styl.

    index.php PHP zmienione: 10 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Artykuły papiernicze</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)
  15. Krok 15: Baner: nagłówek pierwszego stopnia

    ok. 1 min
    Polecenie z arkusza

    Zawartość banera: nagłówek pierwszego stopnia o treści „W naszym sklepie internetowym kupisz najtaniej”

    <h1> w banerze - jedyny nagłówek pierwszego stopnia na stronie.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Artykuły papiernicze</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszym sklepie internetowym kupisz najtaniej</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)
  16. Krok 16: Panel lewy: nagłówek „Kontakt”

    ok. 1 min
    Polecenie z arkusza

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

    <h2> - nagłówek drugiego stopnia. Na obrazie 2 lewy panel nie jest wyśrodkowany - nagłówek stoi przy lewej krawędzi.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Artykuły papiernicze</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszym sklepie internetowym kupisz najtaniej</h1>
        </header>
        <section id="left">
            <h2>Kontakt</h2>
        </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: akapit z telefonem i e-mailem

    ok. 1 min
    Polecenie z arkusza

    Umieszczony w akapicie (paragrafie) tekst „telefon: 444333222 e-mail: [email protected]” podzielony na dwa wiersze (zgodnie z obrazem 2)

    Jeden akapit <p>, a w środku <br> - złamanie wiersza. Telefon i e-mail stoją w dwóch wierszach, ale w jednym akapicie, bez odstępu między nimi (jak na obrazie 2).

    Typowe błędy

    Dwa osobne akapity też są dopuszczone przez klucz, ale wtedy przeglądarka dodaje między nimi odstęp - na obrazie 2 wiersze stoją jeden pod drugim bez przerwy.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Artykuły papiernicze</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszym sklepie internetowym kupisz najtaniej</h1>
        </header>
        <section id="left">
            <h2>Kontakt</h2>
            <p>telefon: 444333222<br>e-mail: [email protected]</p>
        </section>
        <section id="middle">
        </section>
        <section id="right">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  18. Krok 18: Odsyłacz pocztowy [email protected]

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

    Napis [email protected] jest odsyłaczem pocztowym łączącym z tym samym adresem

    Odsyłacz pocztowy to <a> z adresem mailto: przed adresem e-mail. Kliknięcie otwiera program pocztowy z nową wiadomością do [email protected] (R.3.4).

    Typowe błędy

    href="[email protected]" bez mailto: przeglądarka potraktuje jak plik w folderze strony i pokaże błąd 404.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Artykuły papiernicze</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszym sklepie internetowym kupisz najtaniej</h1>
        </header>
        <section id="left">
            <h2>Kontakt</h2>
            <p>telefon: 444333222<br>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
        </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 lewy: obraz promocja2.png

    Kryteria:

    R.2.7 W bloku lewym umieszczono akapit lub 2 akapity z tekstem „telefon: 444333222 e-mail: [email protected]” oraz tekst jest podzielony na dwa wiersze (zgodnie z obrazem 2 w arkuszu) oraz poniżej umieszczono obraz z tekstem alternatywnym "promocja"

    ok. 1 min
    Polecenie z arkusza

    Obraz promocja2.png z tekstem alternatywnym „promocja”

    src - przeskalowany plik, alt - tekst, który przeczyta czytnik ekranu albo pokaże przeglądarka, gdy obraz się nie wczyta. Akapit z dwoma wierszami i obraz pod nim dają R.2.7.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Artykuły papiernicze</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszym sklepie internetowym kupisz najtaniej</h1>
        </header>
        <section id="left">
            <h2>Kontakt</h2>
            <p>telefon: 444333222<br>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
            <img src="promocja2.png" alt="promocja">
        </section>
        <section id="middle">
        </section>
        <section id="right">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  20. Krok 20: Panel środkowy: nagłówek

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku środkowego: Nagłówek drugiego stopnia o treści „Promocja 10% obejmuje artykuły:”

    Drugi <h2> na stronie, z dwukropkiem na końcu - pod nim stanie lista.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Artykuły papiernicze</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszym sklepie internetowym kupisz najtaniej</h1>
        </header>
        <section id="left">
            <h2>Kontakt</h2>
            <p>telefon: 444333222<br>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
            <img src="promocja2.png" alt="promocja">
        </section>
        <section id="middle">
            <h2>Promocja 10% obejmuje artykuły:</h2>
        </section>
        <section id="right">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  21. Krok 21: Panel środkowy: lista punktowana

    ok. 1 min
    Polecenie z arkusza

    Lista punktowana (nieuporządkowana) zgodna z obrazem 3, elementy listy generowane są skryptem 1

    Lista punktowana to <ul> (unordered list). Na razie jest pusta - elementy <li> wypisze skrypt 1 z wyniku zapytania 1, więc każdy towar w promocji dodany do bazy pojawi się na liście bez zmiany strony.

    index.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Artykuły papiernicze</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszym sklepie internetowym kupisz najtaniej</h1>
        </header>
        <section id="left">
            <h2>Kontakt</h2>
            <p>telefon: 444333222<br>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
            <img src="promocja2.png" alt="promocja">
        </section>
        <section id="middle">
            <h2>Promocja 10% obejmuje artykuły:</h2>
            <ul>
            </ul>
        </section>
        <section id="right">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  22. Krok 22: Panel prawy: nagłówek

    ok. 1 min
    Polecenie z arkusza

    Zawartość bloku prawego: Nagłówek drugiego stopnia o treści „Cena wybranego artykułu w promocji”

    Trzeci <h2>. Długi nagłówek w wąskim panelu (25%) łamie się na dwa wiersze, jak na obrazie 2.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Artykuły papiernicze</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszym sklepie internetowym kupisz najtaniej</h1>
        </header>
        <section id="left">
            <h2>Kontakt</h2>
            <p>telefon: 444333222<br>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
            <img src="promocja2.png" alt="promocja">
        </section>
        <section id="middle">
            <h2>Promocja 10% obejmuje artykuły:</h2>
            <ul>
            </ul>
        </section>
        <section id="right">
            <h2>Cena wybranego artykułu w promocji</h2>
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  23. Krok 23: Formularz wysyłany metodą post

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

    Formularz wysyła dane do serwera za pomocą przycisku WYBIERZ, metodą post

    method="post" - dane jadą w treści żądania, nie w adresie strony (R.3.2). Bez atrybutu action formularz wysyła dane do tej samej strony, czyli do index.php - tam będzie czekał skrypt 2.

    index.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Artykuły papiernicze</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszym sklepie internetowym kupisz najtaniej</h1>
        </header>
        <section id="left">
            <h2>Kontakt</h2>
            <p>telefon: 444333222<br>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
            <img src="promocja2.png" alt="promocja">
        </section>
        <section id="middle">
            <h2>Promocja 10% obejmuje artykuły:</h2>
            <ul>
            </ul>
        </section>
        <section id="right">
            <h2>Cena wybranego artykułu w promocji</h2>
            <form method="post">
            </form>
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  24. Krok 24: Lista rozwijana z towarami w promocji

    Kryteria:

    R.2.9 Na stronie umieszczono listę rozwijalną, zawierającą nazwy artykułów objętych promocją pochodzących z bazy danych (Gumka do mazania, Cienkopis, Pisaki 60 szt. Markery 4 szt.). W kodzie HTML użyto znaczników <select>, <option> oraz ich zamknięć

    ok. 2 min
    Polecenie z arkusza

    Formularz składający się z: Listy rozwijalnej zawierającej nazwy wszystkich towarów w promocji, zgodnej z obrazem 4

    Lista rozwijana to <select>, a każda pozycja - <option>. Cztery nazwy to dokładnie wynik zapytania 1, w tej samej kolejności co na obrazie 4 (R.2.9). Atrybut name="product" to nazwa, pod którą wybór trafi do skryptu: $_POST['product']. Opcja bez atrybutu value wysyła swój tekst - skrypt dostanie nazwę towaru, np. Cienkopis.

    Arkusz nie każe generować tej listy skryptem (skrypt 1 wypełnia tylko listę punktowaną), więc nazwy są wpisane w HTML. Wersja nowoczesna pokazuje, jak wypełnić obie listy z jednego zapytania.

    Typowe błędy

    Literówka w nazwie opcji (np. „Pisaki 60 szt” bez kropki) sprawi, że zapytanie 2 nie znajdzie towaru - nazwa z listy musi być identyczna z nazwą w bazie.

    index.php PHP zmienione: 6 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Artykuły papiernicze</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszym sklepie internetowym kupisz najtaniej</h1>
        </header>
        <section id="left">
            <h2>Kontakt</h2>
            <p>telefon: 444333222<br>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
            <img src="promocja2.png" alt="promocja">
        </section>
        <section id="middle">
            <h2>Promocja 10% obejmuje artykuły:</h2>
            <ul>
            </ul>
        </section>
        <section id="right">
            <h2>Cena wybranego artykułu w promocji</h2>
            <form method="post">
                <select name="product">
                    <option>Gumka do mazania</option>
                    <option>Cienkopis</option>
                    <option>Pisaki 60 szt.</option>
                    <option>Markery 4 szt.</option>
                </select>
            </form>
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  25. Krok 25: Przycisk WYBIERZ

    Kryteria: ,

    R.2.10 Na stronie umieszczono przycisk WYBIERZ

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

    ok. 1 min
    Polecenie z arkusza

    Przycisku WYBIERZ – realizującego skrypt 2

    type="submit" wysyła formularz (R.3.3), value to napis na przycisku - wielkimi literami, jak w arkuszu (R.2.10).

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Artykuły papiernicze</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszym sklepie internetowym kupisz najtaniej</h1>
        </header>
        <section id="left">
            <h2>Kontakt</h2>
            <p>telefon: 444333222<br>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
            <img src="promocja2.png" alt="promocja">
        </section>
        <section id="middle">
            <h2>Promocja 10% obejmuje artykuły:</h2>
            <ul>
            </ul>
        </section>
        <section id="right">
            <h2>Cena wybranego artykułu w promocji</h2>
            <form method="post">
                <select name="product">
                    <option>Gumka do mazania</option>
                    <option>Cienkopis</option>
                    <option>Pisaki 60 szt.</option>
                    <option>Markery 4 szt.</option>
                </select>
                <input type="submit" value="WYBIERZ">
            </form>
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  26. Krok 26: Stopka: autor strony

    Kryteria:

    R.2.6 Zapisano wszystkie nagłówki: h1 "W naszym sklepie internetowym kupisz najtaniej” - w banerze, h2 "Kontakt" - w bloku lewym, h2 "Promocja 10% obejmuje artykuły:” - w bloku środkowym, h2 "Cena wybranego artykułu w promocji” - w bloku prawym, h3 "Autor strony" i dalej wstawiony numer PESEL zdającego - w stopce

    ok. 1 min
    Polecenie z arkusza

    Zawartość stopki: nagłówek trzeciego stopnia o treści „Autor strony”, dalej wstawiony Twój numer PESEL

    <h3> w stopce. Na egzaminie po napisie wpisujesz swój numer PESEL; w tym rozwiązaniu w jego miejscu stoi imię i nazwisko autora: Bartosz Bryniarski. Komplet nagłówków: h1 w banerze, trzy h2 w panelach i h3 w stopce (R.2.6). Wygląd nada stronie styl.

    index.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Artykuły papiernicze</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszym sklepie internetowym kupisz najtaniej</h1>
        </header>
        <section id="left">
            <h2>Kontakt</h2>
            <p>telefon: 444333222<br>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
            <img src="promocja2.png" alt="promocja">
        </section>
        <section id="middle">
            <h2>Promocja 10% obejmuje artykuły:</h2>
            <ul>
            </ul>
        </section>
        <section id="right">
            <h2>Cena wybranego artykułu w promocji</h2>
            <form method="post">
                <select name="product">
                    <option>Gumka do mazania</option>
                    <option>Cienkopis</option>
                    <option>Pisaki 60 szt.</option>
                    <option>Markery 4 szt.</option>
                </select>
                <input type="submit" value="WYBIERZ">
            </form>
        </section>
        <footer>
            <h3>Autor strony Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  27. Etap 4: Styl CSS

    Kroki 27-35 · po tym etapie masz 28 z 37 kryteriów (pkt)

    Krok 27: Plik styl.css: baner

    ok. 2 min
    Polecenie z arkusza

    Format banera: kolor tła #B284BE, wysokość 60 px, wielkość czcionki 120%, wyrównanie tekstu do środka

    Nowy plik styl.css w folderze zdającego. Selektor znacznika header:

    • background-color: #B284BE - kolor zapisany szesnastkowo, jak w arkuszu,
    • height: 60px,
    • font-size: 120% - 1,2 rozmiaru czcionki dziedziczonego (nagłówek h1 liczy swój rozmiar od tej wartości),
    • text-align: center.
    styl.css CSS nowy plik
    header {
        background-color: #B284BE;
        height: 60px;
        font-size: 120%;
        text-align: center;
    }
    Podgląd strony po tym kroku (index.php)
  28. Krok 28: Panel lewy

    ok. 1 min
    Polecenie z arkusza

    Format panelu lewego: kolor tła #BCE27F, wysokość 600 px, szerokość 25%, wielkość czcionki 120%

    Selektor identyfikatora #left - z krzyżykiem, bo w HTML-u jest id="left". Szerokość w procentach liczy się od szerokości okna. Panel lewy nie ma wyrównania - tekst zostaje przy lewej krawędzi.

    Typowe błędy

    Kropka zamiast krzyżyka (.left) to selektor klasy - reguła nie trafi w panel, a klucz przy R.3.5 uznaje wtedy połączenie ze stylem za niespełnione.

    styl.css CSS zmienione: 6 linii
    header {
        background-color: #B284BE;
        height: 60px;
        font-size: 120%;
        text-align: center;
    }
    #left {
        background-color: #BCE27F;
        height: 600px;
        width: 25%;
        font-size: 120%;
    }
    Podgląd strony po tym kroku (index.php)
  29. Krok 29: Panel środkowy

    ok. 1 min
    Polecenie z arkusza

    Format panelu środkowego: kolor tła #BCE27F, wysokość 600 px, szerokość 50%, wyrównanie tekstu do środka

    Panel środkowy jest najszerszy (50%) i nie ma powiększonej czcionki.

    styl.css CSS zmienione: 6 linii
    header {
        background-color: #B284BE;
        height: 60px;
        font-size: 120%;
        text-align: center;
    }
    #left {
        background-color: #BCE27F;
        height: 600px;
        width: 25%;
        font-size: 120%;
    }
    #middle {
        background-color: #BCE27F;
        height: 600px;
        width: 50%;
        text-align: center;
    }
    Podgląd strony po tym kroku (index.php)
  30. Krok 30: Panel prawy

    Kryteria: , ,

    R.4.3 Ustawiono wyrównanie tekstu do środka dla banera, panelu środkowego i prawego

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

    R.4.5 Ustawiono wielkość czcionki dla banera, paneli: lewego i prawego na 120%

    ok. 1 min
    Polecenie z arkusza

    Format panelu prawego: kolor tła #BCE27F, wysokość 600 px, szerokość 25%, wielkość czcionki 120%, wyrównanie tekstu do środka

    Szerokości 25% + 50% + 25% = 100% (R.4.4), wyśrodkowane są baner, panel środkowy i prawy (R.4.3), a czcionka 120% jest w banerze i panelach bocznych (R.4.5).

    styl.css CSS zmienione: 7 linii
    header {
        background-color: #B284BE;
        height: 60px;
        font-size: 120%;
        text-align: center;
    }
    #left {
        background-color: #BCE27F;
        height: 600px;
        width: 25%;
        font-size: 120%;
    }
    #middle {
        background-color: #BCE27F;
        height: 600px;
        width: 50%;
        text-align: center;
    }
    #right {
        background-color: #BCE27F;
        height: 600px;
        width: 25%;
        font-size: 120%;
        text-align: center;
    }
    Podgląd strony po tym kroku (index.php)
  31. Krok 31: Stopka

    Kryteria: ,

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

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

    ok. 1 min
    Polecenie z arkusza

    Format stopki: kolor tła #B284BE, wysokość 60 px, wyrównanie tekstu do lewej strony, marginesy wewnętrzne 5 px

    Margines wewnętrzny to padding - padding: 5px ustawia go z czterech stron naraz. text-align: left to wartość domyślna, ale arkusz ją wymienia, więc zostaje wpisana. Kolory tła wszystkich bloków (R.4.1) i wysokości 60/600/60 px (R.4.2) są ustawione.

    styl.css CSS zmienione: 6 linii
    header {
        background-color: #B284BE;
        height: 60px;
        font-size: 120%;
        text-align: center;
    }
    #left {
        background-color: #BCE27F;
        height: 600px;
        width: 25%;
        font-size: 120%;
    }
    #middle {
        background-color: #BCE27F;
        height: 600px;
        width: 50%;
        text-align: center;
    }
    #right {
        background-color: #BCE27F;
        height: 600px;
        width: 25%;
        font-size: 120%;
        text-align: center;
    }
    footer {
        background-color: #B284BE;
        height: 60px;
        text-align: left;
        padding: 5px;
    }
    Podgląd strony po tym kroku (index.php)
  32. Krok 32: Elementy listy punktowanej

    Kryteria: ,

    R.4.6 Ustawiono wszystkie marginesy wewnętrzne (padding) dla stopki na 5px oraz margines wewnętrzny górny (padding-top) dla elementów listy na 20px

    R.4.8 Ustawiono kolor tła elementów listy na #B284BE

    ok. 1 min
    Polecenie z arkusza

    Format elementów listy punktowanej: margines wewnętrzny górny 20 px, kolor tła #B284BE

    Selektor znacznika li - każdy element listy. padding-top: 20px odsuwa tekst od górnej krawędzi elementu, a tło sąsiednich elementów styka się, więc lista wygląda jak jeden fioletowy prostokąt (obraz 3). Elementów jeszcze nie ma - pojawią się, gdy skrypt 1 je wypisze (R.4.6, R.4.8).

    styl.css CSS zmienione: 4 linie
    header {
        background-color: #B284BE;
        height: 60px;
        font-size: 120%;
        text-align: center;
    }
    #left {
        background-color: #BCE27F;
        height: 600px;
        width: 25%;
        font-size: 120%;
    }
    #middle {
        background-color: #BCE27F;
        height: 600px;
        width: 50%;
        text-align: center;
    }
    #right {
        background-color: #BCE27F;
        height: 600px;
        width: 25%;
        font-size: 120%;
        text-align: center;
    }
    footer {
        background-color: #B284BE;
        height: 60px;
        text-align: left;
        padding: 5px;
    }
    li {
        padding-top: 20px;
        background-color: #B284BE;
    }
    Podgląd strony po tym kroku (index.php)
  33. Krok 33: Obraz i nagłówki h2: margines górny 50 px

    ok. 1 min
    Polecenie z arkusza

    Formatowanie obrazu i nagłówka drugiego stopnia: margines zewnętrzny górny 50 px

    Dwa selektory po przecinku dzielą jedną regułę. Margines zewnętrzny to margin - margin-top: 50px odsuwa obraz od akapitu nad nim, a każdy z trzech nagłówków h2 od górnej krawędzi panelu.

    styl.css CSS zmienione: 3 linie
    header {
        background-color: #B284BE;
        height: 60px;
        font-size: 120%;
        text-align: center;
    }
    #left {
        background-color: #BCE27F;
        height: 600px;
        width: 25%;
        font-size: 120%;
    }
    #middle {
        background-color: #BCE27F;
        height: 600px;
        width: 50%;
        text-align: center;
    }
    #right {
        background-color: #BCE27F;
        height: 600px;
        width: 25%;
        font-size: 120%;
        text-align: center;
    }
    footer {
        background-color: #B284BE;
        height: 60px;
        text-align: left;
        padding: 5px;
    }
    li {
        padding-top: 20px;
        background-color: #B284BE;
    }
    img, h2 {
        margin-top: 50px;
    }
    Podgląd strony po tym kroku (index.php)
  34. Krok 34: Formularz: margines dolny 100 px

    Kryteria:

    R.4.7 Ustawiono margines zewnętrzny górny (margin-top) dla obrazu i nagłówka h2 na 50px oraz margines zewnętrzny dolny (margin-bottom) dla formularza na 100px

    ok. 1 min
    Polecenie z arkusza

    Formatowanie formularza: margines zewnętrzny dolny 100 px

    margin-bottom: 100px - wynik skryptu 2 stanie 100 px pod formularzem. Komplet marginesów: obraz, h2 i formularz (R.4.7).

    styl.css CSS zmienione: 3 linie
    header {
        background-color: #B284BE;
        height: 60px;
        font-size: 120%;
        text-align: center;
    }
    #left {
        background-color: #BCE27F;
        height: 600px;
        width: 25%;
        font-size: 120%;
    }
    #middle {
        background-color: #BCE27F;
        height: 600px;
        width: 50%;
        text-align: center;
    }
    #right {
        background-color: #BCE27F;
        height: 600px;
        width: 25%;
        font-size: 120%;
        text-align: center;
    }
    footer {
        background-color: #B284BE;
        height: 60px;
        text-align: left;
        padding: 5px;
    }
    li {
        padding-top: 20px;
        background-color: #B284BE;
    }
    img, h2 {
        margin-top: 50px;
    }
    form {
        margin-bottom: 100px;
    }
    Podgląd strony po tym kroku (index.php)
  35. Krok 35: 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

    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

    Wszystkie trzy sekcje dostają float: left - stają kolejno od lewej krawędzi, a że mają razem dokładnie 100% szerokości, mieszczą się w jednym rzędzie. Stopka z clear: both zaczyna się dopiero pod panelami. Układ odpowiada obrazowi 2 (R.3.1).

    Typowe błędy

    Panele o sumie szerokości 100% nie mogą mieć obramowań ani padding po bokach - każdy piksel więcej spycha panel prawy pod środkowy.

    styl.css CSS zmienione: 6 linii
    header {
        background-color: #B284BE;
        height: 60px;
        font-size: 120%;
        text-align: center;
    }
    #left {
        background-color: #BCE27F;
        height: 600px;
        width: 25%;
        font-size: 120%;
    }
    #middle {
        background-color: #BCE27F;
        height: 600px;
        width: 50%;
        text-align: center;
    }
    #right {
        background-color: #BCE27F;
        height: 600px;
        width: 25%;
        font-size: 120%;
        text-align: center;
    }
    footer {
        background-color: #B284BE;
        height: 60px;
        text-align: left;
        padding: 5px;
    }
    li {
        padding-top: 20px;
        background-color: #B284BE;
    }
    img, h2 {
        margin-top: 50px;
    }
    form {
        margin-bottom: 100px;
    }
    section {
        float: left;
    }
    footer {
        clear: both;
    }
    Podgląd strony po tym kroku (index.php)
  36. Etap 5: Skrypty PHP

    Kroki 36-42 · po tym etapie masz 37 z 37 kryteriów (pkt)

    Krok 36: Połączenie z bazą sklep na początku pliku

    Kryteria: ,

    R.5.1 Skrypt napisano w języku PHP oraz skrypt 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. 3 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

    Z bazy korzystają dwa skrypty w dwóch miejscach strony, a połączenie wystarczy jedno - dlatego blok PHP z połączeniem stoi na samym początku pliku, jeszcze przed <!DOCTYPE html> (R.5.1). Kod między <?php a ?> wykona serwer, zanim strona trafi do przeglądarki; sam niczego nie wypisuje, więc HTML zaczyna się jak dotąd.

    mysqli_connect z tabeli 1 arkusza - kolejno serwer localhost, użytkownik root, puste hasło '' i nazwa bazy sklep. Połączenie trafia do zmiennej $connection, z której skorzystają oba skrypty i zamknięcie (R.5.2). Arkusz nie wymaga sprawdzania, czy operacje się powiodły.

    Od tego kroku podgląd to wynik działania strony po stronie serwera (prawdziwe uruchomienie w PHP 8.2). Stronę sprawdzasz przez http://localhost/…/index.php, a nie otwierając plik z dysku: przeglądarka sama nie wykona PHP.

    index.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'sklep');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Artykuły papiernicze</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszym sklepie internetowym kupisz najtaniej</h1>
        </header>
        <section id="left">
            <h2>Kontakt</h2>
            <p>telefon: 444333222<br>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
            <img src="promocja2.png" alt="promocja">
        </section>
        <section id="middle">
            <h2>Promocja 10% obejmuje artykuły:</h2>
            <ul>
            </ul>
        </section>
        <section id="right">
            <h2>Cena wybranego artykułu w promocji</h2>
            <form method="post">
                <select name="product">
                    <option>Gumka do mazania</option>
                    <option>Cienkopis</option>
                    <option>Pisaki 60 szt.</option>
                    <option>Markery 4 szt.</option>
                </select>
                <input type="submit" value="WYBIERZ">
            </form>
        </section>
        <footer>
            <h3>Autor strony Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  37. Krok 37: Skrypt 1: zapytanie 1

    Kryteria:

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

    ok. 2 min
    Polecenie z arkusza

    Skrypt 1: Wysyła do bazy danych zapytanie 1 (z pliku kwerendy.txt)

    Skrypt 1 stoi wewnątrz <ul>, bo to, co wypisze, ma się znaleźć w liście. mysqli_query wysyła zapytanie 1 z kwerendy.txt przez połączenie $connection i zwraca wynik do $result (R.5.4). Zapytanie jest w apostrofach - nie ma w nim zmiennych, więc PHP niczego nie podstawia.

    Podgląd: zapytanie wykonane, ale lista jest jeszcze pusta.

    index.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'sklep');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Artykuły papiernicze</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszym sklepie internetowym kupisz najtaniej</h1>
        </header>
        <section id="left">
            <h2>Kontakt</h2>
            <p>telefon: 444333222<br>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
            <img src="promocja2.png" alt="promocja">
        </section>
        <section id="middle">
            <h2>Promocja 10% obejmuje artykuły:</h2>
            <ul>
    <?php
    $result = mysqli_query($connection, 'SELECT nazwa FROM towary WHERE promocja = 1;');
    ?>
            </ul>
        </section>
        <section id="right">
            <h2>Cena wybranego artykułu w promocji</h2>
            <form method="post">
                <select name="product">
                    <option>Gumka do mazania</option>
                    <option>Cienkopis</option>
                    <option>Pisaki 60 szt.</option>
                    <option>Markery 4 szt.</option>
                </select>
                <input type="submit" value="WYBIERZ">
            </form>
        </section>
        <footer>
            <h3>Autor strony Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>

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

  38. Krok 38: Skrypt 1: nazwy jako elementy listy

    Kryteria: , ,

    R.2.8 Na stronie umieszczono listę punktowaną (nieuporządkowaną), zawierającą nazwy artykułów z bazy danych. W kodzie HTML lub PHP użyto znaczników <ul>, <li> oraz ich zamknięć

    R.5.5 Skrypt 1 wyświetla 4 nazwy produktów zwrócone zapytaniem (Gumka do mazania, Cienkopis, Pisaki 60 szt., Markery 4 szt.)

    R.5.6 Wyświetlane przez skrypt 1 nazwy towarów są umieszczone w elementach listy (zgodnie z obrazem 3 w arkuszu)

    ok. 3 min
    Polecenie z arkusza

    Nazwy produktów pobrane zapytaniem wypisywane są jako elementy listy punktowanej (nieuporządkowanej) zgodnie z obrazem 3

    • mysqli_fetch_row($result) zwraca kolejny wiersz wyniku jako tablicę numerowaną - $row[0] to nazwa, jedyne pole z SELECT. Gdy wierszy zabraknie, zwraca null i pętla while się kończy,
    • echo otacza każdą nazwę znacznikami <li> i </li> - powstaje element listy.

    Podgląd: cztery towary w fioletowych elementach listy - Gumka do mazania, Cienkopis, Pisaki 60 szt., Markery 4 szt. (R.5.5), ułożone jak na obrazie 3 (R.5.6). Lista generowana z bazy daje też R.2.8.

    index.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'sklep');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Artykuły papiernicze</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszym sklepie internetowym kupisz najtaniej</h1>
        </header>
        <section id="left">
            <h2>Kontakt</h2>
            <p>telefon: 444333222<br>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
            <img src="promocja2.png" alt="promocja">
        </section>
        <section id="middle">
            <h2>Promocja 10% obejmuje artykuły:</h2>
            <ul>
    <?php
    $result = mysqli_query($connection, 'SELECT nazwa FROM towary WHERE promocja = 1;');
    while ($row = mysqli_fetch_row($result)) {
        echo '<li>' . $row[0] . '</li>';
    }
    ?>
            </ul>
        </section>
        <section id="right">
            <h2>Cena wybranego artykułu w promocji</h2>
            <form method="post">
                <select name="product">
                    <option>Gumka do mazania</option>
                    <option>Cienkopis</option>
                    <option>Pisaki 60 szt.</option>
                    <option>Markery 4 szt.</option>
                </select>
                <input type="submit" value="WYBIERZ">
            </form>
        </section>
        <footer>
            <h3>Autor strony Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  39. Krok 39: Skrypt 2: nazwa wybranego towaru

    ok. 2 min
    Polecenie z arkusza

    Skrypt 2: Pobiera z formularza nazwę produktu wybranego w liście rozwijalnej

    Skrypt 2 stoi pod formularzem, bo jego wynik ma się pokazać poniżej formularza.

    • isset($_POST['product']) jest prawdziwe dopiero po kliknięciu WYBIERZ. Przy pierwszym wejściu na stronę formularz nie był wysłany i skrypt niczego nie liczy,
    • $product = $_POST['product']; - nazwa wybrana z listy, np. Cienkopis.

    Podgląd: wysłany formularz z wybranym cienkopisem - wartość jest w zmiennej, ale skrypt jeszcze nic nie wypisuje.

    Typowe błędy

    Odczyt $_POST['product'] bez isset przy pierwszym wejściu na stronę daje ostrzeżenie „Undefined array key” wypisane na stronie.

    index.php PHP zmienione: 5 linii
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'sklep');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Artykuły papiernicze</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszym sklepie internetowym kupisz najtaniej</h1>
        </header>
        <section id="left">
            <h2>Kontakt</h2>
            <p>telefon: 444333222<br>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
            <img src="promocja2.png" alt="promocja">
        </section>
        <section id="middle">
            <h2>Promocja 10% obejmuje artykuły:</h2>
            <ul>
    <?php
    $result = mysqli_query($connection, 'SELECT nazwa FROM towary WHERE promocja = 1;');
    while ($row = mysqli_fetch_row($result)) {
        echo '<li>' . $row[0] . '</li>';
    }
    ?>
            </ul>
        </section>
        <section id="right">
            <h2>Cena wybranego artykułu w promocji</h2>
            <form method="post">
                <select name="product">
                    <option>Gumka do mazania</option>
                    <option>Cienkopis</option>
                    <option>Pisaki 60 szt.</option>
                    <option>Markery 4 szt.</option>
                </select>
                <input type="submit" value="WYBIERZ">
            </form>
    <?php
    if (isset($_POST['product'])) {
        $product = $_POST['product'];
    }
    ?>
        </section>
        <footer>
            <h3>Autor strony Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>

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

  40. Krok 40: Skrypt 2: zapytanie 2 z towarem z formularza

    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 w formularzu

    ok. 2 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

    Zapytanie 2 z kwerendy.txt, w którym w miejsce 'Cienkopis' stoi '$product'. Całe zapytanie jest w cudzysłowie "…", więc PHP podstawia wartość zmiennej; apostrofy wewnątrz są ogranicznikami napisu w SQL (R.5.7).

    Typowe błędy

    Zapytanie w apostrofach ('SELECT ... '$product'') - PHP nie podstawi zmiennej, a apostrofy w środku zakończą napis i powstanie błąd składni.

    index.php PHP zmienione: 1 linia
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'sklep');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Artykuły papiernicze</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszym sklepie internetowym kupisz najtaniej</h1>
        </header>
        <section id="left">
            <h2>Kontakt</h2>
            <p>telefon: 444333222<br>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
            <img src="promocja2.png" alt="promocja">
        </section>
        <section id="middle">
            <h2>Promocja 10% obejmuje artykuły:</h2>
            <ul>
    <?php
    $result = mysqli_query($connection, 'SELECT nazwa FROM towary WHERE promocja = 1;');
    while ($row = mysqli_fetch_row($result)) {
        echo '<li>' . $row[0] . '</li>';
    }
    ?>
            </ul>
        </section>
        <section id="right">
            <h2>Cena wybranego artykułu w promocji</h2>
            <form method="post">
                <select name="product">
                    <option>Gumka do mazania</option>
                    <option>Cienkopis</option>
                    <option>Pisaki 60 szt.</option>
                    <option>Markery 4 szt.</option>
                </select>
                <input type="submit" value="WYBIERZ">
            </form>
    <?php
    if (isset($_POST['product'])) {
        $product = $_POST['product'];
        $result = mysqli_query($connection, "SELECT cena FROM towary WHERE nazwa = '$product';");
    }
    ?>
        </section>
        <footer>
            <h3>Autor strony Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>

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

  41. Krok 41: Skrypt 2: cena po rabacie 10%

    Kryteria:

    R.5.8 Skrypt 2 oblicza i wyświetla zaokrągloną do dwóch miejsc po przecinku cenę w promocji (jako 90% ceny pobranej zapytaniem)

    ok. 3 min
    Polecenie z arkusza

    Oblicza cenę w promocji jako 90% ceny pobranej zapytaniem. Zaokrągla obliczoną cenę do dwóch miejsc po przecinku. Obliczona cena w promocji wyświetlana jest poniżej formularza

    • mysqli_fetch_row($result) - jedyny wiersz wyniku, $row[0] to cena,
    • $row[0] * 0.9 - 90% ceny, czyli cena po rabacie 10%,
    • round(…, 2) - zaokrąglenie do dwóch miejsc po przecinku: obliczenia na liczbach zmiennoprzecinkowych dają czasem wynik w rodzaju 2.8800000000000003, a round zostawia 2.88,
    • echo wypisuje cenę pod formularzem (odstęp 100 px to margines formularza).

    Podgląd: dla cienkopisu (2.5 zł) Cena w promocji: 2.25 zł (R.5.8). Gumka do mazania daje 2.88, pisaki 49.5, markery 20.16.

    index.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'sklep');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Artykuły papiernicze</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszym sklepie internetowym kupisz najtaniej</h1>
        </header>
        <section id="left">
            <h2>Kontakt</h2>
            <p>telefon: 444333222<br>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
            <img src="promocja2.png" alt="promocja">
        </section>
        <section id="middle">
            <h2>Promocja 10% obejmuje artykuły:</h2>
            <ul>
    <?php
    $result = mysqli_query($connection, 'SELECT nazwa FROM towary WHERE promocja = 1;');
    while ($row = mysqli_fetch_row($result)) {
        echo '<li>' . $row[0] . '</li>';
    }
    ?>
            </ul>
        </section>
        <section id="right">
            <h2>Cena wybranego artykułu w promocji</h2>
            <form method="post">
                <select name="product">
                    <option>Gumka do mazania</option>
                    <option>Cienkopis</option>
                    <option>Pisaki 60 szt.</option>
                    <option>Markery 4 szt.</option>
                </select>
                <input type="submit" value="WYBIERZ">
            </form>
    <?php
    if (isset($_POST['product'])) {
        $product = $_POST['product'];
        $result = mysqli_query($connection, "SELECT cena FROM towary WHERE nazwa = '$product';");
        $row = mysqli_fetch_row($result);
        $price = round($row[0] * 0.9, 2);
        echo 'Cena w promocji: ' . $price . ' zł';
    }
    ?>
        </section>
        <footer>
            <h3>Autor strony Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  42. Krok 42: Zamknięcie połączenia z bazą

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

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

    mysqli_close($connection) na końcu skryptu 2, poza warunkiem - to ostatni kod PHP na stronie, więc połączenie zamyka się po obu skryptach, także przy pierwszym wejściu na stronę (R.5.3). Strona spełnia wszystkie kryteria arkusza. Dalej dwa kroki o zabezpieczeniach - klucz ich nie wymaga.

    Typowe błędy

    mysqli_close w bloku skryptu 1 zamknie połączenie, zanim skrypt 2 wyśle zapytanie - w PHP 8 kończy się to błędem.

    index.php PHP zmienione: 1 linia
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'sklep');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Artykuły papiernicze</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszym sklepie internetowym kupisz najtaniej</h1>
        </header>
        <section id="left">
            <h2>Kontakt</h2>
            <p>telefon: 444333222<br>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
            <img src="promocja2.png" alt="promocja">
        </section>
        <section id="middle">
            <h2>Promocja 10% obejmuje artykuły:</h2>
            <ul>
    <?php
    $result = mysqli_query($connection, 'SELECT nazwa FROM towary WHERE promocja = 1;');
    while ($row = mysqli_fetch_row($result)) {
        echo '<li>' . $row[0] . '</li>';
    }
    ?>
            </ul>
        </section>
        <section id="right">
            <h2>Cena wybranego artykułu w promocji</h2>
            <form method="post">
                <select name="product">
                    <option>Gumka do mazania</option>
                    <option>Cienkopis</option>
                    <option>Pisaki 60 szt.</option>
                    <option>Markery 4 szt.</option>
                </select>
                <input type="submit" value="WYBIERZ">
            </form>
    <?php
    if (isset($_POST['product'])) {
        $product = $_POST['product'];
        $result = mysqli_query($connection, "SELECT cena FROM towary WHERE nazwa = '$product';");
        $row = mysqli_fetch_row($result);
        $price = round($row[0] * 0.9, 2);
        echo 'Cena w promocji: ' . $price . ' zł';
    }
    mysqli_close($connection);
    ?>
        </section>
        <footer>
            <h3>Autor strony Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>

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

  43. Etap 6: Dobre praktyki (poza kluczem)

    Kroki 43-44 · po tym etapie masz 37 z 37 kryteriów (pkt)

    Krok 43: Zabezpieczenie: zapytanie przygotowane

    ok. 4 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

    Lista rozwijana podsuwa cztery nazwy, ale serwer dostaje dowolny tekst - formularz da się wysłać z innej strony albo zmienić narzędziami przeglądarki. Wybrany towar trafia do zapytania wprost, czyli to SQL injection. Sprawdzone na wersji z poprzednich kroków:

    • wartość x' OR '1'='1 zamienia warunek w nazwa = 'x' OR '1'='1', prawdziwy dla każdego wiersza - skrypt pokazuje „Cena w promocji: 4.05 zł”, czyli cenę pierwszego zeszytu, którego w promocji nie ma,
    • nazwa z apostrofem rozbija składnię zapytania - PHP 8.1 i nowsze zgłaszają wyjątek mysqli_sql_exception i zamiast strony widać „Fatal error”,
    • nazwa spoza bazy daje pusty wynik, $row jest null, a $row[0] - ostrzeżenie wypisane na stronie.

    Lekarstwo to zapytanie przygotowane: mysqli_prepare ze znakiem ? w miejscu nazwy, mysqli_stmt_bind_param z typem 's' (napis), mysqli_stmt_execute i mysqli_stmt_get_result, który zwraca zwykły wynik. Baza dostaje treść zapytania i dane osobno, więc dane nigdy nie staną się kodem SQL. Warunek if ($row) liczy cenę tylko wtedy, gdy towar się znalazł. Podgląd: ta sama wartość x' OR '1'='1 nie pokazuje już żadnej ceny. Stara linia zostaje w komentarzu.

    Typowe błędy

    Liczba liter w 's' musi równać się liczbie znaków ? i liczbie zmiennych - inaczej mysqli_stmt_bind_param zgłosi błąd.

    index.php PHP zmienione: 9 linii
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'sklep');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Artykuły papiernicze</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszym sklepie internetowym kupisz najtaniej</h1>
        </header>
        <section id="left">
            <h2>Kontakt</h2>
            <p>telefon: 444333222<br>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
            <img src="promocja2.png" alt="promocja">
        </section>
        <section id="middle">
            <h2>Promocja 10% obejmuje artykuły:</h2>
            <ul>
    <?php
    $result = mysqli_query($connection, 'SELECT nazwa FROM towary WHERE promocja = 1;');
    while ($row = mysqli_fetch_row($result)) {
        echo '<li>' . $row[0] . '</li>';
    }
    ?>
            </ul>
        </section>
        <section id="right">
            <h2>Cena wybranego artykułu w promocji</h2>
            <form method="post">
                <select name="product">
                    <option>Gumka do mazania</option>
                    <option>Cienkopis</option>
                    <option>Pisaki 60 szt.</option>
                    <option>Markery 4 szt.</option>
                </select>
                <input type="submit" value="WYBIERZ">
            </form>
    <?php
    if (isset($_POST['product'])) {
        $product = $_POST['product'];
        // $result = mysqli_query($connection, "SELECT cena FROM towary WHERE nazwa = '$product';");
        $statement = mysqli_prepare($connection, 'SELECT cena FROM towary WHERE nazwa = ?;');
        mysqli_stmt_bind_param($statement, 's', $product);
        mysqli_stmt_execute($statement);
        $result = mysqli_stmt_get_result($statement);
        $row = mysqli_fetch_row($result);
        if ($row) {
            $price = round($row[0] * 0.9, 2);
            echo 'Cena w promocji: ' . $price . ' zł';
        }
    }
    mysqli_close($connection);
    ?>
        </section>
        <footer>
            <h3>Autor strony Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  44. Krok 44: Zabezpieczenie: dane z bazy na wyjściu

    ok. 2 min
    Polecenie z arkusza

    Nazwy produktów pobrane zapytaniem wypisywane są jako elementy listy punktowanej (nieuporządkowanej) zgodnie z obrazem 3

    Nazwy towarów pochodzą z bazy, a bazę zapełnia ktoś inny niż skrypt - np. pracownik w panelu sklepu. Gdyby w nazwie stał znacznik <script>, przeglądarka wykonałaby go u każdego, kto otworzy stronę. htmlspecialchars() zamienia <, >, & i cudzysłowy na encje, więc znaczniki wyświetlą się jako tekst. To zasada ogólna: wszystko, co wypisujesz z bazy lub z formularza, przechodzi przez htmlspecialchars.

    Podgląd bez zmian - zwykłe nazwy wyglądają tak samo. Stare linie zostają w komentarzu; na egzaminie wystarczy wersja z kroku „Zamknięcie połączenia”.

    index.php PHP zmienione: 2 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'sklep');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Artykuły papiernicze</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>W naszym sklepie internetowym kupisz najtaniej</h1>
        </header>
        <section id="left">
            <h2>Kontakt</h2>
            <p>telefon: 444333222<br>e-mail: <a href="mailto:[email protected]">[email protected]</a></p>
            <img src="promocja2.png" alt="promocja">
        </section>
        <section id="middle">
            <h2>Promocja 10% obejmuje artykuły:</h2>
            <ul>
    <?php
    $result = mysqli_query($connection, 'SELECT nazwa FROM towary WHERE promocja = 1;');
    while ($row = mysqli_fetch_row($result)) {
        // echo '<li>' . $row[0] . '</li>';
        echo '<li>' . htmlspecialchars($row[0]) . '</li>';
    }
    ?>
            </ul>
        </section>
        <section id="right">
            <h2>Cena wybranego artykułu w promocji</h2>
            <form method="post">
                <select name="product">
                    <option>Gumka do mazania</option>
                    <option>Cienkopis</option>
                    <option>Pisaki 60 szt.</option>
                    <option>Markery 4 szt.</option>
                </select>
                <input type="submit" value="WYBIERZ">
            </form>
    <?php
    if (isset($_POST['product'])) {
        $product = $_POST['product'];
        // $result = mysqli_query($connection, "SELECT cena FROM towary WHERE nazwa = '$product';");
        $statement = mysqli_prepare($connection, 'SELECT cena FROM towary WHERE nazwa = ?;');
        mysqli_stmt_bind_param($statement, 's', $product);
        mysqli_stmt_execute($statement);
        $result = mysqli_stmt_get_result($statement);
        $row = mysqli_fetch_row($result);
        if ($row) {
            $price = round($row[0] * 0.9, 2);
            echo 'Cena w promocji: ' . $price . ' zł';
        }
    }
    mysqli_close($connection);
    ?>
        </section>
        <footer>
            <h3>Autor strony Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.php)
  45. Etap 7: Oddanie pracy

    Krok 45 · po tym etapie masz 37 z 37 kryteriów (pkt)

    Krok 45: 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 wpisujesz nazwę przeglądarki, w której sprawdzasz stronę przez http://localhost/….

    W folderze z numerem zdającego muszą być: przeglądarka.txt, baza.jpg, kw1.jpg, kw2.jpg, kw3.jpg, kw4.jpg, kwerendy.txt, index.php, styl.css, promocja.png, promocja2.png - i może zostać rozpakowany sklep.sql. Nagraj płytę, sprawdź jej odczyt i opisz ją numerem zdającego.

    Typowe błędy

    Płyta, której nie da się odczytać, to zero punktów za wszystkie pięć rezultatów - sprawdź ją, zanim oddasz.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

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

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