Konsep Relationship dalam Laravel
One-to-Many
Relasi dimana satu model memiliki banyak model lain. Contoh: Satu jurusan memiliki banyak mahasiswa
Many-to-Many
Relasi dimana banyak model terhubung dengan banyak model lain. Contoh: Mahasiswa mengambil banyak mata kuliah
Eloquent ORM
Sistem ORM di Laravel yang memudahkan interaksi dengan database termasuk relationship
Pivot Table
Tabel perantara untuk menghubungkan relasi many-to-many, menyimpan foreign key dari kedua model
Langkah Implementasi sesuai Modul
Persiapan Awal
Struktur Database dan Relationship:
- Student belongs to Major (Many-to-One)
- Student belongs to many Subject (Many-to-Many)
- Major has many Student (One-to-Many)
- Subject belongs to many Student (Many-to-Many)
ERD (Entity Relationship Diagram):
| MAJOR | ||
|---|---|---|
| Type | Column | Constraint |
| bigint | id | PK |
| string | name | |
| timestamp | created_at | |
| timestamp | updated_at | |
has many | belongs to
| STUDENT | ||
|---|---|---|
| Type | Column | Constraint |
| bigint | id | PK |
| string | nim | UK |
| string | name | |
| text | address | |
| bigint | major_id | FK |
| timestamp | created_at | |
| timestamp | updated_at | |
Membuat Migration
A. Migration untuk tabel majors
php artisan make:migration create_majors_table
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('majors', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('majors');
}
};
B. Migration untuk tabel students
php artisan make:migration create_students_table
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('students', function (Blueprint $table) {
$table->id();
$table->string('nim')->unique();
$table->string('name');
$table->text('address');
$table->foreignId('major_id')->constrained('majors')->onDelete('cascade');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('students');
}
};
C. Migration untuk tabel subjects
php artisan make:migration create_subjects_table
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('subjects', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->integer('sks');
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('subjects');
}
};
D. Migration untuk tabel pivot student_subject
php artisan make:migration create_student_subject_table
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up()
{
Schema::create('student_subject', function (Blueprint $table) {
$table->id();
$table->foreignId('student_id')->constrained('students')->onDelete('cascade');
$table->foreignId('subject_id')->constrained('subjects')->onDelete('cascade');
$table->timestamps();
// Mencegah duplikasi kombinasi student_id dan subject_id
$table->unique(['student_id', 'subject_id']);
});
}
public function down()
{
Schema::dropIfExists('student_subject');
}
};
Jalankan Migration
php artisan migrate
Membuat Model dengan Relationship
A. Model Major
php artisan make:model Major
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Major extends Model
{
use HasFactory;
protected $fillable = ['name'];
// Relationship: One Major has many Students
public function students()
{
return $this->hasMany(Student::class);
}
}
B. Model Student
php artisan make:model Student
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Student extends Model
{
use HasFactory;
protected $fillable = ['nim', 'name', 'address', 'major_id'];
// Relationship: Many Students belong to one Major
public function major()
{
return $this->belongsTo(Major::class);
}
// Relationship: Many Students belong to many Subjects
public function subjects()
{
return $this->belongsToMany(Subject::class);
}
}
C. Model Subject
php artisan make:model Subject
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Subject extends Model
{
use HasFactory;
protected $fillable = ['name', 'sks'];
// Relationship: Many Subjects belong to many Students
public function students()
{
return $this->belongsToMany(Student::class);
}
}
Seeder untuk Data Sample
A. Seeder untuk Major
php artisan make:seeder MajorSeeder
<?php
namespace Database\Seeders;
use App\Models\Major;
use Illuminate\Database\Seeder;
class MajorSeeder extends Seeder
{
public function run()
{
$majors = [
['name' => 'Teknik Informatika'],
['name' => 'Sistem Informasi'],
['name' => 'Teknik Komputer'],
['name' => 'Manajemen Informatika']
];
foreach ($majors as $major) {
Major::create($major);
}
}
}
B. Seeder untuk Subject
php artisan make:seeder SubjectSeeder
<?php
namespace Database\Seeders;
use App\Models\Subject;
use Illuminate\Database\Seeder;
class SubjectSeeder extends Seeder
{
public function run()
{
$subjects = [
['name' => 'Pemrograman Web', 'sks' => 3],
['name' => 'Database', 'sks' => 3],
['name' => 'Algoritma', 'sks' => 2],
['name' => 'Jaringan Komputer', 'sks' => 3],
['name' => 'Sistem Operasi', 'sks' => 2]
];
foreach ($subjects as $subject) {
Subject::create($subject);
}
}
}
C. Seeder untuk Student
php artisan make:seeder StudentSeeder
<?php
namespace Database\Seeders;
use App\Models\Student;
use App\Models\Major;
use App\Models\Subject;
use Illuminate\Database\Seeder;
class StudentSeeder extends Seeder
{
public function run()
{
$students = [
['nim' => '20210001', 'name' => 'Ahmad Rizki', 'address' => 'Jl. Merdeka No. 1', 'major_id' => 1],
['nim' => '20210002', 'name' => 'Siti Nurhaliza', 'address' => 'Jl. Sudirman No. 15', 'major_id' => 1],
['nim' => '20210003', 'name' => 'Budi Santoso', 'address' => 'Jl. Pahlawan No. 8', 'major_id' => 2],
['nim' => '20210004', 'name' => 'Devi Kartika', 'address' => 'Jl. Diponegoro No. 22', 'major_id' => 2],
['nim' => '20210005', 'name' => 'Eko Prasetyo', 'address' => 'Jl. Gatot Subroto No. 11', 'major_id' => 3],
];
foreach ($students as $studentData) {
$student = Student::create($studentData);
// Assign random subjects to each student
$subjects = Subject::inRandomOrder()->take(rand(2, 4))->pluck('id');
$student->subjects()->attach($subjects);
}
}
}
D. Update DatabaseSeeder
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
public function run()
{
$this->call([
MajorSeeder::class,
SubjectSeeder::class,
StudentSeeder::class,
]);
}
}
Jalankan Seeder
php artisan db:seed
Membuat Controller
StudentController
php artisan make:controller 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()
{
// Eager loading untuk menghindari N+1 problem
$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()
{
$majors = Major::all();
$subjects = Subject::all();
return view('students.create', compact('majors', 'subjects'));
}
public function store(Request $request)
{
$request->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($request->only(['nim', 'name', 'address', 'major_id']));
$student->subjects()->attach($request->subjects);
return redirect()->route('students.index')->with('success', 'Student created successfully');
}
public function edit($id)
{
$student = Student::with('subjects')->findOrFail($id);
$majors = Major::all();
$subjects = Subject::all();
return view('students.edit', compact('student', 'majors', 'subjects'));
}
public function update(Request $request, $id)
{
$student = Student::findOrFail($id);
$request->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($request->only(['nim', 'name', 'address', 'major_id']));
$student->subjects()->sync($request->subjects);
return redirect()->route('students.index')->with('success', 'Student updated successfully');
}
public function destroy($id)
{
$student = Student::findOrFail($id);
$student->subjects()->detach(); // Remove all subject relationships
$student->delete();
return redirect()->route('students.index')->with('success', 'Student deleted successfully');
}
}
Membuat Routes
<?php
use App\Http\Controllers\StudentController;
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return redirect()->route('students.index');
});
Route::resource('students', StudentController::class);
Membuat Views
A. Layout Utama (app.blade.php)
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Student Management System</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-expand-lg 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>
<script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
B. Index Students (index.blade.php)
@extends('layouts.app')
@section('content')
<div class="d-flex justify-content-between align-items-center mb-4">
<h2>Daftar Mahasiswa</h2>
<a href="{{ route('students.create') }}" class="btn btn-primary">Tambah Mahasiswa</a>
</div>
<div class="table-responsive">
<table class="table table-striped">
<thead>
<tr>
<th>NIM</th>
<th>Nama</th>
<th>Jurusan</th>
<th>Mata Kuliah</th>
<th>Total SKS</th>
<th>Aksi</th>
</tr>
</thead>
<tbody>
@foreach($students as $student)
<tr>
<td>{{ $student->nim }}</td>
<td>{{ $student->name }}</td>
<td>{{ $student->major->name }}</td>
<td>
@foreach($student->subjects as $subject)
<span class="badge bg-secondary me-1">{{ $subject->name }}</span>
@endforeach
</td>
<td>{{ $student->subjects->sum('sks') }}</td>
<td>
<a href="{{ route('students.show', $student->id) }}" class="btn btn-info btn-sm">Detail</a>
<a href="{{ route('students.edit', $student->id) }}" class="btn btn-warning btn-sm">Edit</a>
<form action="{{ route('students.destroy', $student->id) }}" method="POST" class="d-inline">
@csrf
@method('DELETE')
<button type="submit" class="btn btn-danger btn-sm"
onclick="return confirm('Yakin ingin menghapus')">Hapus</button>
</form>
</td>
</tr>
@endforeach
</tbody>
</table>
</div>
@endsection
C. Create Student (create.blade.php)
@extends('layouts.app')
@section('content')
<h2>Tambah Mahasiswa</h2>
<div class="card">
<div class="card-body">
<form action="{{ route('students.store') }}" method="POST">
@csrf
<div class="mb-3">
<label for="nim" class="form-label">NIM</label>
<input type="text" class="form-control @error('nim') is-invalid @enderror"
id="nim" name="nim" value="{{ old('nim') }}">
@error('nim')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="name" class="form-label">Nama</label>
<input type="text" class="form-control @error('name') is-invalid @enderror"
id="name" name="name" value="{{ old('name') }}">
@error('name')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="address" class="form-label">Alamat</label>
<textarea class="form-control @error('address') is-invalid @enderror"
id="address" name="address" rows="3">{{ old('address') }}</textarea>
@error('address')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label for="major_id" class="form-label">Jurusan</label>
<select class="form-control @error('major_id') is-invalid @enderror"
id="major_id" name="major_id">
<option value="">Pilih Jurusan</option>
@foreach($majors as $major)
<option value="{{ $major->id }}" {{ old('major_id') == $major->id ? 'selected' : '' }}>
{{ $major->name }}
</option>
@endforeach
</select>
@error('major_id')
<div class="invalid-feedback">{{ $message }}</div>
@enderror
</div>
<div class="mb-3">
<label class="form-label">Mata Kuliah</label>
@error('subjects')
<div class="text-danger">{{ $message }}</div>
@enderror
@foreach($subjects as $subject)
<div class="form-check">
<input class="form-check-input" type="checkbox" name="subjects[]"
value="{{ $subject->id }}" id="subject{{ $subject->id }}"
{{ in_array($subject->id, old('subjects', [])) ? 'checked' : '' }}>
<label class="form-check-label" for="subject{{ $subject->id }}">
{{ $subject->name }} ({{ $subject->sks }} SKS)
</label>
</div>
@endforeach
</div>
<button type="submit" class="btn btn-primary">Simpan</button>
<a href="{{ route('students.index') }}" class="btn btn-secondary">Kembali</a>
</form>
</div>
</div>
@endsection
Latihan dan Tugas
Latihan 1: Query dengan Relationship
Buat query untuk menampilkan:
- Semua mahasiswa beserta jurusan dan mata kuliahnya
- Jurusan yang memiliki mahasiswa terbanyak
- Mata kuliah yang diambil oleh mahasiswa tertentu
- Total SKS yang diambil setiap mahasiswa
Kesimpulan
Dalam modul ini, mahasiswa telah mempelajari:
- Cara membuat relationship One-to-Many dan Many-to-Many di Laravel
- Implementasi foreign key dan pivot table
- Penggunaan Eloquent relationship untuk query data
- Best practices dalam menggunakan eager loading
- Cara menampilkan data relationship di view
Relationship adalah konsep fundamental dalam pengembangan aplikasi web dengan database. Pemahaman yang baik tentang relationship akan membantu dalam membangun aplikasi yang efisien dan mudah di-maintain.