Nette Documentation Preview

syntax
Gruplama hakkında bilmek istediğiniz her şey
********************************************

.[perex]
Şablonlarda veriyle çalışırken sıklıkla öğeleri gruplamanız, onları partilere bölmeniz veya bir koşula göre üzerlerinde dolaşmanız gerekir. Latte bunun için üç araç sunar; her biri biraz farklı bir duruma uygundur.

`|group` filtresi öğeleri belirli bir ölçüte göre gruplar, `|batch` filtresi onları sabit boyutlu partilere böler ve `{iterateWhile}` etiketi veriyi adım adım dolaşıp iç döngüyü ne zaman keseceğine kendisi karar verir. Bunları tek tek ele alacağız.


`group` filtresi ve fonksiyonu .{data-version:3.0.16}
=====================================================

Araç iki biçimde kullanılabilir: bir filtre olarak `$items|group: …` ya da bir fonksiyon olarak `group($items, …)`. Anlamsal olarak eşdeğerdirler; okunabilirliğe göre seçin.

Öğeleri çeşitli kategorilere ait olan bir `items` veritabanı tablosu düşünün:

| 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ürdü:

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

Ancak öğeleri kategoriye göre gruplar halinde istersek, her kategorinin kendi listesi olacak şekilde onları bölmemiz gerekir. İstenen sonuç şöyle görünürdü:

```latte
<ul>
	<li>Apple</li>
	<li>Banana</li>
</ul>

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

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

Bu görev `|group` kullanılarak kolay ve zarif biçimde çözülebilir. Parametre olarak `categoryId` belirtiriz; bu, öğelerin `$item->categoryId` değerine göre daha küçük dizilere bölüneceği anlamına gelir (`$item` bir dizi olsaydı `$item['categoryId']` kullanılırdı):

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

Öğeleri daha karmaşık ölçütlere göre gruplamak isterseniz, filtre parametresinde bir fonksiyon kullanabilirsiniz. Her grubun anahtarı o zaman fonksiyonun dönüş değeri olur; örneğin ad uzunluğuna göre gruplarken karakter sayısı olacaktır:

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

Şunu belirtmek önemlidir: her grup (`$categoryItems` dahil) sıradan bir dizi değil, bir yineleyici gibi davranan bir nesnedir; yani öğelere indeksle erişemezsiniz, örneğin `$categoryItems[0]`. Ancak öğeleri `count($categoryItems)` ile sayabilirsiniz ve gruptaki ilk öğeye erişmek için [`first()` |latte:functions#first()] fonksiyonunu kullanın.

Bu esneklik, `|group`'u veriyi sunmak için son derece yararlı bir araç yapar.


İç içe döngüler
---------------

Veritabanı tablomuzda her öğe için alt kategori tanımlayan ek bir `subcategoryId` sütunu olduğunu düşünelim. Her ana kategoriyi ayrı bir `<ul>` listesinde, o ana kategorideki her alt kategoriyi de ayrı bir iç içe `<ol>` listesinde göstermek istiyoruz:

```latte
{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 birlikte
---------------------------

Veri gruplamanın Nette Database ile birlikte nasıl etkili kullanılacağını gösterelim. Giriş örneğindeki `items` tablosuyla çalıştığımızı ve onun `categoryId` sütunu üzerinden şu `categories` tablosuna bağlı olduğunu varsayalım:

| categoryId | name       |
|------------|------------|
| 1          | Fruits     |
| 2          | Languages  |
| 3          | Colors     |

Veriyi `items` tablosundan Nette Database Explorer ile `$items = $db->table('items')` komutuyla yükleriz. Bu veri üzerinde dolaşırken yalnızca `$item->name` ve `$item->categoryId` gibi niteliklere değil, `categories` tablosuyla olan ilişki sayesinde `$item->category` ile ilgili satıra da erişebiliriz. Bu ilişki ilginç kullanımlara olanak tanır:

```latte
{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, ilişkili satır `$item->category`'ye göre gruplamak için kullanırız. Sonuç olarak anahtar (`$category`) doğrudan o kategorinin `ActiveRow` nesnesini taşır; bu da adını `{$category->name}` ile göstermemize ve `categories`'e ayrı bir sorgu yapmadan başka herhangi bir sütuna erişmemize olanak tanır.


`|batch` filtresi
=================

Filtre, bir öğe listesini sabit boyutlu partilere böler. Izgara düzenleri, sütun yerleşimleri veya her tür görsel gruplama için kullanışlıdır.

Öğeleri, her listenin en fazla üç öğe içerdiği listelerde göstermek istediğimizi düşünelim:

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

Bu örnekte `$items` listesi, her grubun (`$batch`) en fazla üç öğe içerdiği daha küçük gruplara bölünür. Sonra her parti ayrı bir `<ul>` listesinde gösterilir.

Son grup, istenen sayıya ulaşacak kadar öğe içermiyorsa, filtrenin ikinci parametresi bu grubun neyle tamamlanacağını tanımlamanızı sağlar. Bu, eksik bir satırın düzensiz görünebileceği durumlarda öğeleri estetik biçimde hizalamak için idealdir.

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


`{iterateWhile}` etiketi
========================

`|group` filtresiyle çözdüğümüz aynı görevleri `{iterateWhile}` etiketiyle göstereceğiz. İki yaklaşım arasındaki temel fark şudur: `|group` önce tüm girdi verisini işleyip gruplarken, `{iterateWhile}` döngünün ilerlemesini bir koşulla denetler ve yineleme sırayla ilerler.

Önce kategorili tabloyu `{iterateWhile}` ile render edelim:

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

`{foreach}` döngünün dış bölümünü, yani her kategori için liste çizmeyi işaretlerken, `{iterateWhile}` etiketi iç bölümü, yani tek tek öğeleri işaretler. Kapanış etiketindeki koşul, geçerli ve sonraki öğe aynı kategoriye ait olduğu sürece yinelemenin süreceğini söyler (`$iterator->nextValue` [sonraki öğedir |/tags#$iterator]; son öğede iç döngü biter, çünkü sonraki öğe yoktur - Latte koşulu değerlendirmeden önce `$iterator->hasNext()`'i denetler, bu yüzden `null` ile karşılaştırmaya hiç gelinmez).

Koşul her zaman doğru olsaydı, tüm öğeler ilk `<ul>`'un içinde render edilirdi:

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

Sonuç şöyle görünürdü:

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

`{iterateWhile}`'ı böyle kullanmanın yararı ne? `<ul>` dıştaki `{foreach}`'in içinde olduğundan, girdi boş olduğunda hiçbir şey render edilmez; yalnız başına bir `<ul></ul>` kalmaz. `{iterateWhile}` olmadan aynı durumu, etiketi açmadan önce bir `{if}` ile ya da `{foreachelse}` ile ele almanız gerekirdi.

Koşulu açılış `{iterateWhile}` etiketinde belirtirsek davranış değişir: koşul (ve sonraki öğeye geçiş) iç döngünün sonunda değil başında gerçekleştirilir. Yani koşulsuz `{iterateWhile}`'a her zaman girersiniz, ama `{iterateWhile $cond}`'a yalnızca `$cond` koşulu sağlandığında girersiniz. Aynı zamanda sonraki öğe `$item`'a yazılır.

Bu, her kategorideki ilk öğeyi diğerlerinden farklı render etmek istediğimiz durumlarda yararlıdır; örneğin şöyle:

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

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

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

(PHP kategorisindeki boş `<ul></ul>`, yalnızca mekanizmanın bir gösterimidir; gerçek kodda `<ul>` render'ını bir `{if}` ile halledersiniz.)

Özgün kodu, önce öğeyi bir başlık olarak render edecek, sonra aynı kategorideki sonraki öğeleri liste öğesi olarak render etmek için iç `{iterateWhile}` döngüsünü kullanacak şekilde değiştiririz:

```latte
{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ünün içinde birden fazla iç döngü oluşturabilir, hatta onları iç içe geçirebiliriz. Böylece aynı anda birden fazla düzeyde gruplayabilirsiniz; örneğin kategorilerin altındaki alt kategorileri.

Tabloda bir de `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:

```latte
{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}
```

Gruplama hakkında bilmek istediğiniz her şey

Şablonlarda veriyle çalışırken sıklıkla öğeleri gruplamanız, onları partilere bölmeniz veya bir koşula göre üzerlerinde dolaşmanız gerekir. Latte bunun için üç araç sunar; her biri biraz farklı bir duruma uygundur.

|group filtresi öğeleri belirli bir ölçüte göre gruplar, |batch filtresi onları sabit boyutlu partilere böler ve {iterateWhile} etiketi veriyi adım adım dolaşıp iç döngüyü ne zaman keseceğine kendisi karar verir. Bunları tek tek ele alacağız.

group filtresi ve fonksiyonu

Araç iki biçimde kullanılabilir: bir filtre olarak $items|group: … ya da bir fonksiyon olarak group($items, …). Anlamsal olarak eşdeğerdirler; okunabilirliğe göre seçin.

Öğeleri çeşitli kategorilere ait olan bir items veritabanı tablosu düşünün:

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ürdü:

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

Ancak öğeleri kategoriye göre gruplar halinde istersek, her kategorinin kendi listesi olacak şekilde onları bölmemiz gerekir. İstenen sonuç şöyle görünürdü:

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

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

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

Bu görev |group kullanılarak kolay ve zarif biçimde çözülebilir. Parametre olarak categoryId belirtiriz; bu, öğelerin $item->categoryId değerine göre daha küçük dizilere bölüneceği anlamına gelir ($item bir dizi olsaydı $item['categoryId'] kullanılırdı):

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

Öğeleri daha karmaşık ölçütlere göre gruplamak isterseniz, filtre parametresinde bir fonksiyon kullanabilirsiniz. Her grubun anahtarı o zaman fonksiyonun dönüş değeri olur; örneğin ad uzunluğuna göre gruplarken karakter sayısı olacaktır:

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

Şunu belirtmek önemlidir: her grup ($categoryItems dahil) sıradan bir dizi değil, bir yineleyici gibi davranan bir nesnedir; yani öğelere indeksle erişemezsiniz, örneğin $categoryItems[0]. Ancak öğeleri count($categoryItems) ile sayabilirsiniz ve gruptaki ilk öğeye erişmek için first() fonksiyonunu kullanın.

Bu esneklik, |group'u veriyi sunmak için son derece yararlı bir araç yapar.

İç içe döngüler

Veritabanı tablomuzda her öğe için alt kategori tanımlayan ek bir subcategoryId sütunu olduğunu düşünelim. Her ana kategoriyi ayrı bir <ul> listesinde, o ana kategorideki her alt kategoriyi de ayrı bir iç içe <ol> listesinde göstermek 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 birlikte

Veri gruplamanın Nette Database ile birlikte nasıl etkili kullanılacağını gösterelim. Giriş örneğindeki items tablosuyla çalıştığımızı ve onun categoryId sütunu üzerinden şu categories tablosuna bağlı olduğunu varsayalım:

categoryId name
1 Fruits
2 Languages
3 Colors

Veriyi items tablosundan Nette Database Explorer ile $items = $db->table('items') komutuyla yükleriz. Bu veri üzerinde dolaşırken yalnızca $item->name ve $item->categoryId gibi niteliklere değil, categories tablosuyla olan ilişki sayesinde $item->category ile ilgili satıra da erişebiliriz. Bu ilişki ilginç kullanımlara olanak tanır:

{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, ilişkili satır $item->category'ye göre gruplamak için kullanırız. Sonuç olarak anahtar ($category) doğrudan o kategorinin ActiveRow nesnesini taşır; bu da adını {$category->name} ile göstermemize ve categories'e ayrı bir sorgu yapmadan başka herhangi bir sütuna erişmemize olanak tanır.

|batch filtresi

Filtre, bir öğe listesini sabit boyutlu partilere böler. Izgara düzenleri, sütun yerleşimleri veya her tür görsel gruplama için kullanışlıdır.

Öğeleri, her listenin en fazla üç öğe içerdiği listelerde göstermek istediğimizi düşünelim:

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

Bu örnekte $items listesi, her grubun ($batch) en fazla üç öğe içerdiği daha küçük gruplara bölünür. Sonra her parti ayrı bir <ul> listesinde gösterilir.

Son grup, istenen sayıya ulaşacak kadar öğe içermiyorsa, filtrenin ikinci parametresi bu grubun neyle tamamlanacağını tanımlamanızı sağlar. Bu, eksik bir satırın düzensiz görünebileceği durumlarda öğeleri estetik biçimde hizalamak için idealdir.

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

{iterateWhile} etiketi

|group filtresiyle çözdüğümüz aynı görevleri {iterateWhile} etiketiyle göstereceğiz. İki yaklaşım arasındaki temel fark şudur: |group önce tüm girdi verisini işleyip gruplarken, {iterateWhile} döngünün ilerlemesini bir koşulla denetler ve yineleme sırayla ilerler.

Önce kategorili tabloyu {iterateWhile} ile render edelim:

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

{foreach} döngünün dış bölümünü, yani her kategori için liste çizmeyi işaretlerken, {iterateWhile} etiketi iç bölümü, yani tek tek öğeleri işaretler. Kapanış etiketindeki koşul, geçerli ve sonraki öğe aynı kategoriye ait olduğu sürece yinelemenin süreceğini söyler ($iterator->nextValue sonraki öğedir; son öğede iç döngü biter, çünkü sonraki öğe yoktur – Latte koşulu değerlendirmeden önce $iterator->hasNext()'i denetler, bu yüzden null ile karşılaştırmaya hiç gelinmez).

Koşul her zaman doğru olsaydı, tüm öğeler ilk <ul>'un içinde render edilirdi:

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

Sonuç şöyle görünürdü:

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

{iterateWhile}'ı böyle kullanmanın yararı ne? <ul> dıştaki {foreach}'in içinde olduğundan, girdi boş olduğunda hiçbir şey render edilmez; yalnız başına bir <ul></ul> kalmaz. {iterateWhile} olmadan aynı durumu, etiketi açmadan önce bir {if} ile ya da {foreachelse} ile ele almanız gerekirdi.

Koşulu açılış {iterateWhile} etiketinde belirtirsek davranış değişir: koşul (ve sonraki öğeye geçiş) iç döngünün sonunda değil başında gerçekleştirilir. Yani koşulsuz {iterateWhile}'a her zaman girersiniz, ama {iterateWhile $cond}'a yalnızca $cond koşulu sağlandığında girersiniz. Aynı zamanda sonraki öğe $item'a yazılır.

Bu, her kategorideki ilk öğeyi diğerlerinden farklı render etmek istediğimiz durumlarda yararlıdır; örneğin şöyle:

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

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

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

(PHP kategorisindeki boş <ul></ul>, yalnızca mekanizmanın bir gösterimidir; gerçek kodda <ul> render'ını bir {if} ile halledersiniz.)

Özgün kodu, önce öğeyi bir başlık olarak render edecek, sonra aynı kategorideki sonraki öğeleri liste öğesi olarak render etmek için iç {iterateWhile} döngüsünü kullanacak ş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ünün içinde birden fazla iç döngü oluşturabilir, hatta onları iç içe geçirebiliriz. Böylece aynı anda birden fazla düzeyde gruplayabilirsiniz; örneğin kategorilerin altındaki alt kategorileri.

Tabloda bir de 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}