Przejdź do głównej treści
EE.09-02-21.06-SGCzerwiec 2021 · 44 kroki · ok. 86 min · 37 kryteriów z klucza

Rozwiązanie krok po kroku: Sklep internetowy z eko-warzywami (Wersja podstawowa)

Internetowy sklep z eko-warzywami: baza dane2 (import pliku dane.sql - pięć tabel, w zadaniu potrzebne są produkty, producenci i rodzaje), cztery zapytania - owoce i warzywa, produkty z niskim stanem połączone z producentem (JOIN), nowy użytkownik bazy (CREATE USER) i nowy produkt (INSERT). Do tego grafika arbuz.jpg przycięta do proporcji 3:2 i zmniejszona do 300 × 200 px, strona sklep.php z dwoma banerami, blokiem produktów z paskiem przewijania i formularzem w stopce, style w styl2.css oraz skrypt PHP, który z bazy buduje bloki produktów i dopisuje produkt z formularza.

To arkusz EE.09 z czerwca 2021 - kwalifikacji formuły 2017, którą zastąpiła INF.03. 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ą. Skrypt jest najpierw napisany najprościej, funkcjami mysqli z tabeli 1 arkusza, a na końcu sprawdzony na cenie z przecinkiem, na prawdziwym wstrzyknięciu SQL i zabezpieczony.

To jest wersja podstawowa. W zakładce „Nowoczesny kod (flex, PDO)” jest drugi sposób rozwiązania tego samego arkusza: semantyczne znaczniki, układ bez float, PDO z zapytaniem przygotowanym na górze pliku i przekierowanie po zapisie - obie wersje są zgodne z kluczem.

Baza i zdjęcia pochodzą z oryginalnego archiwum CKE zad2.zip (załącznik do pobrania na stronie arkusza, hasło w treści zadania). Zapytania i zrzuty są policzone na MariaDB 10.4 - tej samej, która jest w XAMPP, a podgląd od kroków z PHP to wynik prawdziwego uruchomienia kodu w PHP 8.2.

Jak liczą się punkty. Klucz ma 37 pozycji (R.1.1 - R.5.6), każda za 1 punkt; pozycja R.2.10 jest w kluczu CKE pusta (bez treści), więc w praktyce ocenianych jest 36 kryteriów. Próg zdania części praktycznej to 75%, czyli 28 punktów. 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 zad2.zip

    ok. 2 min
    Polecenie z arkusza

    Na pulpicie znajduje się archiwum ZIP o nazwie zad2.zip zabezpieczone hasłem: #Warzywni@czeK Należy utworzyć folder o nazwie odpowiadającej numerowi PESEL zdającego lub w przypadku jego braku innemu numerowi, którym został podpisany arkusz. Archiwum należy rozpakować do tego folderu.

    Na pulpicie tworzysz folder nazwany numerem PESEL - w tym rozwiązaniu w jego miejscu stoi imię i nazwisko autora, Bartosz Bryniarski. Archiwum zad2.zip rozpakowujesz do niego, podając hasło #Warzywni@czeK - wielkość liter i znaki # oraz @ mają znaczenie. W środku jest plik dane.sql i dziewięć zdjęć produktów (arbuz.jpg, gruszka.jpg, koper.jpg, marchew.jpg, morela.jpg, owoce.jpg, papaja.jpg, papryka.jpg, ziemniak.jpg).

    Arkusz podkreśla, że strona i zdjęcia muszą leżeć w tym samym folderze - skrypt poda w znaczniku obrazu samą nazwę pliku z bazy.

    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.

  2. Krok 2: XAMPP i pusta baza dane2

    ok. 2 min
    Polecenie z arkusza

    Uruchom usługi MySQL i Apache za pomocą XAMPP Control Panel. Za pomocą narzędzia phpMyAdmin wykonaj podane operacje na bazie danych: Utwórz bazę danych o nazwie dane2

    W XAMPP Control Panel klikasz Start przy Apache i MySQL, potem http://localhost/phpmyadmin. Nowa w lewym panelu, nazwa dane2, Utwórz.

  3. Krok 3: Import pliku dane.sql i zrzut egzamin.jpg

    Kryteria:

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

    ok. 3 min
    Polecenie z arkusza

    Do bazy dane2 zaimportuj plik dane.sql z rozpakowanego archiwum. Wykonaj zrzut ekranu po imporcie. Zrzut zapisz w folderze z numerem PESEL lub w przypadku jego braku w folderze z innym numerem, którym został podpisany arkusz, w formacie JPEG i nazwij egzamin. 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ę dane2, karta Import, Wybierz plik → dane.sql, Wykonaj. Powstaje pięć tabel - poza produkty, producenci i rodzaje także hobby i osoby z innego zadania. Po imporcie lista tabel w karcie Struktura - zrzut całego ekranu z paskiem zadań: egzamin.jpg.

    W tabeli produkty są nie tylko owoce i warzywa, ale też filmy (rodzaje 7 - 13) - dlatego zapytania filtrują po Rodzaje_id.

    Typowe błędy

    W tym arkuszu formaty są odwrotne niż zwykle: zrzut importu w JPEG (egzamin.jpg), zrzuty kwerend w PNG (kw1.png - kw4.png). Zły format albo wykadrowany zrzut nie jest oceniany.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 3
  4. Krok 4: Zapytanie 1: owoce i warzywa

    Kryteria: ,

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

    R.1.3 Utworzono zapytanie 1 wybierające jedynie pola nazwa, ilosc, opis, cena, zdjecie z tabeli produkty , dla produktów, których pole Rodzaje_id przyjmuje wartości: 1, 2. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa, ilosc, opis, cena, zdjecie FROM produkty WHERE Rodzaje_id = 1 OR Rodzaje_id = 2; oraz wynik działania zapytania udokumentowano zrzutem, na którym widocznych jest dokładnie 8 wierszy, jedynie pola nazwa, ilosc, opis, cena, zdjecie

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 1: wybierające jedynie pola nazwa, ilosc, opis, cena, zdjecie z tabeli Produkty, dla produktów, których pole Rodzaje_id przyjmuje jedną z wartości: 1, 2

    Karta SQL bazy dane2:

    • SELECT nazwa, ilosc, opis, cena, zdjecie - pięć kolumn w kolejności z arkusza,
    • FROM produkty,
    • WHERE Rodzaje_id = 1 OR Rodzaje_id = 2 - jedna z dwóch wartości: 1 to owoce, 2 to warzywa. To samo da WHERE Rodzaje_id IN (1, 2).

    Wynik: 8 wierszy, od gruszki do papryki - dokładnie jak w kluczu. Zapytanie zapisujesz w pliku kwerendy.txt, a cały ekran - jako kw1.png. Z tego zapytania skrypt zbuduje bloki produktów.

    Typowe błędy

    WHERE Rodzaje_id = 1 AND Rodzaje_id = 2 daje pusty wynik - pole nie może mieć dwóch wartości naraz.

    kwerendy.txt Tekst nowy plik
    -- Zapytanie 1
    SELECT nazwa, ilosc, opis, cena, zdjecie FROM produkty WHERE Rodzaje_id = 1 OR Rodzaje_id = 2;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Zapytanie 2: owoce z niskim stanem i ich producenci

    Kryteria:

    R.1.4 Utworzono zapytanie 2 wybierające jedynie nazwę produktu i odpowiadającą mu nazwę producenta dla produktów, dla których ilość jest mniejsza od 10 oraz pole Rodzaje_id przyjmuje wartość 1. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT produkty.nazwa, producenci.nazwa FROM produkty JOIN producenci ON produkty.Producenci_id = producenci.id WHERE produkty.ilosc < 10 AND Rodzaje_id = 1; możliwe też INNER JOIN lub bez JOIN (porównanie kluczy w warunku) oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczne są dokładnie 2 rekordy: arbuz FruitFarm, papaja Owoce-rolnik

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 2: wybierające jedynie nazwę produktu z tabeli Produkty i odpowiadającą mu nazwę producenta z tabeli Producenci dla produktów, dla których ilość jest mniejsza od 10 oraz pole Rodzaje_id przyjmuje wartość 1

    • SELECT produkty.nazwa, producenci.nazwa - obie tabele mają kolumnę nazwa, więc przed nazwą kolumny stoi nazwa tabeli,
    • FROM produkty JOIN producenci ON produkty.Producenci_id = producenci.id - relacja: każdy produkt łączy się z producentem, którego id zapisano w polu Producenci_id,
    • WHERE produkty.ilosc < 10 AND produkty.Rodzaje_id = 1 - oba warunki naraz.

    Wynik: 2 wiersze - arbuz (FruitFarm) i papaja (Owoce-rolnik), zgodnie z kluczem. Koper też ma 2 sztuki, ale jest warzywem. Zrzut: kw2.png.

    Typowe błędy

    Samo SELECT nazwa, nazwa przy dwóch tabelach kończy się błędem „Column 'nazwa' in field list is ambiguous” - baza nie wie, o którą tabelę chodzi.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT nazwa, ilosc, opis, cena, zdjecie FROM produkty WHERE Rodzaje_id = 1 OR Rodzaje_id = 2;
    
    -- Zapytanie 2
    SELECT produkty.nazwa, producenci.nazwa FROM produkty JOIN producenci ON produkty.Producenci_id = producenci.id WHERE produkty.ilosc < 10 AND produkty.Rodzaje_id = 1;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Zapytanie 3: użytkownik anna

    Kryteria:

    R.1.5 Utworzono zapytanie 3 tworzące użytkownika anna z hasłem @nna23. W pliku z kwerendami lub na zrzucie istnieje zapis np.: CREATE USER 'anna'@'localhost' IDENTIFIED BY '@nna23'; oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczny jest efekt poprawnie wykonanego zapytania. Nie uznajemy za spełnione, jeśli brak widocznego hasła (np. wygenerowano z kreatora)

    ok. 2 min
    Polecenie z arkusza

    Zapytanie 3: tworzące użytkownika anna z hasłem @nna23

    • CREATE USER 'anna'@'localhost' - konto bazy danych: nazwa i host, z którego wolno się logować (tu: ten sam komputer),
    • IDENTIFIED BY '@nna23' - hasło w apostrofach.

    phpMyAdmin odpowiada „MySQL zwrócił pusty wynik” - zrzut kw3.png z widocznym zapytaniem. Klucz wymaga widocznego hasła, więc konto trzeba założyć zapytaniem w karcie SQL, a nie kreatorem Konta użytkowników.

    Typowe błędy

    Drugie uruchomienie tego samego zapytania kończy się błędem „Operation CREATE USER failed” - konto już istnieje. Na zrzut wystarczy pierwsze, udane wykonanie.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT nazwa, ilosc, opis, cena, zdjecie FROM produkty WHERE Rodzaje_id = 1 OR Rodzaje_id = 2;
    
    -- Zapytanie 2
    SELECT produkty.nazwa, producenci.nazwa FROM produkty JOIN producenci ON produkty.Producenci_id = producenci.id WHERE produkty.ilosc < 10 AND produkty.Rodzaje_id = 1;
    
    -- Zapytanie 3
    CREATE USER 'anna'@'localhost' IDENTIFIED BY '@nna23';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Zapytanie 4: nowy produkt owoc1

    Kryteria:

    R.1.6 Utworzono zapytanie 4 wstawiające wiersz do tabeli produkty, klucz główny nadany automatycznie, dane: Rodzaje_id: 1, Producenci_id: 4, nazwa: owoc1, ilość: 10, opis pusty, cena: 9,99, zdjecie: owoce.jpg. W pliku z kwerendami lub na zrzucie istnieje zapis np.: INSERT INTO produkty(`Rodzaje_id`, `Producenci_id`, `nazwa`, `ilosc`, `opis`, `cena`, `zdjecie`) VALUES (1, 4, "owoc1", 10,"",9.99,"owoce.jpg") (możliwe również bez wymieniania pól w nawiasie, a także przez SET + lista przypisań) oraz wynik działania zapytania udokumentowano zrzutem, na którym widoczny jest komunikat o poprawnie wykonanym zapytaniu lub wstawiony rekord

    ok. 3 min
    Polecenie z arkusza

    Zapytanie 4: wstawiające wiersz do tabeli Produkty według specyfikacji: klucz główny nadany automatycznie; Rodzaje_id – id wiersza z nazwą owoce w tabeli Rodzaje; Producenci_id – id wiersza z nazwą warzywa-rolnik w tabeli Producenci; pozostałe dane: nazwa: owoc1, ilość: 10, opis: puste pole, cena: 9,99, zdjecie: owoce.jpg

    Najpierw dwie wartości z innych tabel: w rodzaje owoce mają id 1, a w producenci warzywa-rolnik ma id 4 (karta Przeglądaj obu tabel). Potem:

    • INSERT INTO produkty (Rodzaje_id, Producenci_id, nazwa, ilosc, opis, cena, zdjecie) - lista kolumn bez id: klucz główny ma AUTO_INCREMENT, więc baza nada go sama,
    • VALUES (1, 4, 'owoc1', 10, '', 9.99, 'owoce.jpg') - wartości w tej samej kolejności; puste pole opisu to '', a cena z kropką: w SQL przecinek oddziela wartości.

    phpMyAdmin odpowiada „1 wiersz wstawiony” - zrzut kw4.png. Nowy produkt dostał id 43 i jako owoc pojawi się na stronie.

    Typowe błędy

    9,99 zamiast 9.99 to dla SQL dwie wartości (9 i 99) - baza zgłosi „Column count doesn't match value count”.

    kwerendy.txt Tekst zmienione: 3 linie
    -- Zapytanie 1
    SELECT nazwa, ilosc, opis, cena, zdjecie FROM produkty WHERE Rodzaje_id = 1 OR Rodzaje_id = 2;
    
    -- Zapytanie 2
    SELECT produkty.nazwa, producenci.nazwa FROM produkty JOIN producenci ON produkty.Producenci_id = producenci.id WHERE produkty.ilosc < 10 AND produkty.Rodzaje_id = 1;
    
    -- Zapytanie 3
    CREATE USER 'anna'@'localhost' IDENTIFIED BY '@nna23';
    
    -- Zapytanie 4
    INSERT INTO produkty (Rodzaje_id, Producenci_id, nazwa, ilosc, opis, cena, zdjecie) VALUES (1, 4, 'owoc1', 10, '', 9.99, 'owoce.jpg');
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Etap 2: Grafika i strona HTML

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

    Krok 8: Grafika: arbuz.jpg w proporcji 3:2, 300 × 200 px

    Kryteria:

    R.2.1 Plik arbuz.jpg przeskalowano do rozmiaru 300 px na 200 px (± 2 px)

    ok. 4 min
    Polecenie z arkusza

    Plik arbuz.jpg, wypakowany z archiwum, należy skadrować do proporcji 3:2, a następnie przeskalować z zachowaniem proporcji do rozmiaru 300 px na 200 px. Pozostałe pliki graficzne są przygotowane i nie należy ich zmieniać

    Oryginał ma 500 × 297 px - jest szerszy niż 3:2. Wysokość zostaje, a szerokość dla proporcji 3:2 to 297 × 3 / 2 = 445,5, czyli 446 px. W GIMP-ie:

    • narzędzie Kadrowanie z zaznaczoną opcją Stałe: Proporcje i wartością 3:2, zaznaczenie na całą wysokość, przesunięte na środek (z każdej strony odpada ok. 27 px),
    • Obraz → Skaluj obraz, z zamkniętym łańcuchem szerokość 300 - wysokość sama przeliczy się na 200,
    • Plik → Nadpisz arbuz.jpg.

    Pozostałe zdjęcia mają już 300 × 200 px i zostają bez zmian (R.2.1).

    Typowe błędy

    Samo skalowanie do 300 × 200 px bez kadrowania spłaszcza arbuz (500 × 297 to nie 3:2), a skalowanie z łańcuchem bez kadrowania daje 300 × 178 px - klucz przyjmuje 300 × 200 ± 2 px.

    Podgląd strony po tym kroku
    Podgląd strony po kroku 8
  9. Krok 9: Plik sklep.php

    Kryteria:

    R.2.2 Witrynę internetową zapisano w pliku PHP o nazwie sklep

    ok. 1 min
    Polecenie z arkusza

    Składa się ze strony o nazwie sklep.php

    Plik sklep.php w folderze zdającego, obok zdjęć. Rozszerzenie .php, bo dojdzie skrypt - na razie szkielet HTML5: <!DOCTYPE html> i lang="pl" (R.2.2).

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

    Kryteria:

    R.2.3 Zastosowano właściwy standard kodowania polskich znaków dla witryny

    ok. 1 min
    Polecenie z arkusza

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

    <meta charset="UTF-8"> jako pierwszy element <head>, a plik zapisany w UTF-8 - inaczej „Stronę wykonał” i „zł” wyświetlą się z krzaczkami (R.2.3).

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

    Kryteria:

    R.2.4 Nadano tytuł strony: "Warzywniak"

    ok. 1 min
    Polecenie z arkusza

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

    <title>Warzywniak</title> w <head> (R.2.4).

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

    Kryteria:

    R.3.5 Strona zawiera działające połączenie z zewnętrznym arkuszem stylów o nazwie styl2.css , formatowanie pochodzi jedynie z tego arkusza 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 styl2.css prawidłowo połączony z kodem strony

    <link rel="stylesheet" href="styl2.css"> w <head>. Plik stylów powstanie po HTML-u (R.3.5).

    Typowe błędy

    Klucz uznaje R.3.5 tylko wtedy, gdy całe formatowanie pochodzi z tego pliku i każdy selektor trafia w swój blok (kropka zamiast # odbiera punkt).

    sklep.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Warzywniak</title>
        <link rel="stylesheet" href="styl2.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (sklep.php)
  13. Krok 13: Bloki: dwa banery, blok główny, stopka

    Kryteria:

    R.2.5 Wygląd strony zdefiniowano dzieląc ją na bloki: dwa bloki banera, główny i stopka. Zastosowano znaczniki sekcji

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki: dwa bloki banera obok siebie, blok główny i stopka. W bloku głównym znajdują się bloki produktów generowane przez skrypt. Podział zrealizowany za pomocą znaczników sekcji tak, aby wygląd układu bloków strony po uruchomieniu w przeglądarce, był zgodny z Obrazem 2

    Cztery bloki w kolejności z obrazu 2: dwa banery i blok główny jako <section> z identyfikatorami banner-left, banner-right i main (po angielsku - arkusz nazw nie narzuca), na dole <footer>. Bloki produktów wygeneruje skrypt w bloku głównym. Obok siebie banery ustawi CSS (R.2.5).

    sklep.php PHP zmienione: 8 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Warzywniak</title>
        <link rel="stylesheet" href="styl2.css">
    </head>
    <body>
        <section id="banner-left">
        </section>
        <section id="banner-right">
        </section>
        <section id="main">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (sklep.php)
  14. Krok 14: Baner lewy: nagłówek pierwszego stopnia

    Kryteria:

    R.2.6 W bloku banera lewego zapisano nagłówek h1: "Internetowy sklep z eko-warzywami"

    ok. 1 min
    Polecenie z arkusza

    Zawartość banera lewego: nagłówek pierwszego stopnia o treści „Internetowy sklep z eko-warzywami”

    <h1> - jedyny nagłówek pierwszego stopnia na stronie (R.2.6).

    sklep.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Warzywniak</title>
        <link rel="stylesheet" href="styl2.css">
    </head>
    <body>
        <section id="banner-left">
            <h1>Internetowy sklep z eko-warzywami</h1>
        </section>
        <section id="banner-right">
        </section>
        <section id="main">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (sklep.php)
  15. Krok 15: Baner prawy: lista numerowana

    Kryteria:

    R.2.7 W bloku banera prawego umieszczono listę numerowaną z trzema elementami, zastosowano znaczniki <ol>, <li> oraz ich zamknięcia

    ok. 1 min
    Polecenie z arkusza

    Zawartość banera prawego: Lista numerowana (uporządkowana) z elementami: warzywa, owoce, soki

    Lista numerowana to <ol> (ordered list), każdy element w <li> z zamknięciem - przeglądarka sama dopisze numery 1, 2, 3 (R.2.7).

    Typowe błędy

    <ul> to lista punktowana - na stronie pojawią się kropki zamiast numerów i punkt przepada.

    sklep.php PHP zmienione: 5 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Warzywniak</title>
        <link rel="stylesheet" href="styl2.css">
    </head>
    <body>
        <section id="banner-left">
            <h1>Internetowy sklep z eko-warzywami</h1>
        </section>
        <section id="banner-right">
            <ol>
                <li>warzywa</li>
                <li>owoce</li>
                <li>soki</li>
            </ol>
        </section>
        <section id="main">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (sklep.php)
  16. Krok 16: Odnośnik „soki” w osobnym oknie

    Kryteria:

    R.3.2 Umieszczono odnośnik, którego kliknięcie inicjuje otwarcie w nowym oknie strony https://terapiasokami.pl/

    ok. 1 min
    Polecenie z arkusza

    Element soki jest odnośnikiem prowadzącym do strony o adresie „https://terapiasokami.pl/”, odnośnik otwiera się w osobnym oknie

    Treść trzeciego elementu listy zamknięta w <a>. Atrybut target="_blank" otwiera stronę w nowej karcie lub oknie przeglądarki (R.3.2).

    sklep.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Warzywniak</title>
        <link rel="stylesheet" href="styl2.css">
    </head>
    <body>
        <section id="banner-left">
            <h1>Internetowy sklep z eko-warzywami</h1>
        </section>
        <section id="banner-right">
            <ol>
                <li>warzywa</li>
                <li>owoce</li>
                <li><a href="https://terapiasokami.pl/" target="_blank">soki</a></li>
            </ol>
        </section>
        <section id="main">
        </section>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (sklep.php)
  17. Krok 17: Stopka: formularz metodą POST

    Kryteria:

    R.3.3 Formularz wysyła dane metodą POST

    ok. 1 min
    Polecenie z arkusza

    Zawartość stopki: Formularz wysyłający dane do tego samego pliku metodą POST

    <form method="post"> bez atrybutu action wysyła dane do adresu, z którego został wczytany - czyli do sklep.php. Metoda POST przesyła pola w treści żądania; w PHP odczytasz je z tablicy $_POST (R.3.3).

    sklep.php PHP zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Warzywniak</title>
        <link rel="stylesheet" href="styl2.css">
    </head>
    <body>
        <section id="banner-left">
            <h1>Internetowy sklep z eko-warzywami</h1>
        </section>
        <section id="banner-right">
            <ol>
                <li>warzywa</li>
                <li>owoce</li>
                <li><a href="https://terapiasokami.pl/" target="_blank">soki</a></li>
            </ol>
        </section>
        <section id="main">
        </section>
        <footer>
            <form method="post">
            </form>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (sklep.php)
  18. Krok 18: Pole edycyjne „Nazwa:”

    ok. 1 min
    Polecenie z arkusza

    Pole edycyjne poprzedzone wpisem „Nazwa:”

    Napis wprost w formularzu, za nim <input type="text"> z name="nazwa" - pod tym kluczem tekst trafi do PHP: $_POST['nazwa'].

    sklep.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Warzywniak</title>
        <link rel="stylesheet" href="styl2.css">
    </head>
    <body>
        <section id="banner-left">
            <h1>Internetowy sklep z eko-warzywami</h1>
        </section>
        <section id="banner-right">
            <ol>
                <li>warzywa</li>
                <li>owoce</li>
                <li><a href="https://terapiasokami.pl/" target="_blank">soki</a></li>
            </ol>
        </section>
        <section id="main">
        </section>
        <footer>
            <form method="post">
                Nazwa: <input type="text" name="nazwa">
            </form>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (sklep.php)
  19. Krok 19: Pole edycyjne „Cena:”

    ok. 1 min
    Polecenie z arkusza

    Pole edycyjne poprzedzone wpisem „Cena:”

    Drugie pole, name="cena". Typ text, jak w arkuszu - uczeń może więc wpisać cenę z przecinkiem; skrypt będzie musiał sobie z tym poradzić (krok w dobrych praktykach).

    sklep.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Warzywniak</title>
        <link rel="stylesheet" href="styl2.css">
    </head>
    <body>
        <section id="banner-left">
            <h1>Internetowy sklep z eko-warzywami</h1>
        </section>
        <section id="banner-right">
            <ol>
                <li>warzywa</li>
                <li>owoce</li>
                <li><a href="https://terapiasokami.pl/" target="_blank">soki</a></li>
            </ol>
        </section>
        <section id="main">
        </section>
        <footer>
            <form method="post">
                Nazwa: <input type="text" name="nazwa">
                Cena: <input type="text" name="cena">
            </form>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (sklep.php)
  20. Krok 20: Przycisk „Dodaj produkt”

    Kryteria: ,

    R.3.4 Wciśnięcie przycisku wysyła dane do skryptu (np. typ submit dla input lub button albo button bez typu)

    R.2.9 W stopce umieszczono formularz z dwoma polami edycyjnymi i przyciskiem, zastosowano znacznik <form> oraz jego zamknięcie

    ok. 1 min
    Polecenie z arkusza

    Przycisk „Dodaj produkt” wysyłający dane do skryptu 2

    <input type="submit" value="Dodaj produkt"> - przycisk typu submit wysyła formularz (R.3.4). Formularz ma teraz dwa pola edycyjne i przycisk, z zamknięciem </form> (R.2.9).

    Typowe błędy

    <input type="button"> wygląda tak samo, ale niczego nie wysyła - brak punktu R.3.4.

    sklep.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Warzywniak</title>
        <link rel="stylesheet" href="styl2.css">
    </head>
    <body>
        <section id="banner-left">
            <h1>Internetowy sklep z eko-warzywami</h1>
        </section>
        <section id="banner-right">
            <ol>
                <li>warzywa</li>
                <li>owoce</li>
                <li><a href="https://terapiasokami.pl/" target="_blank">soki</a></li>
            </ol>
        </section>
        <section id="main">
        </section>
        <footer>
            <form method="post">
                Nazwa: <input type="text" name="nazwa">
                Cena: <input type="text" name="cena">
                <input type="submit" value="Dodaj produkt">
            </form>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (sklep.php)
  21. Krok 21: Stopka: napis z autorem strony

    ok. 1 min
    Polecenie z arkusza

    Napis o treści „Stronę wykonał: ”, dalej wstawiony numer PESEL zdającego lub w przypadku jego braku inny numer, którym został podpisany arkusz

    Na egzaminie po dwukropku wpisujesz swój numer PESEL; w tym rozwiązaniu w jego miejscu stoi Bartosz Bryniarski. Arkusz mówi o napisie, nie o akapicie, więc tekst stoi wprost w stopce - formularz jest blokiem, więc napis i tak zaczyna się w nowej linii, jak na obrazie 2.

    sklep.php PHP zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Warzywniak</title>
        <link rel="stylesheet" href="styl2.css">
    </head>
    <body>
        <section id="banner-left">
            <h1>Internetowy sklep z eko-warzywami</h1>
        </section>
        <section id="banner-right">
            <ol>
                <li>warzywa</li>
                <li>owoce</li>
                <li><a href="https://terapiasokami.pl/" target="_blank">soki</a></li>
            </ol>
        </section>
        <section id="main">
        </section>
        <footer>
            <form method="post">
                Nazwa: <input type="text" name="nazwa">
                Cena: <input type="text" name="cena">
                <input type="submit" value="Dodaj produkt">
            </form>
            Stronę wykonał: Bartosz Bryniarski
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (sklep.php)
  22. Etap 3: Styl CSS

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

    Krok 22: Plik styl2.css: krój Garamond dla całej strony

    Kryteria:

    R.4.1 Ustawiono domyślny krój czcionki dla całej strony na Garamond (dla selektora * lub body lub html lub kontenera całej strony)

    ok. 1 min
    Polecenie z arkusza

    Domyślne dla całej strony: krój czcionki Garamond

    Nowy plik styl2.css. Selektor uniwersalny * obejmuje każdy element, także pola formularza, które kroju po body nie dziedziczą (R.4.1).

    styl2.css CSS nowy plik
    * {
        font-family: Garamond;
    }
    Podgląd strony po tym kroku (sklep.php)
  23. Krok 23: Baner lewy

    Kryteria:

    R.4.6 Ustawiono wyrównanie tekstu do środka dla banera lewego

    ok. 1 min
    Polecenie z arkusza

    Dla banera lewego: kolor tła #00600F, biały kolor czcionki, wyrównanie tekstu do środka, szerokość 75%, wysokość 90 px

    Selektor identyfikatora #banner-left, ciemnozielone tło i nagłówek na środku bloku (R.4.6).

    styl2.css CSS zmienione: 7 linii
    * {
        font-family: Garamond;
    }
    #banner-left {
        background-color: #00600F;
        color: white;
        text-align: center;
        width: 75%;
        height: 90px;
    }
    Podgląd strony po tym kroku (sklep.php)
  24. Krok 24: Baner prawy

    Kryteria:

    R.4.5 Ustawiono szerokość banera lewego: 75%, prawego 25%

    ok. 1 min
    Polecenie z arkusza

    Dla banera prawego: kolor tła #00600F, biały kolor czcionki, szerokość 25%, wysokość 90 px

    75% + 25% = 100%, więc oba banery zmieszczą się w jednym rzędzie - bez obramowań i padding, które by je poszerzyły (R.4.5). Biały jest tekst listy; odnośnik „soki” zostaje w kolorze odnośnika, jak na obrazie 2.

    styl2.css CSS zmienione: 6 linii
    * {
        font-family: Garamond;
    }
    #banner-left {
        background-color: #00600F;
        color: white;
        text-align: center;
        width: 75%;
        height: 90px;
    }
    #banner-right {
        background-color: #00600F;
        color: white;
        width: 25%;
        height: 90px;
    }
    Podgląd strony po tym kroku (sklep.php)
  25. Krok 25: Blok główny z paskiem przewijania

    Kryteria: ,

    R.4.4 Ustawiono wysokość obu bloków banera: 90px, bloku głównego: 600px

    R.4.7 Ustawiono przynajmniej jeden pasek przewijania (przesuwania) dla bloku głównego, np. overflow: scroll; (lub auto)

    ok. 2 min
    Polecenie z arkusza

    Dla bloku głównego: kolor tła #C8E6C9, wysokość 600 px, paski przesuwania (scroll)

    Blok ma stałą wysokość 600 px, a produktów jest więcej, niż się w niej zmieści. overflow: scroll dodaje paski przewijania - produkty przewija się wewnątrz bloku, a stopka zostaje na swoim miejscu (R.4.7). Wysokość banerów i bloku głównego jest ustawiona (R.4.4).

    Typowe błędy

    overflow: hidden ucina produkty bez możliwości przewinięcia - klucz przyjmuje scroll albo auto.

    styl2.css CSS zmienione: 5 linii
    * {
        font-family: Garamond;
    }
    #banner-left {
        background-color: #00600F;
        color: white;
        text-align: center;
        width: 75%;
        height: 90px;
    }
    #banner-right {
        background-color: #00600F;
        color: white;
        width: 25%;
        height: 90px;
    }
    #main {
        background-color: #C8E6C9;
        height: 600px;
        overflow: scroll;
    }
    Podgląd strony po tym kroku (sklep.php)
  26. Krok 26: Stopka

    Kryteria: ,

    R.4.2 Ustawiono kolor tła #00600F dla obu bloków banera i stopki, #C8E6C9 dla bloku głównego

    R.4.3 Ustawiono biały kolor czcionki dla obu bloków banera i stopki

    ok. 1 min
    Polecenie z arkusza

    Dla stopki: kolor tła #00600F, biały kolor czcionki

    Ten sam kolor co w banerach: tło obu banerów, stopki i bloku głównego (R.4.2), biały tekst w obu banerach i stopce (R.4.3).

    styl2.css CSS zmienione: 4 linie
    * {
        font-family: Garamond;
    }
    #banner-left {
        background-color: #00600F;
        color: white;
        text-align: center;
        width: 75%;
        height: 90px;
    }
    #banner-right {
        background-color: #00600F;
        color: white;
        width: 25%;
        height: 90px;
    }
    #main {
        background-color: #C8E6C9;
        height: 600px;
        overflow: scroll;
    }
    footer {
        background-color: #00600F;
        color: white;
    }
    Podgląd strony po tym kroku (sklep.php)
  27. Krok 27: Nagłówek piątego stopnia: nazwa produktu

    Kryteria:

    R.4.8 Ustawiono styl selektora h5: kolor tła #00600F, biały kolor czcionki, rozmiar czcionki 200%, wyrównanie tekstu do środka, marginesy zewnętrzne (margin) 0px (0 również poprawne bez jednostki)

    ok. 1 min
    Polecenie z arkusza

    Dla selektora nagłówka piątego stopnia: kolor tła #00600F, biały kolor czcionki, rozmiar czcionki 200%, wyrównanie tekstu do środka, marginesy zewnętrzne 0 px

    W h5 skrypt wypisze nazwę produktu. margin: 0px zdejmuje domyślne odstępy nagłówka - zielony pasek przylega do zdjęcia, jak na obrazie 2 (R.4.8).

    styl2.css CSS zmienione: 7 linii
    * {
        font-family: Garamond;
    }
    #banner-left {
        background-color: #00600F;
        color: white;
        text-align: center;
        width: 75%;
        height: 90px;
    }
    #banner-right {
        background-color: #00600F;
        color: white;
        width: 25%;
        height: 90px;
    }
    #main {
        background-color: #C8E6C9;
        height: 600px;
        overflow: scroll;
    }
    footer {
        background-color: #00600F;
        color: white;
    }
    h5 {
        background-color: #00600F;
        color: white;
        font-size: 200%;
        text-align: center;
        margin: 0px;
    }
    Podgląd strony po tym kroku (sklep.php)
  28. Krok 28: Nagłówek drugiego stopnia do prawej: cena

    Kryteria:

    R.4.9 Ustawiono styl selektora h2: wyrównanie tekstu do prawej

    ok. 1 min
    Polecenie z arkusza

    Dla selektora nagłówka drugiego stopnia: wyrównanie tekstu do prawej

    W h2 skrypt wypisze cenę - przy prawej krawędzi bloku produktu (R.4.9).

    styl2.css CSS zmienione: 3 linie
    * {
        font-family: Garamond;
    }
    #banner-left {
        background-color: #00600F;
        color: white;
        text-align: center;
        width: 75%;
        height: 90px;
    }
    #banner-right {
        background-color: #00600F;
        color: white;
        width: 25%;
        height: 90px;
    }
    #main {
        background-color: #C8E6C9;
        height: 600px;
        overflow: scroll;
    }
    footer {
        background-color: #00600F;
        color: white;
    }
    h5 {
        background-color: #00600F;
        color: white;
        font-size: 200%;
        text-align: center;
        margin: 0px;
    }
    h2 {
        text-align: right;
    }
    Podgląd strony po tym kroku (sklep.php)
  29. Krok 29: Blok produktu

    Kryteria:

    R.4.10 Ustawiono styl bloku produktu: marginesy zewnętrzne 10px (margin), obramowanie linia ciągła 1px w kolorze #00600F, szerokość 300px

    ok. 1 min
    Polecenie z arkusza

    Dla bloku produktu generowanego przez skrypt 1: marginesy zewnętrzne 10 px, obramowanie linia ciągła 1 px w kolorze #00600F, szerokość 300 px

    Bloków produktów będzie kilka, więc selektor klasy .product. Szerokość 300 px równa się szerokości zdjęć (R.4.10). Bloki pojawią się dopiero ze skryptem.

    styl2.css CSS zmienione: 5 linii
    * {
        font-family: Garamond;
    }
    #banner-left {
        background-color: #00600F;
        color: white;
        text-align: center;
        width: 75%;
        height: 90px;
    }
    #banner-right {
        background-color: #00600F;
        color: white;
        width: 25%;
        height: 90px;
    }
    #main {
        background-color: #C8E6C9;
        height: 600px;
        overflow: scroll;
    }
    footer {
        background-color: #00600F;
        color: white;
    }
    h5 {
        background-color: #00600F;
        color: white;
        font-size: 200%;
        text-align: center;
        margin: 0px;
    }
    h2 {
        text-align: right;
    }
    .product {
        margin: 10px;
        border: 1px solid #00600F;
        width: 300px;
    }
    Podgląd strony po tym kroku (sklep.php)
  30. Krok 30: 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 egzaminacyjnym (nie uwzględnia się bloków zawierających produkty)

    ok. 3 min
    Polecenie z arkusza

    Podział zrealizowany za pomocą znaczników sekcji tak, aby wygląd układu bloków strony po uruchomieniu w przeglądarce, był zgodny z Obrazem 2

    • oba banery dostają float: left i stają obok siebie (75% + 25%),
    • blok główny z clear: both zaczyna się pod nimi,
    • bloki produktów też pływają w lewo: każdy zajmuje 300 + 2 + 20 = 322 px, więc na ekranie szerokości ok. 1150 px mieszczą się po trzy w rzędzie, jak na obrazie 2,
    • stopka nie potrzebuje clear: blok główny ma overflow, więc pływające produkty zostają w jego wnętrzu.

    Klucz przy R.3.1 nie uwzględnia bloków produktów - liczy się układ banerów, bloku głównego i stopki.

    styl2.css CSS zmienione: 6 linii
    * {
        font-family: Garamond;
    }
    #banner-left {
        background-color: #00600F;
        color: white;
        text-align: center;
        width: 75%;
        height: 90px;
    }
    #banner-right {
        background-color: #00600F;
        color: white;
        width: 25%;
        height: 90px;
    }
    #main {
        background-color: #C8E6C9;
        height: 600px;
        overflow: scroll;
    }
    footer {
        background-color: #00600F;
        color: white;
    }
    h5 {
        background-color: #00600F;
        color: white;
        font-size: 200%;
        text-align: center;
        margin: 0px;
    }
    h2 {
        text-align: right;
    }
    .product {
        margin: 10px;
        border: 1px solid #00600F;
        width: 300px;
    }
    #banner-left, #banner-right, .product {
        float: left;
    }
    #main {
        clear: both;
    }
    Podgląd strony po tym kroku (sklep.php)
  31. Etap 4: Skrypt PHP

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

    Krok 31: Połączenie z bazą dane2

    Kryteria:

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

    ok. 3 min
    Polecenie z arkusza

    Skrypt łączy się z serwerem bazy danych na localhost, użytkownik root bez hasła, baza danych o nazwie dane2

    Na samej górze sklep.php, przed <!DOCTYPE html>, pierwszy blok PHP. Funkcja mysqli_connect z tabeli 1 arkusza: serwer localhost, użytkownik root, puste hasło '' i nazwa bazy dane2 - czwarty argument od razu wybiera bazę (R.5.1). Połączenie trafia do zmiennej $connection.

    Dlaczego na górze: formularz stoi w stopce, czyli po bloku produktów. Gdyby zapis produktu był przy formularzu, nowy produkt pojawiłby się na liście dopiero po odświeżeniu strony. W tym bloku stanie więc skrypt 2. Od tego kroku podgląd to wynik prawdziwego uruchomienia w PHP 8.2; stronę sprawdzasz przez http://localhost/….

    sklep.php PHP zmienione: 3 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'dane2');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Warzywniak</title>
        <link rel="stylesheet" href="styl2.css">
    </head>
    <body>
        <section id="banner-left">
            <h1>Internetowy sklep z eko-warzywami</h1>
        </section>
        <section id="banner-right">
            <ol>
                <li>warzywa</li>
                <li>owoce</li>
                <li><a href="https://terapiasokami.pl/" target="_blank">soki</a></li>
            </ol>
        </section>
        <section id="main">
        </section>
        <footer>
            <form method="post">
                Nazwa: <input type="text" name="nazwa">
                Cena: <input type="text" name="cena">
                <input type="submit" value="Dodaj produkt">
            </form>
            Stronę wykonał: Bartosz Bryniarski
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (sklep.php)
  32. Krok 32: Skrypt 1: zapytanie 1 i blok dla każdego produktu

    Kryteria: ,

    R.5.3 Skrypt 1 wysyła do bazy zapytanie 1 lub skrypt 2 - zapytanie 4

    R.5.5 Skrypt 1 wyświetla wszystkie zwrócone zapytaniem bloki zawierające pobrane z bazy produkty przy zastosowaniu pętli

    ok. 4 min
    Polecenie z arkusza

    Działanie skryptu 1: Wysyła do bazy zapytanie 1. Dla każdego zwróconego zapytaniem wiersza tworzy blok produktu

    Blok PHP w <section id="main">:

    • mysqli_query z zapytaniem 1 z kwerendy.txt (R.5.3),
    • while ($row = mysqli_fetch_row($result)) - mysqli_fetch_row zwraca kolejny wiersz jako tablicę numerowaną od zera, a gdy wierszy zabraknie - false, i pętla się kończy (R.5.5),
    • w każdym obrocie echo wypisuje znacznik sekcji z klasą product - tą samą, którą ma reguła w CSS.

    W bazie po zapytaniu 4 jest dziewięć owoców i warzyw (osiem z importu i owoc1), więc na stronie jest 9 bloków. Na razie puste - widać same ramki.

    sklep.php PHP zmienione: 7 linii
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'dane2');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Warzywniak</title>
        <link rel="stylesheet" href="styl2.css">
    </head>
    <body>
        <section id="banner-left">
            <h1>Internetowy sklep z eko-warzywami</h1>
        </section>
        <section id="banner-right">
            <ol>
                <li>warzywa</li>
                <li>owoce</li>
                <li><a href="https://terapiasokami.pl/" target="_blank">soki</a></li>
            </ol>
        </section>
        <section id="main">
            <?php
            $result = mysqli_query($connection, "SELECT nazwa, ilosc, opis, cena, zdjecie FROM produkty WHERE Rodzaje_id = 1 OR Rodzaje_id = 2;");
            while ($row = mysqli_fetch_row($result)) {
                echo '<section class="product">';
                echo '</section>';
            }
            ?>
        </section>
        <footer>
            <form method="post">
                Nazwa: <input type="text" name="nazwa">
                Cena: <input type="text" name="cena">
                <input type="submit" value="Dodaj produkt">
            </form>
            Stronę wykonał: Bartosz Bryniarski
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (sklep.php)
  33. Krok 33: Skrypt 1: zdjęcie produktu

    Kryteria:

    R.2.8 Przynajmniej jeden obraz ma tekst alternatywny "warzywniak" (umieszczony skryptem lub HTML)

    ok. 2 min
    Polecenie z arkusza

    Obraz o nazwie pobranej z bazy danych i tekście alternatywnym „warzywniak”

    Kolumna zdjecie jest piąta w zapytaniu, więc to $row[4]; trafia do src. Tekst alternatywny jest stały: warzywniak (R.2.8). Cudzysłowy atrybutów wewnątrz napisu w cudzysłowie trzeba poprzedzić ukośnikiem: \". Zdjęcia leżą obok strony, więc sama nazwa pliku wystarczy jako ścieżka.

    sklep.php PHP zmienione: 1 linia
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'dane2');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Warzywniak</title>
        <link rel="stylesheet" href="styl2.css">
    </head>
    <body>
        <section id="banner-left">
            <h1>Internetowy sklep z eko-warzywami</h1>
        </section>
        <section id="banner-right">
            <ol>
                <li>warzywa</li>
                <li>owoce</li>
                <li><a href="https://terapiasokami.pl/" target="_blank">soki</a></li>
            </ol>
        </section>
        <section id="main">
            <?php
            $result = mysqli_query($connection, "SELECT nazwa, ilosc, opis, cena, zdjecie FROM produkty WHERE Rodzaje_id = 1 OR Rodzaje_id = 2;");
            while ($row = mysqli_fetch_row($result)) {
                echo '<section class="product">';
                echo "<img src=\"$row[4]\" alt=\"warzywniak\">";
                echo '</section>';
            }
            ?>
        </section>
        <footer>
            <form method="post">
                Nazwa: <input type="text" name="nazwa">
                Cena: <input type="text" name="cena">
                <input type="submit" value="Dodaj produkt">
            </form>
            Stronę wykonał: Bartosz Bryniarski
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (sklep.php)
  34. Krok 34: Skrypt 1: nazwa w nagłówku piątego stopnia

    ok. 2 min
    Polecenie z arkusza

    Nagłówek piątego stopnia z nazwą pobraną z bazy

    $row[0] - kolumna nazwa. Nagłówek ma już styl: zielony pasek z białym napisem pod zdjęciem.

    sklep.php PHP zmienione: 1 linia
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'dane2');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Warzywniak</title>
        <link rel="stylesheet" href="styl2.css">
    </head>
    <body>
        <section id="banner-left">
            <h1>Internetowy sklep z eko-warzywami</h1>
        </section>
        <section id="banner-right">
            <ol>
                <li>warzywa</li>
                <li>owoce</li>
                <li><a href="https://terapiasokami.pl/" target="_blank">soki</a></li>
            </ol>
        </section>
        <section id="main">
            <?php
            $result = mysqli_query($connection, "SELECT nazwa, ilosc, opis, cena, zdjecie FROM produkty WHERE Rodzaje_id = 1 OR Rodzaje_id = 2;");
            while ($row = mysqli_fetch_row($result)) {
                echo '<section class="product">';
                echo "<img src=\"$row[4]\" alt=\"warzywniak\">";
                echo "<h5>$row[0]</h5>";
                echo '</section>';
            }
            ?>
        </section>
        <footer>
            <form method="post">
                Nazwa: <input type="text" name="nazwa">
                Cena: <input type="text" name="cena">
                <input type="submit" value="Dodaj produkt">
            </form>
            Stronę wykonał: Bartosz Bryniarski
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (sklep.php)
  35. Krok 35: Skrypt 1: paragraf z opisem

    ok. 2 min
    Polecenie z arkusza

    Paragraf (akapit) o treści „opis: <opis>”

    $row[2] - kolumna opis. Część produktów ma pusty opis - wtedy w akapicie zostaje samo „opis: ”, jak przy moreli na obrazie 2.

    sklep.php PHP zmienione: 1 linia
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'dane2');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Warzywniak</title>
        <link rel="stylesheet" href="styl2.css">
    </head>
    <body>
        <section id="banner-left">
            <h1>Internetowy sklep z eko-warzywami</h1>
        </section>
        <section id="banner-right">
            <ol>
                <li>warzywa</li>
                <li>owoce</li>
                <li><a href="https://terapiasokami.pl/" target="_blank">soki</a></li>
            </ol>
        </section>
        <section id="main">
            <?php
            $result = mysqli_query($connection, "SELECT nazwa, ilosc, opis, cena, zdjecie FROM produkty WHERE Rodzaje_id = 1 OR Rodzaje_id = 2;");
            while ($row = mysqli_fetch_row($result)) {
                echo '<section class="product">';
                echo "<img src=\"$row[4]\" alt=\"warzywniak\">";
                echo "<h5>$row[0]</h5>";
                echo "<p>opis: $row[2]</p>";
                echo '</section>';
            }
            ?>
        </section>
        <footer>
            <form method="post">
                Nazwa: <input type="text" name="nazwa">
                Cena: <input type="text" name="cena">
                <input type="submit" value="Dodaj produkt">
            </form>
            Stronę wykonał: Bartosz Bryniarski
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (sklep.php)
  36. Krok 36: Skrypt 1: paragraf ze stanem magazynu

    ok. 2 min
    Polecenie z arkusza

    Paragraf o treści „na stanie: <ilosc>”

    $row[1] - kolumna ilosc, druga w zapytaniu.

    sklep.php PHP zmienione: 1 linia
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'dane2');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Warzywniak</title>
        <link rel="stylesheet" href="styl2.css">
    </head>
    <body>
        <section id="banner-left">
            <h1>Internetowy sklep z eko-warzywami</h1>
        </section>
        <section id="banner-right">
            <ol>
                <li>warzywa</li>
                <li>owoce</li>
                <li><a href="https://terapiasokami.pl/" target="_blank">soki</a></li>
            </ol>
        </section>
        <section id="main">
            <?php
            $result = mysqli_query($connection, "SELECT nazwa, ilosc, opis, cena, zdjecie FROM produkty WHERE Rodzaje_id = 1 OR Rodzaje_id = 2;");
            while ($row = mysqli_fetch_row($result)) {
                echo '<section class="product">';
                echo "<img src=\"$row[4]\" alt=\"warzywniak\">";
                echo "<h5>$row[0]</h5>";
                echo "<p>opis: $row[2]</p>";
                echo "<p>na stanie: $row[1]</p>";
                echo '</section>';
            }
            ?>
        </section>
        <footer>
            <form method="post">
                Nazwa: <input type="text" name="nazwa">
                Cena: <input type="text" name="cena">
                <input type="submit" value="Dodaj produkt">
            </form>
            Stronę wykonał: Bartosz Bryniarski
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (sklep.php)
  37. Krok 37: Skrypt 1: cena w nagłówku drugiego stopnia

    Kryteria:

    R.5.4 Skrypt 1 tworzy blok, w którym znajdują się elementy: obraz, nazwa w nagłówku h5, treść "opis: <opis>" i "na stanie: <ilosc>" w osobnych paragrafach oraz "<cena> zł" w nagłówku h2. Wartości wszystkich elementów zostały pobrane z bazy

    ok. 2 min
    Polecenie z arkusza

    Nagłówek drugiego stopnia o treści „<cena> zł”, gdzie pola umieszczone w nawiasach < > oznaczają dane pobrane z bazy danych

    $row[3] - kolumna cena, za nią spacja i „zł”. Blok produktu ma komplet: zdjęcie, nazwę w <h5>, opis i stan w osobnych akapitach i cenę w <h2> - wszystko z bazy (R.5.4). Pierwszy blok: gruszka, „opis: Klaps”, „na stanie: 20”, „2.56 zł”, jak na obrazie 2.

    sklep.php PHP zmienione: 1 linia
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'dane2');
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Warzywniak</title>
        <link rel="stylesheet" href="styl2.css">
    </head>
    <body>
        <section id="banner-left">
            <h1>Internetowy sklep z eko-warzywami</h1>
        </section>
        <section id="banner-right">
            <ol>
                <li>warzywa</li>
                <li>owoce</li>
                <li><a href="https://terapiasokami.pl/" target="_blank">soki</a></li>
            </ol>
        </section>
        <section id="main">
            <?php
            $result = mysqli_query($connection, "SELECT nazwa, ilosc, opis, cena, zdjecie FROM produkty WHERE Rodzaje_id = 1 OR Rodzaje_id = 2;");
            while ($row = mysqli_fetch_row($result)) {
                echo '<section class="product">';
                echo "<img src=\"$row[4]\" alt=\"warzywniak\">";
                echo "<h5>$row[0]</h5>";
                echo "<p>opis: $row[2]</p>";
                echo "<p>na stanie: $row[1]</p>";
                echo "<h2>$row[3] zł</h2>";
                echo '</section>';
            }
            ?>
        </section>
        <footer>
            <form method="post">
                Nazwa: <input type="text" name="nazwa">
                Cena: <input type="text" name="cena">
                <input type="submit" value="Dodaj produkt">
            </form>
            Stronę wykonał: Bartosz Bryniarski
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (sklep.php)
  38. Krok 38: Skrypt 2: odczyt danych z formularza

    ok. 2 min
    Polecenie z arkusza

    Działanie skryptu 2: Jeśli wypełniono formularz, pobiera z niego dane

    W bloku na górze pliku, pod połączeniem:

    • !empty($_POST['nazwa']) && !empty($_POST['cena']) - prawdziwe tylko wtedy, gdy oba pola wysłano i nie są puste. Przy zwykłym wejściu na stronę klucza w $_POST nie ma, a empty nie zgłasza wtedy ostrzeżenia,
    • $nazwa i $cena - wartości pól; kluczem w tablicy $_POST jest atrybut name pola.

    Podgląd: formularz wysłany z nazwą „jablko” i ceną „3.50” - skrypt odczytał dane, ale jeszcze nigdzie ich nie zapisał.

    sklep.php PHP zmienione: 4 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'dane2');
    if (!empty($_POST['nazwa']) && !empty($_POST['cena'])) {
        $nazwa = $_POST['nazwa'];
        $cena = $_POST['cena'];
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Warzywniak</title>
        <link rel="stylesheet" href="styl2.css">
    </head>
    <body>
        <section id="banner-left">
            <h1>Internetowy sklep z eko-warzywami</h1>
        </section>
        <section id="banner-right">
            <ol>
                <li>warzywa</li>
                <li>owoce</li>
                <li><a href="https://terapiasokami.pl/" target="_blank">soki</a></li>
            </ol>
        </section>
        <section id="main">
            <?php
            $result = mysqli_query($connection, "SELECT nazwa, ilosc, opis, cena, zdjecie FROM produkty WHERE Rodzaje_id = 1 OR Rodzaje_id = 2;");
            while ($row = mysqli_fetch_row($result)) {
                echo '<section class="product">';
                echo "<img src=\"$row[4]\" alt=\"warzywniak\">";
                echo "<h5>$row[0]</h5>";
                echo "<p>opis: $row[2]</p>";
                echo "<p>na stanie: $row[1]</p>";
                echo "<h2>$row[3] zł</h2>";
                echo '</section>';
            }
            ?>
        </section>
        <footer>
            <form method="post">
                Nazwa: <input type="text" name="nazwa">
                Cena: <input type="text" name="cena">
                <input type="submit" value="Dodaj produkt">
            </form>
            Stronę wykonał: Bartosz Bryniarski
        </footer>
    </body>
    </html>

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

  39. Krok 39: Skrypt 2: zapis produktu zmodyfikowanym zapytaniem 4

    Kryteria:

    R.5.6 Skrypt 2 wysyła do bazy zmodyfikowane zapytanie 4, pola nazwa i cena są pobrane z formularza. Należy sprawdzić w bazie danych czy wiersz został dodany do bazy (uwaga: dodany rekord może być widoczny na 2 stronie w phpMyAdmin)

    ok. 3 min
    Polecenie z arkusza

    Wysyła zmodyfikowane zapytanie 4 do bazy danych. Dane nazwa i cena są pobrane z formularza, reszta danych zapytania 4 nie ulega zmianie

    Zapytanie 4 z kwerendy.txt, w którym w miejsce 'owoc1' stoi '$nazwa' (napis, więc w apostrofach), a w miejsce 9.99 - $cena (liczba, bez apostrofów). Reszta bez zmian: owoce (1), warzywa-rolnik (4), ilość 10, pusty opis, zdjęcie owoce.jpg. Całe zapytanie jest w cudzysłowie, więc PHP podstawia wartości zmiennych.

    Sprawdzenie, jak każe klucz (R.5.6): po wpisaniu „jablko” i „3.50” w tabeli produkty przybył wiersz z tą nazwą i ceną. Zapis stoi na górze pliku, przed skryptem 1, więc nowy produkt widać od razu - na końcu listy.

    Typowe błędy

    Nazwa z literami „ł”, „ś” itp. (np. „jabłko”) kończy się błędem „Incorrect string value”: tabele z archiwum mają kodowanie latin1, które tych liter nie zna, a MariaDB z XAMPP działa w trybie ścisłym. To cecha bazy z archiwum - klucz sprawdza import pliku bez zmian, więc tabeli nie przerabiaj, a zapis sprawdź nazwą bez polskich liter.

    sklep.php PHP zmienione: 1 linia
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'dane2');
    if (!empty($_POST['nazwa']) && !empty($_POST['cena'])) {
        $nazwa = $_POST['nazwa'];
        $cena = $_POST['cena'];
        mysqli_query($connection, "INSERT INTO produkty (Rodzaje_id, Producenci_id, nazwa, ilosc, opis, cena, zdjecie) VALUES (1, 4, '$nazwa', 10, '', $cena, 'owoce.jpg');");
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Warzywniak</title>
        <link rel="stylesheet" href="styl2.css">
    </head>
    <body>
        <section id="banner-left">
            <h1>Internetowy sklep z eko-warzywami</h1>
        </section>
        <section id="banner-right">
            <ol>
                <li>warzywa</li>
                <li>owoce</li>
                <li><a href="https://terapiasokami.pl/" target="_blank">soki</a></li>
            </ol>
        </section>
        <section id="main">
            <?php
            $result = mysqli_query($connection, "SELECT nazwa, ilosc, opis, cena, zdjecie FROM produkty WHERE Rodzaje_id = 1 OR Rodzaje_id = 2;");
            while ($row = mysqli_fetch_row($result)) {
                echo '<section class="product">';
                echo "<img src=\"$row[4]\" alt=\"warzywniak\">";
                echo "<h5>$row[0]</h5>";
                echo "<p>opis: $row[2]</p>";
                echo "<p>na stanie: $row[1]</p>";
                echo "<h2>$row[3] zł</h2>";
                echo '</section>';
            }
            ?>
        </section>
        <footer>
            <form method="post">
                Nazwa: <input type="text" name="nazwa">
                Cena: <input type="text" name="cena">
                <input type="submit" value="Dodaj produkt">
            </form>
            Stronę wykonał: Bartosz Bryniarski
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (sklep.php)
  40. Krok 40: Zamknięcie połączenia z bazą

    Kryteria:

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

    ok. 1 min
    Polecenie z arkusza

    Po wykonaniu operacji na bazie danych skrypt zamyka połączenie z serwerem.

    mysqli_close($connection) po pętli skryptu 1. Skrypt 2 wykonał się wcześniej, na górze pliku, a formularz w stopce nie korzysta już z bazy - więc zamknięcie jest ostatnią operacją na bazie (R.5.2). Strona spełnia wszystkie kryteria arkusza. Dalej trzy kroki o zabezpieczeniach - klucz ich nie wymaga.

    Typowe błędy

    mysqli_close w górnym bloku, zaraz po zapisie, sprawia, że skrypt 1 dostaje zamknięte połączenie i PHP kończy się błędem.

    sklep.php PHP zmienione: 1 linia
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'dane2');
    if (!empty($_POST['nazwa']) && !empty($_POST['cena'])) {
        $nazwa = $_POST['nazwa'];
        $cena = $_POST['cena'];
        mysqli_query($connection, "INSERT INTO produkty (Rodzaje_id, Producenci_id, nazwa, ilosc, opis, cena, zdjecie) VALUES (1, 4, '$nazwa', 10, '', $cena, 'owoce.jpg');");
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Warzywniak</title>
        <link rel="stylesheet" href="styl2.css">
    </head>
    <body>
        <section id="banner-left">
            <h1>Internetowy sklep z eko-warzywami</h1>
        </section>
        <section id="banner-right">
            <ol>
                <li>warzywa</li>
                <li>owoce</li>
                <li><a href="https://terapiasokami.pl/" target="_blank">soki</a></li>
            </ol>
        </section>
        <section id="main">
            <?php
            $result = mysqli_query($connection, "SELECT nazwa, ilosc, opis, cena, zdjecie FROM produkty WHERE Rodzaje_id = 1 OR Rodzaje_id = 2;");
            while ($row = mysqli_fetch_row($result)) {
                echo '<section class="product">';
                echo "<img src=\"$row[4]\" alt=\"warzywniak\">";
                echo "<h5>$row[0]</h5>";
                echo "<p>opis: $row[2]</p>";
                echo "<p>na stanie: $row[1]</p>";
                echo "<h2>$row[3] zł</h2>";
                echo '</section>';
            }
            mysqli_close($connection);
            ?>
        </section>
        <footer>
            <form method="post">
                Nazwa: <input type="text" name="nazwa">
                Cena: <input type="text" name="cena">
                <input type="submit" value="Dodaj produkt">
            </form>
            Stronę wykonał: Bartosz Bryniarski
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (sklep.php)
  41. Etap 5: Dobre praktyki (poza kluczem)

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

    Krok 41: Zabezpieczenie: cena z przecinkiem i walidacja liczby

    ok. 3 min

    Pole ceny jest zwykłym polem tekstowym, a po polsku cenę pisze się z przecinkiem. Sprawdzone na wersji z poprzednich kroków: cena 4,50 zamienia zapytanie w VALUES (1, 4, 'jablko', 10, '', 4,50, 'owoce.jpg') - dla SQL to osiem wartości zamiast siedmiu („Column count doesn't match value count”). W PHP 8.1 i nowszych kończy się to wyjątkiem mysqli_sql_exception i zamiast strony widać „Fatal error”, a w PHP 7.4 z XAMPP produkt po prostu się nie zapisuje - bez żadnego komunikatu.

    Dwie linie naprawiają ten przypadek:

    • str_replace(',', '.', ...) zamienia przecinek na kropkę, a trim zdejmuje spacje z brzegów,
    • is_numeric($cena) przepuszcza do zapytania tylko liczbę - napis w rodzaju „tanio” albo fragment kodu SQL w polu ceny nie dotrze już do bazy.

    Stara linia zostaje w komentarzu. Podgląd: cena „4,50” zapisała się jako 4.5.

    sklep.php PHP zmienione: 5 linii
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'dane2');
    if (!empty($_POST['nazwa']) && !empty($_POST['cena'])) {
        $nazwa = $_POST['nazwa'];
        // $cena = $_POST['cena'];
        $cena = str_replace(',', '.', trim($_POST['cena']));
        if (is_numeric($cena)) {
            mysqli_query($connection, "INSERT INTO produkty (Rodzaje_id, Producenci_id, nazwa, ilosc, opis, cena, zdjecie) VALUES (1, 4, '$nazwa', 10, '', $cena, 'owoce.jpg');");
        }
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Warzywniak</title>
        <link rel="stylesheet" href="styl2.css">
    </head>
    <body>
        <section id="banner-left">
            <h1>Internetowy sklep z eko-warzywami</h1>
        </section>
        <section id="banner-right">
            <ol>
                <li>warzywa</li>
                <li>owoce</li>
                <li><a href="https://terapiasokami.pl/" target="_blank">soki</a></li>
            </ol>
        </section>
        <section id="main">
            <?php
            $result = mysqli_query($connection, "SELECT nazwa, ilosc, opis, cena, zdjecie FROM produkty WHERE Rodzaje_id = 1 OR Rodzaje_id = 2;");
            while ($row = mysqli_fetch_row($result)) {
                echo '<section class="product">';
                echo "<img src=\"$row[4]\" alt=\"warzywniak\">";
                echo "<h5>$row[0]</h5>";
                echo "<p>opis: $row[2]</p>";
                echo "<p>na stanie: $row[1]</p>";
                echo "<h2>$row[3] zł</h2>";
                echo '</section>';
            }
            mysqli_close($connection);
            ?>
        </section>
        <footer>
            <form method="post">
                Nazwa: <input type="text" name="nazwa">
                Cena: <input type="text" name="cena">
                <input type="submit" value="Dodaj produkt">
            </form>
            Stronę wykonał: Bartosz Bryniarski
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (sklep.php)
  42. Krok 42: Zabezpieczenie: zapytanie przygotowane dla INSERT

    ok. 4 min
    Polecenie z arkusza

    Wysyła zmodyfikowane zapytanie 4 do bazy danych. Dane nazwa i cena są pobrane z formularza, reszta danych zapytania 4 nie ulega zmianie

    Walidacja ceny nie chroni nazwy - ta nadal trafia do zapytania wprost (SQL injection). Sprawdzone na wersji z poprzedniego kroku:

    • nazwa x', 10, '', 5, 'owoce.jpg'), (1, 4, 'gratis z ceną 0.00 zamyka napis i dokleja do VALUES drugi wiersz - w sklepie pojawia się dodatkowy produkt „gratis” za 0 zł, którego nikt nie dodawał,
    • zwykła nazwa z apostrofem, np. Jablko 'Ligol', rozbija składnię zapytania - PHP 8.1 i nowsze zgłaszają wyjątek mysqli_sql_exception i strona się nie wyświetla.

    Lekarstwo to zapytanie przygotowane: mysqli_prepare ze znakami ? w miejscu nazwy i ceny, mysqli_stmt_bind_param z typami 'sd' (napis i liczba zmiennoprzecinkowa) i mysqli_stmt_execute. Baza dostaje treść zapytania i dane osobno, więc dane nigdy nie staną się kodem SQL. Podgląd: ten sam „atak” zapisał się jako jeden produkt o dziwnej nazwie - bez „gratisu”. Stara linia zostaje w komentarzu.

    sklep.php PHP zmienione: 4 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'dane2');
    if (!empty($_POST['nazwa']) && !empty($_POST['cena'])) {
        $nazwa = $_POST['nazwa'];
        // $cena = $_POST['cena'];
        $cena = str_replace(',', '.', trim($_POST['cena']));
        if (is_numeric($cena)) {
            // mysqli_query($connection, "INSERT INTO produkty (Rodzaje_id, Producenci_id, nazwa, ilosc, opis, cena, zdjecie) VALUES (1, 4, '$nazwa', 10, '', $cena, 'owoce.jpg');");
            $statement = mysqli_prepare($connection, "INSERT INTO produkty (Rodzaje_id, Producenci_id, nazwa, ilosc, opis, cena, zdjecie) VALUES (1, 4, ?, 10, '', ?, 'owoce.jpg');");
            mysqli_stmt_bind_param($statement, 'sd', $nazwa, $cena);
            mysqli_stmt_execute($statement);
        }
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Warzywniak</title>
        <link rel="stylesheet" href="styl2.css">
    </head>
    <body>
        <section id="banner-left">
            <h1>Internetowy sklep z eko-warzywami</h1>
        </section>
        <section id="banner-right">
            <ol>
                <li>warzywa</li>
                <li>owoce</li>
                <li><a href="https://terapiasokami.pl/" target="_blank">soki</a></li>
            </ol>
        </section>
        <section id="main">
            <?php
            $result = mysqli_query($connection, "SELECT nazwa, ilosc, opis, cena, zdjecie FROM produkty WHERE Rodzaje_id = 1 OR Rodzaje_id = 2;");
            while ($row = mysqli_fetch_row($result)) {
                echo '<section class="product">';
                echo "<img src=\"$row[4]\" alt=\"warzywniak\">";
                echo "<h5>$row[0]</h5>";
                echo "<p>opis: $row[2]</p>";
                echo "<p>na stanie: $row[1]</p>";
                echo "<h2>$row[3] zł</h2>";
                echo '</section>';
            }
            mysqli_close($connection);
            ?>
        </section>
        <footer>
            <form method="post">
                Nazwa: <input type="text" name="nazwa">
                Cena: <input type="text" name="cena">
                <input type="submit" value="Dodaj produkt">
            </form>
            Stronę wykonał: Bartosz Bryniarski
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (sklep.php)
  43. Krok 43: Zabezpieczenie: htmlspecialchars przy wyświetlaniu nazwy i opisu

    ok. 3 min
    Polecenie z arkusza

    Nagłówek piątego stopnia z nazwą pobraną z bazy

    Zapytanie przygotowane chroni bazę, ale nazwa z formularza wraca potem na stronę - i każdy, kto otworzy sklep, zobaczy ją jako kod HTML. Sprawdzone: produkt o nazwie <b>PROMOCJA</b> wyświetlał się jako pogrubiony napis. Tak samo przeszedłby znacznik <script> - kod JavaScript uruchamiałby się u każdego klienta sklepu (stored XSS).

    Funkcja htmlspecialchars zamienia <, >, & i cudzysłowy na encje (np. < na &lt;), więc przeglądarka pokazuje znaki dosłownie. Stosuje się ją przy wypisywaniu tekstów, które mogły przyjść od użytkownika - tutaj nazwy i opisu. Ilość i cena to liczby z kolumn typu double, więc kodu HTML w nich nie ma. Podgląd: nazwę widać dosłownie, ze znacznikami.

    sklep.php PHP zmienione: 4 linie
    <?php
    $connection = mysqli_connect('localhost', 'root', '', 'dane2');
    if (!empty($_POST['nazwa']) && !empty($_POST['cena'])) {
        $nazwa = $_POST['nazwa'];
        // $cena = $_POST['cena'];
        $cena = str_replace(',', '.', trim($_POST['cena']));
        if (is_numeric($cena)) {
            // mysqli_query($connection, "INSERT INTO produkty (Rodzaje_id, Producenci_id, nazwa, ilosc, opis, cena, zdjecie) VALUES (1, 4, '$nazwa', 10, '', $cena, 'owoce.jpg');");
            $statement = mysqli_prepare($connection, "INSERT INTO produkty (Rodzaje_id, Producenci_id, nazwa, ilosc, opis, cena, zdjecie) VALUES (1, 4, ?, 10, '', ?, 'owoce.jpg');");
            mysqli_stmt_bind_param($statement, 'sd', $nazwa, $cena);
            mysqli_stmt_execute($statement);
        }
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <title>Warzywniak</title>
        <link rel="stylesheet" href="styl2.css">
    </head>
    <body>
        <section id="banner-left">
            <h1>Internetowy sklep z eko-warzywami</h1>
        </section>
        <section id="banner-right">
            <ol>
                <li>warzywa</li>
                <li>owoce</li>
                <li><a href="https://terapiasokami.pl/" target="_blank">soki</a></li>
            </ol>
        </section>
        <section id="main">
            <?php
            $result = mysqli_query($connection, "SELECT nazwa, ilosc, opis, cena, zdjecie FROM produkty WHERE Rodzaje_id = 1 OR Rodzaje_id = 2;");
            while ($row = mysqli_fetch_row($result)) {
                echo '<section class="product">';
                echo "<img src=\"$row[4]\" alt=\"warzywniak\">";
                // echo "<h5>$row[0]</h5>";
                // echo "<p>opis: $row[2]</p>";
                echo '<h5>' . htmlspecialchars($row[0]) . '</h5>';
                echo '<p>opis: ' . htmlspecialchars($row[2]) . '</p>';
                echo "<p>na stanie: $row[1]</p>";
                echo "<h2>$row[3] zł</h2>";
                echo '</section>';
            }
            mysqli_close($connection);
            ?>
        </section>
        <footer>
            <form method="post">
                Nazwa: <input type="text" name="nazwa">
                Cena: <input type="text" name="cena">
                <input type="submit" value="Dodaj produkt">
            </form>
            Stronę wykonał: Bartosz Bryniarski
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (sklep.php)
  44. Etap 6: Oddanie pracy

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

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

    ok. 5 min
    Polecenie z arkusza

    Po zakończeniu pracy utwórz w folderze z numerem PESEL lub w przypadku jego braku w folderze z innym numerem, którym został podpisany arkusz plik tekstowy o nazwie przeglądarka.txt. Zapisz w nim nazwę przeglądarki internetowej, w której weryfikowałeś poprawność działania witryny. Nagraj płytę z rezultatami pracy.

    W przeglądarka.txt wpisujesz nazwę przeglądarki, w której sprawdzasz stronę przez http://localhost/….

    W folderze z numerem zdającego muszą być: arbuz.jpg, egzamin.jpg, gruszka.jpg, koper.jpg, kw1.png - kw4.png, kwerendy.txt, marchew.jpg, morela.jpg, owoce.jpg, papaja.jpg, papryka.jpg, przeglądarka.txt, sklep.php, styl2.css i ziemniak.jpg. Nagraj płytę, sprawdź jej odczyt, opisz ją numerem PESEL i zostaw na stanowisku.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

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

Podgląd

To jedno z możliwych rozwiązań. Egzaminator ocenia spełnienie kryteriów z klucza odpowiedzi, a nie zgodność z tym kodem - inna, poprawna droga też daje punkty.

Autor rozwiązania: . Licencja CC BY-NC 4.0 - kopiowanie i przeróbki do celów niekomercyjnych, z podaniem autora i źródła (link do tej strony) (zasady wykorzystania). Treść arkusza i klucza oceniania pochodzi z CKE.

Potwierdzenie

Informacja