findAll(); return $this->withNoSchoolFlag($events); } public function getEventByDate($date) { $events = $this->where('date', $date)->findAll(); return $this->withNoSchoolFlag($events); } public function addEvent($data) { return $this->insert($data); } public function getEventsBySchoolYear($schoolYear) { $events = $this->where('school_year', $schoolYear) ->orderBy('date', 'ASC') ->findAll(); return $this->withNoSchoolFlag($events); } public function getEventsBySchoolYearAndSemester(string $schoolYear, ?string $semester = null): array { $builder = $this->where('school_year', $schoolYear); if (is_string($semester) && $semester !== '') { $builder = $builder->where('semester', $semester); } $events = $builder->orderBy('date', 'ASC')->findAll(); return $this->withNoSchoolFlag($events); } public function supportsEventType(): bool { if ($this->hasEventTypeColumn === null) { $this->hasEventTypeColumn = $this->db->fieldExists('event_type', $this->table); } return $this->hasEventTypeColumn; } /** * Add computed no_school flag to each event without requiring DB schema changes. * An event is considered no-school if title or description contains keywords. * * @param array $events * @return array */ protected function withNoSchoolFlag(array $events): array { return array_map(function ($event) { // Respect DB value if present; otherwise infer heuristically if (array_key_exists('no_school', (array) $event)) { $event['no_school'] = (int) ((array) $event)['no_school']; } else { $event['no_school'] = $this->isNoSchoolEvent((array) $event) ? 1 : 0; } return $event; }, $events); } /** * Heuristic to determine "no school" days from event text. */ protected function isNoSchoolEvent(array $event): bool { $title = strtolower((string)($event['title'] ?? '')); $desc = strtolower((string)($event['description'] ?? '')); $text = $title . ' ' . $desc; $keywords = ['no school', 'no-school', 'cancel', 'vacation', 'holiday', 'break']; foreach ($keywords as $kw) { if ($kw !== '' && strpos($text, $kw) !== false) { return true; } } return false; } }