Όλα όσα πάντα θέλατε να ξέρετε για το {iterateWhile}

Η ετικέτα {iterateWhile} είναι κατάλληλη για διάφορα τεχνάσματα σε κύκλους foreach.

Ας υποθέσουμε ότι έχουμε τον ακόλουθο πίνακα της βάσης δεδομένων, όπου τα στοιχεία χωρίζονται σε κατηγορίες:

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

Φυσικά, η σχεδίαση στοιχείων σε έναν βρόχο foreach ως λίστα είναι εύκολη:

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

Τι να κάνετε όμως αν θέλετε να αποδώσετε κάθε κατηγορία σε ξεχωριστή λίστα; Με άλλα λόγια, πώς να λύσετε το έργο της ομαδοποίησης στοιχείων από μια γραμμική λίστα σε έναν κύκλο foreach. Η έξοδος θα πρέπει να μοιάζει κάπως έτσι:

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

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

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

Θα σας δείξουμε πόσο εύκολα και κομψά μπορεί να επιλυθεί το έργο με το iterateWhile:

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

Ενώ το {foreach} σηματοδοτεί το εξωτερικό μέρος του κύκλου, δηλαδή την κατάρτιση των λιστών για κάθε κατηγορία, οι ετικέτες {iterateWhile} υποδεικνύουν το εσωτερικό μέρος, δηλαδή τα μεμονωμένα στοιχεία. Η συνθήκη στην ετικέτα end λέει ότι η επανάληψη θα συνεχιστεί εφόσον το τρέχον και το επόμενο στοιχείο ανήκουν στην ίδια κατηγορία ($iterator->nextValue είναι το επόμενο στοιχείο).

Εάν η συνθήκη ικανοποιείται πάντα, τότε όλα τα στοιχεία σχεδιάζονται στον εσωτερικό κύκλο:

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

Το αποτέλεσμα θα μοιάζει με αυτό:

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

Σε τι χρησιμεύει μια τέτοια χρήση της iterateWhile; Σε τι διαφέρει από τη λύση που δείξαμε στην αρχή αυτού του σεμιναρίου; Η διαφορά είναι ότι αν ο πίνακας είναι άδειος και δεν περιέχει κανένα στοιχείο, δεν θα αποδώσει κενό <ul></ul>.

Λύση χωρίς {iterateWhile}

Αν λύναμε την ίδια εργασία με εντελώς βασικές κατασκευές συστημάτων προτύπων, για παράδειγμα σε Twig, Blade ή καθαρή PHP, η λύση θα έμοιαζε κάπως έτσι:

{var $prevCatId = null}
{foreach $items as $item}
	{if $item->catId !== $prevCatId}
		{* η κατηγορία έχει αλλάξει *}

		{* κλείνουμε το προηγούμενο <ul>, αν δεν είναι το πρώτο στοιχείο *}
		{if $prevCatId !== null}
			</ul>
		{/if}

		{* θα ανοίξουμε μια νέα λίστα *}
		<ul>

		{do $prevCatId = $item->catId}
	{/if}

	<li>{$item->name}</li>
{/foreach}

{if $prevCatId !== null}
	{* κλείνουμε την προηγούμενη λίστα *}
	</ul>
{/if}

Ωστόσο, αυτός ο κώδικας είναι ακατανόητος και μη διαισθητικός. Η σύνδεση μεταξύ των ετικετών HTML που ανοίγουν και κλείνουν δεν είναι καθόλου σαφής. Δεν είναι σαφές με την πρώτη ματιά αν υπάρχει κάποιο λάθος. Και απαιτεί βοηθητικές μεταβλητές όπως το $prevCatId.

Αντίθετα, η λύση με το {iterateWhile} είναι καθαρή, σαφής, δεν χρειάζεται βοηθητικές μεταβλητές και είναι αλάνθαστη.

Προϋπόθεση στην ετικέτα κλεισίματος

Αν καθορίσουμε μια συνθήκη στην ετικέτα ανοίγματος {iterateWhile}, η συμπεριφορά αλλάζει: η συνθήκη (και η προώθηση στο επόμενο στοιχείο) εκτελείται στην αρχή του εσωτερικού κύκλου, όχι στο τέλος. Έτσι, ενώ το {iterateWhile} χωρίς συνθήκη εισάγεται πάντα, το {iterateWhile $cond} εισάγεται μόνο όταν ικανοποιείται η συνθήκη $cond. Ταυτόχρονα, το ακόλουθο στοιχείο εγγράφεται στο $item.

Αυτό είναι χρήσιμο, για παράδειγμα, σε μια κατάσταση όπου θέλετε να αποδώσετε το πρώτο στοιχείο σε κάθε κατηγορία με διαφορετικό τρόπο, όπως π.χ:

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

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

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

Ας τροποποιήσουμε τον αρχικό κώδικα, σχεδιάζουμε το πρώτο στοιχείο και στη συνέχεια πρόσθετα στοιχεία από την ίδια κατηγορία στον εσωτερικό βρόχο {iterateWhile}:

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

Φωλιασμένοι βρόχοι

Μπορούμε να δημιουργήσουμε πολλαπλούς εσωτερικούς βρόχους σε έναν κύκλο και μάλιστα να τους φωλιάσουμε. Με αυτόν τον τρόπο, για παράδειγμα, θα μπορούσαν να ομαδοποιηθούν οι υποκατηγορίες.

Ας υποθέσουμε ότι υπάρχει μια άλλη στήλη στον πίνακα subCatId και εκτός από το ότι κάθε κατηγορία βρίσκεται σε ξεχωριστή <ul>, κάθε υποκατηγορία θα βρίσκεται σε μια ξεχωριστή <ol>:

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

Φίλτρο |batch

Η ομαδοποίηση των γραμμικών στοιχείων παρέχεται επίσης από ένα φίλτρο batch, σε παρτίδες με σταθερό αριθμό στοιχείων:

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

Μπορεί να αντικατασταθεί με το iterateWhile ως εξής:

<ul>
{foreach $items as $item}
	{iterateWhile}
		<li>{$item->name}</li>
	{/iterateWhile $iterator->counter0 % 3}
{/foreach}
</ul>