LaravelEloquentRelasiMenengah5 mnt baca

HasMany dan BelongsTo: Relasi Satu-ke-Banyak

Relasi paling umum: satu user punya banyak post.

Relasi yang Paling Sering Kamu Tulis

Hampir semua aplikasi punya pola "satu punya banyak": satu user punya banyak post, satu post punya banyak komentar, satu kategori punya banyak produk. Dua method Eloquent ini adalah fondasi semua relasi lain. Kuasai ini sampai di luar kepala sebelum menyentuh relasi yang lebih eksotis seperti many-to-many.

Definisi di Kedua Sisi

php
// app/Models/User.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class User extends Model
{
    public function posts()
    {
        return $this->hasMany(Post::class);
    }
}
php
// app/Models/Post.php
namespace App\Models;

use Illuminate\Database\Eloquent\Model;

class Post extends Model
{
    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

hasMany dibaca "model ini punya banyak model lain", belongsTo dibaca "model ini milik satu model lain". Laravel menebak foreign key dari nama model: hasMany(Post::class) di User mencari kolom user_id di tabel posts. Kalau nama kolomnya tidak mengikuti konvensi, sebutkan eksplisit:

php
// app/Models/User.php
public function posts()
{
    return $this->hasMany(Post::class, 'penulis_id', 'id');
}

Argumen kedua adalah foreign key di tabel child, argumen ketiga adalah local key di tabel parent.

Memakai: Property vs Method (Penting!)

php
// app/Http/Controllers/UserController.php
$user = User::find(1);

foreach ($user->posts as $post) {  // property: langsung query, hasilnya Collection
    echo $post->title;
}

$terbaru = $user->posts()          // method: query builder, bisa di-chain
    ->latest()
    ->take(5)
    ->get();

Ini perbedaan yang paling sering membingungkan pemula. $user->posts (tanpa kurung) langsung mengeksekusi query dan mengembalikan Collection. $user->posts() (dengan kurung) mengembalikan objek relasi yang masih bisa di-chain where, orderBy, dan lain-lain. Mau filter atau batasi? Pakai kurung. Mau langsung loop semuanya? Tanpa kurung.

Membuat Data Lewat Relasi

php
// app/Http/Controllers/PostController.php
$user->posts()->create([
    'title' => 'Post Baru',
    'body' => 'Isi artikel...',
]); // user_id otomatis terisi dari $user->id

Ini pola paling bersih untuk membuat data child: foreign key diisi otomatis, tidak mungkin salah ketik atau lupa. Alternatifnya menyimpan lewat parent: $user->posts()->save($post).

Catatan teknis: belongsTo selalu mengembalikan satu model atau null, bukan Collection. Jadi $post->user->name bisa error kalau user_id-nya null atau user-nya sudah dihapus. Untuk relasi opsional, pakai nullsafe operator: $post->user?->name. Atau definisikan withDefault() pada relasi agar selalu ada objek pengganti: return $this->belongsTo(User::class)->withDefault(['name' => 'Anonim']); Dengan begitu view tidak perlu cek null di mana-mana.

Menghitung dan Mengecek Tanpa Memuat Semua

Relasi sebagai method memberi query builder penuh. Butuh angka tanpa memuat model? Panggil agregat langsung:

php
// app/Http/Controllers/UserController.php
$jumlah = $user->posts()->count(); // SELECT COUNT, tanpa memuat model
$adaBaru = $user->posts()->where('created_at', '>', now()->subDay())->exists();
$rataViews = $user->posts()->avg('views');

Bandingkan dengan $user->posts->count(): itu memuat SEMUA post ke memori dulu baru dihitung. Untuk user dengan ribuan post, bedanya sangat besar. Aturan praktisnya: butuh datanya untuk ditampilkan, pakai property; butuh angka, boolean, atau pengecekan, pakai method dengan agregat.

Tantangan

Relasi Komentar

Buat relasi: Post hasMany Comment, Comment belongsTo Post (asumsi tabel comments ada dengan post_id). Via tinker: ambil 1 post, tampilkan jumlah komentarnya dan nama penulis post-nya.