Course & Lessons

A learning management system structure with courses containing ordered lessons and tracking user enrollment and progress.

ERD Concept

One-to-Many + Many-to-Many: Courses have many lessons. Users enroll in courses. Lessons track completion per user.

Tables

courses

id instructor_id (FK) title slug description price is_published timestamps

lessons

id course_id (FK) title slug content video_url sort_order duration_minutes timestamps

enrollments

id user_id (FK) course_id (FK) enrolled_at completed_at timestamps

lesson_progress

id user_id (FK) lesson_id (FK) completed completed_at timestamps

Relationships

  • Course belongsTo User (instructor)
  • Course hasMany Lesson
  • Course hasMany Enrollment
  • Lesson belongsTo Course
  • Enrollment belongsTo User
  • Enrollment belongsTo Course
  • LessonProgress belongsTo User
  • LessonProgress belongsTo Lesson

Migration

Migration
Schema::create('courses', function (Blueprint $table) {
    $table->id();
    $table->foreignId('instructor_id')->constrained('users')->cascadeOnDelete();
    $table->string('title');
    $table->string('slug')->unique();
    $table->text('description')->nullable();
    $table->decimal('price', 8, 2)->default(0);
    $table->boolean('is_published')->default(false);
    $table->timestamps();
});

Schema::create('lessons', function (Blueprint $table) {
    $table->id();
    $table->foreignId('course_id')->constrained()->cascadeOnDelete();
    $table->string('title');
    $table->string('slug');
    $table->longText('content')->nullable();
    $table->string('video_url')->nullable();
    $table->unsignedInteger('sort_order')->default(0);
    $table->unsignedInteger('duration_minutes')->default(0);
    $table->timestamps();
    $table->index(['course_id', 'sort_order']);
});

Schema::create('enrollments', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained()->cascadeOnDelete();
    $table->foreignId('course_id')->constrained()->cascadeOnDelete();
    $table->timestamp('enrolled_at');
    $table->timestamp('completed_at')->nullable();
    $table->timestamps();
    $table->unique(['user_id', 'course_id']);
});

Schema::create('lesson_progress', function (Blueprint $table) {
    $table->id();
    $table->foreignId('user_id')->constrained()->cascadeOnDelete();
    $table->foreignId('lesson_id')->constrained()->cascadeOnDelete();
    $table->boolean('completed')->default(false);
    $table->timestamp('completed_at')->nullable();
    $table->timestamps();
    $table->unique(['user_id', 'lesson_id']);
});