Πώς λειτουργούν οι εφαρμογές;
Διαβάζετε το βασικό έγγραφο της τεκμηρίωσης του Nette. Θα μάθετε ολόκληρη την αρχή λειτουργίας των διαδικτυακών εφαρμογών. Από το Α έως το Ω, από τη στιγμή της γέννησης μέχρι την τελευταία πνοή του σεναρίου PHP. Αφού το διαβάσετε, θα γνωρίζετε:
- πώς λειτουργεί όλο αυτό
- τι είναι το Bootstrap, ο Presenter και το DI container
- πώς μοιάζει η δομή καταλόγων
Δομή καταλόγου
Ανοίξτε το παράδειγμα του σκελετού της διαδικτυακής εφαρμογής που ονομάζεται WebProject και κατά την ανάγνωση μπορείτε να δείτε τα αρχεία για τα οποία γίνεται λόγος.
Η δομή καταλόγων μοιάζει κάπως έτσι:
web-project/ ├── app/ ← κατάλογος με την εφαρμογή │ ├── Core/ ← βασικές κλάσεις απαραίτητες για τη λειτουργία │ │ └── RouterFactory.php ← διαμόρφωση διευθύνσεων URL │ ├── Presentation/ ← presenters, πρότυπα & λοιπά │ │ ├── @layout.latte ← πρότυπο διάταξης │ │ └── Home/ ← κατάλογος του presenter Home │ │ ├── HomePresenter.php ← κλάση του presenter Home │ │ └── default.latte ← πρότυπο της ενέργειας default │ └── Bootstrap.php ← κλάση εκκίνησης Bootstrap ├── bin/ ← σενάρια που εκτελούνται από τη γραμμή εντολών ├── config/ ← αρχεία διαμόρφωσης │ ├── common.neon │ └── services.neon ├── log/ ← καταγεγραμμένα σφάλματα ├── temp/ ← προσωρινά αρχεία, cache, … ├── vendor/ ← βιβλιοθήκες εγκατεστημένες από τον Composer │ ├── ... │ └── autoload.php ← αυτόματη φόρτωση όλων των εγκατεστημένων πακέτων ├── www/ ← δημόσιος κατάλογος ή document-root του έργου │ ├── .htaccess ← κανόνες mod_rewrite │ └── index.php ← αρχικό αρχείο με το οποίο εκκινεί η εφαρμογή └── .htaccess ← απαγορεύει την πρόσβαση σε όλους τους καταλόγους εκτός του www
Μπορείτε να αλλάξετε τη δομή καταλόγων όπως θέλετε, να μετονομάσετε ή να μετακινήσετε φακέλους, είναι εντελώς ευέλικτη. Το Nette διαθέτει επίσης έξυπνη αυτόματη ανίχνευση και αναγνωρίζει αυτόματα τη θέση της εφαρμογής, συμπεριλαμβανομένης της βασικής της διεύθυνσης URL.
Για λίγο μεγαλύτερες εφαρμογές, μπορούμε να χωρίσουμε τους φακέλους με τους presenters και τα πρότυπα σε υποκαταλόγους και τις κλάσεις σε χώρους ονομάτων, τους οποίους ονομάζουμε modules.
Ο κατάλογος www/
αντιπροσωπεύει τον λεγόμενο δημόσιο κατάλογο ή
document-root του έργου. Μπορείτε να τον μετονομάσετε χωρίς να χρειάζεται να
ρυθμίσετε τίποτα άλλο στην πλευρά της εφαρμογής. Απλά πρέπει να διαμορφώσετε το
hosting έτσι ώστε το document-root να δείχνει σε αυτόν τον κατάλογο.
Μπορείτε επίσης να κατεβάσετε απευθείας το WebProject συμπεριλαμβανομένου του Nette χρησιμοποιώντας τον Composer:
composer create-project nette/web-project
Σε Linux ή macOS, ορίστε δικαιώματα εγγραφής για τους καταλόγους log/
και temp/
δικαιώματα
εγγραφής.
Η εφαρμογή WebProject είναι έτοιμη για εκκίνηση, δεν χρειάζεται να
διαμορφώσετε απολύτως τίποτα και μπορείτε να την εμφανίσετε απευθείας
στο πρόγραμμα περιήγησης μεταβαίνοντας στον φάκελο www/
.
Αίτημα HTTP
Όλα ξεκινούν τη στιγμή που ο χρήστης ανοίγει μια σελίδα στο πρόγραμμα
περιήγησης. Δηλαδή, όταν το πρόγραμμα περιήγησης χτυπάει την πόρτα του
διακομιστή με ένα αίτημα HTTP. Το αίτημα κατευθύνεται σε ένα μόνο αρχείο
PHP, το οποίο βρίσκεται στον δημόσιο κατάλογο www/
, και αυτό είναι
το index.php
. Ας υποθέσουμε ότι πρόκειται για ένα αίτημα στη
διεύθυνση https://example.com/product/123
. Χάρη στην κατάλληλη ρύθμιση του
διακομιστή, ακόμη και αυτό το URL αντιστοιχίζεται στο αρχείο
index.php
και αυτό εκτελείται.
Ο ρόλος του είναι:
- να αρχικοποιήσει το περιβάλλον
- να αποκτήσει το factory
- να εκκινήσει την εφαρμογή Nette, η οποία θα διεκπεραιώσει το αίτημα
Ποιο factory; Δεν κατασκευάζουμε τρακτέρ, αλλά ιστοσελίδες! Υπομονή, θα εξηγηθεί αμέσως.
Με τις λέξεις «αρχικοποίηση περιβάλλοντος» εννοούμε, για παράδειγμα, ότι ενεργοποιείται το Tracy, το οποίο είναι ένα καταπληκτικό εργαλείο για την καταγραφή ή την οπτικοποίηση σφαλμάτων. Στον διακομιστή παραγωγής καταγράφει τα σφάλματα, στον διακομιστή ανάπτυξης τα εμφανίζει απευθείας. Επομένως, η αρχικοποίηση περιλαμβάνει επίσης την απόφαση εάν ο ιστότοπος εκτελείται σε λειτουργία παραγωγής ή ανάπτυξης. Για αυτό, το Nette χρησιμοποιεί έξυπνη αυτόματη ανίχνευση: εάν εκτελείτε τον ιστότοπο στο localhost, εκτελείται σε λειτουργία ανάπτυξης. Έτσι, δεν χρειάζεται να διαμορφώσετε τίποτα και η εφαρμογή είναι αμέσως έτοιμη τόσο για ανάπτυξη όσο και για παραγωγική λειτουργία. Αυτά τα βήματα εκτελούνται και περιγράφονται λεπτομερώς στο κεφάλαιο για την κλάση Bootstrap.
Το τρίτο σημείο (ναι, παραλείψαμε το δεύτερο, αλλά θα επιστρέψουμε σε
αυτό) είναι η εκκίνηση της εφαρμογής. Η διεκπεραίωση των αιτημάτων HTTP
στο Nette γίνεται από την κλάση Nette\Application\Application
(στο εξής
Application
), οπότε όταν λέμε εκκίνηση της εφαρμογής, εννοούμε
συγκεκριμένα την κλήση της μεθόδου με το εύστοχο όνομα run()
στο
αντικείμενο αυτής της κλάσης.
Το Nette είναι ένας μέντορας που σας καθοδηγεί στη συγγραφή καθαρών
εφαρμογών σύμφωνα με δοκιμασμένες μεθοδολογίες. Και μία από τις πιο
δοκιμασμένες ονομάζεται dependency injection, συντομογραφικά DI. Αυτή τη
στιγμή, δεν θέλουμε να σας επιβαρύνουμε με την εξήγηση του DI, γι' αυτό
υπάρχει ένα ξεχωριστό
κεφάλαιο, το σημαντικό αποτέλεσμα είναι ότι τα βασικά αντικείμενα
συνήθως δημιουργούνται από ένα factory αντικειμένων, το οποίο ονομάζεται
DI container (συντομογραφικά DIC). Ναι, αυτό είναι το factory για το οποίο
μιλήσαμε πριν λίγο. Και θα μας δημιουργήσει επίσης το αντικείμενο
Application
, γι' αυτό χρειαζόμαστε πρώτα το container. Το αποκτούμε
χρησιμοποιώντας την κλάση Configurator
και το αφήνουμε να
δημιουργήσει το αντικείμενο Application
, καλούμε τη μέθοδο run()
σε αυτό και έτσι εκκινεί η εφαρμογή Nette. Ακριβώς αυτό συμβαίνει στο
αρχείο index.php.
Nette Application
Η κλάση Application έχει έναν μόνο ρόλο: να απαντήσει στο αίτημα HTTP.
Οι εφαρμογές που γράφονται στο Nette χωρίζονται σε πολλούς λεγόμενους presenters (σε άλλα frameworks μπορεί να συναντήσετε τον όρο controller, πρόκειται για το ίδιο πράγμα), οι οποίοι είναι κλάσεις, καθεμία από τις οποίες αντιπροσωπεύει μια συγκεκριμένη σελίδα του ιστότοπου: π.χ. την αρχική σελίδα, ένα προϊόν σε ένα e-shop, μια φόρμα σύνδεσης, ένα sitemap feed κ.λπ. Μια εφαρμογή μπορεί να έχει από έναν έως χιλιάδες presenters.
Η Application ξεκινά ζητώντας από τον λεγόμενο router να αποφασίσει σε ποιον
από τους presenters θα παραδώσει το τρέχον αίτημα για διεκπεραίωση. Ο router
αποφασίζει ποιος έχει την ευθύνη. Εξετάζει το εισερχόμενο URL
https://example.com/product/123
και με βάση το πώς είναι ρυθμισμένος,
αποφασίζει ότι αυτή είναι δουλειά, για παράδειγμα, για τον presenter
Product
, από τον οποίο θα ζητήσει ως action την εμφάνιση (show
)
του προϊόντος με id: 123
. Το ζεύγος presenter + action συνηθίζεται να
γράφεται χωρισμένο με άνω και κάτω τελεία ως Product:show
.
Έτσι, ο router μετέτρεψε το URL στο ζεύγος Presenter:action
+ παραμέτρους,
στην περίπτωσή μας Product:show
+ id: 123
. Πώς μοιάζει ένας τέτοιος
router μπορείτε να δείτε στο αρχείο app/Core/RouterFactory.php
και τον
περιγράφουμε λεπτομερώς στο κεφάλαιο Routing.
Ας προχωρήσουμε. Η Application γνωρίζει ήδη το όνομα του presenter και μπορεί να
συνεχίσει. Δημιουργώντας το αντικείμενο της κλάσης ProductPresenter
,
που είναι ο κώδικας του presenter Product
. Πιο συγκεκριμένα, ζητά από το
DI container να δημιουργήσει τον presenter, επειδή η δημιουργία είναι δική του
δουλειά.
Ο presenter μπορεί να μοιάζει κάπως έτσι:
class ProductPresenter extends Nette\Application\UI\Presenter
{
public function __construct(
private ProductRepository $repository,
) {
}
public function renderShow(int $id): void
{
// λήψη δεδομένων από το μοντέλο και μεταβίβασή τους στο πρότυπο
$this->template->product = $this->repository->getProduct($id);
}
}
Η διεκπεραίωση του αιτήματος αναλαμβάνεται από τον presenter. Και ο
στόχος είναι σαφής: εκτέλεσε την action show
με id: 123
. Αυτό, στη
γλώσσα των presenters, σημαίνει ότι καλείται η μέθοδος renderShow()
και
στην παράμετρο $id
λαμβάνει το 123
.
Ο presenter μπορεί να εξυπηρετεί πολλαπλές actions, δηλαδή να έχει πολλαπλές
μεθόδους render<Action>()
. Αλλά συνιστούμε να σχεδιάζετε presenters με
μία ή όσο το δυνατόν λιγότερες actions.
Έτσι, κλήθηκε η μέθοδος renderShow(123)
, ο κώδικας της οποίας είναι
μεν ένα φανταστικό παράδειγμα, αλλά μπορείτε να δείτε σε αυτό πώς
μεταβιβάζονται δεδομένα στο πρότυπο, δηλαδή γράφοντας στο
$this->template
.
Στη συνέχεια, ο presenter επιστρέφει μια response. Αυτή μπορεί να είναι μια
σελίδα HTML, μια εικόνα, ένα έγγραφο XML, η αποστολή ενός αρχείου από τον
δίσκο, JSON ή ίσως μια ανακατεύθυνση σε άλλη σελίδα. Είναι σημαντικό ότι
αν δεν πούμε ρητά πώς πρέπει να απαντήσει (που είναι η περίπτωση του
ProductPresenter
), η response θα είναι η απόδοση ενός προτύπου με μια σελίδα
HTML. Γιατί; Επειδή στο 99% των περιπτώσεων θέλουμε να αποδώσουμε ένα
πρότυπο, επομένως ο presenter θεωρεί αυτή τη συμπεριφορά ως προεπιλεγμένη
και θέλει να μας διευκολύνει τη δουλειά. Αυτός είναι ο σκοπός του Nette.
Δεν χρειάζεται καν να καθορίσουμε ποιο πρότυπο να αποδοθεί, θα βρει
τη διαδρομή προς αυτό μόνος του. Στην περίπτωση της action show
, απλά
θα προσπαθήσει να φορτώσει το πρότυπο show.latte
στον κατάλογο με
την κλάση ProductPresenter
. Επίσης, θα προσπαθήσει να βρει τη διάταξη
στο αρχείο @layout.latte
(περισσότερα για την αναζήτηση προτύπων).
Και στη συνέχεια αποδίδει τα πρότυπα. Με αυτό, ο στόχος του presenter και ολόκληρης της εφαρμογής ολοκληρώνεται και το έργο τελειώνει. Αν το πρότυπο δεν υπήρχε, θα επιστρεφόταν μια σελίδα με σφάλμα 404. Περισσότερα για τους presenters μπορείτε να διαβάσετε στη σελίδα Presenters.
Για σιγουριά, ας προσπαθήσουμε να ανακεφαλαιώσουμε ολόκληρη τη διαδικασία με ένα ελαφρώς διαφορετικό URL:
- Το URL θα είναι
https://example.com
- εκκινούμε την εφαρμογή, δημιουργείται το container και εκτελείται
το
Application::run()
- ο router αποκωδικοποιεί το URL ως το ζεύγος
Home:default
- δημιουργείται το αντικείμενο της κλάσης
HomePresenter
- καλείται η μέθοδος
renderDefault()
(αν υπάρχει) - αποδίδεται το πρότυπο π.χ.
default.latte
με τη διάταξη π.χ.@layout.latte
Μπορεί να έχετε συναντήσει τώρα πολλούς νέους όρους, αλλά πιστεύουμε ότι βγάζουν νόημα. Η δημιουργία εφαρμογών στο Nette είναι εξαιρετικά εύκολη.
Πρότυπα
Αφού αναφερθήκαμε στα πρότυπα, στο Nette χρησιμοποιείται το σύστημα
προτύπων Latte. Γι' αυτό και οι καταλήξεις
.latte
στα πρότυπα. Το Latte χρησιμοποιείται αφενός επειδή είναι το
πιο ασφαλές σύστημα προτύπων για PHP, και αφετέρου το πιο διαισθητικό
σύστημα. Δεν χρειάζεται να μάθετε πολλά νέα πράγματα, αρκεί η γνώση της
PHP και μερικών ετικετών. Όλα θα τα μάθετε στην τεκμηρίωση.
Στο πρότυπο, δημιουργούνται σύνδεσμοι προς άλλους presenters & actions ως εξής:
<a n:href="Product:show $productId">λεπτομέρεια προϊόντος</a>
Απλά αντί για το πραγματικό URL, γράφετε το γνωστό ζεύγος
Presenter:action
και καθορίζετε τυχόν παραμέτρους. Το κόλπο είναι στο
n:href
, το οποίο λέει ότι αυτό το attribute θα επεξεργαστεί το Nette. Και θα
δημιουργήσει:
<a href="/product/456">λεπτομέρεια προϊόντος</a>
Η δημιουργία των URL γίνεται από τον προαναφερθέντα router. Συγκεκριμένα, οι routers στο Nette είναι εξαιρετικοί στο ότι μπορούν να εκτελούν όχι μόνο μετασχηματισμούς από URL σε ζεύγος presenter:action, αλλά και αντίστροφα, δηλαδή από το όνομα του presenter + action + παραμέτρους να δημιουργούν ένα URL. Χάρη σε αυτό, στο Nette μπορείτε να αλλάξετε εντελώς τις μορφές των URL σε ολόκληρη την ολοκληρωμένη εφαρμογή, χωρίς να αλλάξετε ούτε έναν χαρακτήρα στο πρότυπο ή τον presenter. Απλά τροποποιώντας τον router. Επίσης, χάρη σε αυτό λειτουργεί η λεγόμενη κανονικοποίηση, η οποία είναι ένα άλλο μοναδικό χαρακτηριστικό του Nette που συμβάλλει στο καλύτερο SEO (βελτιστοποίηση για μηχανές αναζήτησης) αποτρέποντας αυτόματα την ύπαρξη διπλού περιεχομένου σε διαφορετικά URL. Πολλοί προγραμματιστές το θεωρούν εντυπωσιακό.
Διαδραστικά Components
Για τους presenters πρέπει να σας αποκαλύψουμε ακόμα ένα πράγμα: έχουν ενσωματωμένο σύστημα components. Κάτι παρόμοιο μπορεί να θυμούνται οι παλαιότεροι από τα Delphi ή τα ASP.NET Web Forms, ενώ κάτι παρόμοιο αποτελεί τη βάση του React ή του Vue.js. Στον κόσμο των PHP frameworks, πρόκειται για ένα εντελώς μοναδικό χαρακτηριστικό.
Τα components είναι ανεξάρτητες, επαναχρησιμοποιήσιμες μονάδες που ενσωματώνουμε σε σελίδες (δηλαδή presenters). Μπορεί να είναι φόρμες, datagrids, μενού, δημοσκοπήσεις, στην πραγματικότητα οτιδήποτε έχει νόημα να χρησιμοποιείται επανειλημμένα. Μπορούμε να δημιουργήσουμε δικά μας components ή να χρησιμοποιήσουμε κάποια από την τεράστια προσφορά open source components.
Τα components επηρεάζουν θεμελιωδώς την προσέγγιση στην ανάπτυξη εφαρμογών. Θα σας ανοίξουν νέες δυνατότητες σύνθεσης σελίδων από προκατασκευασμένες μονάδες. Και επιπλέον, έχουν κάτι κοινό με το Hollywood.
DI container και Διαμόρφωση
Το DI container ή factory αντικειμένων είναι η καρδιά ολόκληρης της εφαρμογής.
Μην ανησυχείτε, δεν είναι κάποιο μαγικό μαύρο κουτί, όπως ίσως φάνηκε
από τις προηγούμενες γραμμές. Στην πραγματικότητα, είναι μια αρκετά
βαρετή κλάση PHP, την οποία δημιουργεί το Nette και την αποθηκεύει στον
κατάλογο cache. Έχει πολλές μεθόδους με ονόματα όπως createServiceAbcd()
και καθεμία από αυτές μπορεί να δημιουργήσει και να επιστρέψει κάποιο
αντικείμενο. Ναι, υπάρχει και η μέθοδος createServiceApplication()
, η οποία
δημιουργεί το Nette\Application\Application
, το οποίο χρειαζόμασταν στο
αρχείο index.php
για την εκκίνηση της εφαρμογής. Και υπάρχουν
μέθοδοι που δημιουργούν τους επιμέρους presenters. Και ούτω καθεξής.
Τα αντικείμενα που δημιουργεί το DI container ονομάζονται για κάποιο λόγο services.
Αυτό που είναι πραγματικά ιδιαίτερο σε αυτή την κλάση είναι ότι δεν
την προγραμματίζετε εσείς, αλλά το framework. Αυτό πράγματι δημιουργεί τον
κώδικα PHP και τον αποθηκεύει στον δίσκο. Εσείς απλά δίνετε οδηγίες για
το ποια αντικείμενα πρέπει να μπορεί να δημιουργεί το container και πώς
ακριβώς. Και αυτές οι οδηγίες είναι γραμμένες στα αρχεία διαμόρφωσης,
για τα οποία χρησιμοποιείται η μορφή NEON και
επομένως έχουν και την επέκταση .neon
.
Τα αρχεία διαμόρφωσης χρησιμεύουν καθαρά για την καθοδήγηση του DI
container. Έτσι, όταν για παράδειγμα αναφέρω στην ενότητα session την επιλογή expiration: 14 days
,
τότε το DI container κατά τη δημιουργία του αντικειμένου Nette\Http\Session
που αντιπροσωπεύει τη session, καλεί τη μέθοδό του setExpiration('14 days')
και
έτσι η διαμόρφωση γίνεται πραγματικότητα.
Υπάρχει ένα ολόκληρο κεφάλαιο έτοιμο για εσάς που περιγράφει τι μπορείτε να διαμορφώσετε και πώς να ορίσετε τις δικές σας services.
Μόλις εμβαθύνετε λίγο στη δημιουργία services, θα συναντήσετε τη λέξη autowiring. Αυτό είναι ένα χαρακτηριστικό που θα απλοποιήσει απίστευτα τη ζωή σας. Μπορεί να μεταβιβάσει αυτόματα αντικείμενα εκεί που τα χρειάζεστε (για παράδειγμα, στους κατασκευαστές των κλάσεών σας), χωρίς να χρειάζεται να κάνετε τίποτα. Θα διαπιστώσετε ότι το DI container στο Nette είναι ένα μικρό θαύμα.
Πού να πάτε μετά;
Έχουμε καλύψει τις βασικές αρχές των εφαρμογών στο Nette. Μέχρι στιγμής πολύ επιφανειακά, αλλά σύντομα θα εμβαθύνετε και με τον καιρό θα δημιουργήσετε υπέροχες διαδικτυακές εφαρμογές. Πού να συνεχίσετε; Έχετε δοκιμάσει ήδη το tutorial Γράφοντας την πρώτη εφαρμογή?
Εκτός από τα παραπάνω, το Nette διαθέτει ένα ολόκληρο οπλοστάσιο χρήσιμων κλάσεων, επίπεδο βάσης δεδομένων, κ.λπ. Δοκιμάστε απλά να περιηγηθείτε στην τεκμηρίωση. Ή στο blog. Θα ανακαλύψετε πολλά ενδιαφέροντα πράγματα.
Ας σας φέρει το framework πολλή χαρά 💙