werfection Foreign key konusunda yanlış anlaşılan şeyler var;
- Foreign key ilişki kurmanız için gerekli bir key değil, foreign key ilişkiyi tutacak olan sütuna verilen bir nitelik. İster SQL join yapın, ister Eloquent ilişkisi kullanın, foreign key niteliği olmadan da yaparsınız, bir engel yok. Zaten ilişki dediğiniz soyut bir şey, sütunlara verdiğiniz değerler ile siz kurguluyorsunuz, veri tabanında somut bir karşılığı yok.
- category_id isimli bir unsigned big integer foreign key oluşturduğunuzda category_id isimli bir unsigned big integer sütun oluşturup ona foreign key niteliği veriyorsunuz, yani teknik olarak iki işlem. Böylece veri normalizasyonu için bazı kurallar getirmiş oluyorsunuz. Bu kuralların en başında ise foreign key atılan tablodaki (referans tablo) kaydın silinmesini engellemek. Yani bir kategorinin içinde blog yazıları varken kategorinin silinmesini istemiyorsunuz. Silmeye çalışırsanız hata alırsınız. Kategori silinebilsin istiyorsanız 3 seçeneğiniz oluyor;
- Kategorinin altındaki blogları silip sonra kategoriyi silmek.
- ON DELETE CASCADE kullanarak kategori silinirken blogların da otomatik silinmesini sağlamak.
- İşlem sırasında foreign key kontrolünü devre dışı bırakmak, ki bunu yaparsanız blog kayıtları öksüz kalır ve veri tabanınızın bütünlüğü bozulur (bütünlüğü sağlama işine veri ya da veri tabanı normalizasyonu denir).
- Son olarak bir alanı ilişki için kullandığınızda performanslı sorgular için index de oluşturmanız lazım. Yine aynı şekilde çoğu arkadaş MySQL kullandığı için MySQL bunu foreign key oluşturunca otomatik yaptığından dolayı farkında değil. Mesela PostgreSQL oluşturmaz, siz elle kendiniz oluşturursunuz. Index de yine aynı şekilde ilişki için gerekli değil, daha önce dediğim gibi ilişki somut bir şey değil sizin uydurduğunuz soyut bir şey.
Veri tabanı normalizasyonu önerdiğimiz için migrasyon oluştururken $table->foreignId('category_id') kullanılmasını söylüyoruz. Bu yöntem aynı anda hem sütunu hem de foreign key'i oluşturuyor. $table->unsignedBigInteger('category_id'); $table->foreign('category_id') şeklinde eskiden olduğu gibi iki direktif ile de yapabilirsiniz. Sanırım $table->foreignId('category_id') yapılıp iki işlem tek seferde yapılduğı için bazı arkadaşlar bu category_id'yi foreign key sanıyor. Bu bir sütun, foreign key değil, foreign key ayrıca oluşturuluyor.
Sizi yazdığınız sorgu şudur:
SELECT blog.*,
image.link AS image
FROM blog
INNER JOIN image
ON image.content_id = blog.id
WHERE image.modul = "blog"
Eğer HasOne kullanıp eager loading yapsaydınız iki tane sorgu olacaktı:
--- Önce blogları çekecekti
SELECT * FROM blog
--- Sonra gelen id'lerden image kayıtlarını çekecekti ve sonucu birleştirip
--- size verecekti.
SELECT *
FROM image
WHERE modul = 'blog'
AND content_id IN (1, 2, 3)
Normal şartlarda join hızlıdır ama bakımı size aittir, SQL düzeyinde işlem yapıyorsunuz. Eloquent kullandığınızda ise yine benzer bir hız elde edebilirsiniz ama arka planda Collection daha çok yüklendiği için işlemci ve bellek kullanımı artacaktır. Özellikle binlerce kayıt tek seferde çekilecekse join kullanın. Elbette bazı durumlarda yazılan join sorgusuna göre Eloquent daha hızlı olabilir, bu nasıl bir sorgu yaptığınıza tablodaki indexlere vs bağlı. Aynı anda 100 kişinin bile girmeyeceği, içinde 20 30 bin kayıt olan bir uygulamada Eloquent kullanırdım, yavaş olan yerler olursa (ki varsa genellikle yanlış yapıdan dolayıdır) SQL yazardım.