Lesson 47 of 50
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:
Technologies used:
By completing this project, you will understand how routes, controllers, models, migrations, validation, and Blade views work together.
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.
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.
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
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.
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.
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.
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.
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. 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?