Czego się nauczysz?
- Tworzenia formularzy CRUD (Create, Read, Update, Delete)
- Pracy że statusami i ich zmianą
- Filtrowania i sortowania danych
- Wyszukiwania tekstowego
- Tworzenia statystyk i podsumowań
This content is not available in your language yet.
Stworzysz Rejestr książek - aplikację do zarządzania osobistą biblioteką. Użytkownik może dodawać książki, oznaczać je jako przeczytane/nieprzeczytane, filtrować listę i śledzić swoje postępy czytelnicze.
Czego się nauczysz?
W prawdziwej pracy...
Rejestry i listy z możliwością zmiany statusu to podstawa wielu aplikacji - od list zadań (todo), przez systemy CRM, po aplikacje do śledzenia nawyków. Umiejętność projektowania interfejsów do zarządzania danymi że statusami jest fundamentem dla każdego programisty aplikacji biznesowych.
Formularz dodawania książki Użytkownik podaje tytuł i autora książki. Opcjonalnie może dodać gatunek, rok wydania lub ocenę.
Walidacja danych System sprawdza poprawność wprowadzonych danych - czy tytuł i autor nie są puste, czy nie ma duplikatów.
Lista książek Wszystkie książki są wyświetlane w formie listy lub tabeli z informacjami o statusie.
Zmiana statusu Użytkownik może oznaczyć książkę jako przeczytaną lub nieprzeczytaną jednym kliknięciem.
Przykładowa struktura pliku JSON:
{ "books": [ { "id": 1, "title": "Władca Pierścieni", "author": "J.R.R. Tolkien", "genre": "fantasy", "year": 1954, "status": "read", "rating": 5, "added_at": "2026-01-15", "read_at": "2026-02-10" }, { "id": 2, "title": "1984", "author": "George Orwell", "genre": "dystopia", "year": 1949, "status": "unread", "rating": null, "added_at": "2026-02-01", "read_at": null }, { "id": 3, "title": "Wiedźmin: Ostatnie życzenie", "author": "Andrzej Sapkowski", "genre": "fantasy", "year": 1993, "status": "reading", "rating": null, "added_at": "2026-02-05", "read_at": null } ]}Wymagane funkcje:
Przykładowy scenariusz:
Ocena: 3.0Użytkownik wpisuje “Władca Pierścieni” i “J.R.R. Tolkien”. Po kliknięciu “Dodaj” książka pojawia się na liście jako “nieprzeczytana” (szare tło). Klika przycisk “Przeczytane” i status zmienia się na zielony.
Wszystko z wariantu A, plus:
Przykładowy scenariusz:
Ocena: 4.0-5.0Użytkownik filtruje: “pokaż tylko nieprzeczytane” i widzi 5 książek do przeczytania. Na górze strony: “Przeczytane: 12/17 (71%)”. Może wybrać gatunek przy dodawaniu.
Wszystko z wariantu B, plus:
Przykładowy scenariusz:
Ocena: 5.0-6.0Użytkownik wyszukuje “Tolkien” i widzi 3 książki tego autora. Może posortować po ocenie i zobaczyć najlepsze. Statystyki: “Ulubiony gatunek: Fantasy (8 książek), Najlepsza ocena: Władca Pierścieni (5/5)”.
Walidacja danych wejściowych:
$title = trim($_POST['title'] ?? '');$author = trim($_POST['author'] ?? '');$genre = trim($_POST['genre'] ?? '');
if (empty($title)) { $errors[] = "Tytuł jest wymagany";}
if (empty($author)) { $errors[] = "Autor jest wymagany";}
if (strlen($title) > 200) { $errors[] = "Tytuł jest zbyt długi (max 200 znaków)";}Dodawanie książki:
function addBook(array &$books, string $title, string $author, string $genre = ''): int { $newId = empty($books) ? 1 : max(array_column($books, 'id')) + 1;
$newBook = [ 'id' => $newId, 'title' => $title, 'author' => $author, 'genre' => $genre, 'status' => 'unread', 'rating' => null, 'added_at' => date('Y-m-d'), 'read_at' => null, ];
$books[] = $newBook; return $newId;}Zmiana statusu:
function changeStatus(array &$books, int $bookId, string $newStatus): bool { $validStatuses = ['unread', 'reading', 'read'];
if (!in_array($newStatus, $validStatuses)) { return false; }
foreach ($books as &$book) { if ($book['id'] === $bookId) { $book['status'] = $newStatus;
// Ustaw datę przeczytania if ($newStatus === 'read' && $book['read_at'] === null) { $book['read_at'] = date('Y-m-d'); }
return true; } }
return false;}Filtrowanie książek:
function filterBooks(array $books, ?string $status = null, ?string $genre = null): array { return array_filter($books, function($book) use ($status, $genre) { if ($status !== null && $book['status'] !== $status) { return false; } if ($genre !== null && ($book['genre'] ?? '') !== $genre) { return false; } return true; });}Wyszukiwanie:
function searchBooks(array $books, string $query): array { if (empty($query)) { return $books; }
$queryLower = mb_strtolower($query);
return array_filter($books, function($book) use ($queryLower) { $titleLower = mb_strtolower($book['title']); $authorLower = mb_strtolower($book['author']);
return strpos($titleLower, $queryLower) !== false || strpos($authorLower, $queryLower) !== false; });}Sortowanie:
function sortBooks(array $books, string $sortBy = 'added_at', string $order = 'desc'): array { usort($books, function($a, $b) use ($sortBy, $order) { $valueA = $a[$sortBy] ?? ''; $valueB = $b[$sortBy] ?? '';
$comparison = strcmp($valueA, $valueB);
return $order === 'desc' ? -$comparison : $comparison; });
return $books;}Statystyki:
function getBookStatistics(array $books): array { $total = count($books); $read = count(array_filter($books, fn($b) => $b['status'] === 'read')); $reading = count(array_filter($books, fn($b) => $b['status'] === 'reading'));
// Statystyki gatunków $genres = []; foreach ($books as $book) { $genre = $book['genre'] ?? 'inne'; $genres[$genre] = ($genres[$genre] ?? 0) + 1; } arsort($genres);
// Najlepiej ocenione $rated = array_filter($books, fn($b) => $b['rating'] !== null); usort($rated, fn($a, $b) => $b['rating'] <=> $a['rating']); $topRated = array_slice($rated, 0, 3);
return [ 'total' => $total, 'read' => $read, 'reading' => $reading, 'unread' => $total - $read - $reading, 'percentage' => $total > 0 ? round(($read / $total) * 100) : 0, 'genres' => $genres, 'top_rated' => $topRated, ];}Kolorowanie statusów:
function getStatusInfo(string $status): array { $statuses = [ 'unread' => ['label' => 'Do przeczytania', 'color' => '#95a5a6', 'icon' => '📚'], 'reading' => ['label' => 'Czytam', 'color' => '#f39c12', 'icon' => '📖'], 'read' => ['label' => 'Przeczytane', 'color' => '#27ae60', 'icon' => '✅'], ];
return $statuses[$status] ?? $statuses['unread'];}Wykorzystaj lekcje!
Cotygodniowe spotkania podczas lekcji to idealny moment, by:
Pracuj iteracyjnie - lepiej mieć działający wariant A niż niedokończony C!