Laravel 4: İlişkiler üzerinden etkin filtreleme ve gruplama
Birkaç gündür Laravel 4 ile Eloquent ORM ile kompleks bir veritabanı ilişkilendirmesi üzerinde uğraşıyorum. Bu ilişkilendirmede şunlar olacak:
- Bir
A
modeli üzerinde filtreleme yapılacak. - Bu
A
modeli üzerindeki filtreleme,B
,C
veD
modelindeki ilişkilendirmelere sahip. - Be bu ilişkilendirmelerden biri (
B
diyelim) de (B
veyaB
'ye bağlıB.C
ilişkisindeki koşula göre olacak). - Bu
B
,C
veyaD
ilişkilerinde hembelongsTo
, hemhasMany
, hem debelongsToMany
ilişkisi mevcut.
Modellerde tanımlanan ilişki adlarına modelA
, modelB
, modelC
ve modelD
diyelim.
Normalde çıkan çıktıları şunun gibi alabiliyoruz:
Ama bu çıktı ana sorguda modelAsutun
haricinde filtreleme yapmıyor, eager loading'den gelen verileri filtreliyor sadece. Kısaca işime yaramıyordu.
Düz MySQL gibi veritabanlarında spagetti kod yazarken relationlar ve gruplamalar yüklendikten sonra filtreleme yapmak için HAVING
kullanılır.
Laravel 4.1'e kadar having metodunu kullanmanın bir yolu yoktu, sorgunun en sonuna where('tabloadi.sütun', $deger)
yazmak gerekirdi, ORM kullanırken tablo adını yazmak da bence mantıksız olduğu kadar pivot many-to-many ilişkilerde sorun verecek bir takla idi bu.
Laravel 4.1'de gelen yeniliklerden biri olan Constraining Eloquent ‘has’ relationships olayı işte tam burada devreye giriyor. 1, 2.
Bu parametrelerin de filtrelenmesini sağlamak için A modeline whereHas
komutu uygulanmalı:
Sorgu tüm ilişkileri sorgulamaya alıyor olsak şuna dönüşmeli yani:
Ama bu sorguda bir hata var, Nested relationship (ModelB.ModelC
) whereHas
ile kullanılamıyor! Koleksiyon hatası alacaksınız bunu denemeye kalkarsanız.
Burada aklıma ilk gelen yine Laravel 4.1'in yeniliklerinden olan hasManyThrough
ilişkilendirme idi (ModelB.ModelC
ilişkisine modelBC
gibi farklı bir isim vererek nested durumdan kurtulmak), fakat bu da işe yaramıyor.
Bunu çözmenin yolu whereHas içine bir whereHas daha yazmak:
Burada şunu dedik: ModelA
üzerinde ilişkili olan ModelB
içinde bKosul1
sütunu $inputs->bKosul1
değeri ile eşleşen, ModelB
ilişkisine bağlı modelC
içindeki cKosul1
sütunu $inputs->cKosul1
değeri ile eşleşen, vede modelD
ilişkisindeki dKosul1 sütunu $inputs->dKosul1
sütunu ile eşleşen, ve de direkt ModelA
içindeki modelAsutun
'u $inputs->modelAsutun
ile eşleşen değerleri döktük.
Buraya kadar çok güzel, fakat benim şu an üzerinde çalıştığım örneğim biraz daha karılşık.
Öyle bir koşul yapmalıyım ki, eğer varsa, ya modelB
içindeki veyaliKosul
ilişkisi ile (pivot tablo), ya da modelD
içindeki dVeyaliKosul
sütunu (tamam, kötü isim seçtim, kabul :) ) ile ilişkilendirilmeli
(Neden diyebilirsiniz burada, örneğimden verebileceğim detay olarak: yollanabilecek adresler ve de oturduğu yer gibi düşünün. Oturduğu yer yollanabilecek adres olabilir de olmaya da bilir, bu yüzden hem oturduğu yer, hem de yollanabilecek yerler eşleşmeli.)
Bu durumda veya koşulu için orWhereHas
metodunu kullanıyoruz:
Ama benim örneğimde birden fazla whereHas
/ orWhereHas
var, bunları bir şekilde gruplamam lazım. İlk olarak gruplanacak verileri whereHas(function($q) use($inputs){ ... })
gibi bir kod bloğu içine koylayı denedim, fakat kaynak koduna bakınca bunun desteklenmediğini gördüm. İlk parametreye ilişki adı girilmeli. Fakat benim birden fazla ilişkiden gruplamam lazım?
Biraz kurcalamalar sonucunda, bunları where()
ile gruplanabileceğini fark ettim.
En son kodum şunun gibi idi:
Farkındaysanız modelB
için iki koşulum var, biri veyalı olduğundan where()
içinde, diğeri de kendine ait, ayrıca bu modelB içinde modelD
alt ilişkisinde de sorgu var.
Bu sayede whereHas
gruplandığı için birden fazla whereHas
'i kolaylıkla gruplayabilirsiniz.
Not: Bu durum pivotlarda çalışmayacaktır. Pivotlar için önce whereIn
denedim, ama başarılı olamadım (belki bende idi hata? Geri bildirim alabilirsem memnun olurum), sonra bir dizi gibi değerlendirip gibi foreach ile döndürmek geldi aklıma:
ModelD
'yi incelerseniz ne demek istediğimi daha iyi göreceksiniz.
Böylece pivot verilerini de sorunsuz eklemiş oldum.
Umarım bu birilerinin işine yarar.
İyi kodlamalar ;)
Originally published at arda.pw on March 26, 2014.