Czego się nauczysz?
- Pracy z datami ważności (valid_to)
- Automatycznego archiwizowania wygasłych treści
- Kategoryzacji i filtrowania ogłoszeń
- Implementacji systemu CMS-like
- Tworzenia interfejsu administratora
Stworzysz Panel ogłoszeń szkolnych - aplikację do publikowania i przeglądania ogłoszeń z określoną datą ważności. System automatycznie rozróżnia aktywne i archiwalne ogłoszenia, pozwala na kategoryzację i filtrowanie treści.
Czego się nauczysz?
W prawdziwej pracy...
Systemy ogłoszeń i komunikatów są podstawą intranetu każdej organizacji. Od tablic ogłoszeń firmowych, przez systemy powiadomień, po portale informacyjne - umiejętność projektowania systemów z datą ważności i automatyczną archiwizacją jest fundamentem dla każdego programisty aplikacji korporacyjnych.
Formularz dodawania ogłoszenia Użytkownik podaje tytuł, treść, kategorię i datę ważności ogłoszenia.
Walidacja danych System sprawdza poprawność wprowadzonych danych - czy tytuł nie jest pusty, czy data ważności jest w przyszłości.
Lista ogłoszeń Aktywne ogłoszenia (przed datą ważności) są wyświetlane prominentnie, wygasłe trafiają do archiwum.
Podgląd szczegółów Użytkownik może kliknąć na ogłoszenie i zobaczyć pełną treść.
Przykładowa struktura pliku JSON:
{ "announcements": [ { "id": 1, "title": "Zebranie rodziców - klasy 1-3", "content": "Zapraszamy na zebranie rodziców klas 1-3 w dniu 20.02.2026 o godz. 17:00 w auli szkolnej.", "category": "zebrania", "priority": "high", "valid_from": "2026-02-10", "valid_to": "2026-02-20", "author": "Dyrekcja", "created_at": "2026-02-10 08:00:00" }, { "id": 2, "title": "Konkurs recytatorski", "content": "Zapraszamy do udziału w szkolnym konkursie recytatorskim. Zgłoszenia do 15.02.", "category": "wydarzenia", "priority": "normal", "valid_from": "2026-02-01", "valid_to": "2026-02-15", "author": "Biblioteka", "created_at": "2026-02-01 10:30:00" }, { "id": 3, "title": "Zmiana planu lekcji", "content": "W dniu 12.02 obowiązuje zmieniony plan lekcji. Szczegóły u wychowawców.", "category": "organizacja", "priority": "high", "valid_from": "2026-02-11", "valid_to": "2026-02-12", "author": "Sekretariat", "created_at": "2026-02-11 07:00:00" } ]}Wymagane funkcje:
Przykładowy scenariusz:
Ocena: 3.0Użytkownik wchodzi na panel i widzi listę aktywnych ogłoszeń. Klika “Dodaj ogłoszenie”, wpisuje tytuł “Zebranie rodziców”, wybiera kategorię “Zebrania” i ustawia datę ważności na 20.02. Po zapisie ogłoszenie pojawia się na liście.
Wszystko z wariantu A, plus:
htmlspecialchars() przy wyświetlaniuPrzykładowy scenariusz:
Ocena: 4.0-5.0Użytkownik widzi tylko aktywne ogłoszenia (przed datą ważności). Może filtrować: “pokaż tylko zebrania”. Klika “Archiwum” i widzi wygasłe ogłoszenia. Sortuje po dacie, żeby zobaczyć najbliższe najpierw.
Wszystko z wariantu B, plus:
Przykładowy scenariusz:
Ocena: 5.0-6.0Admin loguje się i widzi panel zarządzania. Może edytować ogłoszenie, przedłużyć datę ważności lub usunąć nieaktualne. Ogłoszenia z wysokim priorytetem są wyróżnione czerwoną ramką. Użytkownik może wyszukać “zebranie” i znaleźć powiązane ogłoszenia.
Walidacja danych wejściowych:
$title = trim($_POST['title'] ?? '');$content = trim($_POST['content'] ?? '');$category = $_POST['category'] ?? '';$validTo = $_POST['valid_to'] ?? '';$today = date('Y-m-d');
if (empty($title)) { $errors[] = "Tytuł jest wymagany";}
if (strlen($title) > 200) { $errors[] = "Tytuł może mieć maksymalnie 200 znaków";}
if (empty($content)) { $errors[] = "Treść ogłoszenia jest wymagana";}
$validCategories = ['zebrania', 'wydarzenia', 'organizacja', 'konkursy', 'inne'];if (!in_array($category, $validCategories)) { $errors[] = "Wybierz poprawną kategorię";}
if ($validTo < $today) { $errors[] = "Data ważności musi być dzisiejsza lub przyszła";}Określanie statusu ogłoszenia:
function isAnnouncementActive(array $announcement): bool { $today = date('Y-m-d'); $validFrom = $announcement['valid_from'] ?? $announcement['created_at']; $validTo = $announcement['valid_to'];
return $validFrom <= $today && $validTo >= $today;}
function getAnnouncementStatus(array $announcement): string { $today = date('Y-m-d'); $validFrom = $announcement['valid_from'] ?? $today; $validTo = $announcement['valid_to'];
if ($today < $validFrom) { return 'scheduled'; // Zaplanowane } elseif ($today > $validTo) { return 'expired'; // Wygasłe } return 'active'; // Aktywne}Filtrowanie ogłoszeń:
function getActiveAnnouncements(array $announcements): array { return array_filter($announcements, fn($a) => isAnnouncementActive($a));}
function getArchivedAnnouncements(array $announcements): array { $today = date('Y-m-d'); return array_filter($announcements, fn($a) => $a['valid_to'] < $today);}
function filterByCategory(array $announcements, string $category): array { if (empty($category)) { return $announcements; } return array_filter($announcements, fn($a) => $a['category'] === $category);}Sortowanie ogłoszeń:
function sortAnnouncements(array $announcements, string $sortBy = 'valid_to', string $order = 'asc'): array { usort($announcements, function($a, $b) use ($sortBy, $order) { $result = strcmp($a[$sortBy] ?? '', $b[$sortBy] ?? ''); return $order === 'desc' ? -$result : $result; });
return $announcements;}
// Sortuj: najpierw wysokie priorytety, potem po daciefunction sortByPriorityAndDate(array $announcements): array { usort($announcements, function($a, $b) { // Najpierw po priorytecie (high przed normal) $priorityOrder = ['high' => 0, 'normal' => 1]; $priorityA = $priorityOrder[$a['priority'] ?? 'normal']; $priorityB = $priorityOrder[$b['priority'] ?? 'normal'];
if ($priorityA !== $priorityB) { return $priorityA - $priorityB; }
// Potem po dacie ważności (najbliższe najpierw) return strcmp($a['valid_to'], $b['valid_to']); });
return $announcements;}Dodawanie ogłoszenia:
function addAnnouncement(array &$announcements, array $data): int { $newId = empty($announcements) ? 1 : max(array_column($announcements, 'id')) + 1;
$newAnnouncement = [ 'id' => $newId, 'title' => $data['title'], 'content' => $data['content'], 'category' => $data['category'], 'priority' => $data['priority'] ?? 'normal', 'valid_from' => $data['valid_from'] ?? date('Y-m-d'), 'valid_to' => $data['valid_to'], 'author' => $data['author'] ?? 'Administrator', 'created_at' => date('Y-m-d H:i:s'), ];
$announcements[] = $newAnnouncement; return $newId;}Kategorie z kolorami:
function getCategories(): array { return [ 'zebrania' => ['label' => 'Zebrania', 'icon' => '👥', 'color' => '#3498db'], 'wydarzenia' => ['label' => 'Wydarzenia', 'icon' => '🎉', 'color' => '#9b59b6'], 'organizacja' => ['label' => 'Organizacja', 'icon' => '📋', 'color' => '#e74c3c'], 'konkursy' => ['label' => 'Konkursy', 'icon' => '🏆', 'color' => '#f39c12'], 'inne' => ['label' => 'Inne', 'icon' => '📢', 'color' => '#95a5a6'], ];}Obliczanie dni do wygaśnięcia:
function getDaysUntilExpiry(array $announcement): int { $today = new DateTime(); $validTo = new DateTime($announcement['valid_to']); $diff = $today->diff($validTo);
return $diff->invert ? -$diff->days : $diff->days;}
function formatExpiryInfo(int $days): string { if ($days < 0) { return 'Wygasło ' . abs($days) . ' dni temu'; } elseif ($days === 0) { return 'Wygasa dzisiaj!'; } elseif ($days === 1) { return 'Wygasa jutro'; } else { return 'Wygasa za ' . $days . ' dni'; }}Wykorzystaj lekcje!
Cotygodniowe spotkania podczas lekcji to idealny moment, by:
Pracuj iteracyjnie - lepiej mieć działający wariant A niż niedokończony C!