LaravelEloquentPivotMenengah4 mnt baca

Data Tambahan di Pivot: withPivot

Simpan info tambahan di tabel pivot, mis. peran author.

Pivot Bukan Sekadar Perekat

Kadang hubungan itu sendiri punya data. Post dan user dihubungkan sebagai kontributor, tapi tiap kontributor punya peran yang berbeda: penulis, editor, penerjemah. Menyimpan role di tabel users salah (peran bisa beda di tiap post), menyimpan di tabel posts juga salah (satu post punya banyak peran). Tempat yang benar adalah tabel pivot. Kenapa ini penting? Karena memaksakan data relasi ke tabel yang salah adalah sumber bug logika yang sulit dilacak.

Migration dengan Kolom Tambahan

php
// database/migrations/2026_10_08_000003_create_post_user_table.php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

Schema::create('post_user', function (Blueprint $table) {
    $table->foreignId('post_id')->constrained()->cascadeOnDelete();
    $table->foreignId('user_id')->constrained()->cascadeOnDelete();
    $table->string('role')->default('penulis');
    $table->timestamps();
});

Pivot adalah tabel biasa, jadi migration-nya pun biasa: tambahkan kolom apa pun yang dibutuhkan, seperti role, assigned_at, atau catatan.

Akses Lewat withPivot

php
// app/Models/Post.php
public function authors()
{
    return $this->belongsToMany(User::class)
        ->withPivot('role')
        ->withTimestamps();
}
php
// app/Http/Controllers/PostController.php
foreach ($post->authors as $author) {
    echo $author->name . ' sebagai ' . $author->pivot->role;
}

Setiap model hasil relasi many-to-many membawa objek pivot berisi id kedua sisi plus kolom-kolom yang didaftarkan di withPivot(). Tanpa withPivot(), kolom role tidak bisa diakses meskipun ada di database. withTimestamps() membuat created_at dan updated_at pivot terisi otomatis setiap attach.

Menyimpan Data Pivot

php
// app/Http/Controllers/PostController.php
$post->authors()->attach($userId, ['role' => 'editor']);

$post->authors()->sync([
    1 => ['role' => 'penulis'],
    2 => ['role' => 'editor'],
]);

$post->authors()->updateExistingPivot($userId, ['role' => 'penerjemah']);

attach() menerima array data sebagai argumen kedua, sync() menerima array asosiatif id => data. updateExistingPivot() untuk mengubah data pivot tanpa menyentuh keanggotaan relasinya, cocok untuk form "ubah peran".

Jebakan umum: mengira $author->role bisa diakses langsung. Tidak bisa. Data pivot selalu lewat $author->pivot->role. Ini disengaja agar jelas bahwa role bukan atribut user, melainkan atribut hubungan user tersebut dengan post ini.

Catatan teknis: Kalau tabel pivot mulai butuh method, casting, validasi, atau event sendiri (misalnya mencatat log setiap perubahan peran), saatnya naik kelas ke custom Pivot model. Buat class ContributorPivot extends Pivot, lalu tambahkan ->using(ContributorPivot::class) pada definisi relasi. Pivot model mendukung $casts, model events, dan bahkan SoftDeletes kalau baris pivot perlu diarsipkan alih-alih dihapus permanen.

Filter dan Urut Berdasarkan Kolom Pivot

Laravel menyediakan wherePivot() untuk memfilter langsung lewat data pivot:

php
// app/Http/Controllers/PostController.php
$editors = $post->authors()->wherePivot('role', 'editor')->get();

$timInti = $post->authors()->wherePivotIn('role', ['penulis', 'editor'])
    ->orderByPivot('created_at')
    ->get();

Ini berguna untuk query dari sisi sebaliknya, misalnya "semua post di mana user ini menjadi editor":

php
// app/Http/Controllers/UserController.php
$posts = $user->posts()->wherePivot('role', 'editor')->get();

Jebakan: wherePivot hanya bisa dipakai pada query relasi belongsToMany (dengan kurung), tidak pada Collection yang sudah di-load. Kalau datanya sudah terlanjur di-load, filter manual lewat atribut pivot: $post->authors->where('pivot.role', 'editor').

Tantangan

Peran Kontributor

Tambahkan kolom role ke pivot post_user + withPivot di relasi. Attach 2 user ke 1 post dengan role berbeda, tampilkan via ->pivot->role.