Lesson 50 of 50
Congratulations! You have reached the final lesson.
In this final project, you will build a complete Student Management System using Laravel, PHP, MySQL, Blade, and Bootstrap.
The system will allow authorized administrators to manage student records, courses, admissions, and fee information.
| Technology | Purpose |
|---|---|
| Laravel | Backend framework |
| PHP | Application programming |
| MySQL | Database storage |
| Blade | Dynamic HTML templates |
| Bootstrap | Responsive user interface |
Create a new Laravel project:
composer create-project laravel/laravel StudentManagement
cd StudentManagement
Create a MySQL database named:
student_management
Configure the database in your .env file:
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=student_management
DB_USERNAME=root
DB_PASSWORD=
Use your actual database username and password. Do not use development credentials in production.
Generate the main project files:
php artisan make:model Student -m
php artisan make:model Course -m
php artisan make:model FeePayment -m
php artisan make:controller StudentController --resource
php artisan make:controller CourseController --resource
php artisan make:controller AdminDashboardController
These commands create models, migration files, and controllers. You will still need to define their fields, relationships, and application logic.
Create migrations for the main tables. The following is a simplified database design.
Schema::create('students', function (Blueprint $table) {
$table->id();
$table->string('student_id')->unique();
$table->string('name');
$table->string('email')->unique();
$table->string('phone', 20)->nullable();
$table->foreignId('course_id')
->constrained()
->restrictOnDelete();
$table->date('admission_date')->nullable();
$table->timestamps();
});
Schema::create('courses', function (Blueprint $table) {
$table->id();
$table->string('course_name');
$table->decimal('fee', 10, 2)->default(0);
$table->unsignedInteger('duration_months')->default(0);
$table->timestamps();
});
Schema::create('fee_payments', function (Blueprint $table) {
$table->id();
$table->foreignId('student_id')
->constrained()
->restrictOnDelete();
$table->decimal('amount', 10, 2);
$table->date('payment_date');
$table->string('payment_method')->nullable();
$table->string('receipt_number')->unique();
$table->timestamps();
});
Add the appropriate imports to each migration file.
Each table definition belongs inside its own migration's
up() method.
Because fee payments reference students and students reference courses, create the courses migration first, then students, then fee payments. Check migration timestamps to ensure this order.
Run the migrations:
php artisan migrate
Configure the Student model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Student extends Model
{
protected $fillable = [
'student_id',
'name',
'email',
'phone',
'course_id',
'admission_date',
];
protected function casts(): array
{
return [
'admission_date' => 'date',
];
}
public function course(): BelongsTo
{
return $this->belongsTo(Course::class);
}
public function payments(): HasMany
{
return $this->hasMany(FeePayment::class);
}
}
Configure the Course model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Course extends Model
{
protected $fillable = [
'course_name',
'fee',
'duration_months',
];
public function students(): HasMany
{
return $this->hasMany(Student::class);
}
}
Configure the FeePayment model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class FeePayment extends Model
{
protected $fillable = [
'student_id',
'amount',
'payment_date',
'payment_method',
'receipt_number',
];
protected function casts(): array
{
return [
'amount' => 'decimal:2',
'payment_date' => 'date',
];
}
public function student(): BelongsTo
{
return $this->belongsTo(Student::class);
}
}
These relationships allow you to retrieve a student's course and payment records using Eloquent.
The casts() method is available in modern
Laravel versions. In older versions, you can use the
$casts property instead.
CRUD means Create, Read, Update, and Delete. These operations form the foundation of a student management system.
Create a resource controller:
php artisan make:controller StudentController --resource
Example of listing students with pagination:
use App\Models\Student;
public function index()
{
$students = Student::with('course')
->latest()
->paginate(10);
return view('students.index', compact('students'));
}
Example of creating a student:
use App\Models\Student;
use Illuminate\Http\Request;
public function store(Request $request)
{
$validated = $request->validate([
'student_id' => ['required', 'string', 'max:50', 'unique:students,student_id'],
'name' => ['required', 'string', 'max:255'],
'email' => ['required', 'email', 'unique:students,email'],
'phone' => ['nullable', 'string', 'max:20'],
'course_id' => ['required', 'exists:courses,id'],
'admission_date' => ['nullable', 'date'],
]);
Student::create($validated);
return redirect()
->route('students.index')
->with('success', 'Student added successfully.');
}
Implement the remaining resource methods for viewing, editing, updating, and deleting records. Use validation and authorization for each write operation.
Student resource routes:
use App\Http\Controllers\StudentController;
use Illuminate\Support\Facades\Route;
Route::resource('students', StudentController::class);
The resource route generates standard CRUD routes. In a real application, protect these routes with authentication and suitable authorization rules.
The admin dashboard should summarize the current state of the application.
Example controller statistics:
use App\Models\Student;
use App\Models\Course;
use App\Models\FeePayment;
public function index()
{
$totalStudents = Student::count();
$totalCourses = Course::count();
$totalPayments = FeePayment::sum('amount');
$recentStudents = Student::with('course')
->latest()
->take(5)
->get();
return view('admin.dashboard', compact(
'totalStudents',
'totalCourses',
'totalPayments',
'recentStudents'
));
}
Add the route in routes/web.php:
use App\Http\Controllers\AdminDashboardController;
Route::get(
'/admin/dashboard',
[AdminDashboardController::class, 'index']
)->middleware('auth')->name('admin.dashboard');
Example dashboard card in Blade:
<div class="card border-primary shadow-sm">
<div class="card-body">
<h6 class="text-muted">Total Students</h6>
<h2 class="text-primary">
{{ number_format($totalStudents) }}
</h2>
</div>
</div>
Add similar cards for courses and payments. The payment total above represents recorded payments; it is not necessarily the total amount due or the outstanding balance.
The auth middleware requires authentication
but does not automatically grant or deny administrator
privileges. Add an admin role check, gate, or policy.
The system can record fee payments made by students. Always validate the student, payment amount, and date.
Example controller method:
use App\Models\FeePayment;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
public function storePayment(Request $request)
{
$validated = $request->validate([
'student_id' => ['required', 'exists:students,id'],
'amount' => ['required', 'numeric', 'gt:0', 'max:99999999.99'],
'payment_date' => ['required', 'date'],
'payment_method' => [
'nullable',
Rule::in(['cash', 'bank_transfer', 'upi']),
],
]);
$validated['receipt_number'] =
'RCP-' . strtoupper((string) str()->uuid());
FeePayment::create($validated);
return back()->with(
'success',
'Payment recorded successfully.'
);
}
This method assumes it is placed inside an appropriate controller and that the model and database migration match the example.
Important payment rules:
For a production system, calculate outstanding fees using the actual course fee and all valid payments, taking discounts, adjustments, and refunds into account.
A management system stores personal and financial information. Protect it before using real student data.
@csrf in state-changing forms.Example protected route group:
Route::middleware('auth')->prefix('admin')->name('admin.')->group(function () {
Route::get('/dashboard', [
AdminDashboardController::class,
'index',
])->name('dashboard');
Route::resource('students', StudentController::class);
});
This protects the routes from unauthenticated visitors. Add administrator-specific authorization as well, and ensure the controller methods enforce the same permissions for every sensitive action.
Complete the following tasks before submitting your project:
php artisan migrate
php artisan route:list
php artisan test
php artisan optimize:clear
php artisan serve
Use php artisan serve for local development.
Production deployment requires a properly configured
web server or hosting platform.
Final deliverable:
Submit a working Student Management System with a database, CRUD operations, dashboard, fee records, authentication, authorization, validation, tests, and deployment documentation.
Congratulations! You have reached the end of the Laravel tutorial roadmap.
1. Which Laravel feature defines database schema changes?
2. Which method is commonly used to validate incoming form data in a controller?
3. Is the auth middleware alone enough to guarantee that only administrators can access an admin dashboard?