The School Management Platform uses a MySQL relational database to store all application data. The schema is designed with normalization principles to ensure data integrity and eliminate redundancy.
Stores authentication and basic information for all system users.
Column
Type
Constraints
Description
id
INT
PRIMARY KEY, AUTO_INCREMENT
Unique user identifier
name
VARCHAR(255)
NOT NULL
User’s full name
email
VARCHAR(255)
UNIQUE, NOT NULL
Login email address
password
VARCHAR(255)
NOT NULL
Hashed password
role
ENUM(‘admin’, ‘teacher’, ‘student’)
NOT NULL
User role for access control
Example Query:
app/models/User.php:14
public function findByEmail($email){ $stmt = $this->db->prepare("SELECT * FROM users WHERE email = ?"); $stmt->execute([$email]); return $stmt->fetch(PDO::FETCH_ASSOC);}
Passwords are hashed using password_hash() with the PASSWORD_DEFAULT algorithm before storage. Never store plain text passwords.
Extends user information for students (inheritance pattern).
Column
Type
Constraints
Description
id
INT
PRIMARY KEY, FOREIGN KEY → users(id)
References user record
Example Query:
app/models/Student.php:14
public function getAll(){ $sql = "SELECT users.id, users.name, users.email FROM users JOIN students ON users.id = students.id ORDER BY users.name"; return $this->db->query($sql)->fetchAll(PDO::FETCH_ASSOC);}
Extends user information for teachers (inheritance pattern).
Column
Type
Constraints
Description
id
INT
PRIMARY KEY, FOREIGN KEY → users(id)
References user record
Example Query:
app/models/Teacher.php:14
public function all(){ return $this->db->query(" SELECT users.id, users.name, users.email FROM teachers JOIN users ON teachers.id = users.id ")->fetchAll(PDO::FETCH_ASSOC);}
Represents a specific instance of a subject taught by a teacher.
Column
Type
Constraints
Description
id
INT
PRIMARY KEY, AUTO_INCREMENT
Unique course identifier
subject_id
INT
FOREIGN KEY → subjects(id)
Subject being taught
teacher_id
INT
FOREIGN KEY → teachers(id)
Teacher assigned
A course is the combination of a subject and a teacher. Multiple courses can exist for the same subject if taught by different teachers.
Example Query:
app/models/Course.php:14
public function getAll(){ return $this->db->query(" SELECT courses.id, subjects.name AS subject_name, users.name AS teacher_name FROM courses JOIN subjects ON courses.subject_id = subjects.id JOIN teachers ON courses.teacher_id = teachers.id JOIN users ON teachers.id = users.id ")->fetchAll(PDO::FETCH_ASSOC);}
public function getAll(){ $sql = "SELECT students.id AS student_id, users.name AS student_name, subjects.name AS subject_name, courses.id AS course_id FROM enrollments JOIN students ON enrollments.student_id = students.id JOIN users ON students.id = users.id JOIN courses ON enrollments.course_id = courses.id JOIN subjects ON courses.subject_id = subjects.id"; return $this->db->query($sql)->fetchAll(PDO::FETCH_ASSOC);}
Enrollment Check:
app/models/Enrollment.php:55
public function isAlreadyEnrolled($student_id, $course_id){ $stmt = $this->db->prepare( "SELECT COUNT(*) FROM enrollments WHERE student_id = ? AND course_id = ?" ); $stmt->execute([$student_id, $course_id]); return $stmt->fetchColumn() > 0;}
The REPLACE INTO statement is used to insert or update grades, ensuring only one grade exists per student-course combination.
Example Query:
app/models/Grade.php:29
public function save($student_id, $course_id, $grade){ $stmt = $this->db->prepare(" REPLACE INTO grades (student_id, course_id, grade) VALUES (?, ?, ?) "); $stmt->execute([$student_id, $course_id, $grade]);}
Student Bulletin Query:
app/models/Grade.php:51
public function getBulletinData($student_id){ $sql = " SELECT subjects.name AS subject, users.name AS teacher, grades.grade FROM grades JOIN courses ON grades.course_id = courses.id JOIN subjects ON courses.subject_id = subjects.id JOIN users ON courses.teacher_id = users.id WHERE grades.student_id = ? "; $stmt = $this->db->prepare($sql); $stmt->execute([$student_id]); return $stmt->fetchAll(PDO::FETCH_ASSOC);}
public function getAll(){ $sql = " SELECT timetable.id, subjects.name AS subject, users.name AS teacher, timetable.day, timetable.start_time, timetable.end_time FROM timetable JOIN courses ON courses.id = timetable.course_id JOIN subjects ON subjects.id = courses.subject_id JOIN teachers ON teachers.id = courses.teacher_id JOIN users ON users.id = teachers.id ORDER BY timetable.day, timetable.start_time "; return $this->db->query($sql)->fetchAll(PDO::FETCH_ASSOC);}
Teacher Conflict Detection:
app/models/Timetable.php:44
public function teacherHasConflict($teacher_id, $day, $start, $end){ $sql = " SELECT COUNT(*) FROM timetable t JOIN courses c ON c.id = t.course_id WHERE c.teacher_id = ? AND t.day = ? AND (? < t.end_time AND ? > t.start_time) "; $stmt = $this->db->prepare($sql); $stmt->execute([$teacher_id, $day, $start, $end]); return $stmt->fetchColumn() > 0;}
-- Get all courses for a studentSELECT courses.*, subjects.nameFROM enrollmentsJOIN courses ON enrollments.course_id = courses.idJOIN subjects ON courses.subject_id = subjects.idWHERE enrollments.student_id = ?
Students ↔ Grades ↔ Courses
-- Get all grades for a student with subject infoSELECT subjects.name AS subject, grades.gradeFROM gradesJOIN courses ON grades.course_id = courses.idJOIN subjects ON courses.subject_id = subjects.idWHERE grades.student_id = ?
public function getByTeacher($teacher_id){ $stmt = $this->db->prepare(" SELECT courses.id, subjects.name FROM courses JOIN subjects ON courses.subject_id = subjects.id WHERE courses.teacher_id = ? "); $stmt->execute([$teacher_id]); return $stmt->fetchAll();}
public function getForStudent($student_id){ $sql = " SELECT subjects.name AS subject, users.name AS teacher, timetable.day, timetable.start_time, timetable.end_time FROM timetable JOIN courses ON courses.id = timetable.course_id JOIN enrollments ON enrollments.course_id = courses.id JOIN subjects ON subjects.id = courses.subject_id JOIN teachers ON teachers.id = courses.teacher_id JOIN users ON users.id = teachers.id WHERE enrollments.student_id = ? ORDER BY timetable.day, timetable.start_time "; $stmt = $this->db->prepare($sql); $stmt->execute([$student_id]); return $stmt->fetchAll(PDO::FETCH_ASSOC);}
public function getForTeacher($teacher_id){ $sql = " SELECT subjects.name AS subject, timetable.day, timetable.start_time, timetable.end_time FROM timetable JOIN courses ON courses.id = timetable.course_id JOIN subjects ON subjects.id = courses.subject_id WHERE courses.teacher_id = ? ORDER BY timetable.day, timetable.start_time "; $stmt = $this->db->prepare($sql); $stmt->execute([$teacher_id]); return $stmt->fetchAll(PDO::FETCH_ASSOC);}
public function getStudentsByCourse($course_id){ $stmt = $this->db->prepare(" SELECT students.id, users.name, grades.grade FROM enrollments JOIN students ON enrollments.student_id = students.id JOIN users ON students.id = users.id LEFT JOIN grades ON grades.student_id = students.id AND grades.course_id = enrollments.course_id WHERE enrollments.course_id = ? "); $stmt->execute([$course_id]); return $stmt->fetchAll();}