Laravel Eloquent ORM (Object-Relational Mapping) adalah fitur bawaan Laravel yang menyediakan cara elegan untuk berinteraksi dengan database. Setiap tabel database memiliki Model yang berkorespondensi. Eloquent memungkinkan developer mendefinisikan hubungan antar tabel secara deklaratif langsung di dalam kelas Model.
ITujuan Praktikum
Setelah menyelesaikan praktikum ini, mahasiswa diharapkan mampu:
- ✓Memahami konsep relationship dalam Laravel
- ✓Mengimplementasikan One-to-Many dan Many-to-Many relationship
- ✓Membuat migration dengan foreign key
- ✓Menggunakan Eloquent relationship untuk query data
- ✓Menampilkan data dengan relationship di view
IIDasar Teori
IIIAlat dan Bahan
IVLangkah Kerja Praktikum
Langkah 1: Membuat Migration
Migration digunakan untuk mendefinisikan struktur database secara programatik. Terdapat empat migration yang dibuat sesuai ERD.
1.1 — Migration Tabel majors
php artisan make:migration create_majors_table1.2 — Migration Tabel students
php artisan make:migration create_students_table1.3 — Migration untuk tabel subjects
php artisan make:migration create_subjects_table<?php
// database/migrations/xxxx_xx_xx_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');
}
};1.4 — Migration untuk tabel pivot student_subject
php artisan make:migration create_student_subject_table
<?php
// database/migrations/xxxx_xx_xx_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');
}
};1.5 — Jalankan Migration
php artisan migrateMembuat Model dengan Relationship
Hubungkan antar model yang ada
2.1 — Model Major
php artisan make:model Major<?php
// app/Models/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);
}
}2.2 — Model Student
php artisan make:model Student<?php
// app/Models/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);
}
}2.3 — Model Subject
php artisan make:model Subject<?php
// app/Models/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
Buat seeder untuk masing masing model
3.1 — Seeder untuk Major
php artisan make:seeder MajorSeeder<?php
// database/seeders/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);
}
}
}3.2 — Seeder untuk Subject
php artisan make:seeder SubjectSeeder<?php
// database/seeders/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);
}
}
}3.3 — Seeder untuk Student
php artisan make:seeder StudentSeeder<?php
// database/seeders/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' => 'Dewi 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);
}
}
}3.4 — Update DatabaseSeeder
<?php // database/seeders/DatabaseSeeder.php namespace Database\Seeders; use Illuminate\Database\Seeder; class DatabaseSeeder extends Seeder { public function run() { $this->call([ MajorSeeder::class, SubjectSeeder::class, StudentSeeder::class, ]); } }
3.5 — Jalankan Seeder
php artisan db:seedMembuat Controller
Folder tempat logika sistem dibuat
4.1 — StudentController
php artisan make:controller StudentController<?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()
{
// 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
Langkah selanjutnya adalah mendefinisikan rute (routes) sebagai jalur navigasi aplikasi. Pada file routes/web.php, kita menggunakan metode Route::resource yang akan secara otomatis men-generate seluruh rute standar untuk operasi CRUD (Create, Read, Update, Delete) yang mengarah ke StudentController. Selain itu, kita juga menambahkan rute root (/) yang berfungsi untuk langsung mengarahkan (redirect) pengguna ke halaman utama daftar mahasiswa (students.index) ketika pertama kali mengakses aplikasi.
<?php
// routes/web.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
Setelah Model, Controller, dan Route terhubung, tahap terakhir adalah membangun antarmuka pengguna (User Interface) menggunakan Blade Template Engine bawaan Laravel. Pada tahap ini, kita akan menstrukturkan tampilan dengan membuat sebuah layout utama (app.blade.php) sebagai kerangka dasar (termasuk memuat library Bootstrap 5). Selanjutnya, kita akan memecah tampilan ke dalam beberapa file spesifik, seperti index.blade.php untuk menampilkan tabel daftar mahasiswa secara menyeluruh beserta relasinya, dan create.blade.php untuk menampilkan form input data mahasiswa baru yang dinamis.
6.1 — Layout utama
{{-- resources/views/layouts/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>6.2 — Index Students
{{-- resources/views/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>
@endsection6.3 — Create Student
{{-- resources/views/students/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