Laravel Eloquent İlişkileri Hızlı Başvuru Kılavuzu

Ana Sayfa Blog Laravel Eloquent İlişkileri Hızlı Başvuru Kılavuzu

Laravel Eloquent İlişkileri Hızlı Başvuru Kılavuzu

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ı:

  • Bir Student'ın bir Profile'ı olabilir.

  • Bir Profile yalnızca bir Student'a ait olabilir.

İ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ı:

  • Bir Classroom'da birden fazla Student olabilir.

  • Bir Student yalnızca bir Classroom'a ait olabilir.

İ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ı:

  • Bir Student birden fazla Course'a kayıt olabilir.

  • Bir Course'a birden fazla Student kayıt olabilir.

İ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(),
]);

Sinan Eldem

Fullstack Web Developer

Laravel Framework ile PHP ve MySQL üzerine özel ders, danışmanlık ve web programcılığı hizmetleri veriyorum.

Danışmak istedikleriniz ile ilgili benimle irtibat kurabilirsiniz.

Benzer Yazılar

Laravel Unit Testlerini Google Chrome eklentisi ile hazırlama

Marcel Pociot adındaki geliştirici geçenlerde kabul testlerini (acceptance tests) doğrudan Google Chrome’a eklenen bir Extension (uzantı) ile görsel olarak oluşturulabilmesini sağladı.

Linux İşletim Sistemi Tarih ve Saatini Yapılandırma

Yeni bir Linux Sunucu kurulumu, sunucuda köklü bir değişiklik yapıldığında veya gün ışığından dolayı saatler ileri veya geri alındığında bazen Linux Sunucu tarih ve saatini yeniden yapılandırmak gerekir.

Laravel Valet

5 Mayıs 2016 tarihi itibariyle, web yazılımı geliştirme ortamı olarak MAC OSX kullananlar için Laravel geliştiricisi Taylor Otwell tarafından yeni bir sunucu arabirimi duyuruldu: Valet

Yorumlar