Czego się nauczysz?
- Pracy z datami i terminami zwrotu
- Obliczania opóźnień i statusów
- Implementacji przepływu pracy (wypożyczenie → zwrot)
- Filtrowania danych według statusu
- Tworzenia raportów i powiadomień
This content is not available in your language yet.
Stworzysz Rejestr wypożyczeń - aplikację do zarządzania wypożyczeniami zasobów (np. książki w bibliotece, sprzęt w firmie). System śledzi kto wypożyczył dany przedmiot, kiedy i do kiedy powinien go zwrócić.
Czego się nauczysz?
W prawdziwej pracy...
Systemy wypożyczeń są podstawą wielu instytucji - od bibliotek, przez wypożyczalnie sprzętu, po systemy zarządzania zasobami firmowymi. Umiejętność projektowania systemów z kontrolą terminów, statusami i historią transakcji jest fundamentem dla każdego programisty aplikacji logistycznych i inwentaryzacyjnych.
Formularz wypożyczenia Użytkownik podaje dane osoby wypożyczającej, wybiera przedmiot z listy i określa termin zwrotu.
Walidacja danych System sprawdza poprawność wprowadzonych danych - czy termin zwrotu jest późniejszy niż data wypożyczenia, czy przedmiot jest dostępny.
Lista wypożyczeń Wszystkie wypożyczenia są wyświetlane z informacją o statusie (aktywne, zaległe, zwrócone).
Oznaczanie zwrotu Operator może oznaczyć wypożyczenie jako zwrócone, zapisując datę zwrotu.
Przykładowa struktura pliku JSON:
{ "loans": [ { "id": 1, "person": "Jan Kowalski", "contact": "jan@email.pl", "item": "Władca Pierścieni", "item_type": "book", "borrowed_at": "2026-02-01", "due_at": "2026-02-15", "returned_at": null, "notes": "Wydanie kolekcjonerskie", "created_at": "2026-02-01 10:30:00" }, { "id": 2, "person": "Anna Nowak", "contact": "anna@email.pl", "item": "Projektor Epson", "item_type": "equipment", "borrowed_at": "2026-02-05", "due_at": "2026-02-10", "returned_at": "2026-02-09", "notes": "", "created_at": "2026-02-05 14:00:00" }, { "id": 3, "person": "Piotr Wiśniewski", "contact": "piotr@email.pl", "item": "1984", "item_type": "book", "borrowed_at": "2026-01-20", "due_at": "2026-02-03", "returned_at": null, "notes": "Przedłużenie niemożliwe", "created_at": "2026-01-20 09:15:00" } ]}Wymagane funkcje:
Przykładowy scenariusz:
Ocena: 3.0Użytkownik wpisuje “Jan Kowalski”, wybiera książkę “Władca Pierścieni” i termin zwrotu 15.02.2026. Po zapisie widzi wypożyczenie na liście że statusem “aktywne” i datą zwrotu.
Wszystko z wariantu A, plus:
htmlspecialchars() przy wyświetlaniuPrzykładowy scenariusz:
Ocena: 4.0-5.0Operator filtruje: “pokaż zaległe” i widzi 3 wypożyczenia po terminie (czerwone tło). Sortuje po terminie, żeby zobaczyć najbardziej zaległe. Może oznaczyć zwrot klikając przycisk.
Wszystko z wariantu B, plus:
Przykładowy scenariusz:
Ocena: 5.0-6.0Admin klika na książkę i widzi jej historię: “5 wypożyczeń, ostatnie 3 na czas”. Oznacza zwrot - system zapisuje datę. Raport pokazuje: “Zaległe: 3, Średni czas wypożyczenia: 12 dni, Top 3: Władca Pierścieni, 1984, Harry Potter”.
Walidacja dat:
$borrowedAt = $_POST['borrowed_at'] ?? date('Y-m-d');$dueAt = $_POST['due_at'] ?? '';$today = date('Y-m-d');
if (empty($dueAt)) { $errors[] = "Termin zwrotu jest wymagany";}
if ($dueAt <= $borrowedAt) { $errors[] = "Termin zwrotu musi być późniejszy niż data wypożyczenia";}
// Maksymalny okres wypożyczenia (np. 30 dni)$maxDue = date('Y-m-d', strtotime($borrowedAt . ' + 30 days'));if ($dueAt > $maxDue) { $errors[] = "Maksymalny okres wypożyczenia to 30 dni";}Określanie statusu wypożyczenia:
function getLoanStatus(array $loan): string { // Zwrócone if (!empty($loan['returned_at'])) { return 'returned'; }
// Zaległe (po terminie) $today = date('Y-m-d'); if ($loan['due_at'] < $today) { return 'overdue'; }
// Aktywne return 'active';}
function getStatusInfo(string $status): array { $statuses = [ 'active' => ['label' => 'Aktywne', 'color' => '#27ae60', 'icon' => '📗'], 'overdue' => ['label' => 'Zaległe', 'color' => '#e74c3c', 'icon' => '⚠️'], 'returned' => ['label' => 'Zwrócone', 'color' => '#95a5a6', 'icon' => '✅'], ];
return $statuses[$status] ?? $statuses['active'];}Dodawanie wypożyczenia:
function addLoan(array &$loans, array $data): int { $newId = empty($loans) ? 1 : max(array_column($loans, 'id')) + 1;
$newLoan = [ 'id' => $newId, 'person' => $data['person'], 'contact' => $data['contact'], 'item' => $data['item'], 'item_type' => $data['item_type'] ?? 'book', 'borrowed_at' => $data['borrowed_at'] ?? date('Y-m-d'), 'due_at' => $data['due_at'], 'returned_at' => null, 'notes' => $data['notes'] ?? '', 'created_at' => date('Y-m-d H:i:s'), ];
$loans[] = $newLoan; return $newId;}Oznaczanie zwrotu:
function markAsReturned(array &$loans, int $loanId): bool { foreach ($loans as &$loan) { if ($loan['id'] === $loanId) { $loan['returned_at'] = date('Y-m-d'); return true; } }
return false;}Filtrowanie wypożyczeń:
function filterLoans(array $loans, string $status): array { if (empty($status) || $status === 'all') { return $loans; }
return array_filter($loans, function($loan) use ($status) { return getLoanStatus($loan) === $status; });}
function getOverdueLoans(array $loans): array { return filterLoans($loans, 'overdue');}
function getActiveLoans(array $loans): array { return filterLoans($loans, 'active');}Obliczanie dni do terminu / po terminie:
function getDaysRemaining(array $loan): int { if (!empty($loan['returned_at'])) { return 0; }
$today = new DateTime(); $dueDate = new DateTime($loan['due_at']); $diff = $today->diff($dueDate);
// Ujemne = po terminie, dodatnie = dni do zwrotu return $diff->invert ? -$diff->days : $diff->days;}
function formatDaysRemaining(int $days): string { if ($days < 0) { return abs($days) . ' dni po terminie'; } elseif ($days === 0) { return 'Termin dzisiaj!'; } else { return $days . ' dni do zwrotu'; }}Sortowanie po terminie:
function sortByDueDate(array $loans, string $order = 'asc'): array { usort($loans, function($a, $b) use ($order) { $result = strcmp($a['due_at'], $b['due_at']); return $order === 'desc' ? -$result : $result; });
return $loans;}Statystyki wypożyczeń (wariant C):
function getLoanStatistics(array $loans): array { $total = count($loans); $active = count(array_filter($loans, fn($l) => getLoanStatus($l) === 'active')); $overdue = count(array_filter($loans, fn($l) => getLoanStatus($l) === 'overdue')); $returned = count(array_filter($loans, fn($l) => getLoanStatus($l) === 'returned'));
// Średni czas wypożyczenia (dla zwróconych) $returnedLoans = array_filter($loans, fn($l) => !empty($l['returned_at'])); $avgDays = 0; if (!empty($returnedLoans)) { $totalDays = 0; foreach ($returnedLoans as $loan) { $borrowed = new DateTime($loan['borrowed_at']); $returned = new DateTime($loan['returned_at']); $totalDays += $borrowed->diff($returned)->days; } $avgDays = round($totalDays / count($returnedLoans), 1); }
// Najczęściej wypożyczane przedmioty $itemCounts = array_count_values(array_column($loans, 'item')); arsort($itemCounts); $topItems = array_slice($itemCounts, 0, 5, true);
return [ 'total' => $total, 'active' => $active, 'overdue' => $overdue, 'returned' => $returned, 'avg_days' => $avgDays, 'top_items' => $topItems, ];}Wykorzystaj lekcje!
Cotygodniowe spotkania podczas lekcji to idealny moment, by:
Pracuj iteracyjnie - lepiej mieć działający wariant A niż niedokończony C!