Bu başvuru kılavuzu, en yaygın ilişki türlerini sade örneklerle açıklayan pratik bir rehberdir. hasOne, hasMany, belongsTo, belongsToMany ve polymorphic ilişkileri, eager loading ve performans ipuçlarıyla birlikte sunarak geliştiricilere hızlı ve doğru referans sağlar.
Laravel 12, PHP 8.2+ gerektirir ve migration sözdizimi, model yapısı ile ilişki tanımlamalarında modern yaklaşımları benimser. Bu kılavuzdaki tüm örnekler güncel Laravel 12+ standartlarıyla yazılmıştır.
1. One to One (Bire Bir) İlişki
Senaryo
2 model: Student (Öğrenci) ve Profile (Profil), 2 tablo: students ve profiles.
İş Kuralları:
İlişki Detayı: profiles tablosu student_id foreign key'ini taşır.
Eloquent Modeller
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasOne;
class Student extends Model
{
protected $fillable = ['name'];
public function profile(): HasOne
{
return $this->hasOne(Profile::class);
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Profile extends Model
{
protected $fillable = ['bio', 'avatar', 'student_id'];
public function student(): BelongsTo
{
return $this->belongsTo(Student::class);
}
}
Migration
Schema::create('students', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
Schema::create('profiles', function (Blueprint $table) {
$table->id();
$table->string('bio')->nullable();
$table->string('avatar')->nullable();
$table->foreignId('student_id')->nullable()->constrained()->nullOnDelete();
$table->timestamps();
});
Kayıt Oluşturma
// Student üzerinden Profile ilişkisi kur
$student->profile()->save($profile);
// Profile üzerinden Student ilişkisi kur
$profile->student()->associate($student)->save();
Kayıt Sorgulama
// Student'ın profilini getir
$student->profile;
// Profile'ın öğrencisini getir
$profile->student;
2. One to Many (Bire Çok) İlişki
Senaryo
2 model: Classroom (Sınıf) ve Student (Öğrenci), 2 tablo: classrooms ve students.
İş Kuralları:
İlişki Detayı: students tablosu classroom_id foreign key'ini taşır.
Eloquent Modeller
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\HasMany;
class Classroom extends Model
{
protected $fillable = ['name', 'capacity'];
public function students(): HasMany
{
return $this->hasMany(Student::class);
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
class Student extends Model
{
protected $fillable = ['name', 'classroom_id'];
public function classroom(): BelongsTo
{
return $this->belongsTo(Classroom::class);
}
}
Migration
Schema::create('classrooms', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->integer('capacity');
$table->timestamps();
});
Schema::create('students', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->foreignId('classroom_id')->nullable()->constrained()->nullOnDelete();
$table->timestamps();
});
Kayıt Oluşturma
// Classroom'a birden fazla Student ekle
$classroom->students()->saveMany([$student1, $student2]);
// Tek bir Student ekle
$classroom->students()->save($student);
// Student'a Classroom ata
$student->classroom()->associate($classroom)->save();
Kayıt Sorgulama
// Classroom'daki tüm öğrencileri getir
$classroom->students;
// Student'ın sınıfını getir
$student->classroom;
3. Polymorphic One to Many (Polimorfik Bire Çok) İlişki
Senaryo
3 model: Teacher (Öğretmen), Principal (Müdür) ve Announcement (Duyuru), 3 tablo: teachers, principals, announcements.
İş Kuralları:
Bir Teacher birden fazla Announcement yayınlayabilir.
Bir Principal birden fazla Announcement yayınlayabilir.
Bir Announcement yalnızca bir yazar tarafından yayınlanabilir (Teacher veya Principal).
İlişki Detayı: announcements tablosu author_id ve author_type sütunlarını taşır. "author" (yazar), Teacher ve Principal modellerini kapsayan ortak polimorfik isimdir.
Eloquent Modeller
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphMany;
class Teacher extends Model
{
protected $fillable = ['name'];
public function announcements(): MorphMany
{
return $this->morphMany(Announcement::class, 'author');
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphMany;
class Principal extends Model
{
protected $fillable = ['name'];
public function announcements(): MorphMany
{
return $this->morphMany(Announcement::class, 'author');
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;
class Announcement extends Model
{
protected $fillable = ['title', 'body'];
public function author(): MorphTo
{
return $this->morphTo();
}
}
Migration
Schema::create('teachers', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
Schema::create('principals', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
Schema::create('announcements', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('body');
// author_id (unsignedBigInteger) + author_type (string) sütunlarını otomatik oluşturur
$table->nullableMorphs('author');
$table->timestamps();
});
Kayıt Oluşturma
// Teacher veya Principal üzerinden Announcement ekle
$teacher->announcements()->saveMany([$announcement1, $announcement2]);
$principal->announcements()->saveMany([$announcement1, $announcement2]);
// Tek bir Announcement için
$teacher->announcements()->save($announcement);
$principal->announcements()->save($announcement);
// Announcement üzerinden yazar ilişkisi kur
$announcement1->author()->associate($teacher)->save();
$announcement2->author()->associate($principal)->save();
Kayıt Sorgulama
// Teacher veya Principal'ın duyurularını getir
$teacher->announcements;
$principal->announcements;
// Announcement'ın yazarını getir (Teacher veya Principal instance'ı döner)
$announcement->author;
4. Many to Many (Çoka Çok) İlişki
Senaryo
2 model: Student (Öğrenci) ve Course (Ders), 3 tablo: students, courses ve pivot tablo course_student.
İş Kuralları:
İlişki Detayı: course_student pivot tablosu student_id ve course_id sütunlarını taşır.
Eloquent Modeller
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class Student extends Model
{
protected $fillable = ['name'];
public function courses(): BelongsToMany
{
return $this->belongsToMany(Course::class);
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
class Course extends Model
{
protected $fillable = ['title', 'description'];
public function students(): BelongsToMany
{
return $this->belongsToMany(Student::class);
}
}
Migration
Schema::create('students', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
Schema::create('courses', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('description')->nullable();
$table->timestamps();
});
Schema::create('course_student', function (Blueprint $table) {
$table->id();
$table->foreignId('course_id')->constrained()->cascadeOnDelete();
$table->foreignId('student_id')->constrained()->cascadeOnDelete();
});
Kayıt Oluşturma
// Student'ı birden fazla Course'a kaydet
$student->courses()->attach([$course1->id, $course2->id]);
// Mükerrer kayıtları önlemek için sync() kullan
$student->courses()->sync([$course1->id, $course2->id]);
// Course'a birden fazla Student ekle
$course->students()->attach([$student1->id, $student2->id]);
// Mükerrer kayıtları önlemek için sync() kullan
$course->students()->sync([$student1->id, $student2->id]);
Kayıt Sorgulama
// Student'ın kayıtlı olduğu tüm dersleri getir
$student->courses;
// Course'a kayıtlı tüm öğrencileri getir
$course->students;
5. Polymorphic Many to Many (Polimorfik Çoka Çok) İlişki
Senaryo
3 model: Student (Öğrenci), Teacher (Öğretmen) ve Event (Etkinlik), 4 tablo: students, teachers, events ve attendees.
İş Kuralları:
Bir Student birden fazla Event'e katılabilir.
Bir Teacher birden fazla Event'e katılabilir.
Bir Event'e birden fazla katılımcı (Student ve/veya Teacher) katılabilir.
İlişki Detayı: attendees pivot tablosu participant_id, participant_type ve event_id sütunlarını taşır. "participant" (katılımcı), Student ve Teacher modellerini kapsayan ortak polimorfik isimdir.
Eloquent Modeller
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
class Student extends Model
{
protected $fillable = ['name'];
public function events(): MorphToMany
{
return $this->morphToMany(Event::class, 'participant');
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
class Teacher extends Model
{
protected $fillable = ['name'];
public function events(): MorphToMany
{
return $this->morphToMany(Event::class, 'participant');
}
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphedByMany;
class Event extends Model
{
protected $fillable = ['title', 'date'];
public function students(): MorphedByMany
{
return $this->morphedByMany(Student::class, 'participant');
}
public function teachers(): MorphedByMany
{
return $this->morphedByMany(Teacher::class, 'participant');
}
}
Migration
Schema::create('students', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
Schema::create('teachers', function (Blueprint $table) {
$table->id();
$table->string('name');
$table->timestamps();
});
Schema::create('events', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->date('date');
$table->timestamps();
});
Schema::create('attendees', function (Blueprint $table) {
$table->id();
$table->morphs('participant'); // participant_id + participant_type
$table->foreignId('event_id')->constrained()->cascadeOnDelete();
});
Kayıt Oluşturma
// Student veya Teacher'ı Event'lere kaydet
$student->events()->saveMany([$event1, $event2]);
$teacher->events()->saveMany([$event1, $event2]);
// Tek bir Event için
$student->events()->save($event1);
$teacher->events()->save($event1);
// Event'e katılımcı ekle
$event->students()->attach([$student1->id, $student2->id]);
$event->teachers()->attach([$teacher1->id, $teacher2->id]);
// Mükerrer kayıtları önlemek için sync() kullan
$event->students()->sync([$student1->id, $student2->id]);
$event->teachers()->sync([$teacher1->id, $teacher2->id]);
Kayıt Sorgulama
// Student veya Teacher'ın katıldığı tüm etkinlikleri getir
$student->events;
$teacher->events;
// Event'in tüm katılımcılarını getir
$event->students;
$event->teachers;
Laravel 12 Sözdizimi Değişiklikleri — Özet
Eski Sözdizimi (Laravel 5.x) | Yeni Sözdizimi (Laravel 12) |
|---|
$table->increments('id')
| $table->id()
|
$table->integer('student_id')->unsigned()->index()->nullable() + ayrı foreign()
| $table->foreignId('student_id')->nullable()->constrained()->nullOnDelete()
|
$table->integer('course_id')->unsigned()->index() + ayrı foreign()
| $table->foreignId('course_id')->constrained()->cascadeOnDelete()
|
Dönüş tipi yok: public function profile() | Dönüş tipi ile: public function profile(): HasOne |
Manuel author_id + author_type sütunları | $table->nullableMorphs('author')
|
Bonus: Pratik İpuçları
Eager Loading — N+1 Sorununu Önle
N+1 sorunu, bir koleksiyondaki her kayıt için ayrı ayrı sorgu çalıştırılmasıyla ortaya çıkar. with() kullanarak tüm ilişkili veriler tek sorguda yüklenir.
// İlişkiyi önceden yükle
$classrooms = Classroom::with('students')->get();
$students = Student::with(['classroom', 'courses'])->get();
Lazy Eager Loading
Model zaten yüklendikten sonra ilişkiyi yüklemek için kullanılır.
$student->loadMissing('courses');
İlişkinin Varlığına Göre Sorgulama
// En az bir öğrencisi olan sınıfları getir
Classroom::has('students')->get();
// 10'dan fazla öğrencisi olan sınıfları getir
Classroom::has('students', '>=', 10)->get();
// İlişki koşuluna göre filtrele
Classroom::whereHas('students', function ($query) {
$query->where('name', 'Ali');
})->get();
Pivot Tabloya Ekstra Sütun Ekleme (Many to Many)
// Migration'da ek sütunlar tanımla
Schema::create('course_student', function (Blueprint $table) {
$table->id();
$table->foreignId('course_id')->constrained()->cascadeOnDelete();
$table->foreignId('student_id')->constrained()->cascadeOnDelete();
$table->decimal('grade', 4, 2)->nullable(); // not sütunu
$table->timestamp('enrolled_at')->nullable(); // kayıt tarihi
});
// Model'de withPivot() ile tanımla
public function courses(): BelongsToMany
{
return $this->belongsToMany(Course::class)
->withPivot('grade', 'enrolled_at')
->withTimestamps();
}
// Pivot verisine eriş
foreach ($student->courses as $course) {
echo $course->pivot->grade;
echo $course->pivot->enrolled_at;
}
Pivot Verisiyle Birlikte Kayıt Ekleme
// Ekstra pivot verisiyle ilişki kur
$student->courses()->attach($course->id, [
'grade' => 95.5,
'enrolled_at' => now(),
]);