Pada artikel kali ini kita akan membahas mengenai Laravel Eloquent Relationship untuk tipe One to Many atau hasMany

Setelah pada pembahasan sebelumnya kita membahas relationship One to One atau hasOne dan relationship invers nya yaitu belongsTo, pada pembahasan kali ini kita akan membahas relationship One to Many atau hasMany.
Sebuah relationship one-to-many digunakan untuk mendefinisikan hubungan dimana satu model adalah parent atau induk dari satu atau lebih model turunan. Misalnya, sebuah postingan blog mungkin memiliki jumlah comment atau komentar yang tidak terbatas. Agar lebih jelas atau terbayang mengenai relationship antar model Post dan Comment tersebut kalian bisa lihat gambar dibawah ini
Catatan: Tips
Seperti pada semua Eloquent Relationship, relationship one-to-many juga di definisikan melalui sebuah method pada model Eloquent
Design Model Post and Model CommentUntuk membuat sebuah relationship dengan studi kasus satu postingan blog memiliki jumlah komentar yang tidak terbatas, kita perlu membuat model, migration, factory dan seeder nya terlebih dahulu. Kita bisa mulai membuat dari model Post beserta migartion, factory dan seeder nya secara sekaligus menggunakan perintah artisan berikut ini:
Catatan: Tips
Flag atau option
-mfsdisini artinya kita buat model sekaligusmigration,factory, dan jugaseedernya
php artisan make:model -mfs PostSelanjutnya kita ubah file model Post nya di app\Models\Post.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use HasFactory;
protected $guarded = ['id'];
}Sekarang kita sesuaikan kode migration nya agar kolom dari tabel posts nya sesuai dengan design yang sudah saya buat, file migration tersebut berada di database\migration\2024_01_22_072537_create_posts_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('posts', function (Blueprint $table) {
$table->id();
$table->foreignId('user_id');
$table->string('title');
$table->string('slug')->unique();
$table->text('excerpt');
$table->text('body');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('posts');
}
};Selanjutnya kita siapkan data dummy untuk model Post tersebut menggunakan faker di file factory database\factories\PostFactory.php
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
class PostFactory extends Factory
{
public function definition(): array
{
return [
'user_id' => mt_rand(1, 5),
'title' => fake()->sentence(mt_rand(2, 8)),
'slug' => fake()->slug(),
'excerpt' => fake()->paragraph(),
'body' => collect(fake()->paragraphs(mt_rand(5, 10)))
->map(fn ($p) => "<p>$p</p>")
->implode('')
];
}
}Setelah factory faker nya sudah siap, sekarang kita eksekusi pembuatan data dummy nya di file database\seeders\PostSeeder.php
<?php
namespace Database\Seeders;
use App\Models\Post;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class PostSeeder extends Seeder
{
public function run(): void
{
Post::factory(20)->create();
}
}Jangan lupa kita panggil class PostSeeder tersebut di file database\seeders\DatabaseSeeder.php
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
public function run(): void
{
$this->call([
UserSeeder::class,
PhoneSeeder::class,
PostSeeder::class
]);
}
}Setelah semua nya siap, sekarang kita jalankan migration nya menggunakan perintah artisan berikut ini
php artisan migrate:fresh --seedMaka sekarang seharusnya sudah terbuat sebuah tabel baru dengan nama posts didalam database nya dengan isian kolom dan data dummy seperti berikut ini
| Column | Data Type |
|---|---|
| id | bigint unsigned |
| user_id | bigint unsigned |
| title | varchar(255) |
| slug | varchar(255) |
| excerpt | text |
| body | text |
| created_at | timestamp |
| updated_at | timestamp |
Data Dummy Model PostSetelah model Post siap, selanjutnya kita siapkan untuk model Comment, untuk membuat model, migration, factory dan seeder nya secara sekaligus kita bisa gunakan perintah artisan berikut ini:
php artisan make:model -mfs CommentSelanjutnya kita ubah file model Comment nya di app\Models\Comment.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Comment extends Model
{
use HasFactory;
protected $guarded = ['id'];
}Sekarang kita sesuaikan kode migration nya agar kolom dari tabel comments sesuai dengan design yang sudah saya buat, file migration tersebut berada di database\migrations\2024_01_22_074358_create_comments_table.php
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
return new class extends Migration
{
public function up(): void
{
Schema::create('comments', function (Blueprint $table) {
$table->id();
$table->foreignId('post_id');
$table->foreignId('user_id');
$table->text('body');
$table->timestamps();
});
}
public function down(): void
{
Schema::dropIfExists('comments');
}
};Selanjutnya kita siapkan data dummy untuk model Comment tersebut menggunakan faker di file factory database\factories\CommentFactory.php
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
class CommentFactory extends Factory
{
public function definition(): array
{
return [
'post_id' => mt_rand(1, 20),
'user_id' => mt_rand(1, 5),
'body' => fake()->paragraph()
];
}
}Setelah factory faker nya sudah siap, sekarang kita bisa eksekusi pembuatan data dummy nya di file database\seeders\CommentSeeder.php
<?php
namespace Database\Seeders;
use App\Models\Comment;
use Illuminate\Database\Console\Seeds\WithoutModelEvents;
use Illuminate\Database\Seeder;
class CommentSeeder extends Seeder
{
public function run(): void
{
Comment::factory(40)->create();
}
}Jangan lupa kita panggil class CommentSeeder tersebut di file database\seeders\DatabaseSeeder.php
<?php
namespace Database\Seeders;
use Illuminate\Database\Seeder;
class DatabaseSeeder extends Seeder
{
public function run(): void
{
$this->call([
UserSeeder::class,
PhoneSeeder::class,
PostSeeder::class,
CommentSeeder::class
]);
}
}Setelah semuanya siap, sekarang kita bisa jalankan migration nya menggunakan perintah artisan berikut ini:
php artisan migration:fresh --seedMaka sekarang seharusnya sudah terbuat sebuah tabel baru dengan nama comments didalam database dengan isian kolom dan dummy data seperti berikut ini
| Column | Data Type |
|---|---|
| id | bigint unsigned |
| post_id | bigint unsigned |
| user_id | bigint unsigned |
| body | text |
| created_at | timestamp |
| updated_at | timestamp |
Data Dummy Model CommentSetelah kita siapkan model, migration, factory dan seeder untuk masing-masing model yaitu Post dan Comment. Sekarang kita akan membuat method hasMany di parent model atau model Post agar membuat sebuah relationship one-to-many
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Post extends Model
{
use HasFactory;
protected $guarded = ['id'];
public function comments(): HasMany
{
return $this->hasMany(Comment::class);
}
}Perlu kalian ingat, Eloquent akan secara otomatis menentukan kolom foreign key yang tepat untuk model Comment. Berdasarkan konvensi atau aturan Laravel, Eloquent akan mengambil nama "snake case" dari parent atau induk model dan menambahkan akhiran atau suffix nya dengan _id. Jadi, dalam contoh ini, Eloquent akan menganggap nama kolom dari foreign key post pada model Comment adalah post_id.
Setelah method relationship di definisikan, kita dapat mengakses collection atau kumpulan komentar terkait post nya dengan mengakses dynamic property comments. Ingat, karena Eloquent menyediakan "dynamic relationship properties", maka kita dapat mengakses relationship method tersebut seolah-olah method tersebut di definisikan sebagai properti pada model.
Untuk mencoba nya seperti biasa kita bisa gunakan shell tinker dengan cara masuk terlebih dahulu kedalam shell nya menggunakan perintah berikut ini
php artisan tinkerJika sudah didalam shell nya, sekarang kita bisa buat sebuah variabel dengan nama comments yang value nya adalah model Post yang men-chaining relationship method atau dynamic property nya
Catatan: Tips
Nama dari dynamic property Eloquent
commentsberikut ini adalah merepresentasikan nama relationship method yang ada di modelPost
$comments = App\Models\Post::find(1)->comments;Maka seharusnya perintah diatas akan me-return value dari data komentar yang terkait dengan postingan nya
Tinker Get Comment PostKarena semua relationship juga berfungsi sebagai pembuat query, jika kalian ingin menambahkan atau memberikan batasan atau kondisi tertentu lebih lanjut pada query relationship tersebut, kalian bisa men-chaining nya seperti berikut ini
$comment = Post::find(1)->comments()->where('title', 'foobar')->first();Sama seperti method hasOne, jika kalian ingin mengganti atau menggunakan nama lain dari column foreign key dan primary key yang digunakan sebagai relationship, kalian bisa menambahkan nya pada argument kedua dan ketiga di method hasMany nya seperti berikut ini:
public function comments(): HasMany
{
return $this->hasMany(Comment::class, 'foreign_key', 'local_key');
}Setelah mendefinisikan relationship hasMany atau One to Many dari model Post ke model Comment, maka kita sekarang dapat mengakses model Comment secara langsung dari model Post dengan cara men-chaining method comments pada instance Post. Selanjutnya, kita akan tentukan relationship kebalikannya atau inverse dari model Comment ke model Post, relationship inverse tersebut nantinya memungkinkan kita dapat mengakses data post secara langsung melalui model Comment. Kita dapat mendefinisikan invers relationship dari method hasMany dengan menggunakan method belongsTo.
Agar lebih terbayang, kalian bisa lihat gambar dibawah ini mengenai relationship hasMany dari model Post ke model Comment dan belongsTo (inverse) dari model Comment ke model Post
Invers Relation DesignUntuk membuat inverse relation nya, kita perlu membuat sebuah method dengan nama post di model Comment. Method post tersebut harus memanggil method belongsTo dan mengembalikan nilai atau return value nya.
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Comment extends Model
{
use HasFactory;
protected $guarded = ['id'];
public function post(): BelongsTo
{
return $this->belongsTo(Post::class);
}
}Pada contoh diatas, Eloquent akan beruasaha mencari model Post yang memiliki id yang cocok dengan kolom post_id pada model Comment.
Eloquent menentukan foreign key secara default dengan cara memeriksa nama relationship method nya dan menambahkan akhiran atau suffix _ diikuti dengan nama kolom primary key nya pada parent atau induk model. Jadi, dalam contoh ini, Eloquent akan menganggap bahwa foreign key model Post di tabel comments adalah post_id
post = nama method yang di definisikan pada model Comment_ = otomatis ditambahkan oleh eloquentid = nama primary key yang ada di parent atau induk model yaitu PostNamun seperti biasa, jika kalian tidak menggunakan aturan standar dari Laravel misalkan nama kolom dari foreign key dan primary key nya berbeda, kalian bisa menambahkannya pada argument keuda dan ketiga di method belongsTo
public function post(): BelongsTo
{
return $this->belongsTo(Post::class, 'foreign_key', 'owner_key');
}Setelah inverse relationship ditentukan, sekarang kita dapat mengambil data postingan yang berada di parent atau induk model nya melalui model Comment dengan cara mengakses "dynamic relationship property" seperti berikut ini
Catatan: Tips
Kalian perlu melakukan restart session tinker nya dengan cara exit kemudian masuk kembali agar pembaruan kode yang sudah dilakukan dapat dijalankan
$post = App\Models\Comment::find(1)->postMaka seharusnya perintah diatas akan me-return value data postingan yang terkait dengan komentar nya
Tinker Get Post CommentSeperti pada pembahasan One to One, kita sudah mempunyai sebuah controller khusus untuk membuat Eloquent Relationship yang sudah kita buat bisa diakses melalui web, kita bisa tambahkan 2 buah method yaitu oneToMany dan oneToManyInverse pada RelationController
<?php
namespace App\Http\Controllers;
use App\Models\Comment;
use App\Models\Phone;
use Illuminate\Http\Request;
class RelationController extends Controller
{
...
public function oneToMany(Request $request)
{
$comments = Post::find($request->id)->comments;
return $comments;
}
public function oneToManyInverse(Request $request)
{
$post = Comment::find($request->id)->post;
return $post;
}
}Selanjutnya kita tambahkan routing untuk mengakses method controller tersebut di file routes\web.php
Route::get('/relation/oneToMany', [RelationController::class, 'oneToMany']);
Route::get('/relation/oneToManyInverse', [RelationController::class, 'oneToManyInverse']);
One to Many Via Web
One to Many Inverse Via Web