Building a Student Management System in Laravel

Lesson 47 of 50

94%
1. Introduction to the Student Management System

In this project, we will build a Student Management System using Laravel, MySQL, Blade, and Bootstrap. The application will allow an administrator to manage student records.

Project features:

  • Add new students.
  • Display all students.
  • View individual student details.
  • Edit student information.
  • Delete student records.
  • Search students by name or email.
  • Validate form data.
  • Store student information in a MySQL database.

Technologies used:

  • Laravel — backend framework.
  • PHP — programming language.
  • MySQL — database.
  • Blade — template engine.
  • Bootstrap — responsive user interface.

By completing this project, you will understand how routes, controllers, models, migrations, validation, and Blade views work together.

2. Create the Laravel Project and Database

First, create a Laravel project:

composer create-project laravel/laravel StudentManagement
cd StudentManagement

Open the project in your code editor.

Create a MySQL database named student_management using phpMyAdmin or your preferred MySQL client.

Update the database settings in the .env file:

DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=student_management
DB_USERNAME=root
DB_PASSWORD=

Replace the username and password with your actual MySQL credentials.

Create the model, migration, and controller:

php artisan make:model Student -m
php artisan make:controller StudentController --resource

The -m option creates a migration. The resource controller provides methods for common CRUD operations.

3. Create the Students Database Table

Open the migration created inside the database/migrations directory.

Define the students table:

use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

return new class extends Migration
{
    public function up(): void
    {
        Schema::create('students', function (Blueprint $table) {
            $table->id();
            $table->string('name');
            $table->string('email')->unique();
            $table->string('phone', 20)->nullable();
            $table->string('course');
            $table->date('admission_date')->nullable();
            $table->timestamps();
        });
    }

    public function down(): void
    {
        Schema::dropIfExists('students');
    }
};

Run the migration:

php artisan migrate

Table fields:

  • id — unique student record ID.
  • name — student's name.
  • email — unique email address.
  • phone — optional contact number.
  • course — student's course.
  • admission_date — admission date.
  • timestamps — creation and update times.
4. Configure the Student Model and Routes

Open app/Models/Student.php and configure the fields that can be assigned using mass assignment.

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Student extends Model
{
    protected $fillable = [
        'name',
        'email',
        'phone',
        'course',
        'admission_date',
    ];

    protected function casts(): array
    {
        return [
            'admission_date' => 'date',
        ];
    }
}

The casts() method is available in modern Laravel versions. In older versions, you can use the $casts property instead.

Now open routes/web.php:

use App\Http\Controllers\StudentController;
use Illuminate\Support\Facades\Route;

Route::get('/', function () {
    return redirect()->route('students.index');
});

Route::resource('students', StudentController::class);

This resource route registers routes for listing, creating, saving, viewing, editing, updating, and deleting student records.

To inspect the registered routes, run:

php artisan route:list --path=students
5. Build the Student Controller

Open app/Http/Controllers/StudentController.php. Add the following CRUD logic:

<?php

namespace App\Http\Controllers;

use App\Models\Student;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;

class StudentController extends Controller
{
    public function index(Request $request)
    {
        $students = Student::query()
            ->when($request->filled('search'), function ($query) use ($request) {
                $search = $request->input('search');

                $query->where(function ($q) use ($search) {
                    $q->where('name', 'like', "%{$search}%")
                      ->orWhere('email', 'like', "%{$search}%");
                });
            })
            ->latest()
            ->paginate(10)
            ->withQueryString();

        return view('students.index', compact('students'));
    }

    public function create()
    {
        return view('students.create');
    }

    public function store(Request $request)
    {
        $validated = $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'email' => ['required', 'email', 'max:255', 'unique:students,email'],
            'phone' => ['nullable', 'string', 'max:20'],
            'course' => ['required', 'string', 'max:255'],
            'admission_date' => ['nullable', 'date'],
        ]);

        Student::create($validated);

        return redirect()
            ->route('students.index')
            ->with('success', 'Student added successfully.');
    }

    public function show(Student $student)
    {
        return view('students.show', compact('student'));
    }

    public function edit(Student $student)
    {
        return view('students.edit', compact('student'));
    }

    public function update(Request $request, Student $student)
    {
        $validated = $request->validate([
            'name' => ['required', 'string', 'max:255'],
            'email' => [
                'required',
                'email',
                'max:255',
                Rule::unique('students', 'email')->ignore($student->id),
            ],
            'phone' => ['nullable', 'string', 'max:20'],
            'course' => ['required', 'string', 'max:255'],
            'admission_date' => ['nullable', 'date'],
        ]);

        $student->update($validated);

        return redirect()
            ->route('students.index')
            ->with('success', 'Student updated successfully.');
    }

    public function destroy(Student $student)
    {
        $student->delete();

        return redirect()
            ->route('students.index')
            ->with('success', 'Student deleted successfully.');
    }
}

This controller handles listing, searching, creating, viewing, editing, updating, and deleting student records.

The controller validates incoming data before saving it to the database.

6. Create the Student List View

Create the folder resources/views/students.

Create resources/views/students/index.blade.php:

<!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">
    <div class="d-flex justify-content-between align-items-center mb-4">
        <h2>Student Management</h2>
        <a href="{{ route('students.create') }}"
           class="btn btn-primary">Add Student</a>
    </div>

    @if (session('success'))
        <div class="alert alert-success">
            {{ session('success') }}
        </div>
    @endif

    <form method="GET" action="{{ route('students.index') }}"
          class="row g-2 mb-3">
        <div class="col-md-8">
            <input type="text" name="search"
                   value="{{ request('search') }}"
                   class="form-control"
                   placeholder="Search by name or email">
        </div>
        <div class="col-md-4">
            <button type="submit" class="btn btn-dark">
                Search
            </button>
            <a href="{{ route('students.index') }}"
               class="btn btn-secondary">Reset</a>
        </div>
    </form>

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

            <tbody>
                @forelse ($students as $student)
                    <tr>
                        <td>{{ $student->id }}</td>
                        <td>{{ $student->name }}</td>
                        <td>{{ $student->email }}</td>
                        <td>{{ $student->course }}</td>
                        <td>
                            <a href="{{ route('students.show', $student) }}"
                               class="btn btn-info btn-sm">View</a>

                            <a href="{{ route('students.edit', $student) }}"
                               class="btn btn-warning btn-sm">Edit</a>

                            <form action="{{ route('students.destroy', $student) }}"
                                  method="POST" class="d-inline"
                                  onsubmit="return confirm('Delete this student?')">
                                @csrf
                                @method('DELETE')
                                <button type="submit"
                                        class="btn btn-danger btn-sm">
                                    Delete
                                </button>
                            </form>
                        </td>
                    </tr>
                @empty
                    <tr>
                        <td colspan="5" class="text-center">
                            No students found.
                        </td>
                    </tr>
                @endforelse
            </tbody>
        </table>
    </div>

    <div class="mt-3">
        {{ $students->links() }}
    </div>
</div>

</body>
</html>

The page displays student records in a table and provides search, pagination, and CRUD action links.

This example uses Bootstrap 5.3.3 from a CDN. You can replace it with your project's existing stylesheet or layout.

7. Create the Add Student Form

Create resources/views/students/create.blade.php.

<!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">
        <div class="card-header bg-primary text-white">
            <h4 class="mb-0">Add New Student</h4>
        </div>

        <div class="card-body">
            <form action="{{ route('students.store') }}" method="POST">
                @csrf

                <div class="mb-3">
                    <label class="form-label">Student Name</label>
                    <input type="text" name="name"
                           value="{{ old('name') }}"
                           class="form-control"
                           required>
                    @error('name')
                        <div class="text-danger">{{ $message }}</div>
                    @enderror
                </div>

                <div class="mb-3">
                    <label class="form-label">Email</label>
                    <input type="email" name="email"
                           value="{{ old('email') }}"
                           class="form-control"
                           required>
                    @error('email')
                        <div class="text-danger">{{ $message }}</div>
                    @enderror
                </div>

                <div class="mb-3">
                    <label class="form-label">Phone</label>
                    <input type="text" name="phone"
                           value="{{ old('phone') }}"
                           class="form-control">
                    @error('phone')
                        <div class="text-danger">{{ $message }}</div>
                    @enderror
                </div>

                <div class="mb-3">
                    <label class="form-label">Course</label>
                    <select name="course" class="form-select" required>
                        <option value="">Select Course</option>
                        @foreach (['ADCA', 'Web Development', 'Python', 'Tally Prime'] as $course)
                            <option value="{{ $course }}"
                                @selected(old('course') === $course)>
                                {{ $course }}
                            </option>
                        @endforeach
                    </select>
                    @error('course')
                        <div class="text-danger">{{ $message }}</div>
                    @enderror
                </div>

                <div class="mb-3">
                    <label class="form-label">Admission Date</label>
                    <input type="date" name="admission_date"
                           value="{{ old('admission_date') }}"
                           class="form-control">
                    @error('admission_date')
                        <div class="text-danger">{{ $message }}</div>
                    @enderror
                </div>

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

                <a href="{{ route('students.index') }}"
                   class="btn btn-secondary">Cancel</a>
            </form>
        </div>
    </div>
</div>

</body>
</html>

The @csrf directive protects the form against cross-site request forgery. Laravel's validation errors are displayed beside the relevant fields.

8. Create View and Edit Student Pages

Create resources/views/students/show.blade.php to display an individual student's information.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Student Details</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">
        <div class="card-header bg-info text-white">
            <h4>Student Details</h4>
        </div>

        <div class="card-body">
            <p><strong>ID:</strong> {{ $student->id }}</p>
            <p><strong>Name:</strong> {{ $student->name }}</p>
            <p><strong>Email:</strong> {{ $student->email }}</p>
            <p><strong>Phone:</strong> {{ $student->phone ?? 'Not provided' }}</p>
            <p><strong>Course:</strong> {{ $student->course }}</p>
            <p><strong>Admission Date:</strong>
                {{ $student->admission_date?->format('d-m-Y') ?? 'Not provided' }}
            </p>

            <a href="{{ route('students.edit', $student) }}"
               class="btn btn-warning">Edit Student</a>

            <a href="{{ route('students.index') }}"
               class="btn btn-secondary">Back</a>
        </div>
    </div>
</div>

</body>
</html>

Now create the edit page:

Create resources/views/students/edit.blade.php. You can reuse the Add Student form and change the action, HTTP method, and default field values.

<form action="{{ route('students.update', $student) }}"
      method="POST">

    @csrf
    @method('PUT')

    <label>Student Name</label>
    <input type="text" name="name"
           value="{{ old('name', $student->name) }}" required>

    <label>Email</label>
    <input type="email" name="email"
           value="{{ old('email', $student->email) }}" required>

    <label>Phone</label>
    <input type="text" name="phone"
           value="{{ old('phone', $student->phone) }}">

    <label>Course</label>
    <input type="text" name="course"
           value="{{ old('course', $student->course) }}" required>

    <label>Admission Date</label>
    <input type="date" name="admission_date"
           value="{{ old('admission_date', $student->admission_date?->format('Y-m-d')) }}">

    <button type="submit">Update Student</button>
</form>

Add Bootstrap classes and validation messages to the edit form as needed. The controller validates all fields before updating the record.

9. Run and Test the Project

Run database migrations if you have not already done so:

php artisan migrate

Start the Laravel development server:

php artisan serve

Open the following URLs:

URL Purpose
/students List and search students
/students/create Add a student
/students/1 View student ID 1, if it exists
/students/1/edit Edit student ID 1, if it exists

Important security note:

This is a learning example, not a complete production administration system. Before deploying it, add authentication and authorization so only permitted users can view, create, update, or delete student records.

Final project tasks:

  1. Add at least five student records.
  2. Test searching by name and email.
  3. Edit a student's course and phone number.
  4. Try submitting an invalid email address.
  5. Test deleting a record.
  6. Add authentication and restrict administrative actions.
Quick Quiz

1. Which command creates a model with a migration?




2. Which Blade directive adds a CSRF token to a form?




3. Which method handles updating a student in a resource controller?