Przejdź do głównej treści
E.14-03-20.06-SGCzerwiec 2020 · 34 kroki · ok. 78 min · 36 kryteriów z klucza

Rozwiązanie krok po kroku: Kwiaciarnie i wyszukiwarka po mieście (Nowoczesny kod (flex, PDO))

To drugi sposób rozwiązania tego samego arkusza. Wynik jest ten sam - baza kwiaciarnia z dwiema tabelami i trzema zapytaniami, zdjęcia kwiatów 190 × 190 px oraz strony index.html i znajdz.php z wyszukiwarką kwiaciarni - i tak samo spełnia każde kryterium klucza. Inna jest droga:

  • flex zamiast float: dwa panele stoją we wspólnym znaczniku <main>, który układa je obok siebie, a stopka nie potrzebuje clear. Szerokości 20% i 80% zostają na panelach, bo tam sprawdza je klucz z 2020 roku,
  • zmienne CSS dla kolorów z arkusza, odnośniki w znaczniku <nav> ustawione jeden pod drugim stylem zamiast <br>,
  • PHP na PDO na górze pliku znajdz.php, przed HTML-em - pod formularzem zostaje tylko pętla wypisująca wyniki,
  • zapytanie przygotowane z parametrem :city, wpis bez spacji z brzegów, wyniki przez htmlspecialchars, wpisana miejscowość wraca do pola i komunikat, gdy w mieście nie ma kwiaciarni.

To arkusz E.14 z czerwca 2020 - kwalifikacji formuły 2012, którą zastąpiła INF.03. Zdjęcia pochodzą z oryginalnego archiwum CKE egzamin3.zip (załącznik na stronie arkusza), zapytania są wykonane na MariaDB 10.4, a podgląd od kroków z PHP to wynik prawdziwego uruchomienia kodu w PHP 8.2.

Jak liczą się punkty. Klucz ma 36 kryteriów (R.1.1 - R.5.5), każde za 1 punkt. Próg zdania części praktycznej to 75%, czyli 27 punktów. Przy każdym kroku jest napisane, które kryteria spełnia; klucz nie narzuca biblioteki do obsługi bazy.

  1. Etap 1: Baza danych

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

    Krok 1: Folder zdającego, archiwum i baza kwiaciarnia

    ok. 3 min
    Polecenie z arkusza

    Na pulpicie utwórz folder i nazwij go Twoim numerem PESEL. Wszystkie pliki utworzone w czasie egzaminu umieść w tym folderze. Za pomocą wizualnych elementów (kreatorów) przystawki phpMyAdmin programu XAMPP utwórz bazę danych według wytycznych: Nazwa bazy kwiaciarnia

    Folder na pulpicie nazwany numerem PESEL (w tym rozwiązaniu w jego miejscu stoi Bartosz Bryniarski), do niego zdjęcia z archiwum egzamin3.zip rozpakowanego z hasłem 5T2k@W$2n. W phpMyAdmin nowa baza kwiaciarnia z porównywaniem utf8mb4_polish_ci - pełne UTF-8 i polska kolejność alfabetyczna.

    Arkusz każe utworzyć tabele kreatorem. Formularz kreatora wypełniasz tak, żeby powstał SQL pokazany w kolejnych krokach - phpMyAdmin wyświetla go po zapisie.

  2. Krok 2: Tabela kwiaciarnie

    Kryteria:

    R.1.4 Tabela kwiaciarnie ma pola nazwa, miasto i ulica dowolnego typu tekstowego

    ok. 3 min
    Polecenie z arkusza

    Tabela kwiaciarnie z autonumerowanym kluczem podstawowym id_kwiaciarni

    CREATE TABLE kwiaciarnie (
        id_kwiaciarni INT UNSIGNED NOT NULL AUTO_INCREMENT,
        nazwa VARCHAR(50) NOT NULL,
        miasto VARCHAR(50) NOT NULL,
        ulica VARCHAR(50) NOT NULL,
        PRIMARY KEY (id_kwiaciarni)
    );
    
    • INT UNSIGNED - numer kwiaciarni nigdy nie jest ujemny, więc zakres dodatnich liczb jest dwa razy większy,
    • NOT NULL przy nazwie, mieście i ulicy - kwiaciarnia bez adresu nie ma sensu, baza odrzuci taki wiersz,
    • pola tekstowe VARCHAR(50) (R.1.4), w kreatorze: zaznaczone A_I i indeks PRIMARY przy id_kwiaciarni.
  3. Krok 3: Tabela zamowienia

    Kryteria: ,

    R.1.2 Tabela kwiaciarnie ma klucz podstawowy id_kwiaciarni oraz tabela zamowienia ma klucz podstawowy id_zam, oba klucze są autoinkrementowane

    R.1.3 Tabela zamowienia ma pola: id_kwiaciarni dowolnego typu całkowitego, data typu date, kwiaty typu tekstowego i cena dowolnego typu liczbowego

    ok. 3 min
    Polecenie z arkusza

    Tabela zamowienia z autonumerowanym kluczem podstawowym id_zam. Pola oraz dane obu tabel znajdują się w tabelach 1 i 2, należy je wprowadzić do tabel

    CREATE TABLE zamowienia (
        id_zam INT UNSIGNED NOT NULL AUTO_INCREMENT,
        id_kwiaciarni INT UNSIGNED NOT NULL,
        data DATE NOT NULL,
        kwiaty VARCHAR(50) NOT NULL,
        cena DECIMAL(8, 2) NOT NULL,
        PRIMARY KEY (id_zam)
    );
    
    • data DATE - typ daty, którego wymaga klucz (R.1.3),
    • cena DECIMAL(8, 2) - kwoty pieniędzy zapisuje się typem dziesiętnym o stałej liczbie miejsc po przecinku: 125.00. Ceny w arkuszu są całkowite, ale w prawdziwej kwiaciarni róża kosztuje 12,50 zł; typ FLOAT zaokrąglałby grosze. Klucz przyjmuje dowolny typ liczbowy,
    • id_kwiaciarni INT UNSIGNED - ten sam typ co klucz w tabeli kwiaciarnie, na którą wskazuje.

    Obie tabele mają autonumerowane klucze podstawowe (R.1.2).

  4. Krok 4: Dane obu tabel

    Kryteria:

    R.1.5 Tabele zamowienia i kwiaciarnie wypełniono zgodnie z tabelą 1 i 2 arkusza

    ok. 4 min
    Polecenie z arkusza

    Pola oraz dane obu tabel znajdują się w tabelach 1 i 2, należy je wprowadzić do tabel

    INSERT INTO kwiaciarnie (nazwa, miasto, ulica) VALUES
    ('Astra', 'Warszawa', 'Sobieskiego'),
    ('Melisa', 'Malbork', 'Konopnickiej');
    INSERT INTO zamowienia (id_kwiaciarni, data, kwiaty, cena) VALUES
    (1, '2017-01-16', 'róża', 250),
    (1, '2017-01-10', 'gerbera', 120),
    (2, '2017-01-06', 'róża', 200),
    (2, '2017-01-09', 'goździk', 170),
    (1, '2017-01-07', 'róża', 125),
    (1, '2017-01-11', 'róża', 75);
    

    Numerów id nie trzeba podawać - autonumerowanie nada je kolejno od 1, dokładnie jak w tabelach arkusza. Daty w formacie RRRR-MM-DD, kwiaty z polskimi literami (R.1.5). Podgląd: tabela zamowienia (cena w wersji nowoczesnej wyświetla się jako 125.00).

    Podgląd strony po tym kroku
    Podgląd strony po kroku 4
  5. Krok 5: Kwerenda 1: kwiaciarnie z Malborka

    Kryteria: ,

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

    R.1.7 Zapisano zapytanie 1 wybierające jedynie pole nazwa i ulica dla kwiaciarni z miasta Malbork. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT nazwa, ulica FROM kwiaciarnie WHERE miasto="Malbork"; oraz na zrzucie ekranu widoczne są jedynie dwa pola: nazwa, ulica; wartości Melisa, Konopnickiej

    ok. 3 min
    Polecenie z arkusza

    Kwerenda 1: wybierająca jedynie pole nazwa i ulica dla kwiaciarni z miasta Malbork

    Każda klauzula w osobnej linii - łatwiej czytać i porównać z poleceniem: dwa pola, tabela kwiaciarnie, warunek na mieście. Wynik: Melisa, Konopnickiej. Zapytanie do kwerendy.txt, zrzut całego ekranu do kw1.jpg (R.1.6, R.1.7). To samo zapytanie, z parametrem w miejscu miasta, wykona skrypt PHP.

    kwerendy.txt Tekst nowy plik
    -- Kwerenda 1
    SELECT nazwa, ulica
    FROM kwiaciarnie
    WHERE miasto = 'Malbork';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 5
  6. Krok 6: Kwerenda 2: zamówienie z 7 stycznia 2017

    Kryteria:

    R.1.8 Zapisano zapytanie 2 wybierające jedynie id zamówienia, złożonego z datą 7 stycznia 2017 r. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT id_zam FROM zamowienia WHERE data="2017.01.07"; oraz na zrzucie ekranu widoczne jest jedynie pole id_zam z jedną wartością: 5

    ok. 2 min
    Polecenie z arkusza

    Kwerenda 2: wybierająca jedynie id zamówienia, złożonego z datą 7 stycznia 2017 r.

    Data w zapytaniu w formacie ISO 2017-01-07 - tym samym, w którym zapisuje ją pole DATE. Wynik: zamówienie 5, zrzut kw2.jpg (R.1.8).

    kwerendy.txt Tekst zmienione: 5 linii
    -- Kwerenda 1
    SELECT nazwa, ulica
    FROM kwiaciarnie
    WHERE miasto = 'Malbork';
    
    -- Kwerenda 2
    SELECT id_zam
    FROM zamowienia
    WHERE data = '2017-01-07';
    Podgląd strony po tym kroku
    Podgląd strony po kroku 6
  7. Krok 7: Kwerenda 3: liczba zamówień każdej kwiaciarni

    Kryteria:

    R.1.9 Zapisano zapytanie 3 wybierające jedynie pole id_kwiaciarni oraz liczące liczbę zamówień (wierszy) dla poszczególnych kwiaciarni. W pliku z kwerendami lub na zrzucie istnieje zapis np.: SELECT id_kwiaciarni, COUNT(id_kwiaciarni) FROM zamowienia GROUP BY id_kwiaciarni; oraz na zrzucie ekranu widoczne są dwa wiersze o wartościach 1, 4 oraz 2, 2; odpowiadają one polom id_kwiaciarni i policzonej liczbie kwiaciarni

    ok. 3 min
    Polecenie z arkusza

    Kwerenda 3: wybierająca jedynie pole id_kwiaciarni oraz licząca liczbę zamówień (wierszy) dla poszczególnych kwiaciarni

    GROUP BY id_kwiaciarni dzieli zamówienia na grupy, a COUNT(*) liczy wiersze każdej grupy. AS liczba_zamowien nadaje kolumnie wyniku czytelną nazwę zamiast COUNT(*) - przydaje się, gdy wynik odczytuje skrypt. Wynik: 1, 4 i 2, 2, zrzut kw3.jpg (R.1.9).

    kwerendy.txt Tekst zmienione: 5 linii
    -- Kwerenda 1
    SELECT nazwa, ulica
    FROM kwiaciarnie
    WHERE miasto = 'Malbork';
    
    -- Kwerenda 2
    SELECT id_zam
    FROM zamowienia
    WHERE data = '2017-01-07';
    
    -- Kwerenda 3
    SELECT id_kwiaciarni, COUNT(*) AS liczba_zamowien
    FROM zamowienia
    GROUP BY id_kwiaciarni;
    Podgląd strony po tym kroku
    Podgląd strony po kroku 7
  8. Krok 8: Eksport bazy do pliku kwiaciarnia.sql

    Kryteria:

    R.1.1 Wykonano eksport bazy lub tabel z bazy do pliku kwiaciarnia.sql . Czas generowania wpisany w nagłówku pliku jest zgodny z datą i godziną egzaminu

    ok. 2 min
    Polecenie z arkusza

    Wyeksportuj tabele bazy danych do pliku kwiaciarnia.sql

    Zaznaczona baza kwiaciarnia, karta Eksport, format SQL. Czas generowania w nagłówku pliku musi się zgadzać z datą i godziną egzaminu (R.1.1). Plik w archiwum rozwiązania to eksport z wersji podstawowej (mysqldump, bez linii z czasem generowania).

    kwiaciarnia.sql SQL nowy plik
    -- MariaDB dump 10.19  Distrib 10.4.27-MariaDB, for Win64 (AMD64)
    --
    -- Host: localhost    Database: kwiaciarnia
    -- ------------------------------------------------------
    -- Server version	10.4.27-MariaDB
    
    /*!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 */;
    /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
    /*!40103 SET TIME_ZONE='+00:00' */;
    /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
    /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
    /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
    /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
    
    --
    -- Table structure for table `kwiaciarnie`
    --
    
    DROP TABLE IF EXISTS `kwiaciarnie`;
    /*!40101 SET @saved_cs_client     = @@character_set_client */;
    /*!40101 SET character_set_client = utf8 */;
    CREATE TABLE `kwiaciarnie` (
      `id_kwiaciarni` int(11) NOT NULL AUTO_INCREMENT,
      `nazwa` varchar(50) DEFAULT NULL,
      `miasto` varchar(50) DEFAULT NULL,
      `ulica` varchar(50) DEFAULT NULL,
      PRIMARY KEY (`id_kwiaciarni`)
    ) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_polish_ci;
    /*!40101 SET character_set_client = @saved_cs_client */;
    
    --
    -- Dumping data for table `kwiaciarnie`
    --
    
    LOCK TABLES `kwiaciarnie` WRITE;
    /*!40000 ALTER TABLE `kwiaciarnie` DISABLE KEYS */;
    INSERT INTO `kwiaciarnie` VALUES (1,'Astra','Warszawa','Sobieskiego'),(2,'Melisa','Malbork','Konopnickiej');
    /*!40000 ALTER TABLE `kwiaciarnie` ENABLE KEYS */;
    UNLOCK TABLES;
    
    --
    -- Table structure for table `zamowienia`
    --
    
    DROP TABLE IF EXISTS `zamowienia`;
    /*!40101 SET @saved_cs_client     = @@character_set_client */;
    /*!40101 SET character_set_client = utf8 */;
    CREATE TABLE `zamowienia` (
      `id_zam` int(11) NOT NULL AUTO_INCREMENT,
      `id_kwiaciarni` int(11) DEFAULT NULL,
      `data` date DEFAULT NULL,
      `kwiaty` varchar(50) DEFAULT NULL,
      `cena` int(11) DEFAULT NULL,
      PRIMARY KEY (`id_zam`)
    ) ENGINE=InnoDB AUTO_INCREMENT=7 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_polish_ci;
    /*!40101 SET character_set_client = @saved_cs_client */;
    
    --
    -- Dumping data for table `zamowienia`
    --
    
    LOCK TABLES `zamowienia` WRITE;
    /*!40000 ALTER TABLE `zamowienia` DISABLE KEYS */;
    INSERT INTO `zamowienia` VALUES (1,1,'2017-01-16','róża',250),(2,1,'2017-01-10','gerbera',120),(3,2,'2017-01-06','róża',200),(4,2,'2017-01-09','goździk',170),(5,1,'2017-01-07','róża',125),(6,1,'2017-01-11','róża',75);
    /*!40000 ALTER TABLE `zamowienia` ENABLE KEYS */;
    UNLOCK TABLES;
    /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
    
    /*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
    /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
    /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
    /*!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 */;
    /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
    
    -- Dump completed
  9. Etap 2: Grafika

    Krok 9 · po tym etapie masz 10 z 36 kryteriów (pkt)

    Krok 9: Zdjęcia 1:1, 190 × 190 px, JPEG

    Kryteria:

    R.2.1 Zapisano przynajmniej jeden obraz z roza, gerbera, gozdzik w formacie JPEG, o rozmiarze 190 px na 190 px (± 2 px, zachowując proporcję 1:1)

    ok. 5 min
    Polecenie z arkusza

    Wytyczne odnośnie grafiki zapisanej w plikach roza.png, gerbera.png, gozdzik.png: obrazy przycięte (wykadrowane) do proporcji 1:1, rozmiar 190 px na 190 px, format JPEG

    Grafika jest taka sama jak w wersji podstawowej - wymagania arkusza nie zostawiają tu wyboru. W GIMP-ie dla każdego zdjęcia: Kadrowanie z proporcjami 1:1 wyśrodkowane na kwiecie, Obraz → Skaluj obraz do 190 × 190 px i Plik → Eksportuj jako gerbera.jpg, gozdzik.jpg, roza.jpg (R.2.1).

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

    Kroki 10-17 · po tym etapie masz 18 z 36 kryteriów (pkt)

    Krok 10: Szkielet strony i kodowanie znaków

    ok. 1 min
    Polecenie z arkusza

    Witryna internetowa składa się ze stron index.html i znajdz.php. Standard kodowania polskich znaków

    <meta charset="UTF-8"> i plik zapisany w UTF-8. Druga linia viewport mówi telefonom, żeby nie udawały szerokiego ekranu - arkusz jej nie wymaga, ale należy do każdej współczesnej strony.

    index.html HTML nowy plik
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  11. Krok 11: Tytuł i arkusz stylów

    Kryteria: ,

    R.2.3 Nadano tytuł przynajmniej jednej stronie "Kwiaty”

    R.3.5 Przynajmniej jedna 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 zdający źle przypisał co najmniej jeden selektor do bloku (np. kropka zamiast #)

    ok. 1 min
    Polecenie z arkusza

    Tytuł strony: „Kwiaty”. Połączenie z zewnętrznym arkuszem stylów o nazwie styl.css

    <title>Kwiaty</title> (R.2.3) i <link rel="stylesheet" href="styl.css"> (R.3.5). Całe formatowanie trafi do pliku stylów.

    index.html HTML zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Kwiaty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  12. Krok 12: Baner, panele w znaczniku main i stopka

    Kryteria:

    R.2.4 Układ przynajmniej jednej strony zdefiniowano dzieląc ją na bloki: baner, panel lewy i prawy oraz stopkę za pomocą znaczników sekcji

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki: baner, panel lewy i prawy oraz stopkę za pomocą znaczników sekcji

    <header>, dwa znaczniki <section> i <footer> (R.2.4). Panele obejmuje znacznik <main> - główna treść strony, bez banera i stopki. Ma dwa zadania: czytnik ekranu pozwala do niego przeskoczyć jednym skrótem, a w CSS będzie pojemnikiem, który ustawi panele obok siebie.

    index.html HTML zmienione: 10 linii
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Kwiaty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
        </header>
        <main>
            <section id="left">
            </section>
            <section id="right">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  13. Krok 13: Nagłówki banera i panelu lewego

    ok. 1 min
    Polecenie z arkusza

    Zawartość banera: nagłówek pierwszego stopnia o treści: „Moje kwiaty”. Zawartość panelu lewego: Nagłówek trzeciego stopnia o treści: „Kwiaty dla Ciebie!”

    <h1> w banerze, <h3> w panelu lewym - stopnie nagłówków narzuca arkusz.

    index.html HTML zmienione: 2 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Kwiaty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Moje kwiaty</h1>
        </header>
        <main>
            <section id="left">
                <h3>Kwiaty dla Ciebie!</h3>
            </section>
            <section id="right">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  14. Krok 14: Dwa odnośniki w nawigacji

    Kryteria: ,

    R.3.3 Na przynajmniej jednej stronie wybranie odnośnika o treści „Znajdź kwiaciarnię” prowadzi do strony znajdz.php

    R.3.4 Na przynajmniej jednej stronie wybranie odnośnika o treści „Rozpoznaj kwiaty” prowadzi do strony https://www.swiatkwiatow.pl/

    ok. 2 min
    Polecenie z arkusza

    Odnośnik o treści „Rozpoznaj kwiaty” prowadzący do strony „https://www.swiatkwiatow.pl/”. Poniżej odnośnik o treści „Znajdź kwiaciarnię” prowadzący do pliku znajdz.php

    Odnośniki to menu strony, więc stoją w znaczniku <nav>. Adres zewnętrzny pełny, z https:// (R.3.4), plik znajdz.php - ścieżką względną (R.3.3). Zamiast <br> między nimi - jeden pod drugim ustawi je CSS (display: block), bo układ to sprawa stylu, nie treści.

    index.html HTML zmienione: 4 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Kwiaty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Moje kwiaty</h1>
        </header>
        <main>
            <section id="left">
                <h3>Kwiaty dla Ciebie!</h3>
                <nav>
                    <a href="https://www.swiatkwiatow.pl/">Rozpoznaj kwiaty</a>
                    <a href="znajdz.php">Znajdź kwiaciarnię</a>
                </nav>
            </section>
            <section id="right">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  15. Krok 15: Obraz goździka w panelu lewym

    Kryteria:

    R.2.6 Na przynajmniej jednej stronie w bloku lewym umieszczono obraz gozdzik z tekstem alternatywnym „Goździk"

    ok. 1 min
    Polecenie z arkusza

    Poniżej obraz gozdzik.jpg z tekstem alternatywnym „Goździk”

    <img> z tekstem alternatywnym (R.2.6).

    index.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Kwiaty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Moje kwiaty</h1>
        </header>
        <main>
            <section id="left">
                <h3>Kwiaty dla Ciebie!</h3>
                <nav>
                    <a href="https://www.swiatkwiatow.pl/">Rozpoznaj kwiaty</a>
                    <a href="znajdz.php">Znajdź kwiaciarnię</a>
                </nav>
                <img src="gozdzik.jpg" alt="Goździk">
            </section>
            <section id="right">
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  16. Krok 16: Panel prawy: trzy kwiaty i paragraf

    Kryteria:

    R.2.7 Na przynajmniej jednej stronie w bloku prawym umieszczono trzy obrazy kwiatów z tekstem alternatywnym odpowiadającym nazwie kwiatu

    ok. 2 min
    Polecenie z arkusza

    Panel prawy dla strony index.html: Obok siebie trzy obrazy z gerberą, goździkiem, różą, tekst alternatywny to nazwa kwiatu. Poniżej paragraf (akapit) o treści „Strona poświęcona miłośnikom kwiatów”

    Trzy obrazy w jednej linii z nazwami kwiatów w alt (R.2.7), pod nimi paragraf <p>.

    index.html HTML zmienione: 4 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Kwiaty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Moje kwiaty</h1>
        </header>
        <main>
            <section id="left">
                <h3>Kwiaty dla Ciebie!</h3>
                <nav>
                    <a href="https://www.swiatkwiatow.pl/">Rozpoznaj kwiaty</a>
                    <a href="znajdz.php">Znajdź kwiaciarnię</a>
                </nav>
                <img src="gozdzik.jpg" alt="Goździk">
            </section>
            <section id="right">
                <img src="gerbera.jpg" alt="gerbera">
                <img src="gozdzik.jpg" alt="goździk">
                <img src="roza.jpg" alt="róża">
                <p>Strona poświęcona miłośnikom kwiatów</p>
            </section>
        </main>
        <footer>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  17. Krok 17: Stopka

    Kryteria:

    R.2.5 Zapisano na przynajmniej jednej stronie nagłówki: w banerze h1 "Moje kwiaty", w panelu lewym h3 "Kwiaty dla Ciebie!", w stopce h3 "Stronę opracował <PESEL>" oraz w panelu prawym paragraf (znacznik <p>)

    ok. 1 min
    Polecenie z arkusza

    Zawartość stopki: nagłówek trzeciego stopnia o treści: „Stronę opracował:”, dalej wstawiony numer PESEL zdającego

    <h3> z numerem PESEL - tu w jego miejscu Bartosz Bryniarski. Nagłówki h1, dwa h3 i paragraf panelu prawego dają R.2.5.

    index.html HTML zmienione: 1 linia
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Kwiaty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Moje kwiaty</h1>
        </header>
        <main>
            <section id="left">
                <h3>Kwiaty dla Ciebie!</h3>
                <nav>
                    <a href="https://www.swiatkwiatow.pl/">Rozpoznaj kwiaty</a>
                    <a href="znajdz.php">Znajdź kwiaciarnię</a>
                </nav>
                <img src="gozdzik.jpg" alt="Goździk">
            </section>
            <section id="right">
                <img src="gerbera.jpg" alt="gerbera">
                <img src="gozdzik.jpg" alt="goździk">
                <img src="roza.jpg" alt="róża">
                <p>Strona poświęcona miłośnikom kwiatów</p>
            </section>
        </main>
        <footer>
            <h3>Stronę opracował: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (index.html)
  18. Etap 4: Styl CSS

    Kroki 18-25 · po tym etapie masz 28 z 36 kryteriów (pkt)

    Krok 18: Zmienne z kolorami arkusza

    ok. 2 min
    Polecenie z arkusza

    Styl CSS zawiera formatowanie

    Plik styl.css zaczyna się od zmiennych CSS w selektorze :root (cały dokument). LightPink występuje w arkuszu cztery razy - zmienna --pink pozwala zmienić odcień jednym wpisem, a w regułach odczytuje się ją funkcją var(--pink).

    styl.css CSS nowy plik
    :root {
        --pink: LightPink;
        --dark: black;
    }
    Podgląd strony po tym kroku (index.html)
  19. Krok 19: Krój, wyśrodkowanie i kursywa

    Kryteria:

    R.4.1 Ustawiono styl domyślny (dla selektora *, lub body, lub html, lub kontenera całej strony) czcionka Times New Roman, wyśrodkowana, kursywa

    ok. 1 min
    Polecenie z arkusza

    domyślnie dla całej strony: czcionka Times New Roman, tekst wyśrodkowany, kursywa

    Po kroju z arkusza stoją kroje zapasowe: Times (macOS) i rodzina ogólna serif - gdyby na komputerze nie było Times New Roman, przeglądarka weźmie najbliższy szeryfowy krój (R.4.1).

    styl.css CSS zmienione: 6 linii
    :root {
        --pink: LightPink;
        --dark: black;
    }
    
    body {
        font-family: "Times New Roman", Times, serif;
        text-align: center;
        font-style: italic;
    }
    Podgląd strony po tym kroku (index.html)
  20. Krok 20: Baner

    Kryteria:

    R.4.4 Ustawiono margines wewnętrzny górny 10px (padding-top lub padding: 10px 0 0 0) dla banera

    ok. 1 min
    Polecenie z arkusza

    dla banera: kolor tła LightPink, wysokość 80 px, margines wewnętrzny górny 10 px

    Tło ze zmiennej, wysokość i padding-top (R.4.4).

    styl.css CSS zmienione: 6 linii
    :root {
        --pink: LightPink;
        --dark: black;
    }
    
    body {
        font-family: "Times New Roman", Times, serif;
        text-align: center;
        font-style: italic;
    }
    
    header {
        background-color: var(--pink);
        height: 80px;
        padding-top: 10px;
    }
    Podgląd strony po tym kroku (index.html)
  21. Krok 21: Panele obok siebie: flex w main

    ok. 2 min
    Polecenie z arkusza

    Podział strony na bloki: baner, panel lewy i prawy oraz stopkę za pomocą znaczników sekcji

    display: flex na <main> ustawia jego dzieci - dwie sekcje - w jednym wierszu. Nie ma float, więc stopka sama staje pod <main> bez clear: both, a panele w wierszu flex mają z natury równą wysokość.

    Siatka (grid-template-columns: 20% 80%) byłaby równie dobra, ale klucz z 2020 roku ocenia szerokość na panelu (R.4.5) - dlatego pojemnik flex tylko układa, a szerokości zostają w regułach paneli.

    styl.css CSS zmienione: 4 linie
    :root {
        --pink: LightPink;
        --dark: black;
    }
    
    body {
        font-family: "Times New Roman", Times, serif;
        text-align: center;
        font-style: italic;
    }
    
    header {
        background-color: var(--pink);
        height: 80px;
        padding-top: 10px;
    }
    
    main {
        display: flex;
    }
    Podgląd strony po tym kroku (index.html)
  22. Krok 22: Panel lewy i prawy

    Kryteria: , , ,

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

    R.4.5 Ustawiono szerokość panelu lewego na 20%, panelu prawego na 80%

    R.4.6 Ustawiono wielkość czcionki dla panelu lewego 20px i prawego 30px

    R.4.7 Ustawiono kolor czcionki LightPink dla panelu prawego

    ok. 2 min
    Polecenie z arkusza

    dla panelu lewego: kolor tła LightPink, wysokość 500 px, szerokość 20%, rozmiar czcionki 20 px; dla panelu prawego: czarny kolor tła, wysokość 500 px, szerokość 80%, rozmiar czcionki 30 px, kolor czcionki LightPink

    Szerokości 20% + 80% (R.4.5), rozmiary czcionek 20 i 30 px (R.4.6), różowy tekst na czarnym tle (R.4.7). Selektory z krzyżykiem, bo panele mają id. Panele układa flex z poprzedniego kroku - układ jak na obrazie 1 (R.3.1).

    styl.css CSS zmienione: 15 linii
    :root {
        --pink: LightPink;
        --dark: black;
    }
    
    body {
        font-family: "Times New Roman", Times, serif;
        text-align: center;
        font-style: italic;
    }
    
    header {
        background-color: var(--pink);
        height: 80px;
        padding-top: 10px;
    }
    
    main {
        display: flex;
    }
    
    #left {
        background-color: var(--pink);
        height: 500px;
        width: 20%;
        font-size: 20px;
    }
    
    #right {
        background-color: var(--dark);
        color: var(--pink);
        height: 500px;
        width: 80%;
        font-size: 30px;
    }
    Podgląd strony po tym kroku (index.html)
  23. Krok 23: Stopka

    Kryteria: ,

    R.4.2 Ustawiono kolor tła dla banera, panelu lewego i stopki na LightPink oraz dla panelu prawego na czarny

    R.4.3 Ustawiono wysokość banera 80px, obu paneli 500px, stopki 60px

    ok. 1 min
    Polecenie z arkusza

    dla stopki: kolor tła LightPink, wysokość 60 px

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

    styl.css CSS zmienione: 6 linii
    :root {
        --pink: LightPink;
        --dark: black;
    }
    
    body {
        font-family: "Times New Roman", Times, serif;
        text-align: center;
        font-style: italic;
    }
    
    header {
        background-color: var(--pink);
        height: 80px;
        padding-top: 10px;
    }
    
    main {
        display: flex;
    }
    
    #left {
        background-color: var(--pink);
        height: 500px;
        width: 20%;
        font-size: 20px;
    }
    
    #right {
        background-color: var(--dark);
        color: var(--pink);
        height: 500px;
        width: 80%;
        font-size: 30px;
    }
    
    footer {
        background-color: var(--pink);
        height: 60px;
        display: flow-root;
    }
    Podgląd strony po tym kroku (index.html)
  24. Krok 24: Odnośniki jeden pod drugim, obraz 180 px

    Kryteria:

    R.4.8 Ustawiono styl wyłącznie dla obrazu z panelu lewego: szerokość i wysokość 180px

    ok. 1 min
    Polecenie z arkusza

    wyłącznie dla obrazu z panelu lewego: szerokość i wysokość 180 px

    #left img - tylko obraz panelu lewego (R.4.8). nav a { display: block; } - odnośnik zajmuje całą linię, więc drugi staje pod pierwszym, a obszar do kliknięcia jest szerszy niż sam napis.

    styl.css CSS zmienione: 9 linii
    :root {
        --pink: LightPink;
        --dark: black;
    }
    
    body {
        font-family: "Times New Roman", Times, serif;
        text-align: center;
        font-style: italic;
    }
    
    header {
        background-color: var(--pink);
        height: 80px;
        padding-top: 10px;
    }
    
    main {
        display: flex;
    }
    
    #left {
        background-color: var(--pink);
        height: 500px;
        width: 20%;
        font-size: 20px;
    }
    
    #right {
        background-color: var(--dark);
        color: var(--pink);
        height: 500px;
        width: 80%;
        font-size: 30px;
    }
    
    footer {
        background-color: var(--pink);
        height: 60px;
        display: flow-root;
    }
    
    nav a {
        display: block;
    }
    
    #left img {
        width: 180px;
        height: 180px;
    }
    Podgląd strony po tym kroku (index.html)
  25. Krok 25: Margines paragrafu

    Kryteria:

    R.4.9 Ustawiono dla selektora paragrafu: górny margines zewnętrzny 50px

    ok. 1 min
    Polecenie z arkusza

    dla selektora paragrafu: górny margines zewnętrzny 50 px

    p { margin-top: 50px; } (R.4.9).

    styl.css CSS zmienione: 4 linie
    :root {
        --pink: LightPink;
        --dark: black;
    }
    
    body {
        font-family: "Times New Roman", Times, serif;
        text-align: center;
        font-style: italic;
    }
    
    header {
        background-color: var(--pink);
        height: 80px;
        padding-top: 10px;
    }
    
    main {
        display: flex;
    }
    
    #left {
        background-color: var(--pink);
        height: 500px;
        width: 20%;
        font-size: 20px;
    }
    
    #right {
        background-color: var(--dark);
        color: var(--pink);
        height: 500px;
        width: 80%;
        font-size: 30px;
    }
    
    footer {
        background-color: var(--pink);
        height: 60px;
        display: flow-root;
    }
    
    nav a {
        display: block;
    }
    
    #left img {
        width: 180px;
        height: 180px;
    }
    
    p {
        margin-top: 50px;
    }
    Podgląd strony po tym kroku (index.html)
  26. Etap 5: Strona znajdz.php

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

    Krok 26: Plik znajdz.php z nowym paragrafem

    Kryteria:

    R.2.2 Witrynę internetową zapisano w plikach index.html i znajdz.php oraz zastosowano właściwy standard kodowania polskich znaków na przynajmniej jednej stronie

    ok. 2 min
    Polecenie z arkusza

    Panel prawy dla strony znajdz.php: Obok siebie trzy obrazy z gerberą, goździkiem, różą, tekst alternatywny to nazwa kwiatu. Paragraf o treści: „Podaj miejscowość, w której poszukujesz kwiaciarni:”

    Kopia index.html z innym paragrafem w panelu prawym (R.2.2). Paragraf dostaje id="city-label" - za chwilę posłuży jako podpis pola.

    znajdz.php PHP nowy plik
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Kwiaty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Moje kwiaty</h1>
        </header>
        <main>
            <section id="left">
                <h3>Kwiaty dla Ciebie!</h3>
                <nav>
                    <a href="https://www.swiatkwiatow.pl/">Rozpoznaj kwiaty</a>
                    <a href="znajdz.php">Znajdź kwiaciarnię</a>
                </nav>
                <img src="gozdzik.jpg" alt="Goździk">
            </section>
            <section id="right">
                <img src="gerbera.jpg" alt="gerbera">
                <img src="gozdzik.jpg" alt="goździk">
                <img src="roza.jpg" alt="róża">
                <p id="city-label">Podaj miejscowość, w której poszukujesz kwiaciarni:</p>
            </section>
        </main>
        <footer>
            <h3>Stronę opracował: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (znajdz.php)
  27. Krok 27: Formularz: pole i przycisk

    Kryteria: ,

    R.2.8 Na stronie PHP umieszczono formularz z polem edycyjnym i przyciskiem SPRAWDŹ

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

    ok. 2 min
    Polecenie z arkusza

    Formularz wysyłający dane metodą post do tego samego pliku, zawierający: pole edycyjne, przycisk „SPRAWDŹ”, który wysyła dane

    • method="post" do tego samego pliku (R.3.2),
    • aria-labelledby="city-label" - paragraf nad polem staje się jego podpisem: czytnik ekranu przeczyta pytanie, wchodząc w pole,
    • required - przeglądarka nie wyśle pustego formularza,
    • <button type="submit"> zamiast <input type="submit"> - w przycisku może stać dowolna treść, a nie tylko napis z atrybutu.

    Pole i przycisk wysyłający formularz dają R.2.8, metoda POST - R.3.2.

    znajdz.php PHP zmienione: 4 linie
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Kwiaty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Moje kwiaty</h1>
        </header>
        <main>
            <section id="left">
                <h3>Kwiaty dla Ciebie!</h3>
                <nav>
                    <a href="https://www.swiatkwiatow.pl/">Rozpoznaj kwiaty</a>
                    <a href="znajdz.php">Znajdź kwiaciarnię</a>
                </nav>
                <img src="gozdzik.jpg" alt="Goździk">
            </section>
            <section id="right">
                <img src="gerbera.jpg" alt="gerbera">
                <img src="gozdzik.jpg" alt="goździk">
                <img src="roza.jpg" alt="róża">
                <p id="city-label">Podaj miejscowość, w której poszukujesz kwiaciarni:</p>
                <form method="post" action="znajdz.php">
                    <input type="text" name="city" aria-labelledby="city-label" required>
                    <button type="submit">SPRAWDŹ</button>
                </form>
            </section>
        </main>
        <footer>
            <h3>Stronę opracował: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (znajdz.php)
  28. Etap 6: Skrypt PHP

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

    Krok 28: Skrypt na górze pliku i połączenie PDO z bazą kwiaciarnia

    Kryteria: ,

    R.5.1 Skrypt napisano w języku PHP i umieszczono w pliku znajdz.php oraz zawiera on przynajmniej jedną instrukcję języka zgodną ze składnią

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

    ok. 4 min
    Polecenie z arkusza

    Wymagania dotyczące skryptu: Napisany w języku PHP. Skrypt łączy się z serwerem bazy danych na localhost, użytkownik root bez hasła, baza danych o nazwie kwiaciarnia

    Skrypt stoi na samej górze znajdz.php, przed <!DOCTYPE html>: najpierw przygotowuje dane, a HTML tylko je wypisuje (R.5.1).

    • e() - krótka funkcja z htmlspecialchars, przez którą przejdzie każdy tekst z bazy wypisany na stronie,
    • new PDO(...) - w pierwszym napisie serwer localhost, baza kwiaciarnia i kodowanie utf8mb4, dalej użytkownik root i puste hasło (R.5.2),
    • ERRMODE_EXCEPTION - każdy błąd bazy rzuca wyjątek, a FETCH_ASSOC - wiersze wyniku będą tablicami z nazwami kolumn,
    • try / catch - gdy serwer bazy nie działa, na stronie pojawi się zwykłe zdanie zamiast komunikatu PHP z danymi serwera.

    Klucz nie narzuca biblioteki - tabela 3 arkusza jest tylko „wyborem funkcji”. Od tego kroku podgląd to wynik prawdziwego uruchomienia w PHP 8.2.

    znajdz.php PHP zmienione: 17 linii
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $florists = [];
    $message = '';
    try {
        $pdo = new PDO('mysql:host=localhost;dbname=kwiaciarnia;charset=utf8mb4', 'root', '', [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    } catch (PDOException $exception) {
        $message = 'Nie udało się połączyć z bazą danych, spróbuj ponownie za chwilę.';
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Kwiaty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Moje kwiaty</h1>
        </header>
        <main>
            <section id="left">
                <h3>Kwiaty dla Ciebie!</h3>
                <nav>
                    <a href="https://www.swiatkwiatow.pl/">Rozpoznaj kwiaty</a>
                    <a href="znajdz.php">Znajdź kwiaciarnię</a>
                </nav>
                <img src="gozdzik.jpg" alt="Goździk">
            </section>
            <section id="right">
                <img src="gerbera.jpg" alt="gerbera">
                <img src="gozdzik.jpg" alt="goździk">
                <img src="roza.jpg" alt="róża">
                <p id="city-label">Podaj miejscowość, w której poszukujesz kwiaciarni:</p>
                <form method="post" action="znajdz.php">
                    <input type="text" name="city" aria-labelledby="city-label" required>
                    <button type="submit">SPRAWDŹ</button>
                </form>
            </section>
        </main>
        <footer>
            <h3>Stronę opracował: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (znajdz.php)
  29. Krok 29: Miejscowość z formularza

    ok. 3 min
    Polecenie z arkusza

    Skrypt pobiera dane z pola edycyjnego

    • filter_input(INPUT_POST, 'city') odczytuje pole z wysłanego formularza, a gdy go nie ma - zwraca null, bez ostrzeżenia. (string) zamienia null na pusty napis,
    • trim() zdejmuje spacje z brzegów: „ Malbork ” to Malbork,
    • połączenie i zapytanie tylko wtedy, gdy miejscowość nie jest pusta - zwykłe wejście na stronę nie łączy się z bazą.

    Pole dostało value="<?= e($city) ?>" - po wysłaniu wpisana miejscowość zostaje w polu. Podgląd: wysłane „Malbork”.

    znajdz.php PHP zmienione: 11 linii
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $city = trim((string) filter_input(INPUT_POST, 'city'));
    $florists = [];
    $message = '';
    if ($city !== '') {
        try {
            $pdo = new PDO('mysql:host=localhost;dbname=kwiaciarnia;charset=utf8mb4', 'root', '', [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            ]);
        } catch (PDOException $exception) {
            $message = 'Nie udało się połączyć z bazą danych, spróbuj ponownie za chwilę.';
        }
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Kwiaty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Moje kwiaty</h1>
        </header>
        <main>
            <section id="left">
                <h3>Kwiaty dla Ciebie!</h3>
                <nav>
                    <a href="https://www.swiatkwiatow.pl/">Rozpoznaj kwiaty</a>
                    <a href="znajdz.php">Znajdź kwiaciarnię</a>
                </nav>
                <img src="gozdzik.jpg" alt="Goździk">
            </section>
            <section id="right">
                <img src="gerbera.jpg" alt="gerbera">
                <img src="gozdzik.jpg" alt="goździk">
                <img src="roza.jpg" alt="róża">
                <p id="city-label">Podaj miejscowość, w której poszukujesz kwiaciarni:</p>
                <form method="post" action="znajdz.php">
                    <input type="text" name="city" aria-labelledby="city-label" required value="<?= e($city) ?>">
                    <button type="submit">SPRAWDŹ</button>
                </form>
            </section>
        </main>
        <footer>
            <h3>Stronę opracował: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (znajdz.php)
  30. Krok 30: Kwerenda 1 jako zapytanie przygotowane

    Kryteria:

    R.5.4 Skrypt wysyła do bazy danych zmodyfikowane zapytanie 1 w ten sposób, że w miejscu miasta jest wpisana wartość z pola edycyjnego

    ok. 3 min
    Polecenie z arkusza

    Wysyła do bazy danych zmodyfikowane zapytanie 1 w ten sposób, że w miejscu miasta jest wpisana wartość z pola edycyjnego

    Kwerenda 1, w której miasto zastępuje parametr :city:

    • $pdo->prepare(...) - baza dostaje samą treść zapytania,
    • $statement->execute(['city' => $city]) - osobno wartość z formularza,
    • fetchAll() - od razu wszystkie wiersze wyniku jako tablica.

    Wpisana miejscowość nigdy nie staje się częścią kodu SQL: x' OR '1'='1 to po prostu nazwa miasta, którego nie ma, a „Kędzierzyn'Koźle” z apostrofem nie psuje zapytania (R.5.4).

    znajdz.php PHP zmienione: 3 linie
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $city = trim((string) filter_input(INPUT_POST, 'city'));
    $florists = [];
    $message = '';
    if ($city !== '') {
        try {
            $pdo = new PDO('mysql:host=localhost;dbname=kwiaciarnia;charset=utf8mb4', 'root', '', [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            ]);
            $statement = $pdo->prepare('SELECT nazwa, ulica FROM kwiaciarnie WHERE miasto = :city;');
            $statement->execute(['city' => $city]);
            $florists = $statement->fetchAll();
        } catch (PDOException $exception) {
            $message = 'Nie udało się połączyć z bazą danych, spróbuj ponownie za chwilę.';
        }
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Kwiaty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Moje kwiaty</h1>
        </header>
        <main>
            <section id="left">
                <h3>Kwiaty dla Ciebie!</h3>
                <nav>
                    <a href="https://www.swiatkwiatow.pl/">Rozpoznaj kwiaty</a>
                    <a href="znajdz.php">Znajdź kwiaciarnię</a>
                </nav>
                <img src="gozdzik.jpg" alt="Goździk">
            </section>
            <section id="right">
                <img src="gerbera.jpg" alt="gerbera">
                <img src="gozdzik.jpg" alt="goździk">
                <img src="roza.jpg" alt="róża">
                <p id="city-label">Podaj miejscowość, w której poszukujesz kwiaciarni:</p>
                <form method="post" action="znajdz.php">
                    <input type="text" name="city" aria-labelledby="city-label" required value="<?= e($city) ?>">
                    <button type="submit">SPRAWDŹ</button>
                </form>
            </section>
        </main>
        <footer>
            <h3>Stronę opracował: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>

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

  31. Krok 31: Nazwa i ulica pod przyciskiem

    Kryteria:

    R.5.5 Skrypt wyświetla pod przyciskiem zwrócone zapytaniem dane: nazwę i ulicę oddzielone przecinkiem

    ok. 3 min
    Polecenie z arkusza

    Pod przyciskiem wyświetla zwrócone zapytaniem dane: nazwę i ulicę oddzielone przecinkiem

    Pod formularzem tylko wypisanie: foreach po wierszach, a w każdym <?= e($florist['nazwa']) ?>, <?= e($florist['ulica']) ?> i <br>. Zapis <?= … ?> to skrót od <?php echo … ?>, a składnia foreach (...): / endforeach; czyta się w HTML-u łatwiej niż nawiasy klamrowe. Nazwy kolumn zamiast numerów [0], [1] - kod nie zależy od kolejności pól w SELECT.

    Podgląd: Melisa, Konopnickiej pod przyciskiem, jak na obrazie 2 (R.5.5).

    znajdz.php PHP zmienione: 3 linie
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $city = trim((string) filter_input(INPUT_POST, 'city'));
    $florists = [];
    $message = '';
    if ($city !== '') {
        try {
            $pdo = new PDO('mysql:host=localhost;dbname=kwiaciarnia;charset=utf8mb4', 'root', '', [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            ]);
            $statement = $pdo->prepare('SELECT nazwa, ulica FROM kwiaciarnie WHERE miasto = :city;');
            $statement->execute(['city' => $city]);
            $florists = $statement->fetchAll();
        } catch (PDOException $exception) {
            $message = 'Nie udało się połączyć z bazą danych, spróbuj ponownie za chwilę.';
        }
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Kwiaty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Moje kwiaty</h1>
        </header>
        <main>
            <section id="left">
                <h3>Kwiaty dla Ciebie!</h3>
                <nav>
                    <a href="https://www.swiatkwiatow.pl/">Rozpoznaj kwiaty</a>
                    <a href="znajdz.php">Znajdź kwiaciarnię</a>
                </nav>
                <img src="gozdzik.jpg" alt="Goździk">
            </section>
            <section id="right">
                <img src="gerbera.jpg" alt="gerbera">
                <img src="gozdzik.jpg" alt="goździk">
                <img src="roza.jpg" alt="róża">
                <p id="city-label">Podaj miejscowość, w której poszukujesz kwiaciarni:</p>
                <form method="post" action="znajdz.php">
                    <input type="text" name="city" aria-labelledby="city-label" required value="<?= e($city) ?>">
                    <button type="submit">SPRAWDŹ</button>
                </form>
                <?php foreach ($florists as $florist): ?>
                <?= e($florist['nazwa']) ?>, <?= e($florist['ulica']) ?><br>
                <?php endforeach; ?>
            </section>
        </main>
        <footer>
            <h3>Stronę opracował: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (znajdz.php)
  32. Krok 32: Zamknięcie połączenia i komunikat o braku wyników

    Kryteria:

    R.5.3 Po wykonaniu wszystkich operacji na bazie zastosowano funkcję zamknięcia połączenia z serwerem bazy danych

    ok. 3 min
    Polecenie z arkusza

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

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

    Dodatek spoza klucza: gdy zapytanie nic nie znalazło, pod przyciskiem pojawia się „W tej miejscowości nie ma kwiaciarni w bazie.” - w wersji podstawowej pusty wynik wygląda tak samo jak niewysłany formularz. Podgląd: wyszukanie „Kraków”.

    znajdz.php PHP zmienione: 9 linii
    <?php
    function e(string $text): string
    {
        return htmlspecialchars($text, ENT_QUOTES, 'UTF-8');
    }
    
    $city = trim((string) filter_input(INPUT_POST, 'city'));
    $florists = [];
    $message = '';
    if ($city !== '') {
        try {
            $pdo = new PDO('mysql:host=localhost;dbname=kwiaciarnia;charset=utf8mb4', 'root', '', [
                PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            ]);
            $statement = $pdo->prepare('SELECT nazwa, ulica FROM kwiaciarnie WHERE miasto = :city;');
            $statement->execute(['city' => $city]);
            $florists = $statement->fetchAll();
        } catch (PDOException $exception) {
            $message = 'Nie udało się połączyć z bazą danych, spróbuj ponownie za chwilę.';
        } finally {
            $statement = null;
            $pdo = null;
        }
        if ($message === '' && count($florists) === 0) {
            $message = 'W tej miejscowości nie ma kwiaciarni w bazie.';
        }
    }
    ?>
    <!DOCTYPE html>
    <html lang="pl">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1">
        <title>Kwiaty</title>
        <link rel="stylesheet" href="styl.css">
    </head>
    <body>
        <header>
            <h1>Moje kwiaty</h1>
        </header>
        <main>
            <section id="left">
                <h3>Kwiaty dla Ciebie!</h3>
                <nav>
                    <a href="https://www.swiatkwiatow.pl/">Rozpoznaj kwiaty</a>
                    <a href="znajdz.php">Znajdź kwiaciarnię</a>
                </nav>
                <img src="gozdzik.jpg" alt="Goździk">
            </section>
            <section id="right">
                <img src="gerbera.jpg" alt="gerbera">
                <img src="gozdzik.jpg" alt="goździk">
                <img src="roza.jpg" alt="róża">
                <p id="city-label">Podaj miejscowość, w której poszukujesz kwiaciarni:</p>
                <form method="post" action="znajdz.php">
                    <input type="text" name="city" aria-labelledby="city-label" required value="<?= e($city) ?>">
                    <button type="submit">SPRAWDŹ</button>
                </form>
                <?php foreach ($florists as $florist): ?>
                <?= e($florist['nazwa']) ?>, <?= e($florist['ulica']) ?><br>
                <?php endforeach; ?>
                <?php if ($message !== ''): ?>
                <?= e($message) ?>
                <?php endif; ?>
            </section>
        </main>
        <footer>
            <h3>Stronę opracował: Bartosz Bryniarski</h3>
        </footer>
    </body>
    </html>
    Podgląd strony po tym kroku (znajdz.php)
  33. Etap 7: Porównanie z wersją podstawową

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

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

    ok. 3 min

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

    wersja podstawowawersja nowoczesna
    układfloat: left sekcji, clear stopki<main> z display: flex
    szerokości 20% i 80%width na panelachwidth na panelach (tak ocenia klucz)
    kolorywpisane w każdej regulezmienne --pink, --dark
    odnośniki<br> między nimi<nav>, display: block
    pole formularzabez podpisuparagraf jako podpis (aria-labelledby), required
    tabelepola bez ograniczeń, cena INTNOT NULL, UNSIGNED, cena DECIMAL(8, 2)
    bibliotekamysqli (tabela 3 arkusza)PDO z wyjątkami
    gdzie jest skryptw całości pod formularzemna górze pliku, pod formularzem tylko pętla
    miasto w zapytaniuwklejone w treść zapytaniaparametr :city
    wypisywanieecho wprost z bazye() z htmlspecialchars
    zamknięcie bazymysqli_close na końcu$pdo = null w finally
    po wysłaniupuste pole, pusty wynik bez słowamiejscowość w polu, komunikat o braku wyników

    Na egzaminie wersja podstawowa trzyma się funkcji z tabeli 3 i łatwiej ją napisać bez dokumentacji. Nowoczesna nie daje się oszukać wpisem x' OR '1'='1, nie wywraca się na apostrofie i mówi użytkownikowi, że nic nie znalazła - a klucz przyjmuje obie.

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

  34. Etap 8: Oddanie pracy

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

    Krok 34: 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 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 nazwa przeglądarki. W folderze z numerem zdającego: gerbera.jpg, gozdzik.jpg, index.html, kw1.jpg - kw3.jpg, kwerendy.txt, kwiaciarnia.sql, przeglądarka.txt, roza.jpg, styl.css i znajdz.php (arkusz pomija go na liście, a to jedyny plik ze skryptem). Nagraj płytę, sprawdź jej odczyt i opisz ją numerem zdającego.

    przeglądarka.txt Tekst nowy plik
    Google Chrome

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

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