Presenters
Θα εξοικειωθούμε με τον τρόπο συγγραφής presenters και προτύπων στο Nette. Μετά την ανάγνωση, θα γνωρίζετε:
- πώς λειτουργεί ένας presenter
- τι είναι οι persistent παράμετροι
- πώς αποδίδονται τα πρότυπα
Γνωρίζουμε ήδη ότι ένας presenter είναι μια κλάση που αντιπροσωπεύει μια συγκεκριμένη σελίδα μιας διαδικτυακής εφαρμογής, π.χ. την αρχική σελίδα, ένα προϊόν σε ένα e-shop, μια φόρμα σύνδεσης, ένα sitemap feed κ.λπ. Μια εφαρμογή μπορεί να έχει από έναν έως χιλιάδες presenters. Σε άλλα frameworks, ονομάζονται επίσης controllers.
Συνήθως, με τον όρο presenter εννοούμε έναν απόγονο της κλάσης Nette\Application\UI\Presenter, ο οποίος είναι κατάλληλος για τη δημιουργία διαδικτυακών διεπαφών και στον οποίο θα επικεντρωθούμε στο υπόλοιπο αυτού του κεφαλαίου. Με γενική έννοια, ένας presenter είναι οποιοδήποτε αντικείμενο που υλοποιεί το interface Nette\Application\IPresenter.
Κύκλος ζωής του presenter
Ο ρόλος του presenter είναι να διεκπεραιώσει ένα αίτημα και να επιστρέψει μια response (η οποία μπορεί να είναι μια σελίδα HTML, μια εικόνα, μια ανακατεύθυνση κ.λπ.).
Έτσι, στην αρχή, του παραδίδεται ένα αίτημα. Δεν είναι απευθείας ένα αίτημα HTTP, αλλά ένα αντικείμενο Nette\Application\Request, στο οποίο το αίτημα HTTP μετασχηματίστηκε με τη βοήθεια του router. Συνήθως δεν ερχόμαστε σε επαφή με αυτό το αντικείμενο, καθώς ο presenter αναθέτει έξυπνα την επεξεργασία του αιτήματος σε άλλες μεθόδους, τις οποίες θα δείξουμε τώρα.
Η εικόνα παρουσιάζει μια λίστα μεθόδων που καλούνται διαδοχικά από πάνω προς τα κάτω, αν υπάρχουν. Καμία από αυτές δεν χρειάζεται να υπάρχει, μπορούμε να έχουμε έναν εντελώς κενό presenter χωρίς ούτε μία μέθοδο και να χτίσουμε πάνω του έναν απλό στατικό ιστότοπο.
__construct()
Ο κατασκευαστής δεν ανήκει ακριβώς στον κύκλο ζωής του presenter, επειδή καλείται τη στιγμή της δημιουργίας του αντικειμένου. Αλλά τον αναφέρουμε λόγω της σημασίας του. Ο κατασκευαστής (μαζί με τη μέθοδο inject) χρησιμεύει για τη μεταβίβαση εξαρτήσεων.
Ο presenter δεν θα πρέπει να χειρίζεται την επιχειρηματική λογική της
εφαρμογής, να γράφει και να διαβάζει από τη βάση δεδομένων, να εκτελεί
υπολογισμούς κ.λπ. Γι' αυτό υπάρχουν κλάσεις από το επίπεδο που
ονομάζουμε model. Για παράδειγμα, η κλάση ArticleRepository
μπορεί να είναι
υπεύθυνη για τη φόρτωση και την αποθήκευση άρθρων. Για να μπορεί ο presenter
να συνεργαστεί μαζί της, ζητά να του περαστεί μέσω dependency injection:
class ArticlePresenter extends Nette\Application\UI\Presenter
{
public function __construct(
private ArticleRepository $articles,
) {
}
}
startup()
Αμέσως μετά τη λήψη του αιτήματος, καλείται η μέθοδος startup()
.
Μπορείτε να τη χρησιμοποιήσετε για την αρχικοποίηση ιδιοτήτων, την
επαλήθευση δικαιωμάτων χρήστη κ.λπ. Απαιτείται η μέθοδος να καλεί
πάντα τον πρόγονο parent::startup()
.
action<Action>(args...)
Αντίστοιχο της μεθόδου render<View>()
. Ενώ η render<View>()
προορίζεται για την προετοιμασία δεδομένων για ένα συγκεκριμένο
πρότυπο που θα αποδοθεί στη συνέχεια, στην action<Action>()
επεξεργάζεται το αίτημα χωρίς σύνδεση με την απόδοση του προτύπου. Για
παράδειγμα, επεξεργάζονται δεδομένα, συνδέεται ή αποσυνδέεται ο
χρήστης, και ούτω καθεξής, και στη συνέχεια ανακατευθύνεται αλλού.
Είναι σημαντικό ότι η action<Action>()
καλείται νωρίτερα από την
render<View>()
, οπότε σε αυτήν μπορούμε ενδεχομένως να αλλάξουμε
την περαιτέρω πορεία των γεγονότων, δηλαδή να αλλάξουμε το πρότυπο που
θα αποδοθεί, καθώς και τη μέθοδο render<View>()
που θα κληθεί. Και
αυτό γίνεται χρησιμοποιώντας το setView('jineView')
.
Στη μέθοδο μεταβιβάζονται παράμετροι από το αίτημα. Είναι δυνατό και
συνιστάται να καθορίσετε τύπους για τις παραμέτρους, π.χ.
actionShow(int $id, ?string $slug = null)
– αν η παράμετρος id
λείπει ή αν
δεν είναι integer, ο presenter θα επιστρέψει σφάλμα 404 και
θα τερματίσει τη λειτουργία του.
handle<Signal>(args...)
Η μέθοδος επεξεργάζεται τα λεγόμενα signals, με τα οποία θα εξοικειωθούμε στο κεφάλαιο που είναι αφιερωμένο στα components. Προορίζεται κυρίως για components και την επεξεργασία αιτήσεων AJAX.
Στη μέθοδο μεταβιβάζονται παράμετροι από το αίτημα, όπως στην
περίπτωση της action<Action>()
, συμπεριλαμβανομένου του
ελέγχου τύπου.
beforeRender()
Η μέθοδος beforeRender
, όπως υποδηλώνει και το όνομά της, καλείται
πριν από κάθε μέθοδο render<View>()
. Χρησιμοποιείται για την κοινή
διαμόρφωση του προτύπου, τη μεταβίβαση μεταβλητών για τη διάταξη και
παρόμοια.
render<View>(args...)
Το μέρος όπου προετοιμάζουμε το πρότυπο για την επακόλουθη απόδοση, του μεταβιβάζουμε δεδομένα κ.λπ.
Στη μέθοδο μεταβιβάζονται παράμετροι από το αίτημα, όπως στην
περίπτωση της action<Action>()
, συμπεριλαμβανομένου του
ελέγχου τύπου.
public function renderShow(int $id): void
{
// λήψη δεδομένων από το μοντέλο και μεταβίβασή τους στο πρότυπο
$this->template->article = $this->articles->getById($id);
}
afterRender()
Η μέθοδος afterRender
, όπως υποδηλώνει ξανά το όνομα, καλείται μετά
από κάθε μέθοδο render<View>()
. Χρησιμοποιείται μάλλον σπάνια.
shutdown()
Καλείται στο τέλος του κύκλου ζωής του presenter.
Καλή συμβουλή, πριν προχωρήσουμε. Ο presenter, όπως φαίνεται, μπορεί να
εξυπηρετεί πολλαπλές actions/views, δηλαδή να έχει πολλαπλές μεθόδους
render<View>()
. Αλλά συνιστούμε να σχεδιάζετε presenters με μία ή όσο το
δυνατόν λιγότερες actions.
Αποστολή απάντησης
Η response του presenter είναι συνήθως η απόδοση ενός προτύπου με μια σελίδα HTML, αλλά μπορεί επίσης να είναι η αποστολή ενός αρχείου, JSON ή ίσως μια ανακατεύθυνση σε άλλη σελίδα.
Οποιαδήποτε στιγμή κατά τη διάρκεια του κύκλου ζωής, μπορούμε να στείλουμε μια response με μία από τις παρακάτω μεθόδους και ταυτόχρονα να τερματίσουμε τον presenter:
redirect()
,redirectPermanent()
,redirectUrl()
καιforward()
ανακατευθύνουνerror()
τερματίζει τον presenter λόγω σφάλματοςsendJson($data)
τερματίζει τον presenter και στέλνει δεδομένα σε μορφή JSONsendTemplate()
τερματίζει τον presenter και αμέσως αποδίδει το πρότυποsendResponse($response)
τερματίζει τον presenter και στέλνει μια προσαρμοσμένη responseterminate()
τερματίζει τον presenter χωρίς response
Αν δεν καλέσετε καμία από αυτές τις μεθόδους, ο presenter θα προχωρήσει αυτόματα στην απόδοση του προτύπου. Γιατί; Επειδή στο 99% των περιπτώσεων θέλουμε να αποδώσουμε ένα πρότυπο, επομένως ο presenter θεωρεί αυτή τη συμπεριφορά ως προεπιλεγμένη και θέλει να μας διευκολύνει τη δουλειά.
Δημιουργία συνδέσμων
Ο presenter διαθέτει τη μέθοδο link()
, με την οποία μπορείτε να
δημιουργήσετε συνδέσμους URL προς άλλους presenters. Η πρώτη παράμετρος
είναι ο presenter & η action προορισμού, ακολουθούν τα μεταβιβαζόμενα
ορίσματα, τα οποία μπορούν να καθοριστούν ως array:
$url = $this->link('Product:show', $id);
$url = $this->link('Product:show', [$id, 'lang' => 'cs']);
Στο πρότυπο, οι σύνδεσμοι προς άλλους presenters & actions δημιουργούνται με αυτόν τον τρόπο:
<a n:href="Product:show $id">λεπτομέρεια προϊόντος</a>
Απλά αντί για το πραγματικό URL, γράφετε το γνωστό ζεύγος
Presenter:action
και καθορίζετε τυχόν παραμέτρους. Το κόλπο είναι στο
n:href
, το οποίο λέει ότι αυτό το attribute θα επεξεργαστεί το Latte και θα
δημιουργήσει το πραγματικό URL. Στο Nette, επομένως, δεν χρειάζεται καθόλου
να σκέφτεστε τα URL, μόνο τους presenters και τις actions.
Περισσότερες πληροφορίες θα βρείτε στο κεφάλαιο Δημιουργία συνδέσμων URL.
Ανακατεύθυνση
Για τη μετάβαση σε άλλο presenter, χρησιμοποιούνται οι μέθοδοι
redirect()
και forward()
, οι οποίες έχουν πολύ παρόμοια σύνταξη με
τη μέθοδο link().
Η μέθοδος forward()
μεταβαίνει στον νέο presenter αμέσως χωρίς
ανακατεύθυνση HTTP:
$this->forward('Product:show');
Παράδειγμα της λεγόμενης προσωρινής ανακατεύθυνσης με κωδικό HTTP 302 (ή 303, αν η μέθοδος της τρέχουσας αίτησης είναι POST):
$this->redirect('Product:show', $id);
Μόνιμη ανακατεύθυνση με κωδικό HTTP 301 επιτυγχάνεται ως εξής:
$this->redirectPermanent('Product:show', $id);
Σε άλλη διεύθυνση URL εκτός της εφαρμογής μπορείτε να ανακατευθύνετε
με τη μέθοδο redirectUrl()
. Ως δεύτερη παράμετρο, μπορείτε να
καθορίσετε τον κωδικό HTTP, ο προεπιλεγμένος είναι 302 (ή 303, αν η μέθοδος
της τρέχουσας αίτησης είναι POST):
$this->redirectUrl('https://nette.org');
Η ανακατεύθυνση τερματίζει αμέσως τη λειτουργία του presenter
δημιουργώντας τη λεγόμενη σιωπηλή εξαίρεση τερματισμού
Nette\Application\AbortException
.
Πριν από την ανακατεύθυνση, μπορείτε να στείλετε flash message, δηλαδή μηνύματα που θα εμφανιστούν στο πρότυπο μετά την ανακατεύθυνση.
Flash μηνύματα
Πρόκειται για μηνύματα που συνήθως ενημερώνουν για το αποτέλεσμα κάποιας λειτουργίας. Ένα σημαντικό χαρακτηριστικό των flash μηνυμάτων είναι ότι είναι διαθέσιμα στο πρότυπο ακόμη και μετά από ανακατεύθυνση. Ακόμη και μετά την εμφάνισή τους, παραμένουν ενεργά για άλλα 30 δευτερόλεπτα – για παράδειγμα, σε περίπτωση που ο χρήστης ανανεώσει τη σελίδα λόγω σφάλματος μετάδοσης – το μήνυμα δεν εξαφανίζεται αμέσως.
Αρκεί να καλέσετε τη μέθοδο flashMessage() και ο
presenter θα φροντίσει για τη μεταβίβασή τους στο πρότυπο. Η πρώτη
παράμετρος είναι το κείμενο του μηνύματος και η προαιρετική δεύτερη
παράμετρος ο τύπος του (error, warning, info κ.λπ.). Η μέθοδος flashMessage()
επιστρέφει μια παρουσία του flash μηνύματος, στην οποία μπορούν να
προστεθούν περαιτέρω πληροφορίες.
$this->flashMessage('Το στοιχείο διαγράφηκε.');
$this->redirect(/* ... */); // και ανακατεύθυνση
Στο πρότυπο, αυτά τα μηνύματα είναι διαθέσιμα στη μεταβλητή
$flashes
ως αντικείμενα stdClass
, τα οποία περιέχουν τις
ιδιότητες message
(κείμενο μηνύματος), type
(τύπος μηνύματος)
και μπορούν να περιέχουν τις ήδη αναφερθείσες πληροφορίες χρήστη. Τα
αποδίδουμε, για παράδειγμα, ως εξής:
{foreach $flashes as $flash}
<div class="flash {$flash->type}">{$flash->message}</div>
{/foreach}
Σφάλμα 404 κ.λπ.
Αν δεν είναι δυνατό να ικανοποιηθεί το αίτημα, για παράδειγμα, επειδή
το άρθρο που θέλουμε να εμφανίσουμε δεν υπάρχει στη βάση δεδομένων,
δημιουργούμε σφάλμα 404 με τη μέθοδο
error(?string $message = null, int $httpCode = 404)
.
public function renderShow(int $id): void
{
$article = $this->articles->getById($id);
if (!$article) {
$this->error();
}
// ...
}
Ο κωδικός HTTP του σφάλματος μπορεί να περαστεί ως δεύτερη παράμετρος,
ο προεπιλεγμένος είναι 404. Η μέθοδος λειτουργεί δημιουργώντας την
εξαίρεση Nette\Application\BadRequestException
, οπότε η Application
παραδίδει
τον έλεγχο στον error-presenter. Αυτός είναι ένας presenter του οποίου ο ρόλος
είναι να εμφανίσει μια σελίδα που ενημερώνει για το σφάλμα που
προέκυψε. Η ρύθμιση του error-presenter γίνεται στη διαμόρφωση application.
Αποστολή JSON
Παράδειγμα μεθόδου action που στέλνει δεδομένα σε μορφή JSON και τερματίζει τον presenter:
public function actionData(): void
{
$data = ['hello' => 'nette'];
$this->sendJson($data);
}
Παράμετροι αιτήματος
Ο presenter και επίσης κάθε component λαμβάνει τις παραμέτρους του από το
αίτημα HTTP. Μπορείτε να βρείτε την τιμή τους χρησιμοποιώντας τη μέθοδο
getParameter($name)
ή getParameters()
. Οι τιμές είναι strings ή arrays από strings,
πρόκειται ουσιαστικά για ακατέργαστα δεδομένα που λαμβάνονται
απευθείας από το URL.
Για μεγαλύτερη ευκολία, συνιστούμε να κάνετε τις παραμέτρους
προσβάσιμες μέσω ιδιοτήτων. Αρκεί να τις επισημάνετε με το attribute
#[Parameter]
:
use Nette\Application\Attributes\Parameter; // αυτή η γραμμή είναι σημαντική
class HomePresenter extends Nette\Application\UI\Presenter
{
#[Parameter]
public string $theme; // πρέπει να είναι public
}
Συνιστούμε να καθορίσετε τον τύπο δεδομένων για την ιδιότητα (π.χ.
string
) και το Nette θα μετατρέψει αυτόματα την τιμή σύμφωνα με αυτόν.
Οι τιμές των παραμέτρων μπορούν επίσης να επικυρωθούν.
Κατά τη δημιουργία ενός συνδέσμου, η τιμή των παραμέτρων μπορεί να οριστεί απευθείας:
<a n:href="Home:default theme: dark">κάντε κλικ</a>
Persistent παράμετροι
Οι persistent παράμετροι χρησιμοποιούνται για τη διατήρηση της
κατάστασης μεταξύ διαφορετικών αιτήσεων. Η τιμή τους παραμένει η ίδια
ακόμη και μετά το κλικ σε έναν σύνδεσμο. Σε αντίθεση με τα δεδομένα στη
session, μεταφέρονται στη διεύθυνση URL. Και αυτό γίνεται εντελώς αυτόματα,
δεν χρειάζεται δηλαδή να τις καθορίσετε ρητά στο link()
ή στο
n:href
.
Παράδειγμα χρήσης; Έχετε μια πολύγλωσση εφαρμογή. Η τρέχουσα γλώσσα
είναι μια παράμετρος που πρέπει να είναι συνεχώς μέρος της διεύθυνσης
URL. Αλλά θα ήταν απίστευτα κουραστικό να την καθορίζετε σε κάθε
σύνδεσμο. Έτσι, την κάνετε μια persistent παράμετρο lang
και θα
μεταφέρεται μόνη της. Υπέροχο!
Η δημιουργία μιας persistent παραμέτρου στο Nette είναι εξαιρετικά απλή.
Αρκεί να δημιουργήσετε μια δημόσια ιδιότητα και να την επισημάνετε με
ένα attribute: (παλαιότερα χρησιμοποιούνταν το /** @persistent */
)
use Nette\Application\Attributes\Persistent; // αυτή η γραμμή είναι σημαντική
class ProductPresenter extends Nette\Application\UI\Presenter
{
#[Persistent]
public string $lang; // πρέπει να είναι public
}
Αν το $this->lang
έχει την τιμή, για παράδειγμα, 'en'
, τότε και
οι σύνδεσμοι που δημιουργούνται χρησιμοποιώντας το link()
ή το
n:href
θα περιέχουν την παράμετρο lang=en
. Και μετά το κλικ στον
σύνδεσμο, το $this->lang
θα είναι ξανά 'en'
.
Συνιστούμε να καθορίσετε τον τύπο δεδομένων για την ιδιότητα (π.χ.
string
) και μπορείτε επίσης να καθορίσετε μια προεπιλεγμένη τιμή.
Οι τιμές των παραμέτρων μπορούν να επικυρωθούν.
Οι persistent παράμετροι μεταφέρονται κανονικά μεταξύ όλων των actions του συγκεκριμένου presenter. Για να μεταφέρονται και μεταξύ πολλών presenters, πρέπει να οριστούν είτε:
- σε έναν κοινό πρόγονο, από τον οποίο κληρονομούν οι presenters
- σε ένα trait, το οποίο χρησιμοποιούν οι presenters:
trait LanguageAware
{
#[Persistent]
public string $lang;
}
class ProductPresenter extends Nette\Application\UI\Presenter
{
use LanguageAware;
}
Κατά τη δημιουργία ενός συνδέσμου, η τιμή της persistent παραμέτρου μπορεί να αλλάξει:
<a n:href="Product:show $id, lang: cs">λεπτομέρεια στα Τσέχικα</a>
Ή μπορεί να επαναφερθεί, δηλαδή να αφαιρεθεί από τη διεύθυνση URL. Στη συνέχεια, θα πάρει την προεπιλεγμένη της τιμή:
<a n:href="Product:show $id, lang: null">κάντε κλικ</a>
Διαδραστικά Components
Οι presenters έχουν ενσωματωμένο σύστημα components. Τα components είναι ανεξάρτητες, επαναχρησιμοποιήσιμες μονάδες που ενσωματώνουμε στους presenters. Μπορεί να είναι φόρμες, datagrids, μενού, στην πραγματικότητα οτιδήποτε έχει νόημα να χρησιμοποιείται επανειλημμένα.
Πώς ενσωματώνονται και στη συνέχεια χρησιμοποιούνται τα components στον presenter; Αυτό θα το μάθετε στο κεφάλαιο Components. Θα ανακαλύψετε ακόμη και τι κοινό έχουν με το Hollywood.
Και πού μπορώ να βρω components; Στη σελίδα Componette θα βρείτε open-source components και επίσης μια σειρά από άλλα πρόσθετα για το Nette, τα οποία έχουν τοποθετηθεί εδώ από εθελοντές της κοινότητας γύρω από το framework.
Πάμε βαθύτερα
Με όσα έχουμε δείξει μέχρι τώρα σε αυτό το κεφάλαιο, πιθανότατα θα τα βγάλετε πέρα. Οι παρακάτω γραμμές προορίζονται για όσους ενδιαφέρονται για τους presenters σε βάθος και θέλουν να μάθουν τα πάντα.
Επικύρωση παραμέτρων
Οι τιμές των παραμέτρων αιτήματος και των persistent παραμέτρων που λαμβάνονται από τη
διεύθυνση URL γράφονται στις ιδιότητες από τη μέθοδο loadState()
. Αυτή
ελέγχει επίσης εάν ο τύπος δεδομένων που καθορίζεται στην ιδιότητα
αντιστοιχεί, διαφορετικά απαντά με σφάλμα 404 και η σελίδα δεν
εμφανίζεται.
Ποτέ μην εμπιστεύεστε τυφλά τις παραμέτρους, επειδή μπορούν εύκολα
να αντικατασταθούν από τον χρήστη στη διεύθυνση URL. Έτσι, για
παράδειγμα, επαληθεύουμε εάν η γλώσσα $this->lang
είναι μεταξύ των
υποστηριζόμενων. Ένας κατάλληλος τρόπος είναι να αντικαταστήσετε την
αναφερόμενη μέθοδο loadState()
:
class ProductPresenter extends Nette\Application\UI\Presenter
{
#[Persistent]
public string $lang;
public function loadState(array $params): void
{
parent::loadState($params); // εδώ ορίζεται το $this->lang
// ακολουθεί προσαρμοσμένος έλεγχος τιμής:
if (!in_array($this->lang, ['en', 'cs'])) {
$this->error();
}
}
}
Αποθήκευση και ανάκτηση αιτήματος
Το αίτημα που διεκπεραιώνει ο presenter είναι ένα αντικείμενο Nette\Application\Request και
επιστρέφεται από τη μέθοδο του presenter getRequest()
.
Το τρέχον αίτημα μπορεί να αποθηκευτεί στη session ή, αντίθετα, να
ανακτηθεί από αυτήν και να αφεθεί ο presenter να το εκτελέσει ξανά. Αυτό
είναι χρήσιμο, για παράδειγμα, σε μια κατάσταση όπου ο χρήστης
συμπληρώνει μια φόρμα και η σύνδεσή του λήγει. Για να μην χάσει τα
δεδομένα, πριν από την ανακατεύθυνση στη σελίδα σύνδεσης, αποθηκεύουμε
το τρέχον αίτημα στη session χρησιμοποιώντας το $reqId = $this->storeRequest()
,
το οποίο επιστρέφει το αναγνωριστικό του με τη μορφή μιας σύντομης
συμβολοσειράς και το μεταβιβάζουμε ως παράμετρο στον presenter σύνδεσης.
Μετά τη σύνδεση, καλούμε τη μέθοδο $this->restoreRequest($reqId)
, η οποία
ανακτά το αίτημα από τη session και προωθεί σε αυτό. Η μέθοδος επαληθεύει
ταυτόχρονα ότι το αίτημα δημιουργήθηκε από τον ίδιο χρήστη που
συνδέθηκε τώρα. Αν συνδεθεί άλλος χρήστης ή το κλειδί είναι άκυρο, δεν
κάνει τίποτα και το πρόγραμμα συνεχίζει.
Δείτε τον οδηγό Πώς να επιστρέψετε σε προηγούμενη σελίδα.
Κανονικοποίηση
Οι presenters έχουν ένα πραγματικά εξαιρετικό χαρακτηριστικό που
συμβάλλει στο καλύτερο SEO (βελτιστοποίηση για μηχανές αναζήτησης).
Αποτρέπουν αυτόματα την ύπαρξη διπλού περιεχομένου σε διαφορετικά URL.
Αν υπάρχουν πολλαπλά URL που οδηγούν στον ίδιο στόχο, π.χ. /index
και
/index?page=1
, το framework καθορίζει ένα από αυτά ως το κύριο (κανονικό)
και ανακατευθύνει τα υπόλοιπα σε αυτό χρησιμοποιώντας τον κωδικό HTTP
301. Χάρη σε αυτό, οι μηχανές αναζήτησης δεν ευρετηριάζουν τις σελίδες
σας δύο φορές και δεν διασπούν το page rank τους.
Αυτή η διαδικασία ονομάζεται κανονικοποίηση. Η κανονική διεύθυνση URL είναι αυτή που δημιουργείται από τον router, συνήθως δηλαδή η πρώτη αντίστοιχη διαδρομή στη συλλογή.
Η κανονικοποίηση είναι ενεργοποιημένη από προεπιλογή και μπορεί να
απενεργοποιηθεί μέσω του $this->autoCanonicalize = false
.
Η ανακατεύθυνση δεν πραγματοποιείται κατά τη διάρκεια μιας αίτησης AJAX ή POST, επειδή θα προκαλούσε απώλεια δεδομένων ή δεν θα είχε προστιθέμενη αξία από άποψη SEO.
Μπορείτε επίσης να καλέσετε την κανονικοποίηση χειροκίνητα
χρησιμοποιώντας τη μέθοδο canonicalize()
, στην οποία, παρόμοια με τη
μέθοδο link()
, περνιέται ο presenter, η action και οι παράμετροι.
Δημιουργεί έναν σύνδεσμο και τον συγκρίνει με την τρέχουσα διεύθυνση
URL. Αν διαφέρουν, ανακατευθύνει στον δημιουργημένο σύνδεσμο.
public function actionShow(int $id, ?string $slug = null): void
{
$realSlug = $this->facade->getSlugForId($id);
// ανακατευθύνει αν το $slug διαφέρει από το $realSlug
$this->canonicalize('Product:show', [$id, $realSlug]);
}
Γεγονότα
Εκτός από τις μεθόδους startup()
, beforeRender()
και shutdown()
,
οι οποίες καλούνται ως μέρος του κύκλου ζωής του presenter, μπορούν να
οριστούν και άλλες συναρτήσεις που θα καλούνται αυτόματα. Ο presenter
ορίζει τα λεγόμενα γεγονότα, των
οποίων τους handlers προσθέτετε στους πίνακες $onStartup
, $onRender
και $onShutdown
.
class ArticlePresenter extends Nette\Application\UI\Presenter
{
public function __construct()
{
$this->onStartup[] = function () {
// ...
};
}
}
Οι handlers στον πίνακα $onStartup
καλούνται ακριβώς πριν από τη μέθοδο
startup()
, στη συνέχεια το $onRender
μεταξύ beforeRender()
και
render<View>()
και τέλος το $onShutdown
ακριβώς πριν από το
shutdown()
.
Απαντήσεις
Η response που επιστρέφει ο presenter είναι ένα αντικείμενο που υλοποιεί το interface Nette\Application\Response. Υπάρχουν διαθέσιμες πολλές έτοιμες responses:
- Nette\Application\Responses\CallbackResponse – στέλνει ένα callback
- Nette\Application\Responses\FileResponse – στέλνει ένα αρχείο
- Nette\Application\Responses\ForwardResponse – forward()
- Nette\Application\Responses\JsonResponse – στέλνει JSON
- Nette\Application\Responses\RedirectResponse – ανακατεύθυνση
- Nette\Application\Responses\TextResponse – στέλνει κείμενο
- Nette\Application\Responses\VoidResponse – κενή response
Οι responses στέλνονται με τη μέθοδο sendResponse()
:
use Nette\Application\Responses;
// Απλό κείμενο
$this->sendResponse(new Responses\TextResponse('Hello Nette!'));
// Στέλνει ένα αρχείο
$this->sendResponse(new Responses\FileResponse(__DIR__ . '/invoice.pdf', 'Invoice13.pdf'));
// Η response θα είναι ένα callback
$callback = function (Nette\Http\IRequest $httpRequest, Nette\Http\IResponse $httpResponse) {
if ($httpResponse->getHeader('Content-Type') === 'text/html') {
echo '<h1>Hello</h1>';
}
};
$this->sendResponse(new Responses\CallbackResponse($callback));
Περιορισμός πρόσβασης με
#[Requires]
Το attribute #[Requires]
παρέχει προηγμένες δυνατότητες για τον
περιορισμό της πρόσβασης σε presenters και τις μεθόδους τους. Μπορεί να
χρησιμοποιηθεί για τον καθορισμό μεθόδων HTTP, την απαίτηση αίτησης AJAX,
τον περιορισμό στην ίδια προέλευση (same origin), και την πρόσβαση μόνο μέσω
προώθησης (forwarding). Το attribute μπορεί να εφαρμοστεί τόσο στις κλάσεις των
presenters όσο και στις μεμονωμένες μεθόδους action<Action>()
,
render<View>()
, handle<Signal>()
και createComponent<Name>()
.
Μπορείτε να καθορίσετε αυτούς τους περιορισμούς:
- σε μεθόδους HTTP:
#[Requires(methods: ['GET', 'POST'])]
- απαίτηση αίτησης AJAX:
#[Requires(ajax: true)]
- πρόσβαση μόνο από την ίδια προέλευση:
#[Requires(sameOrigin: true)]
- πρόσβαση μόνο μέσω forward:
#[Requires(forward: true)]
- περιορισμός σε συγκεκριμένες actions:
#[Requires(actions: 'default')]
Λεπτομέρειες θα βρείτε στον οδηγό Πώς να χρησιμοποιήσετε το attribute Requires.
Έλεγχος μεθόδου HTTP
Οι presenters στο Nette επαληθεύουν αυτόματα τη μέθοδο HTTP κάθε εισερχόμενου
αιτήματος. Ο λόγος για αυτόν τον έλεγχο είναι κυρίως η ασφάλεια. Από
προεπιλογή, επιτρέπονται οι μέθοδοι GET
, POST
, HEAD
,
PUT
, DELETE
, PATCH
.
Αν θέλετε να επιτρέψετε επιπλέον, για παράδειγμα, τη μέθοδο
OPTIONS
, χρησιμοποιήστε το attribute #[Requires]
(από το Nette
Application v3.2):
#[Requires(methods: ['GET', 'POST', 'HEAD', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'])]
class MyPresenter extends Nette\Application\UI\Presenter
{
}
Στην έκδοση 3.1, η επαλήθευση γίνεται στην checkHttpMethod()
, η οποία
ελέγχει εάν η μέθοδος που καθορίζεται στην αίτηση περιλαμβάνεται στον
πίνακα $presenter->allowedMethods
. Η προσθήκη της μεθόδου γίνεται
ως εξής:
class MyPresenter extends Nette\Application\UI\Presenter
{
protected function checkHttpMethod(): void
{
$this->allowedMethods[] = 'OPTIONS';
parent::checkHttpMethod();
}
}
Είναι σημαντικό να τονιστεί ότι αν επιτρέψετε τη μέθοδο OPTIONS
,
πρέπει στη συνέχεια να την χειριστείτε κατάλληλα εντός του presenter σας. Η
μέθοδος χρησιμοποιείται συχνά ως το λεγόμενο preflight request, το οποίο το
πρόγραμμα περιήγησης στέλνει αυτόματα πριν από το πραγματικό αίτημα,
όταν χρειάζεται να διαπιστωθεί εάν το αίτημα επιτρέπεται από την
πολιτική CORS (Cross-Origin Resource Sharing). Αν επιτρέψετε τη μέθοδο, αλλά δεν
υλοποιήσετε τη σωστή response, μπορεί να οδηγήσει σε ασυνέπειες και πιθανά
προβλήματα ασφάλειας.