Laravel E-Learning Rebuild

1

Part 1: Introduction & Why I'm Upgrading

2

Part 2: Setting Up Laravel 12 + Filament

3

Part 3: Database Schema Design

4

Part 4: Authentication & User Roles

5

Part 5: Building the Exam System

6

Part 6: Real-Time Features

7

Part 7: Testing Strategy

8

Part 8: Deployment

This article is available in Indonesian

๐Ÿ‡ฎ๐Ÿ‡ฉ Baca dalam Bahasa Indonesia

March 10, 2026

๐Ÿ‡ฌ๐Ÿ‡ง English

Rebuilding My Laravel E-Learning App: A Journey from 5.2 to Modern Laravel

The story of upgrading a legacy Laravel 5.2 e-learning platform to modern Laravel with Filament, Livewire, and best practices. Plus, a sneak peek at the upcoming tutorial series.

5 min read


The Problem: A 7-Year-Old Codebase

Back in 2017, I built an e-learning platform for online examinations at a junior high school. It was written in Laravel 5.2 with PHP 5.6โ€”the standard stack at the time.

Fast forward to 2026, and that codebase is still being used. It works, but:

  • ๐Ÿ”ด Security vulnerabilities โ€” Laravel 5.2 hasn't received security patches in years
  • ๐Ÿ”ด PHP 5.6 is dead โ€” Most hosting providers don't support it anymore
  • ๐Ÿ”ด Modern features missing โ€” No real-time updates, poor mobile experience
  • ๐Ÿ”ด Maintenance nightmare โ€” Dependencies are outdated, documentation is scarce

It was time for a complete rebuild.

What the Original System Does

Before diving into the upgrade, let me explain what this e-learning platform does:

For Students

  • ๐Ÿ“Š Dashboard โ€” View enrolled courses and upcoming exams
  • ๐Ÿ“ Online Examinations โ€” Take timed exams with multiple choice questions
  • ๐Ÿ“ˆ Grade Reports โ€” View exam results and overall progress
  • ๐Ÿ’ฌ Discussion Forums โ€” Interact with teachers and classmates

For Teachers

  • ๐Ÿ“š Exam Management โ€” Create, edit, and schedule online exams
  • โ“ Question Bank โ€” Manage questions with different difficulty levels
  • ๐Ÿ“Š Analytics โ€” View student performance and identify struggling areas
  • ๐Ÿ“‹ Assignment Grading โ€” Review and grade student submissions

The Upgrade Strategy

I considered two approaches:

ApproachProsCons
Incremental UpgradeLess risky, keep existing code5.2 โ†’ 5.3 โ†’ 5.4 โ†’ ... โ†’ 11 (painful)
Full RebuildClean slate, modern architectureMore effort upfront, migration needed

I chose the full rebuild. Here's why:

  1. The jump is too big โ€” Laravel 5.2 to 11 spans 9 major versions
  2. Architecture has evolved โ€” Modern Laravel uses different patterns
  3. Better tools exist now โ€” Filament, Livewire, and Inertia didn't exist in 2017
  4. Learning opportunity โ€” Building from scratch means understanding every line

The New Tech Stack

Here's what I'm using for the rebuilt version:

ComponentOld (2017)New (2026)
FrameworkLaravel 5.2Laravel 12
PHP5.68.3
Admin PanelCustom BladeFilament 3
FrontendjQuery + BootstrapLivewire 3 + Tailwind
AuthenticationCustomLaravel Breeze
DatabaseMySQL 5.6MySQL 8 / PostgreSQL
TestingNone ๐Ÿ˜…Pest PHP
DeploymentFTP (yes, really)GitHub Actions + VPS

Key Architectural Decisions

1. Filament for Admin Panel

Instead of building custom admin CRUD from scratch, I'm using Filament 3. It provides:

// This is all you need for a complete CRUD
class ExamResource extends Resource
{
    protected static ?string $model = Exam::class;
    
    public static function form(Form $form): Form
    {
        return $form->schema([
            TextInput::make('title')->required(),
            DateTimePicker::make('start_time'),
            DateTimePicker::make('end_time'),
            Select::make('subject_id')
                ->relationship('subject', 'name'),
        ]);
    }
}

What took 500+ lines of custom code in 2017 now takes 50 lines with Filament.

2. Livewire for Real-Time Exam

The old exam system used JavaScript polling to check time remaining. Now with Livewire:

class TakeExam extends Component
{
    public Exam $exam;
    public int $timeRemaining;
    
    public function mount(Exam $exam)
    {
        $this->exam = $exam;
        $this->timeRemaining = $exam->duration_minutes * 60;
    }
    
    #[Computed]
    public function isTimeUp(): bool
    {
        return $this->timeRemaining <= 0;
    }
}

Real-time updates without writing custom WebSocket code.

3. Proper Testing from Day One

The original codebase had zero tests. The new version uses Pest PHP:

it('prevents students from taking expired exams', function () {
    $exam = Exam::factory()->expired()->create();
    $student = User::factory()->student()->create();
    
    actingAs($student)
        ->get(route('exams.take', $exam))
        ->assertForbidden();
});

Every feature is tested before it's considered "done."

Migration Strategy

The trickiest part is migrating data from the old system. Here's my approach:

Step 1: Export Old Data

-- Export from old MySQL database
SELECT * FROM siswa INTO OUTFILE '/tmp/students.csv';
SELECT * FROM guru INTO OUTFILE '/tmp/teachers.csv';
SELECT * FROM ujian INTO OUTFILE '/tmp/exams.csv';

Step 2: Transform to New Schema

// Laravel Seeder for migration
class MigrateOldDataSeeder extends Seeder
{
    public function run(): void
    {
        $oldStudents = json_decode(
            file_get_contents(database_path('legacy/students.json'))
        );
        
        foreach ($oldStudents as $old) {
            User::create([
                'name' => $old->nama_siswa,
                'email' => $old->email ?? $old->nis . '@school.local',
                'role' => 'student',
                'legacy_id' => $old->id,
            ]);
        }
    }
}

Step 3: Validate & Test

Run both systems in parallel, compare outputs, and verify data integrity.

Lessons Learned (So Far)

1. Don't Fear the Rebuild

Sometimes, the best refactor is a rewrite. The old codebase served its purpose, but clinging to it would have caused more pain long-term.

2. Modern Laravel is a Joy

The developer experience in Laravel 12 is incredible. Artisan commands, Tinker, Laravel Herdโ€”everything just works.

3. Document As You Build

I'm writing this article as I build. It forces me to explain my decisions, which often reveals flaws in my thinking.

Complete Tutorial Series

This article is Part 1 of a complete series documenting the entire rebuild:

PartTopicLink
1Introduction & Why I'm Upgradingโœ… This article
2Setting Up Laravel 12 + FilamentRead โ†’
3Database Schema DesignRead โ†’
4User Authentication & RolesRead โ†’
5Building the Exam SystemRead โ†’
6Real-Time Features with LivewireRead โ†’
7Testing StrategyRead โ†’
8Deployment to ProductionRead โ†’

The Repository

The original legacy codebase is available on GitHub:

๐Ÿ”— mfarim/laravel-elearning โ€” 42 โญ | 15 Forks

The new version will be released as a separate repository once it's ready for production.

Conclusion

Upgrading a 7-year-old Laravel application isn't just about changing version numbersโ€”it's about rethinking architecture, adopting modern tools, and building something that will last another 7 years.

If you're facing a similar legacy upgrade challenge, I hope this series helps you navigate the process. Feel free to reach out with questions or share your own upgrade stories!


Have a legacy Laravel app that needs upgrading? I offer consultation services for Laravel modernization. Get in touch.

Continue Reading

Next Article โ†’

Laravel E-Learning Part 2: Setting Up Laravel 12 with Filament Admin Panel

Next article