table = $db->prefix . 'us_guardians'; } /** * Link a child to a guardian. Returns 0 without inserting when the child * already has a guardian: v1 is one guardian per child, and the check lives * here so every caller (signup, the family screen, admin) gets it. */ public function insert( GuardianLink $link ): int { if ( null !== $this->findByStudent( $link->studentId ) ) { return 0; } $this->db->insert( $this->table, [ 'guardian_id' => $link->guardianId, 'student_id' => $link->studentId, 'relationship' => $link->relationship, 'created_at' => current_time( 'mysql' ), ], [ '%d', '%d', '%s', '%s' ] ); return $this->db->insert_id; } /** * The link naming this child's guardian, or null when they book for * themselves. */ public function findByStudent( int $studentId ): ?GuardianLink { $row = $this->db->get_row( $this->db->prepare( 'SELECT * FROM %i WHERE student_id = %d LIMIT 1', $this->table, $studentId ) ); return $row ? GuardianLink::fromRow( $row ) : null; } /** * Every child linked to a guardian, oldest link first — the order they are * offered in the booking selector, so it stays stable as children are added. * * @return list */ public function findByGuardian( int $guardianId ): array { $rows = $this->db->get_results( $this->db->prepare( 'SELECT * FROM %i WHERE guardian_id = %d ORDER BY created_at ASC, id ASC', $this->table, $guardianId ) ); return array_map( GuardianLink::fromRow( ... ), $rows ?? [] ); } /** * Whether this exact guardian↔child pair is linked — the authorisation check * behind every "act for this student" boundary. */ public function isGuardianOf( int $guardianId, int $studentId ): bool { $found = $this->db->get_var( $this->db->prepare( 'SELECT id FROM %i WHERE guardian_id = %d AND student_id = %d LIMIT 1', $this->table, $guardianId, $studentId ) ); return null !== $found; } /** * Remove the link between a guardian and one of their children. Deleting the * child user itself is the caller's decision ({@see GuardianService::removeChild()}); * this only unlinks. */ public function delete( int $guardianId, int $studentId ): bool { $deleted = $this->db->delete( $this->table, [ 'guardian_id' => $guardianId, 'student_id' => $studentId, ], [ '%d', '%d' ] ); return (int) $deleted > 0; } /** * How many children a guardian has — enough to decide whether the booking * page needs a "who is this for?" selector at all. */ public function countChildren( int $guardianId ): int { $count = $this->db->get_var( $this->db->prepare( 'SELECT COUNT(*) FROM %i WHERE guardian_id = %d', $this->table, $guardianId ) ); return (int) $count; } }