Gruplama Hakkında Bilmek İstediğiniz Her Şey

Şablonlardaki verilerle çalışırken, bunları belirli kriterlere göre gruplama veya özel olarak görüntüleme ihtiyacıyla sık sık karşılaşabilirsiniz. Latte bu amaçla birkaç güçlü araç sunar.

|group filtresi ve fonksiyonu, verileri belirtilen kritere göre verimli bir şekilde gruplamayı sağlar, |batch filtresi verileri sabit boyutlu gruplara ayırmayı kolaylaştırır ve {iterateWhile} etiketi, koşullarla döngülerin ilerleyişini daha karmaşık bir şekilde kontrol etme imkanı sunar. Bu etiketlerin her biri, verilerle çalışmak için özel seçenekler sunar, bu da onları Latte şablonlarında bilgilerin dinamik ve yapılandırılmış bir şekilde görüntülenmesi için vazgeçilmez araçlar haline getirir.

group Filtresi ve Fonksiyonu

Kategorilere ayrılmış öğeleri içeren bir items veritabanı tablosu hayal edin:

id categoryId name
1 1 Apple
2 1 Banana
3 2 PHP
4 3 Green
5 3 Red
6 3 Blue

Latte şablonu kullanarak tüm öğelerin basit bir listesi şöyle görünür:

<ul>
{foreach $items as $item}
	<li>{$item->name}</li>
{/foreach}
</ul>

Ancak öğelerin kategoriye göre gruplar halinde düzenlenmesini isteseydik, her kategorinin kendi listesine sahip olacak şekilde onları bölmemiz gerekirdi. Sonuç şöyle görünmelidir:

<ul>
	<li>Apple</li>
	<li>Banana</li>
</ul>

<ul>
	<li>PHP</li>
</ul>

<ul>
	<li>Green</li>
	<li>Red</li>
	<li>Blue</li>
</ul>

Görev, |group kullanarak kolayca ve zarif bir şekilde çözülebilir. Parametre olarak categoryId belirtiriz, bu da öğelerin $item->categoryId değerine göre daha küçük dizilere bölüneceği anlamına gelir (eğer $item bir dizi olsaydı, $item['categoryId'] kullanılır):

{foreach ($items|group: categoryId) as $categoryId => $categoryItems}
	<ul>
		{foreach $categoryItems as $item}
			<li>{$item->name}</li>
		{/foreach}
	</ul>
{/foreach}

Filtre Latte'de bir fonksiyon olarak da kullanılabilir, bu da bize alternatif bir sözdizimi sunar: {foreach group($items, categoryId) ...}.

Öğeleri daha karmaşık kriterlere göre gruplamak istiyorsanız, filtre parametresinde bir fonksiyon kullanabilirsiniz. Örneğin, öğeleri ad uzunluğuna göre gruplamak şöyle görünür:

{foreach ($items|group: fn($item) => strlen($item->name)) as $items}
	...
{/foreach}

$categoryItems'ın sıradan bir dizi değil, bir iteratör gibi davranan bir nesne olduğunu unutmamak önemlidir. Grubun ilk öğesine erişmek için first() fonksiyonunu kullanabilirsiniz.

Verileri gruplamadaki bu esneklik, group'u Latte şablonlarında verileri sunmak için son derece kullanışlı bir araç haline getirir.

İç İçe Döngüler

Tek tek öğelerin alt kategorilerini tanımlayan ek bir subcategoryId sütununa sahip bir veritabanı tablomuz olduğunu varsayalım. Her ana kategoriyi ayrı bir <ul> listesinde ve her alt kategoriyi ayrı bir iç içe <ol> listesinde görüntülemek istiyoruz:

{foreach ($items|group: categoryId) as $categoryItems}
	<ul>
		{foreach ($categoryItems|group: subcategoryId) as $subcategoryItems}
			<ol>
				{foreach $subcategoryItems as $item}
					<li>{$item->name}
				{/foreach}
			</ol>
		{/foreach}
	</ul>
{/foreach}

Nette Database ile Bağlantı

Veri gruplamayı Nette Database ile birlikte nasıl verimli bir şekilde kullanacağımızı gösterelim. Giriş örneğindeki items tablosuyla çalıştığımızı varsayalım, bu tablo categoryId sütunu aracılığıyla şu categories tablosuna bağlıdır:

categoryId name
1 Fruits
2 Languages
3 Colors

items tablosundaki verileri Nette Database Explorer kullanarak $items = $db->table('items') komutuyla okuruz. Bu veriler üzerinde iterasyon yaparken, $item->name ve $item->categoryId gibi niteliklere erişmenin yanı sıra, categories tablosuyla olan bağlantı sayesinde ilgili satıra $item->category üzerinden de erişebiliriz. Bu bağlantı üzerinde ilginç bir kullanım gösterilebilir:

{foreach ($items|group: category) as $category => $categoryItems}
	<h1>{$category->name}</h1>
	<ul>
		{foreach $categoryItems as $item}
			<li>{$item->name}</li>
		{/foreach}
	</ul>
{/foreach}

Bu durumda, |group filtresini yalnızca categoryId sütununa göre değil, bağlantılı $item->category satırına göre gruplamak için kullanıyoruz. Bu sayede, anahtar değişkeninde doğrudan ilgili kategorinin ActiveRow'unu alırız, bu da bize doğrudan {$category->name} kullanarak adını yazdırmamızı sağlar. Bu, gruplamanın şablonları nasıl netleştirebileceğine ve verilerle çalışmayı nasıl kolaylaştırabileceğine dair pratik bir örnektir.

|batch Filtresi

Filtre, bir öğe listesini önceden belirlenmiş sayıda öğe içeren gruplara ayırmayı sağlar. Bu filtre, verileri daha iyi netlik veya sayfada görsel düzenleme gibi nedenlerle birden fazla küçük grupta sunmak istediğiniz durumlar için idealdir.

Bir öğe listemiz olduğunu ve bunları her biri en fazla üç öğe içeren listelerde görüntülemek istediğimizi varsayalım. |batch filtresinin kullanımı bu durumda çok pratiktir:

<ul>
{foreach ($items|batch: 3) as $batch}
	{foreach $batch as $item}
		<li>{$item->name}</li>
	{/foreach}
{/foreach}
</ul>

Bu örnekte, $items listesi daha küçük gruplara ayrılmıştır ve her grup ($batch) en fazla üç öğe içerir. Her grup daha sonra ayrı bir <ul> listesinde görüntülenir.

Son grup istenen sayıya ulaşmak için yeterli öğe içermiyorsa, filtrenin ikinci parametresi bu grubun neyle tamamlanacağını tanımlamanıza olanak tanır. Bu, eksik bir sıranın düzensiz görünebileceği yerlerde öğeleri estetik olarak hizalamak için idealdir.

{foreach ($items|batch: 3, '—') as $batch}
	...
{/foreach}

{iterateWhile} Etiketi

|group filtresiyle çözdüğümüz aynı görevleri {iterateWhile} etiketini kullanarak göstereceğiz. İki yaklaşım arasındaki temel fark, group'un önce tüm giriş verilerini işlemesi ve gruplaması, {iterateWhile}'ın ise koşullarla döngülerin ilerleyişini kontrol etmesi, böylece iterasyonun aşamalı olarak gerçekleşmesidir.

Önce iterateWhile kullanarak kategorilerle tabloyu oluşturalım:

{foreach $items as $item}
	<ul>
		{iterateWhile}
			<li>{$item->name}</li>
		{/iterateWhile $item->categoryId === $iterator->nextValue->categoryId}
	</ul>
{/foreach}

{foreach} döngünün dış kısmını, yani her kategori için listelerin oluşturulmasını işaret ederken, {iterateWhile} etiketi iç kısmı, yani tek tek öğeleri işaret eder. Bitiş etiketindeki koşul, tekrarın geçerli ve sonraki öğe aynı kategoriye ait olduğu sürece ($iterator->nextValue sonraki öğe) devam edeceğini söyler.

Koşul her zaman karşılanırsa, iç döngüde tüm öğeler oluşturulur:

{foreach $items as $item}
	<ul>
		{iterateWhile}
			<li>{$item->name}
		{/iterateWhile true}
	</ul>
{/foreach}

Sonuç şöyle görünecektir:

<ul>
	<li>Apple</li>
	<li>Banana</li>
	<li>PHP</li>
	<li>Green</li>
	<li>Red</li>
	<li>Blue</li>
</ul>

Böyle bir iterateWhile kullanımının ne faydası var? Tablo boşsa ve hiçbir öğe içermiyorsa, boş <ul></ul> yazdırılmaz.

Açılış etiketinde {iterateWhile} bir koşul belirtirsek, davranış değişir: koşul (ve sonraki öğeye geçiş) iç döngünün sonunda değil, başında zaten yürütülür. Yani, koşulsuz {iterateWhile}'a her zaman girilirken, {iterateWhile $cond}'a yalnızca $cond koşulu karşılandığında girilir. Ve aynı zamanda, sonraki öğe $item'a yazılır.

Bu, örneğin her kategorideki ilk öğeyi farklı bir şekilde, örneğin şöyle oluşturmak istediğimiz bir durumda kullanışlıdır:

<h1>Apple</h1>
<ul>
	<li>Banana</li>
</ul>

<h1>PHP</h1>
<ul>
</ul>

<h1>Green</h1>
<ul>
	<li>Red</li>
	<li>Blue</li>
</ul>

Orijinal kodu, önce ilk öğeyi oluşturacak ve ardından iç {iterateWhile} döngüsünde aynı kategoriden diğer öğeleri oluşturacak şekilde değiştiririz:

{foreach $items as $item}
	<h1>{$item->name}</h1>
	<ul>
		{iterateWhile $item->categoryId === $iterator->nextValue->categoryId}
			<li>{$item->name}</li>
		{/iterateWhile}
	</ul>
{/foreach}

Tek bir döngü içinde birden fazla iç döngü oluşturabilir ve hatta bunları iç içe geçirebiliriz. Bu şekilde alt kategoriler vb. gruplandırılabilir.

Tabloda ek bir subcategoryId sütunu olduğunu ve her kategorinin ayrı bir <ul> içinde olmasının yanı sıra, her alt kategorinin ayrı bir <ol> içinde olacağını varsayalım:

{foreach $items as $item}
	<ul>
		{iterateWhile}
			<ol>
				{iterateWhile}
					<li>{$item->name}
				{/iterateWhile $item->subcategoryId === $iterator->nextValue->subcategoryId}
			</ol>
		{/iterateWhile $item->categoryId === $iterator->nextValue->categoryId}
	</ul>
{/foreach}
versiyon: 3.0