Laravel Relationship Student, Major, dan Subject
Mempelajari tentang:
• Student belongs to Major (Many-to-One)
• Student belongs to many Subject through pivot table (Many-to-Many)
• Major has many Student (One-to-Many)
• Subject belongs to many Student through pivot table (Many-to-Many)
Migration Database
Migration adalah cara Laravel untuk membuat dan mengelola struktur tabel di database melalui kode PHP. Dengan migration, kita bisa membangun, mengubah, dan menghapus tabel secara versi terkontrol.
A. Migration untuk Tabel majors
Pada file migration ini, kita membuat struktur tabel majors, yang berisi:
timestamps(): untuk menyimpan waktu pembuatan dan pembaruan (created_at, updated_at)
id: kunci utama (auto increment)
name: nama jurusan, bertipe string
php artisan make:migration create_majors_table
Dalam fungsi up(), kita mendefinisikan struktur tabel:
Schema::create('majors', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
Tabel ini adalah referensi utama untuk relasi ke mahasiswa.
B. Migration untuk Tabel students
Tabel ini menyimpan data mahasiswa. Perintah:
php artisan make:migration create_students_table
Strukturnya:
nimdannamemenyimpan identitas mahasiswaaddressmenyimpan alamatmajor_idadalah foreign key yang merujuk ke tabelmajorsonDelete('cascade')berarti jika jurusan dihapus, data mahasiswa juga ikut dihapus
Struktur tabel mahasiswa:
nim: nomor induk mahasiswa (unik)name: nama mahasiswaaddress: alamat mahasiswamajor_id: foreign key yang menunjuk ke tabelmajorstimestamps(): waktu catatan dibuat/diperbarui
$table->foreignId('major_id')
->constrained('majors')
->onDelete('cascade');
Artinya, jika suatu jurusan dihapus, seluruh mahasiswa dalam jurusan itu juga otomatis terhapus.
C. Migration: subjects Table
php artisan make:migration create_subjects_table
Tabel ini menyimpan informasi mata kuliah:
name: nama mata kuliahsks: jumlah satuan kredit semestertimestamps()
Sangat penting karena akan direlasikan ke mahasiswa melalui pivot table.
D. Migration: student_subject (Pivot Table)
php artisan make:migration create_student_subject_table
Pivot table ini bertujuan untuk menghubungkan mahasiswa dan mata kuliah secara many-to-many:
- Satu mahasiswa bisa mengambil banyak mata kuliah.
- Satu mata kuliah bisa diambil oleh banyak mahasiswa.
$table->foreignId('student_id')->constrained('students')->onDelete('cascade');
$table->foreignId('subject_id')->constrained('subjects')->onDelete('cascade');
$table->unique(['student_id', 'subject_id']);
unique()digunakan untuk mencegah mahasiswa mengambil mata kuliah yang sama lebih dari satu kali.
Jalankan Migration:
php artisan migrate
Jika berhasil, keempat tabel akan muncul di database.
2. MODEL – Representasi Objek & Relasi Antar Tabel
Model adalah penghubung antara tabel di database dan logika bisnis aplikasi. Laravel menyediakan Eloquent ORM untuk mempermudah pemetaan ini.
A. Model Major
php artisan make:model Major
public function students() {
return $this->hasMany(Student::class);
}
- Ini adalah relasi one-to-many, satu jurusan memiliki banyak mahasiswa.
fillableberisi atribut yang bisa di-mass-assign, seperti saat membuat objek dengancreate().
B. Model Student
php artisan make:model Student
public function major() {
return $this->belongsTo(Major::class);
}
public function subjects() {
return $this->belongsToMany(Subject::class);
}
belongsTo(Major::class)→ mahasiswa memiliki satu jurusan.belongsToMany(Subject::class)→ mahasiswa bisa mengambil banyak mata kuliah melalui pivot table.- Pastikan tidak ada typo: gunakan
->belongsToMany(), bukan->>belongsToMany().
C. Model Subject
php artisan make:model Subject
public function students() {
return $this->belongsToMany(Student::class);
}
- Ini adalah kebalikan dari relasi mahasiswa.
- Setiap mata kuliah bisa diambil banyak mahasiswa.
3. SEEDER – Mengisi Database dengan Data Awal
Seeder membantu mengisi database dengan data awal (dummy) agar bisa langsung digunakan atau diuji.
A. Seeder MajorSeeder
php artisan make:seeder MajorSeeder
$majors = [
['name' => 'Teknik Informatika'],
['name' => 'Sistem Informasi'],
['name' => 'Teknik Komputer'],
['name' => 'Manajemen Informatika'],
];
foreach ($majors as $major) {
Major::create($major);
}
Seeder ini membuat 4 jurusan dan menyimpannya ke tabel majors.
B. Seeder SubjectSeeder
php artisan make:seeder SubjectSeeder
$subjects = [
['name' => 'Pemrograman Web', 'sks' => 3],
['name' => 'Database', 'sks' => 3],
['name' => 'Algoritma', 'sks' => 2],
['name' => 'Jaringan Komputer', 'sks' => 3],
['name' => 'Sistem Operasi', 'sks' => 2],
];
Seeder ini mengisi daftar mata kuliah lengkap dengan nilai SKS-nya.
C. Seeder StudentSeeder
php artisan make:seeder StudentSeeder
Seeder ini:
- Membuat 5 mahasiswa dengan data lengkap.
- Untuk setiap mahasiswa, dipilih 2–4 mata kuliah secara acak.
- Lalu disambungkan ke pivot table
student_subject.
$student->subjects()->attach($subjects);
Ini membuat setiap mahasiswa memiliki relasi ke beberapa mata kuliah sekaligus.
D. Update DatabaseSeeder
public function run()
{
$this->call([
MajorSeeder::class,
SubjectSeeder::class,
StudentSeeder::class,
]);
}
Seeder utama ini memanggil semua seeder lainnya agar bisa dijalankan sekaligus.
Jalankan Seeder:
php artisan db:seed
Setelah dijalankan, maka:
- Semua jurusan terisi
- Semua mata kuliah tersedia
- Mahasiswa lengkap dengan jurusannya
- Pivot table terisi otomatis
4. Membuat Controller
A. StudentController
php artisan make:controller StudentController
Ketik kode berikut di file controller:
>?php
// app/Http/Controllers/StudentController.php
namespace App\Http\Controllers;
use App\Models\Student;
use App\Models\Major;
use App\Models\Subject;
use Illuminate\Http\Request;
class StudentController extends Controller
{
public function index() {
$students = Student::with(['major', 'subjects'])->get();
return view('students.index', compact('students'));
}
public function show($id) {
$student = Student::with(['major', 'subjects'])->findOrFail($id);
return view('students.show', compact('student'));
}
public function create() {
return view('students.create', [
'majors' => Major::all(),
'subjects' => Subject::all()
]);
}
public function store(Request $r) {
$r->validate([
'nim' => 'required|unique:students',
'name' => 'required',
'address' => 'required',
'major_id' => 'required|exists:majors,id',
'subjects' => 'required|array',
'subjects.*' => 'exists:subjects,id',
]);
$student = Student::create($r->only(['nim', 'name', 'address', 'major_id']));
$student->subjects()->attach($r->subjects);
return redirect()->route('students.index')->with('success', 'Student created');
}
public function edit($id) {
return view('students.edit', [
'student' => Student::with('subjects')->findOrFail($id),
'majors' => Major::all(),
'subjects' => Subject::all()
]);
}
public function update(Request $r, $id) {
$student = Student::findOrFail($id);
$r->validate([
'nim' => 'required|unique:students,nim,' . $student->id,
'name' => 'required',
'address' => 'required',
'major_id' => 'required|exists:majors,id',
'subjects' => 'required|array',
'subjects.*' => 'exists:subjects,id',
]);
$student->update($r->only(['nim', 'name', 'address', 'major_id']));
$student->subjects()->sync($r->subjects);
return redirect()->route('students.index')->with('success', 'Student updated');
}
public function destroy($id) {
$student = Student::findOrFail($id);
$student->subjects()->detach();
$student->delete();
return redirect()->route('students.index')->with('success', 'Student deleted');
}
}
5. Membuat Routes
- Mengarahkan
'/'ke daftar mahasiswa. Route::resource()otomatis membuat semua route CRUD untuk mahasiswa.
Ketik kode berikut di web.php :
>?php
// routes/web.php
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\StudentController;
Route::get('/', fn() => redirect()->route('students.index'));
Route::resource('students', StudentController::class);
6. Membuat Views
A. Layout utama
- Template HTML utama. Menyediakan navbar, tampilan notifikasi
success, dan slot@yield('content')untuk konten halaman.
{{-- resources/views/layouts/app.blade.php --}}
Buat file pada resources/views/layouts/app.blade.php. kodenya adalah seperti berikut:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Student Management</title>
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
</head>
<body>
<nav class="navbar navbar-dark bg-primary">
<div class="container">
<a class="navbar-brand" href="{{ route('students.index') }}">Student Management</a>
</div>
</nav>
<div class="container mt-4">
@if(session('success'))
<div class="alert alert-success">{{ session('success') }}</div>
@endif
@yield('content')
</div>
</body>
</html>
B. Index Students
Menampilkan tabel mahasiswa dengan NIM, nama, jurusan, mata kuliah, total SKS, dan tombol aksi (detail, edit, hapus).
Buat file pada resources/views/students/index.blade.php. kodenya adalah seperti berikut:
@extends('layouts.app')
@section('content')
<div class="d-flex justify-content-between mb-4">
<h2>Daftar Mahasiswa</h2>
<a href="{{ route('students.create') }}" class="btn btn-primary">Tambah</a>
</div>
<table class="table table-bordered">
<thead>
<tr><th>NIM</th><th>Nama</th><th>Jurusan</th><th>Mata Kuliah</th><th>SKS</th><th>Aksi</th></tr>
</thead>
<tbody>
@foreach($students as $s)
<tr>
<td>{{ $s->nim }}</td>
<td>{{ $s->name }}</td>
<td>{{ $s->major->name }}</td>
<td>
@foreach($s->subjects as $sub)
<span class="badge bg-secondary">{{ $sub->name }}</span>
@endforeach
</td>
<td>{{ $s->subjects->sum('sks') }}</td>
<td>
<a href="{{ route('students.show', $s->id) }}" class="btn btn-info btn-sm">Detail</a>
<a href="{{ route('students.edit', $s->id) }}" class="btn btn-warning btn-sm">Edit</a>
<form method="POST" action="{{ route('students.destroy', $s->id) }}" class="d-inline">
@csrf @method('DELETE')
<button class="btn btn-danger btn-sm" onclick="return confirm('Yakin?')">Hapus</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
@endsection
C. Create Student
{{-- resources/views/students/create.blade.php --}}
Buat file pada resources/views/students/create.blade.php. kodenya adalah seperti berikut:
@extends('layouts.app')
@section('content')
<h2>Tambah Mahasiswa</h2>
<div class="card">
<div class="card-body">
<form action="{{ route('students.store') }}" method="POST">
@csrf
@foreach (['nim'=>'NIM', 'name'=>'Nama', 'address'=>'Alamat'] as $f => $label)
<div class="mb-3">
<label class="form-label">{{ $label }}</label>
<input type="text" name="{{ $f }}" class="form-control @error($f) is-invalid @enderror"
value="{{ old($f) }}">
@error($f) <div class="invalid-feedback">{{ $message }}</div> @enderror
</div>
@endforeach
<div class="mb-3">
<label>Jurusan</label>
<select name="major_id" class="form-control @error('major_id') is-invalid @enderror">
<option value="">Pilih Jurusan</option>
@foreach($majors as $m)
<option value="{{ $m->id }}" {{ old('major_id') == $m->id ? 'selected' : '' }}>
{{ $m->name }}
</option>
@endforeach
</select>
@error('major_id') <div class="invalid-feedback">{{ $message }}</div> @enderror
</div>
<div class="mb-3">
<label>Mata Kuliah</label>
@error('subjects') <div class="text-danger">{{ $message }}</div> @enderror
@foreach($subjects as $sub)
<div class="form-check">
<input class="form-check-input" type="checkbox" name="subjects[]"
value="{{ $sub->id }}" id="sub{{ $sub->id }}"
{{ in_array($sub->id, old('subjects', [])) ? 'checked' : '' }}>
<label class="form-check-label" for="sub{{ $sub->id }}">
{{ $sub->name }} ({{ $sub->sks }} SKS)
</label>
</div>
@endforeach
</div>
<button class="btn btn-primary">Simpan</button>
<a href="{{ route('students.index') }}" class="btn btn-secondary">Kembali</a>
</form>
</div>
</div>
@endsection