Dalam artikel ini kita akan kupas tuntas tentang bagaimana cara mengimplementasikan relasi One to Many di Laravel.
"One to Many" adalah salah satu tipe relasi paling umum dalam basis data relasional. Dalam relasi ini, satu baris dalam tabel bisa memiliki banyak baris yang berhubungan dengannya di tabel lain, tetapi sebuah baris di tabel kedua hanya bisa berhubungan dengan satu baris di tabel pertama.
Sebagai contoh, pertimbangkan skenario di mana Anda memiliki tabel 'Authors' dan 'Books'. Seorang penulis dapat menulis banyak buku, tetapi setiap buku hanya memiliki satu penulis. Ini adalah relasi "One to Many" di mana satu penulis (One) dapat memiliki banyak buku (Many).
Mari kita lihat bagaimana struktur tabel mungkin tampak dalam contoh ini:
Tabel categories
:
id | name |
---|---|
1 | Mystery |
2 | Horror |
Tabel books
:
id | title | category_id |
---|---|---|
1 | Murder on the Orient Express | 1 |
2 | The Shining | 2 |
3 | Endless Night | 1 |
4 | Pet Sematary | 2 |
Dalam kasus ini, kolom category_id
pada tabel books
adalah kunci asing (foreign key) yang menghubungkan buku ke kategorinya. Setiap buku mengacu pada satu baris di tabel categories
.
Relasi semacam ini memungkinkan kita untuk membuat query yang efisien dan mudah dimengerti untuk mengambil data yang terkait. Misalnya, kita dapat dengan mudah mengambil semua buku yang telah dikategorikan dalam 1 klompok.
Relasi "One to Many" sangat penting dalam pembuatan aplikasi berbasis database karena memungkinkan kita untuk menghindari duplikasi data dan menjaga integritas data.
Untuk merepresentasikan relasi ini dalam Laravel, kita akan membuat dua model: satu untuk Kategori dan satu lagi untuk Buku. Di dalam model Kategori, kita akan mendefinisikan metode yang mengembalikan relasi ke Buku. Ini dilakukan dengan memanggil metode hasMany()
pada model Kategori dan meneruskan model Buku sebagai argumen.
Di sisi lain, di dalam model Buku, kita akan mendefinisikan metode yang mengembalikan relasi ke Kategori. Ini dilakukan dengan memanggil metode belongsTo()
pada model Buku dan meneruskan model Kategori sebagai argumen.
Dengan mendefinisikan relasi ini, Laravel sekarang dapat secara otomatis menangani interaksi antara kategori dan buku. Misalnya, kita dapat dengan mudah mendapatkan semua buku dalam suatu kategori atau menemukan kategori dari buku tertentu. Laravel mengurus semua detail yang rumit di belakang layar, memungkinkan kita untuk fokus pada logika bisnis aplikasi kita.
Di dalam laravel, kita bisa membuat model sekalian dengan migration nya. Maka untuk itu, kita bisa menjalankan perintah berikut.
php artisan make:model Category -m
Perintah itu akan memberikan kita 2 file sekaligus, yang pertama adalah app/Models/Category.php
, dan yang satunya lagi adalah file migration nya yang berada di database/migrations/2021_07_16_215356_create_categories_table.php
.
Sebelum kita melangkah lebih jauh kesana, disini saya ingin memastikan bahwa ketika kita melakukan instalasi laravel, maka kita akan diberikan tabel bawaan seperti tabel users
. Laravel sangat detail sekali karena menganggap kita pasti ingin membuat sebuah aplikasi yang itu pasti memerlukan tabel users
. Itulah mengapa tabel users
ada by default.
Kenapa saya menjelaskan tentang tabel users
, dikarenakan pada tabel selanjutnya yaitu books
akan kita relasikan dengan tabel users
tadi. Seakan-akan tabel users
akan menjadi seoarang penulis dari buku itu. Dan pastinya Anda harus tau, bahwa relasi yang kita implementasikan ke dalam tabel categories
akan sama dengan tabel users
.
Mari kita lanjut untuk membuat tabel books
nya dengan menjalankan perintah berikut:
php artisan make:model Book -m
Lagi-lagi disini kita juga menggunakan flag -m
agar model ini otomatis membuat migration nya.
Silakan buka file migration yang pertama kali kita buat yaitu 2021_07_16_215356_create_categories_table.php
dan modifikasi isinya seperti:
public function up()
{
Schema::create('categories', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->string('slug');
$table->timestamps();
});
}
Setelah itu, silakan buka file migration yang kedua kita buat yaitu 2021_07_16_215433_create_books_table.php
. Buka file nya dan silakan modifikasi seperti:
public function up()
{
Schema::create('books', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id')->constrained('users')->cascadeOnDelete();
$table->bigInteger('category_id');
$table->string('title');
$table->text('description');
$table->timestamps();
});
}
Jika Anda perhatikan, bahwa disini kita memastikan bahwa ketika penulisnya di hapus, maka harusnya buku nya juga ikut terhapus. Tapi tidak dengan kategori pastinya. Karena jika kategori di hapus, kita tentu tidak mau menghapus kategorinya. Sengaja saya buat seperti itu, agar nanti Anda mengerti bagaimana mengatasi errornya.
Setelah kedua migration itu selesai di edit, maka kita bisa menjalankan yang namanya migrate dengan menjalankan perintah berikut:
php artisan migrate
Dengan perintah itu, maka akan terbuat ke database yang Anda miliki saat ini.
Pertama, silakan buka model app/Models/Book.php
, karena model ini akan berelasi ke 2 tabel yaitu users
dan categories
, maka kita menggunakan method yang namanya belongsTo
.
public function category(): BelongsTo
{
return $this->belongsTo(Category::class, 'category_id');
}
public function user(): BelongsTo
{
return $this->belongsTo(User::class, 'user_id');
}
Ada BelongsTo
yang saya tambah setelah nama relasinya. Itu hanya istilah return type dari method tersebut. Silakan import class nya di bawah namespace
seperti:
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
Kemudian buka model app/Models/Category.php
dan mari kita tambahkan relasi ke tabel books
nya seperti:
use Illuminate\Database\Eloquent\Relations\HasMany; // tambahkan di bawah namespace
public function books(): HasMany
{
return $this->hasMany(Book::class, 'category_id');
}
Setelah itu, buka juga model app/Models/User.php
dan tambahkan relasi yang sama seperti:
use Illuminate\Database\Eloquent\Relations\HasMany; // tambahkan di bawah namespace
public function books(): HasMany
{
return $this->hasMany(Book::class, 'user_id');
}
Jika Anda perhatikan baik-baik, ada kode yang sama disini, hanya saja mereka berbeda file. Nanti kita akan coba untuk refactor. Untuk sekarang, pastikan Anda sudah paham dulu mengenai konsep ini.
key category_id
dan user_id
adalah foreign key yang ada di tabel itu, jadi istilanya mereka akan kita relasikan dari tabel itu.
Jika Anda ingin mengetahui tentang ini lebih jauh, Anda bisa lihat video di bawah ini:
Harusnya Anda sudah pasti mengetahui bagaimana cara menampilkan di Laravel dengan menggunakan table.
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\DB;
Route::get('/', function () {
return DB::table('books')->get();
});
Oia disini saya sudah menganggap bahwa Anda sudah mempunyai beberapa data di database, jika Anda tidak mengetahui caranya, maka Anda bisa langsung menonton video di bawah ini:
Terkait tadi cara kita menampilkan semua books
menggunakanan DB Facade, karena kita sudah mempunyai model, maka kita menampilkan semua bukunya dengan model yang telah kita buat tadi.
use Illuminate\Support\Facades\Route;
Route::get('/', function () {
return \App\Models\Book::query()->get();
});
Dengan begitu, jauh lebih simpel dan hasilnya akan tetap sama saja. Pada saat ini, kita hanya akan mendapatkan data yang kurang lebih seperti ini:
[
{
"id": 1,
"user_id": 1,
"category_id": 1,
"title": "consequuntur",
"description": "Iusto et qui nostrum cum. Maxime et hic nihil ut itaque numquam consequuntur.",
"created_at": "2023-07-16T15:28:13.000000Z",
"updated_at": "2023-07-16T15:28:13.000000Z"
},
{
"id": 2,
"user_id": 1,
"category_id": 2,
"title": "in",
"description": "Enim repellat distinctio id eius. Rerum aliquam ad veniam ut. Nostrum rerum qui et rerum mollitia rerum.",
"created_at": "2023-07-16T15:28:13.000000Z",
"updated_at": "2023-07-16T15:28:13.000000Z"
},
...
]
Yang mana pada saat ini, kita tidak mendapatkan informasi seperti penulis dan juga kategorinya. Untuk menambahkan nya, kita bisa menggunakan teknik yang nama nya eager loading.
Route::get('/', function () {
return \App\Models\Book::query()
->with(['category', 'user'])
->get();
});
Dengan begitu, maka kita akan mendapatkan hasil seperti:
[
{
"id": 1,
"user_id": 1,
"category_id": 1,
"title": "consequuntur",
"description": "Iusto et qui nostrum cum. Maxime et hic nihil ut itaque numquam consequuntur.",
"created_at": "2023-07-16T15:28:13.000000Z",
"updated_at": "2023-07-16T15:28:13.000000Z",
"category": {
"id": 1,
"name": "autem",
"slug": "autem",
"created_at": "2023-07-16T15:28:13.000000Z",
"updated_at": "2023-07-16T15:28:13.000000Z"
},
"user": {
"id": 1,
"name": "Jodie Lehner DDS",
"email": "[email protected]",
"email_verified_at": "2023-07-16T15:28:13.000000Z",
"created_at": "2023-07-16T15:28:13.000000Z",
"updated_at": "2023-07-16T15:28:13.000000Z"
}
},
...
]
Baik, namun ada beberapa yang harus Anda fikirkan, masak ia semua informasi harus di tunjukkan. Harusnya tidak, karena itu juga mempengaruhi kinerja dari aplikasi Anda. Untuk itu, kita bisa memodifikasi query nya dengan cara seperti:
\App\Models\Book::query()
->select(['id', 'user_id', 'category_id', 'title', 'description'])
->with(['category:id,name,slug', 'user:id,name'])
->get();
Dengan begitu, kita akan mendapatkan hasil seperti:
[
{
"id": 1,
"user_id": 1,
"category_id": 1,
"title": "consequuntur",
"description": "Iusto et qui nostrum cum. Maxime et hic nihil ut itaque numquam consequuntur.",
"category": {
"id": 1,
"name": "autem",
"slug": "autem"
},
"user": {
"id": 1,
"name": "Jodie Lehner DDS"
}
},
...
]
Jika semua sudah kita ketahui, maka kita bisa lanjut untuk langsung menggunakan views
. Pertama sekali, saya akan merubah route ini menjadi seperti:
Route::get('/', function () {
return view('books.index', [
'books' => \App\Models\Book::query()
->select(['id', 'user_id', 'category_id', 'title', 'description'])
->with(['category:id,name,slug', 'user:id,name'])
->get()
]);
});
Setelah itu, mari kita buat folder baru dengan nama books
dan di dalamnya buat file dengan nama index.blade.php
. Di dalam nya silakan loop seperti:
<script src="https://cdn.tailwindcss.com"></script>
<div class="grid grid-cols-3 gap-6">
@foreach($books as $book)
<div class="p-4">
<h2 class="text-2xl font-bold">{{ $book->title }}</h2>
<p>{{ $book->description }}</p>
<hr class="my-5"/>
<div>
Penulis: <strong>{{ $book->user->name }}</strong>
</div>
<div>
Kategori: <strong>{{ $book->category->name }}</strong>
</div>
</div>
@endforeach
</div>
Dengan begitu, kita akan mendapatkan hasil yang sesuai dengan kemauan kita.
Jika Anda ingin menampilkan semua buku dari seorang penulis, maka bisa dengan mudah membuat query seperti:
Route::get('books/author/{user}', function (App\Models\User $user) {
return \App\Models\Book::query()
->select(['id', 'category_id', 'user_id', 'title', 'description'])
->whereBelongsTo($user, 'user')
->with(['category:id,name,slug'])
->get();
});
Dengan begitu, kita bisa mengunjungi url http://localhost:8000/books/author/2
kita akan mendapatkan hasil seperti:
[
{
"id": 6,
"category_id": 6,
"title": "voluptatem",
"description": "Porro rerum perferendis ratione sequi. Esse eveniet ratione est molestias quo est qui. Occaecati consequatur minus id dolore ad porro recusandae.",
"category": {
"id": 6,
"name": "fuga",
"slug": "fuga"
}
},
{
"id": 7,
"category_id": 7,
"title": "rem",
"description": "Veritatis tempore sed aut et quo est dolores. Doloremque hic et aut deleniti accusantium omnis. Ea nam vel est incidunt esse. Natus quis officiis dicta modi id et aspernatur.",
"category": {
"id": 7,
"name": "expedita",
"slug": "expedita"
}
},
...
]
Atau dengan cara lain, kita bisa memanggilnya melalui relasi yang ada pada model user tadi seperti:
return $user->books()
->select(['id', 'category_id', 'user_id', 'title', 'description'])
->with(['category:id,name,slug'])
->get();
Dengan begitu kita akan mendapatkan hasil yang sama. Dan harusnya, Anda akan sudah mengetahui bagaimana cara menampilkan berdasarkan dari kategori. Karena mereka menggunakan konsep relasi yang sama.
Perhatikan pada model User
dan juga Category
, ada kode yang duplikat yaitu relasi untuk tabel buku. Oleh karena itu, kita bisa membuat nya ke dalam trait. Silakan buat 1 folder tepat di dalam folder Models
dengan nama Traits
, dan didalamnya silakan buat 1 file dengan nama HasBooks
. Kemudian, isinya silakan buat seperti ini:
namespace App\Models\Traits;
use App\Models\Book;
use Illuminate\Database\Eloquent\Relations\HasMany;
trait HasBooks
{
public function books(): HasMany
{
return $this->hasMany(Book::class);
}
}
Oia, perhatikan baik-baik, kita disini bisa menggunakan traits, karena kita mengikuti aturan yang dibuat oleh laravel, yaitu akan menggap bahwa foreign key dari method itu adalah nama model yang diikuti dengan _id
. Jika seandainya kita memakai trait ini di user, maka laravel akan otomatis menganggap bahwa foreign key dari method books
itu adalah user_id
, begitu juga dengan category
.
Setelah itu, silakan hapus method books
yang ada pada model User
dan juga Category
. Dan tambahkan trait nya seperti:
//...
use App\Models\Traits\HasBooks;
class User extends Authenticatable
{
use HasBooks;
use HasApiTokens;
use HasFactory;
use Notifiable;
protected $fillable = [
'name',
'email',
'password',
];
protected $hidden = [
'password',
'remember_token',
];
protected $casts = [
'email_verified_at' => 'datetime',
'password' => 'hashed',
];
}
Kemudian begitu juga dengan model Category
.
use App\Models\Traits\HasBooks;
class Category extends Model
{
use HasBooks;
use HasFactory;
protected $fillable = [
'name',
'slug',
];
}
Dengan begitu, maka kita sudah melakukan hal yang bagus yaitu menghindari duplikasi.
"Explicit" lebih baik dari pada "Implicit"
Menggunakan relasi "One to Many" di Laravel adalah praktek yang penting dan umum dalam pengembangan aplikasi yang berinteraksi dengan basis data relasional. Dengan memahami dan menerapkan relasi ini, kita dapat merancang struktur data yang lebih efisien dan logis, yang pada akhirnya akan meningkatkan kinerja aplikasi kita.
Selain itu, fitur relasi yang disediakan oleh Laravel memudahkan kita dalam penanganan dan manipulasi data antar tabel. Dengan demikian, kita dapat lebih fokus pada logika bisnis aplikasi dan meningkatkan produktivitas pengembangan kita.
Terakhir, memahami relasi "One to Many" juga penting sebagai langkah awal untuk memahami jenis relasi lainnya seperti "Many to Many", "Polymorphic Relations", dan lainnya yang ditawarkan oleh Laravel.
Semoga artikel ini bermanfaat untuk Anda dan begitu juga dengan saya. Saya irsyad, sampai jumpa lagi.
Let's start living like no one can help us in any event, so that when we are helped in certain times, it becomes a plus in itself.