table = $db->prefix . 'us_lessons'; } public function insert( Lesson $lesson ): int { $this->db->insert( $this->table, [ 'slot_id' => $lesson->slotId, 'offering_id' => $lesson->offeringId, 'student_id' => $lesson->studentId, 'instructor_id' => $lesson->instructorId, 'recurrence' => $lesson->recurrence, 'series_id' => $lesson->seriesId, 'status' => $lesson->status, 'payment_id' => $lesson->paymentId, 'notes' => $lesson->notes, 'created_at' => current_time( 'mysql' ), ], [ '%d', '%d', '%d', '%d', '%s', '%d', '%s', '%d', '%s', '%s' ] ); return $this->db->insert_id; } /** * Create a weekly lesson series — one lesson per slot, all sharing a * `series_id` (the id of the first lesson row). * * @param list $slotIds Availability slot IDs to reserve, in order. * @return list Inserted lesson IDs. */ public function insertSeries( Lesson $template, array $slotIds ): array { $ids = []; $seriesId = 0; foreach ( $slotIds as $slotId ) { $id = $this->insert( new Lesson( slotId: $slotId, studentId: $template->studentId, instructorId: $template->instructorId, offeringId: $template->offeringId, recurrence: Lesson::RECURRENCE_WEEKLY, seriesId: $seriesId > 0 ? $seriesId : null, status: $template->status, notes: $template->notes, ) ); if ( 0 === $seriesId ) { $seriesId = $id; $this->setSeriesId( $id, $seriesId ); } $ids[] = $id; } return $ids; } private function setSeriesId( int $id, int $seriesId ): void { $this->db->update( $this->table, [ 'series_id' => $seriesId ], [ 'id' => $id ], [ '%d' ], [ '%d' ] ); } public function findById( int $id ): ?Lesson { $row = $this->db->get_row( $this->db->prepare( 'SELECT * FROM %i WHERE id = %d', $this->table, $id ) ); return $row ? Lesson::fromRow( $row ) : null; } /** * Upcoming lessons for an instructor (status != cancelled, slot in the future). * * @return list */ public function findUpcomingForInstructor( int $instructorId ): array { $avTable = str_replace( 'us_lessons', 'us_availability', $this->table ); $rows = $this->db->get_results( $this->db->prepare( 'SELECT l.* FROM %i l JOIN %i a ON a.id = l.slot_id WHERE l.instructor_id = %d AND l.status != %s AND a.start_dt >= %s ORDER BY a.start_dt ASC', $this->table, $avTable, $instructorId, Lesson::STATUS_CANCELLED, current_time( 'mysql' ) ) ); return array_map( Lesson::fromRow( ... ), $rows ?? [] ); } /** * Count a student's upcoming, non-cancelled lessons (slot in the future). */ public function countUpcomingForStudent( int $studentId ): int { $avTable = str_replace( 'us_lessons', 'us_availability', $this->table ); return (int) $this->db->get_var( $this->db->prepare( 'SELECT COUNT(*) FROM %i l JOIN %i a ON a.id = l.slot_id WHERE l.student_id = %d AND l.status != %s AND a.start_dt >= %s', $this->table, $avTable, $studentId, Lesson::STATUS_CANCELLED, current_time( 'mysql' ) ) ); } /** * All lessons for a student. * * @return list */ public function findByStudent( int $studentId ): array { $rows = $this->db->get_results( $this->db->prepare( 'SELECT * FROM %i WHERE student_id = %d ORDER BY created_at DESC', $this->table, $studentId ) ); return array_map( Lesson::fromRow( ... ), $rows ?? [] ); } /** * All upcoming lessons across all instructors (admin view). * * @return list */ public function findAllUpcoming(): array { $avTable = str_replace( 'us_lessons', 'us_availability', $this->table ); $rows = $this->db->get_results( $this->db->prepare( 'SELECT l.* FROM %i l JOIN %i a ON a.id = l.slot_id WHERE l.status != %s AND a.start_dt >= %s ORDER BY a.start_dt ASC', $this->table, $avTable, Lesson::STATUS_CANCELLED, current_time( 'mysql' ) ) ); return array_map( Lesson::fromRow( ... ), $rows ?? [] ); } public function setPaymentId( int $id, int $paymentId ): bool { return false !== $this->db->update( $this->table, [ 'payment_id' => $paymentId ], [ 'id' => $id ], [ '%d' ], [ '%d' ] ); } public function updateStatus( int $id, string $status ): bool { if ( ! in_array( $status, Lesson::VALID_STATUSES, true ) ) { return false; } return (bool) $this->db->update( $this->table, [ 'status' => $status ], [ 'id' => $id ], [ '%s' ], [ '%d' ] ); } }