İçeriğe geç

Laravel hasManyDeep İlişkisi

Merhabalar efendim, bu yazımda sizlerle Laravel’de Many türündeki zincir ilişki yapısının karmaşıklığını azaltacak ve erişimi kolaylaştıracak bir ilişkiden bahsedeceğim.

Bu ilişkinin adı hasManyDeep ve Laravel’in içinde default olarak gelmiyor, composer ile ek paket olarak kurmak gerekiyor.

Neye ihtiyacımız var? Ne istiyoruz?

Bu bölümü biraz uzun tutacağım ki neye ihtiyacımız olduğunu tam olarak anlayalım…

Varsayalım ki şöyle bir ilişki dizimiz olsun:

Country → hasMany → User → hasMany → Post → hasMany → Comment

Şimdi… Diyelim ki bir country deki userların postlarının commentlerini istiyoruz, yani kısaca bir countrydeki commentleri istiyoruz.

Country::users()->posts()...

Bızzztttt!!!… O da ne bir many ilişki üzerinden başka bir ilişki kurulmuyor… Tamam bende tersten giderim o halde. Nasılsa hasMany nin tersine ilişkisi olan belongsTo ilişkisi ile ardışık geriye doğru gidebiliyoruz.

Comment::whereHas('post.user.country', function($query) {
     $query->where('id',1);
 })->get();

Postunun userinin country sinin id si 1 olan commentler… Evet şimdi sıralı belongsTo ilişkisini kullanarak aldık ama… Yağğğ ben bunda post ve user katmanında ek query eklemek istiyordum !? Tamam sorun yok tersten ilişkiyi genişleterek gideriz bizde… Öhömm…

Comment::whereHas('post', function($query) {
    $query->whereHas('user', function($query) {
        $query->whereHas('country', function ($query) {
            $query->where('id',1);
        });
    });
})->get();

Postu olan, bu postun userinin countrysi olan, bu countrynin de id si 1 olan, Comment leri getir. Öhhhh ne söyledik be… Kodumuz da güzel oldu ortadan aduket yemiş gibi. Birde bu kod parçasını projenizin bir çok yerinde kullandığınızı düşünün, rezalet…

Ya birde tek tip ilişkiye bağlı kalmak istemiyordum ben…. Bir yerde hasMany bir yerde belongsTo bir yerde morphToMany ilişkisi olan bir zincir yapım var ben nasıl çekeceğim bunu? ( Yeter be kardeşim Allah çektirmesin !! ) Tamam tamam o kadar derine gitmiyorum 🙂

Yapı nasıl olursa olsun… Şöyle yapabilseydik ne güzel olurdu değil mi yaaa.

Country::comments()->get();

Country’nin commentleri” ohhh ne kadar kolay söyledik ve yazdık..

Nasıl Çözeceğiz?

Bu söylediklerimi Jonas Staudenmeir kardeşimiz düşünmüş ve bize bu sorunları ortadan kaldıracak hasManyDeep adında bir paket hazırlamış. Gelin şimdi bu paketi biraz inceleyelim.

Bu paket ile birlikte gelen ilişki türleri yukarıda bahsettiğim tüm sorunları büyük ölçüde çözüyor.

Tam olarak ne yapıyor dersek, birden fazla ilişkiyi zincir olarak birbirine bağlayıp tek bir ilişki olarak bize veriyor. Aslında Laravel’de HasManyThrough adındaki ilişki bunu sağlıyor ancak maalesef sadece 2 katman ilerleyebiliyoruz.

Aslında bu paket HasManyThrough ilişkisinin genişletilmiş halidir ve katman olarak daha derine inmemize olanak sağlıyor. Ayrıca many-to-many ve polymorphic ilişkileri de dahil etmemize olanak sağlıyor.

Kurulum

composer require staudenmeir/eloquent-has-many-deep:"^1.7"

Bu kadar 😀

Kullanım

Yukarıda bahsettiğim gibi böyle bir yapımız olduğunu varsayalım.

Country → hasMany → User → hasMany → Post → hasMany → Comment

Önce Has Many Through ile ne yapabilirdik bir bakalım.

class Country extends Model
{
    public function posts()
    {
        return $this->hasManyThrough('App\Post', 'App\User');
    }
}

Gördüğünüz gibi Country üzerinden, User modelini kullanarak Post modeline ulaşabildik, daha derine gidemedik.

Yukarıda bahsettiğim gibi hasManyDeep aslında hasManyThrough ilişkisinin genişletilmiş halidir ve daha derine inmemize olanak sağlar.

class Country extends Model
{
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

    public function comments()
    {
        return $this->hasManyDeep('App\Comment', ['App\User', 'App\Post']);
    }
}

İşte bu… Country üzerinden önce Usera, Userdan Posta ordan Commente atladık. Ve işin güzel yanı Array içindeki ilişki sayısı önemsiz.

Yapıya biraz daha geniş bakarsak şöyle bir şey görüyoruz.

class Country extends Model
{
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

    public function comments()
    {
        return $this->hasManyDeep(
            'App\Comment',
            ['App\User', 'App\Post'], // İlişki Country den Usera doğru başlıyor.
            [
               'country_id', // users tablosundaki Foreign key
               'user_id',    // posts tablosundaki Foreign key 
               'post_id'     // comments tablosundaki Foreign key 
            ],
            [
              'id', // countries tablosundaki primary key
              'id', // users tablosundaki primary key 
              'id'  // posts tablosundaki primary key 
            ]
        );
    }
}

Evet artık ilişkimizi yaratmış olduk ve şu şekilde kullanabiliriz.

Country::comments()->get()

Bu yapı yukarıda bahsettiğim zincir yapısında soldan sağa doğru hasMany yapısını temsil eder ve varsayılan da öyledir. Ancak biz bunu değiştirebiliyoruz birazdan bahsedeceğim. Ama ondan önce Foreign Key leri ve Primary Key leri yazmak zorunda olmadığımızı belirtmek istiyorum, yerlerine null yazarak yada o sıradaki kaydı yazmaz isek, varsayılan neyse ( modeladi_id ) şeklinde alacaktır.

class Country extends Model
{
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

    public function comments()
    {
        return $this->hasManyDeep('App\Comment', ['App\User', 'App\Post'], [null, 'custom_user_id']);
    }
}

Haydi gelelim sıralı hasMany ilişkiyi değiştirmeye… Varsayalım ki;

Post ve Comment arasındaki ilişki hasMany yerine belongsTo ilişki olsun.

 Country → hasMany → User → hasMany → Post → hasMany → Comment 
 Country → hasMany → User → hasMany → Post → belongsTo → Comment 

Bu durumda yapmamız gereken tek şey ilişki pozisyonu değiştiği için, array içinde ilgili indexlerde primary key ile foreign key’in yerlerini değiştirmektir.

class Country extends Model
{
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

    public function comments()
    {
        return $this->hasManyDeep(
            'App\Comment',
            ['App\User', 'App\Post'], // İlişki Country den Usera doğru başlıyor.
            [
               null', // users tablosundaki Foreign key
               null,    // posts tablosundaki Foreign key 
               'post_id'     // comments tablosundaki Foreign key 
            ],
            [
              null, // countries tablosundaki primary key
              null, // users tablosundaki primary key 
              'id'  // posts tablosundaki primary key 
            ]
        );
    }
}

Kalın olarak işaretlenmiş indexler swap yaparak yer değiştirecekler ve şöyle olacaklar.

class Country extends Model
{
    use \Staudenmeir\EloquentHasManyDeep\HasRelationships;

    public function comments()
    {
        return $this->hasManyDeep(
            'App\Comment',
            ['App\User', 'App\Post'], // İlişki Country den Usera doğru başlıyor.
            [
               null', // users tablosundaki Foreign key
               null,    // posts tablosundaki Foreign key 
               'id'     // comments tablosundaki Foreign key 
            ],
            [
              null, // countries tablosundaki primary key
              null, // users tablosundaki primary key 
              'post_id'  // posts tablosundaki primary key 
            ]
        );
    }
}

İşte bu kadar artık bu yapıya uygun ilişki zincirini tek bir ilişkide birleştirdik.

 Country → hasMany → User → hasMany → Post → belongsTo → Comment 

Aynı işlemi diğer Modeller arasındaki ilişkiye de uygulayabilrisiniz.

Bu dokümanda yeterince detaylı anlattığımı düşünüyorum. Eğer diğer ilişki türlerini nasıl yaparım derseniz sizi bu paketin Github sayfasına yönlendirebilirim.

Github: https://github.com/staudenmeir/eloquent-has-many-deep

Tarih:Laravel

İlk Yorumu Siz Yapın

Bir cevap yazın

E-posta hesabınız yayımlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir