Όλα όσα πάντα θέλατε να ξέρετε για την ομαδοποίηση
Όταν εργάζεστε με δεδομένα σε πρότυπα, συναντάτε συχνά την ανάγκη να τα ομαδοποιήσετε ή να τα εμφανίσετε συγκεκριμένα σύμφωνα με συγκεκριμένα κριτήρια. Για το σκοπό αυτό, το Latte προσφέρει αρκετά ισχυρά εργαλεία.
Το φίλτρο και η λειτουργία |group
επιτρέπουν την αποτελεσματική
ομαδοποίηση δεδομένων βάσει καθορισμένων κριτηρίων, ενώ το φίλτρο
|batch
διευκολύνει τον διαχωρισμό των δεδομένων σε σταθερές
παρτίδες και η ετικέτα {iterateWhile}
παρέχει τη δυνατότητα πιο
σύνθετου ελέγχου του κύκλου με συνθήκες. Κάθε μία από αυτές τις
ετικέτες προσφέρει συγκεκριμένες επιλογές για την εργασία με
δεδομένα, καθιστώντας τις απαραίτητα εργαλεία για τη δυναμική και
δομημένη εμφάνιση πληροφοριών σε πρότυπα Latte.
Φίλτρο και λειτουργία group
Φανταστείτε έναν πίνακα βάσης δεδομένων items
με στοιχεία
χωρισμένα σε κατηγορίες:
id | categoryId | name |
---|---|---|
1 | 1 | Apple |
2 | 1 | Μπανάνα |
3 | 2 | PHP |
4 | 3 | Πράσινο |
5 | 3 | Κόκκινο |
6 | 3 | Μπλε |
Μια απλή λίστα με όλα τα στοιχεία χρησιμοποιώντας ένα πρότυπο Latte θα έμοιαζε ως εξής:
<ul>
{foreach $items as $item}
<li>{$item->name}</li>
{/foreach}
</ul>
Ωστόσο, αν θέλαμε τα αντικείμενα να οργανωθούν σε ομάδες ανά κατηγορία, θα πρέπει να τα χωρίσουμε έτσι ώστε κάθε κατηγορία να έχει τη δική της λίστα. Το αποτέλεσμα θα έμοιαζε τότε ως εξής:
<ul>
<li>Apple</li>
<li>Banana</li>
</ul>
<ul>
<li>PHP</li>
</ul>
<ul>
<li>Green</li>
<li>Red</li>
<li>Blue</li>
</ul>
Η εργασία μπορεί να επιλυθεί εύκολα και κομψά με τη χρήση του
|group
. Καθορίζουμε το categoryId
ως παράμετρο, που σημαίνει ότι
τα στοιχεία θα χωριστούν σε μικρότερους πίνακες με βάση την τιμή
$item->categoryId
(αν το $item
ήταν ένας πίνακας, θα
χρησιμοποιούσαμε $item['categoryId']
):
{foreach ($items|group: categoryId) as $categoryId => $categoryItems}
<ul>
{foreach $categoryItems as $item}
<li>{$item->name}</li>
{/foreach}
</ul>
{/foreach}
Το φίλτρο μπορεί επίσης να χρησιμοποιηθεί ως συνάρτηση στο Latte,
δίνοντάς μας μια εναλλακτική σύνταξη: {foreach group($items, categoryId) ...}
.
Αν θέλετε να ομαδοποιήσετε στοιχεία σύμφωνα με πιο σύνθετα κριτήρια, μπορείτε να χρησιμοποιήσετε μια συνάρτηση στην παράμετρο φίλτρο. Για παράδειγμα, η ομαδοποίηση στοιχείων με βάση το μήκος του ονόματός τους θα έμοιαζε ως εξής:
{foreach ($items|group: fn($item) => strlen($item->name)) as $items}
...
{/foreach}
Είναι σημαντικό να σημειωθεί ότι το $categoryItems
δεν είναι ένας
κανονικός πίνακας, αλλά ένα αντικείμενο που συμπεριφέρεται σαν ένας
επαναλήπτης. Για να αποκτήσετε πρόσβαση στο πρώτο στοιχείο της ομάδας,
μπορείτε να χρησιμοποιήσετε την εντολή first()
συνάρτηση.
Αυτή η ευελιξία στην ομαδοποίηση δεδομένων καθιστά το group
ένα
εξαιρετικά χρήσιμο εργαλείο για την παρουσίαση δεδομένων σε
πρότυπα Latte.
Ενσωματωμένοι βρόχοι
Ας υποθέσουμε ότι έχουμε έναν πίνακα βάσης δεδομένων με μια άλλη
στήλη subcategoryId
που ορίζει υποκατηγορίες για κάθε στοιχείο.
Θέλουμε να εμφανίσουμε κάθε κύρια κατηγορία σε μια ξεχωριστή
<ul>
λίστα και κάθε υποκατηγορία σε μια ξεχωριστή εμφωλευμένη
<ol>
λίστα:
{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
Ας δείξουμε πώς μπορείτε να χρησιμοποιήσετε αποτελεσματικά την
ομαδοποίηση δεδομένων σε συνδυασμό με τη Nette Database. Ας υποθέσουμε ότι
εργαζόμαστε με τον πίνακα items
από το αρχικό παράδειγμα, ο οποίος
συνδέεται μέσω της στήλης categoryId
με αυτόν τον πίνακα
categories
:
categoryId | name |
---|---|
1 | Φρούτα |
2 | Γλώσσες |
3 | Χρώματα |
Φορτώνουμε δεδομένα από τον πίνακα items
χρησιμοποιώντας την
εντολή $items = $db->table('items')
του Nette Database Explorer. Κατά την επανάληψη
αυτών των δεδομένων, έχουμε τη δυνατότητα όχι μόνο να έχουμε πρόσβαση
σε χαρακτηριστικά όπως τα $item->name
και $item->categoryId
, αλλά
χάρη στη σύνδεση με τον πίνακα categories
, και στη σχετική γραμμή του
πίνακα μέσω του $item->category
. Αυτή η σύνδεση μπορεί να επιδείξει
ενδιαφέρουσες χρήσεις:
{foreach ($items|group: category) as $category => $categoryItems}
<h1>{$category->name}</h1>
<ul>
{foreach $categoryItems as $item}
<li>{$item->name}</li>
{/foreach}
</ul>
{/foreach}
Στην περίπτωση αυτή, χρησιμοποιούμε το φίλτρο |group
για να
ομαδοποιήσουμε με βάση τη συνδεδεμένη γραμμή $item->category
, όχι
μόνο με βάση τη στήλη categoryId
. Αυτό μας δίνει το ActiveRow
της
συγκεκριμένης κατηγορίας στο κλειδί της μεταβλητής, επιτρέποντάς μας
να εμφανίσουμε απευθείας το όνομά της χρησιμοποιώντας το
{$category->name}
. Αυτό είναι ένα πρακτικό παράδειγμα για το πώς η
ομαδοποίηση μπορεί να απλοποιήσει τα πρότυπα και να διευκολύνει το
χειρισμό των δεδομένων.
Φίλτρο |batch
Το φίλτρο σας επιτρέπει να χωρίσετε μια λίστα στοιχείων σε ομάδες με προκαθορισμένο αριθμό στοιχείων. Αυτό το φίλτρο είναι ιδανικό για περιπτώσεις όπου θέλετε να παρουσιάσετε δεδομένα σε πολλές μικρότερες ομάδες, για παράδειγμα, για καλύτερη σαφήνεια ή οπτική οργάνωση στη σελίδα.
Φανταστείτε ότι έχουμε μια λίστα με στοιχεία και θέλουμε να τα
εμφανίσουμε σε λίστες, κάθε μία από τις οποίες θα περιέχει το πολύ τρία
στοιχεία. Η χρήση του φίλτρου |batch
είναι πολύ πρακτική σε μια
τέτοια περίπτωση:
<ul>
{foreach ($items|batch: 3) as $batch}
{foreach $batch as $item}
<li>{$item->name}</li>
{/foreach}
{/foreach}
</ul>
Σε αυτό το παράδειγμα, η λίστα $items
χωρίζεται σε μικρότερες
ομάδες, με κάθε ομάδα ($batch
) να περιέχει μέχρι τρία στοιχεία. Κάθε
ομάδα εμφανίζεται στη συνέχεια σε ξεχωριστή <ul>
λίστα.
Εάν η τελευταία ομάδα δεν περιέχει αρκετά στοιχεία για να φτάσει τον επιθυμητό αριθμό, η δεύτερη παράμετρος του φίλτρου σας επιτρέπει να ορίσετε με τι θα συμπληρωθεί αυτή η ομάδα. Αυτό είναι ιδανικό για την αισθητική ευθυγράμμιση στοιχείων όπου μια ελλιπής σειρά μπορεί να φαίνεται ακατάστατη.
{foreach ($items|batch: 3, '—') as $batch}
...
{/foreach}
Ετικέτα {iterateWhile}
Θα παρουσιάσουμε τις ίδιες εργασίες που αντιμετωπίσαμε με το φίλτρο
|group
χρησιμοποιώντας την ετικέτα {iterateWhile}
. Η κύρια διαφορά
μεταξύ των δύο προσεγγίσεων είναι ότι το group
επεξεργάζεται και
ομαδοποιεί πρώτα όλα τα δεδομένα εισόδου, ενώ το {iterateWhile}
ελέγχει την πρόοδο των κύκλων με συνθήκες, οπότε η επανάληψη γίνεται
διαδοχικά.
Αρχικά, σχεδιάζουμε έναν πίνακα με κατηγορίες χρησιμοποιώντας το iterateWhile:
{foreach $items as $item}
<ul>
{iterateWhile}
<li>{$item->name}</li>
{/iterateWhile $item->categoryId === $iterator->nextValue->categoryId}
</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}
, η
συμπεριφορά αλλάζει: η συνθήκη (και η μετάβαση στο επόμενο στοιχείο)
εκτελείται στην αρχή του εσωτερικού κύκλου, όχι στο τέλος. Έτσι, ενώ
εισάγετε πάντα το {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->categoryId === $iterator->nextValue->categoryId}
<li>{$item->name}</li>
{/iterateWhile}
</ul>
{/foreach}
Μέσα σε έναν κύκλο, μπορούμε να δημιουργήσουμε πολλαπλούς εσωτερικούς βρόχους και μάλιστα να τους φωλιάσουμε. Με αυτόν τον τρόπο θα μπορούσαν να ομαδοποιηθούν, για παράδειγμα, υποκατηγορίες.
Ας υποθέσουμε ότι ο πίνακας έχει άλλη μια στήλη subcategoryId
, και
εκτός από το ότι κάθε κατηγορία βρίσκεται σε ξεχωριστή <ul>
,
κάθε υποκατηγορία σε μια ξεχωριστή <ol>
:
{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}