Genel olarak @unstable77 ile aynı görüşteyim.
Bu konuda birkaç hususa dikkat çekmek isterim.
Kategoriler tablosunun satır sayısı çok fazla olmayacaktır.
Bununla birlikte nested set tarzı yapı bana cazip gelmiyor. Hiç kullanmadım ama, yeni bir kategori eklendiğinde ve silindiğinde neredeyse tüm tablo satırlarının başlangıç ve bitiş yerlerini güncellemek gerekir diye anlıyorum.
İster @xseach'in dediği gibi where sınırlaması ister @mevlut44'ün söylediği gibi tabloyu kendine hasmany ilişki ile bağlama şeklinde yapılan rekürsif fonksiyonların önemli bir sorunu, fonksiyon içerisinde echo işlemleri yapılmasıdır. Fonksiyonlarda echo yapmak yerine bir veri tipinde veriler döndürülmesi uygundur.
Önceki paragrafta belirtilen sıkıntı, view'lere veriler nasıl geçilecek sorununu getirmiştir. Bu sorunu aşmak için hem @sdoksanbir hem de mevlut44 view'lere veri geçmek yerine view'lere iş mantığını koymuşlardır. Çok zorlanınca bu yapılabilirse de, optimum olarak iş mantığının modellerde, hadi belki controllerde yer alması gerekir. View'ler mümkünse sadece geçilen verileri yazdırmalıdır.
Rekürsif fonksiyonda tekrarlı sql sorguları yerine, tek bir sorgu ile tüm kategori satırlarını elde ettikten sonra, bu nesne veya dizide rekürsif fonksiyon çalıştırmak (@unstable77 ve @Rony'nin önerisi gibi) bence daha uygun bir yöntem. Ancak @Rony'ninkinde de fonksiyon içinde echo yaptırma sorunu dolayısıyla view'e geçme sorunu var ve @unstable77'nin önerisi menü oluşturmaya özgü bir çevirim kullanıyor.
Rekürsif fonksiyonda echo yaptırma sorununun asıl kaynağı fonksiyon içi değişkenlere yapılan değişikliğin bir sonraki çağrıda kullanılamamasıdır. Global değişken kullanarak bu çözülebilir ise de, o da nesne yönelimli programlamada bir başka sorun olacak.
Rekürsif fonksiyonda echo yaptırma sorununu en iyi şekilde referans türünde parametre geçerek aşabiliriz. Bunun için parametrenin başına "&" eklemek yeterlidir.
Tüm bunlar dikkate alındığında, bir controllerde Kategoriler tablosunun tüm satrlarını aldıktan sonra, bu sonucu başka bir rekürsif fonksiyona (bu bir helper metodu olabilir ya da controllere eklenecek private veya protected bir metod olabilir) göndererek bir veri elde etmek ve view'e bu veriyi bir değişken olarak geçmek en doğrusu olacaktır. Örneğin controllerde,
function getCats($parentId)
{
$secenekler = array();
$kats = Kategori::all();
VeAltKategoriler($kats, $secenekler, $parentId); //Bir helper metodu. Controllerin kendinde ise $this->VeAltKategoriler...
return View::make('birview', $secenekler);
}
Rekürsif fonksiyon
function VeAltKategoriler($array, &$secen, $parent = 0, $derinlik = -1) {
++$derinlik;
foreach ($array as $row) {
if ($row['parentID'] == $parent) {
$secen[] = array("id"=>$row['id'], "ad"=>$row['kategoriadi'], "derinlik"=>$derinlik);
VeAltKategoriler($array, $secen, $row['id'], $derinlik);
}
}
}
Eğer @Rony'nin verdiği örnek dizi geçilirse, view'e sunulan $secenekler değişkeni şu veriyi taşıyacaktır:
array(5) {
[0]=> array(3) { ["id"]=> int(1) ["ad"]=> string(12) "Ana Kategori" ["derinlik"]=> int(0) }
[1]=> array(3) { ["id"]=> int(2) ["ad"]=> string(14) "Alt kategori 1" ["derinlik"]=> int(1) }
[2]=> array(3) { ["id"]=> int(3) ["ad"]=> string(14) "Alt kategori 2" ["derinlik"]=> int(1) }
[3]=> array(3) { ["id"]=> int(4) ["ad"]=> string(17) "En alt kategori 1" ["derinlik"]=> int(2) }
[4]=> array(3) { ["id"]=> int(5) ["ad"]=> string(24) "En alt seviye kategori 1" ["derinlik"]=> int(3) }
}
Bu değişkeni view dosyasında bildik tarza kullanabiliriz.