PHP Student Management System Project
100%

PHP Student Management System Project

In this practical project, you will learn how to develop a Student Management System using PHP, MySQL, PDO, HTML, Bootstrap, and JavaScript.

The project will allow an administrator to add students, display student records, search for students, update details, and delete records.

1. Project Overview

Project name: Student Management System

Technologies used:

  • Frontend: HTML, CSS, Bootstrap
  • Backend: PHP
  • Database: MySQL
  • Database access: PDO
  • Client-side interaction: JavaScript

Main features:

  • Add a new student.
  • View all student records.
  • Search students by name or course.
  • Edit student details.
  • Delete a student record.
  • Validate form inputs.
  • Use prepared statements for database queries.

This lesson presents the core CRUD application. Authentication, role-based permissions, attendance, and fee management can be added as future features.

2. Create the Database and Table

First, create a MySQL database named student_management.

CREATE DATABASE student_management;

USE student_management;

CREATE TABLE students (
    id INT AUTO_INCREMENT PRIMARY KEY,
    name VARCHAR(100) NOT NULL,
    email VARCHAR(150) NOT NULL UNIQUE,
    course VARCHAR(100) NOT NULL,
    created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);

Column explanation:

  • id uniquely identifies each student.
  • name stores the student's name.
  • email stores a unique email address.
  • course stores the course name.
  • created_at stores the creation timestamp.

Run this SQL in phpMyAdmin or another MySQL client before running the PHP application.

3. Create the Database Connection

Create a file named db.php. This file establishes a reusable PDO connection.

File: db.php

<?php

$host = "localhost";
$dbname = "student_management";
$username = "YOUR_DB_USERNAME";
$password = "YOUR_DB_PASSWORD";

try {
    $pdo = new PDO(
        "mysql:host=$host;dbname=$dbname;charset=utf8mb4",
        $username,
        $password,
        [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES => false
        ]
    );
} catch (PDOException $e) {
    error_log($e->getMessage());

    http_response_code(500);
    exit("Unable to connect to the database.");
}
?>

Replace the placeholder credentials with your actual MySQL credentials. Keep this file outside publicly accessible locations when your deployment structure allows it.

The $pdo variable will be used by other PHP files to execute database queries.

4. Add a New Student

Create add_student.php. This page displays a form and saves a student using a prepared statement.

<?php
require_once "db.php";

$message = "";

if ($_SERVER["REQUEST_METHOD"] === "POST") {
    $name = trim($_POST["name"] ?? "");
    $email = trim($_POST["email"] ?? "");
    $course = trim($_POST["course"] ?? "");

    if ($name === "" || $course === "") {
        $message = "Name and course are required.";
    } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $message = "Enter a valid email address.";
    } else {
        try {
            $stmt = $pdo->prepare(
                "INSERT INTO students (name, email, course)
                 VALUES (:name, :email, :course)"
            );

            $stmt->execute([
                "name" => $name,
                "email" => $email,
                "course" => $course
            ]);

            header("Location: index.php?added=1");
            exit;

        } catch (PDOException $e) {
            error_log($e->getMessage());

            if ($e->getCode() === "23000") {
                $message = "This email address may already exist.";
            } else {
                $message = "Unable to save the student.";
            }
        }
    }
}
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Add Student</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
          rel="stylesheet">
</head>
<body class="bg-light">

<div class="container py-5">
    <div class="card shadow-sm mx-auto" style="max-width:600px;">
        <div class="card-header bg-primary text-white">
            Add New Student
        </div>

        <div class="card-body">

            <?php if ($message !== ""): ?>
                <div class="alert alert-danger">
                    <?= htmlspecialchars($message, ENT_QUOTES, "UTF-8") ?>
                </div>
            <?php endif; ?>

            <form method="POST">
                <div class="mb-3">
                    <label class="form-label">Student Name</label>
                    <input type="text" name="name"
                           class="form-control" required maxlength="100">
                </div>

                <div class="mb-3">
                    <label class="form-label">Email Address</label>
                    <input type="email" name="email"
                           class="form-control" required maxlength="150">
                </div>

                <div class="mb-3">
                    <label class="form-label">Course</label>
                    <input type="text" name="course"
                           class="form-control" required maxlength="100">
                </div>

                <button type="submit" class="btn btn-success">
                    Save Student
                </button>

                <a href="index.php" class="btn btn-secondary">
                    Back
                </a>
            </form>

        </div>
    </div>
</div>

</body>
</html>

This example validates the basic fields and uses a prepared statement. A production application should also implement CSRF protection and appropriate authorization.

5. Display and Search Students

Create index.php to display student records and search by name or course.

<?php
require_once "db.php";

$search = trim($_GET["search"] ?? "");

$sql = "SELECT id, name, email, course, created_at
        FROM students";

$params = [];

if ($search !== "") {
    $sql .= " WHERE name LIKE :name
              OR course LIKE :course";

    $params["name"] = "%" . $search . "%";
    $params["course"] = "%" . $search . "%";
}

$sql .= " ORDER BY id DESC";

$stmt = $pdo->prepare($sql);
$stmt->execute($params);

$students = $stmt->fetchAll();

function e($value) {
    return htmlspecialchars(
        (string) $value,
        ENT_QUOTES,
        "UTF-8"
    );
}
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Student Management</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
          rel="stylesheet">
</head>
<body class="bg-light">

<div class="container py-5">

    <h1 class="mb-4">Student Management System</h1>

    <?php if (isset($_GET["added"])): ?>
        <div class="alert alert-success">
            Student added successfully.
        </div>
    <?php endif; ?>

    <form method="GET" class="row g-2 mb-3">
        <div class="col-md-8">
            <input type="search" name="search"
                   class="form-control"
                   placeholder="Search name or course"
                   value="<?= e($search) ?>">
        </div>

        <div class="col-md-4">
            <button class="btn btn-primary" type="submit">
                Search
            </button>

            <a href="index.php" class="btn btn-secondary">
                Reset
            </a>
        </div>
    </form>

    <a href="add_student.php" class="btn btn-success mb-3">
        + Add Student
    </a>

    <div class="table-responsive">
        <table class="table table-bordered table-striped bg-white">
            <thead class="table-dark">
                <tr>
                    <th>ID</th>
                    <th>Name</th>
                    <th>Email</th>
                    <th>Course</th>
                    <th>Actions</th>
                </tr>
            </thead>

            <tbody>
                <?php if (!$students): ?>
                    <tr>
                        <td colspan="5" class="text-center">
                            No students found.
                        </td>
                    </tr>
                <?php else: ?>

                    <?php foreach ($students as $student): ?>
                        <tr>
                            <td><?= e($student["id"]) ?></td>
                            <td><?= e($student["name"]) ?></td>
                            <td><?= e($student["email"]) ?></td>
                            <td><?= e($student["course"]) ?></td>
                            <td>
                                <a class="btn btn-sm btn-primary"
                                   href="edit_student.php?id=<?= (int) $student["id"] ?>">
                                    Edit
                                </a>

                                <form method="POST"
                                      action="delete_student.php"
                                      class="d-inline"
                                      onsubmit="return confirm('Delete this student?');">
                                    <input type="hidden" name="id"
                                           value="<?= (int) $student["id"] ?>">

                                    <input type="hidden" name="csrf_token"
                                           value="<?= e($_SESSION["csrf_token"] ?? "") ?>">

                                    <button type="submit"
                                            class="btn btn-sm btn-danger">
                                        Delete
                                    </button>
                                </form>
                            </td>
                        </tr>
                    <?php endforeach; ?>

                <?php endif; ?>
            </tbody>
        </table>
    </div>

</div>

</body>
</html>

Before using the delete form above, add the session and CSRF initialization described in the security section below. The example illustrates the listing layout and prepared search query.

6. Update Student Details

Create edit_student.php. The page retrieves a student by ID and updates the student's details.

<?php
require_once "db.php";

$id = filter_input(INPUT_GET, "id", FILTER_VALIDATE_INT);

if (!$id || $id < 1) {
    http_response_code(400);
    exit("Invalid student ID.");
}

$stmt = $pdo->prepare(
    "SELECT id, name, email, course
     FROM students WHERE id = :id"
);
$stmt->execute(["id" => $id]);

$student = $stmt->fetch();

if (!$student) {
    http_response_code(404);
    exit("Student not found.");
}

$message = "";

if ($_SERVER["REQUEST_METHOD"] === "POST") {
    $name = trim($_POST["name"] ?? "");
    $email = trim($_POST["email"] ?? "");
    $course = trim($_POST["course"] ?? "");

    if ($name === "" || $course === "") {
        $message = "Name and course are required.";
    } elseif (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        $message = "Enter a valid email address.";
    } else {
        try {
            $stmt = $pdo->prepare(
                "UPDATE students
                 SET name = :name,
                     email = :email,
                     course = :course
                 WHERE id = :id"
            );

            $stmt->execute([
                "name" => $name,
                "email" => $email,
                "course" => $course,
                "id" => $id
            ]);

            header("Location: index.php?updated=1");
            exit;

        } catch (PDOException $e) {
            error_log($e->getMessage());
            $message = "Unable to update student details.";
        }
    }

    $student["name"] = $name;
    $student["email"] = $email;
    $student["course"] = $course;
}
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Edit Student</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css"
          rel="stylesheet">
</head>
<body class="bg-light">

<div class="container py-5">
    <div class="card mx-auto shadow-sm" style="max-width:600px;">
        <div class="card-header bg-primary text-white">
            Edit Student
        </div>

        <div class="card-body">

            <?php if ($message !== ""): ?>
                <div class="alert alert-danger">
                    <?= htmlspecialchars($message, ENT_QUOTES, "UTF-8") ?>
                </div>
            <?php endif; ?>

            <form method="POST">
                <div class="mb-3">
                    <label class="form-label">Name</label>
                    <input type="text" name="name"
                           class="form-control" required maxlength="100"
                           value="<?= htmlspecialchars($student["name"], ENT_QUOTES, "UTF-8") ?>">
                </div>

                <div class="mb-3">
                    <label class="form-label">Email</label>
                    <input type="email" name="email"
                           class="form-control" required maxlength="150"
                           value="<?= htmlspecialchars($student["email"], ENT_QUOTES, "UTF-8") ?>">
                </div>

                <div class="mb-3">
                    <label class="form-label">Course</label>
                    <input type="text" name="course"
                           class="form-control" required maxlength="100"
                           value="<?= htmlspecialchars($student["course"], ENT_QUOTES, "UTF-8") ?>">
                </div>

                <button class="btn btn-success" type="submit">
                    Update Student
                </button>

                <a href="index.php" class="btn btn-secondary">
                    Cancel
                </a>
            </form>

        </div>
    </div>
</div>

</body>
</html>

This illustrates the update query. Before deploying it, protect the form with authentication and CSRF validation, as shown in the next section.

7. Delete Student Records

Create delete_student.php. Deletion should use POST rather than a GET link, because it changes application data.

<?php
session_start();

header("Content-Type: text/plain; charset=UTF-8");

if ($_SERVER["REQUEST_METHOD"] !== "POST") {
    header("Allow: POST");
    http_response_code(405);
    exit("Method not allowed.");
}

$token = $_POST["csrf_token"] ?? "";

if (
    !is_string($token) ||
    empty($_SESSION["csrf_token"]) ||
    !hash_equals($_SESSION["csrf_token"], $token)
) {
    http_response_code(403);
    exit("Invalid security token.");
}

// Require an authenticated administrator here.
// A CSRF token alone does not provide authorization.

$id = filter_input(INPUT_POST, "id", FILTER_VALIDATE_INT);

if (!$id || $id < 1) {
    http_response_code(400);
    exit("Invalid student ID.");
}

require_once "db.php";

$stmt = $pdo->prepare(
    "DELETE FROM students WHERE id = :id"
);
$stmt->execute(["id" => $id]);

header("Location: index.php?deleted=1");
exit;
?>

Initialize the CSRF token on pages that display forms. For example, put the following near the beginning of index.php, before any HTML output:

<?php
session_start();

if (empty($_SESSION["csrf_token"])) {
    $_SESSION["csrf_token"] = bin2hex(random_bytes(32));
}

require_once "db.php";
?>

Use the same session initialization on the add and edit pages and include the token in their forms. Validate it before processing every state-changing request. Also check that the logged-in user has permission to delete the selected record.

8. Testing and Troubleshooting

Test the application in this order:

  1. Create the database and students table.
  2. Configure your PDO database credentials.
  3. Open add_student.php and add a student.
  4. Open index.php and verify the record.
  5. Search using a student name or course.
  6. Edit a student's name, email, or course.
  7. Test deleting a student using POST and a valid CSRF token.
  8. Try invalid input and a nonexistent student ID.

Common problems:

  • Connection error: Check MySQL credentials and database name.
  • Table not found: Run the SQL creation script.
  • Duplicate email: Use a different email address.
  • Blank page: Check the PHP server logs.
  • Delete request rejected: Confirm session initialization and CSRF validation.
  • Page not found: Check filenames and project paths.

On a production server, log errors privately and do not display database exceptions or stack traces to visitors.

9. Final Project Assignment

Extend the Student Management System into a complete application with an administrator dashboard.

Required features:

  • Admin login and logout.
  • Student registration form.
  • Unique student ID generation.
  • Student list with search and pagination.
  • View, edit, and delete student records.
  • Course management.
  • Fee payment and pending-fee reports.
  • Attendance tracking.
  • Input validation and CSRF protection.
  • Role-based authorization for protected pages.
  • Responsive Bootstrap interface.

Suggested folder structure:

student_management/
|
|-- db.php
|-- index.php
|-- add_student.php
|-- edit_student.php
|-- delete_student.php
|
|-- admin/
|   |-- login.php
|   |-- dashboard.php
|   |-- students.php
|   |-- courses.php
|   |-- fees.php
|   |-- attendance.php
|
|-- assets/
|   |-- css/
|   |-- js/
|
|-- database.sql

Final goal: Build a working CRUD application using PHP and MySQL, then expand it with secure login, fee management, and attendance features.


🧠 Quick Quiz

1. Which PHP extension is used in this project to connect to MySQL?




2. Which SQL operation changes an existing student record?




3. Why should a delete form use CSRF protection?