Nette Documentation Preview

syntax
Παρουσιαστές
************

<div class=perex>

Θα μάθουμε πώς να γράφουμε παρουσιαστές και πρότυπα στη Nette. Μετά την ανάγνωση θα ξέρετε:

- πώς λειτουργεί ο παρουσιαστής
- τι είναι οι μόνιμες παράμετροι
- πώς να αποδώσετε ένα πρότυπο

</div>

[Γνωρίζουμε ήδη |how-it-works#nette-application] ότι ένας παρουσιαστής είναι μια κλάση που αναπαριστά μια συγκεκριμένη σελίδα μιας διαδικτυακής εφαρμογής, όπως μια αρχική σελίδα, ένα προϊόν στο ηλεκτρονικό κατάστημα, μια φόρμα εγγραφής, μια τροφοδοσία χάρτη σελίδων κ.λπ. Η εφαρμογή μπορεί να έχει από έναν έως χιλιάδες παρουσιαστές. Σε άλλα πλαίσια, είναι επίσης γνωστοί ως ελεγκτές.

Συνήθως, ο όρος παρουσιαστής αναφέρεται σε έναν απόγονο της κλάσης [api:Nette\Application\UI\Presenter], η οποία είναι κατάλληλη για διεπαφές ιστού και την οποία θα συζητήσουμε στο υπόλοιπο του κεφαλαίου. Σε γενικές γραμμές, παρουσιαστής είναι κάθε αντικείμενο που υλοποιεί τη διεπαφή [api:Nette\Application\IPresenter].


Κύκλος ζωής του παρουσιαστή .[#toc-life-cycle-of-presenter]
===========================================================

Η δουλειά του παρουσιαστή είναι να επεξεργάζεται το αίτημα και να επιστρέφει μια απάντηση (η οποία μπορεί να είναι μια σελίδα HTML, μια εικόνα, μια ανακατεύθυνση κ.λπ.).

Έτσι, στην αρχή υπάρχει ένα αίτημα. Δεν είναι άμεσα ένα αίτημα HTTP, αλλά ένα αντικείμενο [api:Nette\Application\Request] στο οποίο μετατράπηκε το αίτημα HTTP με τη χρήση ενός δρομολογητή. Συνήθως δεν ερχόμαστε σε επαφή με αυτό το αντικείμενο, επειδή ο παρουσιαστής αναθέτει έξυπνα την επεξεργασία του αιτήματος σε ειδικές μεθόδους, τις οποίες θα δούμε τώρα.

[* lifecycle.svg *] *** *Κύκλος ζωής του παρουσιαστή* .<>

Το σχήμα δείχνει μια λίστα μεθόδων που καλούνται διαδοχικά από πάνω προς τα κάτω, εφόσον υπάρχουν. Καμία από αυτές δεν χρειάζεται να υπάρχει, μπορούμε να έχουμε έναν εντελώς άδειο presenter χωρίς ούτε μία μέθοδο και να φτιάξουμε έναν απλό στατικό ιστό πάνω του.


`__construct()`
---------------

Ο κατασκευαστής δεν ανήκει ακριβώς στον κύκλο ζωής του παρουσιαστή, επειδή καλείται τη στιγμή της δημιουργίας του αντικειμένου. Τον αναφέρουμε όμως λόγω της σημασίας του. Ο κατασκευαστής (μαζί με τη [μέθοδο inject |best-practices:inject-method-attribute]) χρησιμοποιείται για να περάσει εξαρτήσεις.

Ο παρουσιαστής δεν πρέπει να φροντίζει την επιχειρησιακή λογική της εφαρμογής, να γράφει και να διαβάζει από τη βάση δεδομένων, να εκτελεί υπολογισμούς κ.λπ. Αυτό είναι το καθήκον για τις κλάσεις από ένα επίπεδο, το οποίο ονομάζουμε μοντέλο. Για παράδειγμα, η κλάση `ArticleRepository` μπορεί να είναι υπεύθυνη για τη φόρτωση και την αποθήκευση άρθρων. Προκειμένου ο παρουσιαστής να τη χρησιμοποιήσει, [περνάει χρησιμοποιώντας την έγχυση εξάρτησης (dependency injection |dependency-injection:passing-dependencies]):


```php
class ArticlePresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private ArticleRepository $articles,
	) {
	}
}
```


`startup()`
-----------

Αμέσως μετά τη λήψη της αίτησης, καλείται η μέθοδος `startup ()`. Μπορείτε να τη χρησιμοποιήσετε για να αρχικοποιήσετε τις ιδιότητες, να ελέγξετε τα προνόμια του χρήστη κ.λπ. Απαιτείται να καλείται πάντα ο πρόγονος `parent::startup()`.


`action<Action>(args...)` .{toc: action<Action>()}
--------------------------------------------------

Παρόμοια με τη μέθοδο `render<View>()`. Ενώ `render<View>()` προορίζεται για την προετοιμασία δεδομένων για ένα συγκεκριμένο πρότυπο, το οποίο στη συνέχεια αποδίδεται, στην `action<Action>()` μια αίτηση επεξεργάζεται χωρίς να ακολουθεί η απόδοση του προτύπου. Για παράδειγμα, γίνεται επεξεργασία δεδομένων, γίνεται είσοδος ή έξοδος ενός χρήστη κ.ο.κ. και στη συνέχεια γίνεται [ανακατεύθυνση αλλού |#Redirection].

Είναι σημαντικό ότι `action<Action>()` καλείται πριν από την `render<View>()`, ώστε μέσα σε αυτό να μπορούμε ενδεχομένως να αλλάξουμε την επόμενη πορεία του κύκλου ζωής, δηλαδή να αλλάξουμε το πρότυπο που θα αποδοθεί και επίσης τη μέθοδο `render<View>()` που θα κληθεί, χρησιμοποιώντας το `setView('otherView')`.

Οι παράμετροι από το αίτημα περνούν στη μέθοδο. Είναι δυνατόν και συνιστάται να καθορίσετε τύπους για τις παραμέτρους, π.χ. `actionShow(int $id, ?string $slug = null)` - αν η παράμετρος `id` λείπει ή αν δεν είναι ακέραιος αριθμός, ο παρουσιαστής επιστρέφει [σφάλμα 404 |#Error 404 etc.] και τερματίζει τη λειτουργία.


`handle<Signal>(args...)` .{toc: handle<Signal>()}
--------------------------------------------------

Αυτή η μέθοδος επεξεργάζεται τα λεγόμενα σήματα, τα οποία θα συζητήσουμε στο κεφάλαιο για τα [συστατικά |components#Signal]. Προορίζεται κυρίως για συστατικά και επεξεργασία αιτήσεων AJAX.

Οι παράμετροι μεταβιβάζονται στη μέθοδο, όπως στην περίπτωση της `action<Action>()`, συμπεριλαμβανομένου του ελέγχου τύπου.


`beforeRender()`
----------------

Η μέθοδος `beforeRender`, όπως υποδηλώνει το όνομα, καλείται πριν από κάθε μέθοδο `render<View>()`. Χρησιμοποιείται για την κοινή διαμόρφωση του προτύπου, το πέρασμα μεταβλητών για τη διάταξη και ούτω καθεξής.


`render<View>(args...)` .{toc: render<View>()}
----------------------------------------------

Το μέρος όπου προετοιμάζουμε το πρότυπο για την μετέπειτα απόδοση, του περνάμε δεδομένα κ.λπ.

Οι παράμετροι περνούν στη μέθοδο, όπως στην περίπτωση της `action<Action>()`, συμπεριλαμβανομένου του ελέγχου τύπου.

```php
public function renderShow(int $id): void
{
	// λαμβάνουμε δεδομένα από το μοντέλο και τα περνάμε στο πρότυπο
	$this->template->article = $this->articles->getById($id);
}
```


`afterRender()`
---------------

Η μέθοδος `afterRender`, όπως υποδηλώνει και πάλι το όνομα, καλείται μετά από κάθε `render<View>()` μέθοδο. Χρησιμοποιείται μάλλον σπάνια.


`shutdown()`
------------

Καλείται στο τέλος του κύκλου ζωής του παρουσιαστή.


**Καλή συμβουλή πριν προχωρήσουμε**. Όπως μπορείτε να δείτε, ο παρουσιαστής μπορεί να χειριστεί περισσότερες ενέργειες/προβολές, δηλαδή να έχει περισσότερες μεθόδους `render<View>()`. Αλλά συνιστούμε να σχεδιάζετε παρουσιαστές με μία ή όσο το δυνατόν λιγότερες ενέργειες.


Αποστολή μιας απάντησης .[#toc-sending-a-response]
==================================================

Η απάντηση του παρουσιαστή είναι συνήθως η [απόδοση του προτύπου με τη σελίδα HTML |templates], αλλά μπορεί επίσης να είναι η αποστολή ενός αρχείου, JSON ή ακόμα και η ανακατεύθυνση σε μια άλλη σελίδα.

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

- `redirect()`, `redirectPermanent()`, `redirectUrl()` και `forward()` [ανακατευθύνσεις |#Redirection]
- `error()` τερματίζει τον παρουσιαστή [λόγω σφάλματος |#Error 404 etc.]
- `sendJson($data)` εγκαταλείπει τον παρουσιαστή και [αποστέλλει τα δεδομένα |#Sending JSON] σε μορφή JSON
- `sendTemplate()` εγκαταλείπει τον παρουσιαστή και αποδίδει αμέσως [το πρότυπο |templates].
- `sendResponse($response)` εγκαταλείπει τον παρουσιαστή και αποστέλλει [τη δική του απάντηση |#Responses].
- `terminate()` εγκαταλείπει τον παρουσιαστή χωρίς απάντηση

Εάν δεν καλέσετε καμία από αυτές τις μεθόδους, ο παρουσιαστής θα προχωρήσει αυτόματα στην απόδοση του προτύπου. Γιατί; Λοιπόν, επειδή στο 99% των περιπτώσεων θέλουμε να σχεδιάσουμε ένα πρότυπο, οπότε ο παρουσιαστής θεωρεί αυτή τη συμπεριφορά ως προεπιλεγμένη και θέλει να διευκολύνει τη δουλειά μας.


Δημιουργία συνδέσμων .[#toc-creating-links]
===========================================

Ο παρουσιαστής διαθέτει τη μέθοδο `link()`, η οποία χρησιμοποιείται για τη δημιουργία συνδέσμων URL προς άλλους παρουσιαστές. Η πρώτη παράμετρος είναι ο παρουσιαστής & η ενέργεια-στόχος, ακολουθούμενη από τα ορίσματα, τα οποία μπορούν να περάσουν ως πίνακας:

```php
$url = $this->link('Product:show', $id);

$url = $this->link('Product:show', [$id, 'lang' => 'en']);
```

Στο πρότυπο δημιουργούμε συνδέσμους προς άλλους παρουσιαστές & ενέργειες ως εξής:

```latte
<a n:href="Product:show $id">product detail</a>
```

Απλά γράψτε το γνωστό ζεύγος `Presenter:action` αντί της πραγματικής διεύθυνσης URL και συμπεριλάβετε τυχόν παραμέτρους. Το κόλπο είναι το `n:href`, το οποίο λέει ότι αυτό το χαρακτηριστικό θα επεξεργαστεί από το Latte και θα δημιουργήσει ένα πραγματικό URL. Στη Nette, δεν χρειάζεται να σκέφτεστε καθόλου τις διευθύνσεις URL, παρά μόνο τους παρουσιαστές και τις ενέργειες.

Για περισσότερες πληροφορίες, ανατρέξτε στην ενότητα [Δημιουργία συνδέσμων |Creating Links].


Ανακατεύθυνση .[#toc-redirection]
=================================

Οι μέθοδοι `redirect()` και `forward()` χρησιμοποιούνται για τη μετάβαση σε έναν άλλο παρουσιαστή, οι οποίες έχουν πολύ παρόμοια σύνταξη με τη μέθοδο [link() |#Creating Links].

Η `forward()` μεταβαίνει στον νέο παρουσιαστή αμέσως χωρίς ανακατεύθυνση HTTP:

```php
$this->forward('Product:show');
```

Παράδειγμα μιας λεγόμενης προσωρινής ανακατεύθυνσης με κωδικό HTTP 302 (ή 303, εάν η τρέχουσα μέθοδος αίτησης είναι POST):

```php
$this->redirect('Product:show', $id);
```

Για να επιτύχετε μόνιμη ανακατεύθυνση με κωδικό HTTP 301 χρησιμοποιήστε:

```php
$this->redirectPermanent('Product:show', $id);
```

Μπορείτε να ανακατευθύνετε σε μια άλλη διεύθυνση URL εκτός της εφαρμογής χρησιμοποιώντας τη μέθοδο `redirectUrl()`. Ο κωδικός HTTP μπορεί να καθοριστεί ως δεύτερη παράμετρος, με προεπιλεγμένη τιμή 302 (ή 303, αν η τρέχουσα μέθοδος αίτησης είναι POST):

```php
$this->redirectUrl('https://nette.org');
```

Η ανακατεύθυνση τερματίζει αμέσως τον κύκλο ζωής του παρουσιαστή πετώντας τη λεγόμενη εξαίρεση σιωπηλού τερματισμού `Nette\Application\AbortException`.

Πριν από την ανακατεύθυνση, είναι δυνατή η αποστολή ενός [μηνύματος flash |#Flash Messages], μηνύματα που θα εμφανιστούν στο πρότυπο μετά την ανακατεύθυνση.


Μηνύματα flash .[#toc-flash-messages]
=====================================

Πρόκειται για μηνύματα που συνήθως ενημερώνουν για το αποτέλεσμα μιας λειτουργίας. Ένα σημαντικό χαρακτηριστικό των μηνυμάτων flash είναι ότι είναι διαθέσιμα στο πρότυπο ακόμη και μετά την ανακατεύθυνση. Ακόμη και μετά την εμφάνισή τους, θα παραμείνουν ζωντανά για άλλα 30 δευτερόλεπτα - για παράδειγμα, σε περίπτωση που ο χρήστης θα ανανεώσει ακούσια τη σελίδα - το μήνυμα δεν θα χαθεί.

Απλά καλέστε τη μέθοδο [flashMessage() |api:Nette\Application\UI\Control::flashMessage()] και ο presenter θα φροντίσει να περάσει το μήνυμα στο πρότυπο. Το πρώτο όρισμα είναι το κείμενο του μηνύματος και το δεύτερο προαιρετικό όρισμα είναι ο τύπος του (σφάλμα, προειδοποίηση, πληροφορία κ.λπ.). Η μέθοδος `flashMessage()` επιστρέφει ένα instance του flash message, για να μας επιτρέψει να προσθέσουμε περισσότερες πληροφορίες.

```php
$this->flashMessage('Item was removed.');
$this->redirect(/* ... */);
```

Στο πρότυπο, τα μηνύματα αυτά είναι διαθέσιμα στη μεταβλητή `$flashes` ως αντικείμενα `stdClass`, τα οποία περιέχουν τις ιδιότητες `message` (κείμενο μηνύματος), `type` (τύπος μηνύματος) και μπορούν να περιέχουν τις ήδη αναφερθείσες πληροφορίες χρήστη. Τα σχεδιάζουμε ως εξής:

```latte
{foreach $flashes as $flash}
	<div class="flash {$flash->type}">{$flash->message}</div>
{/foreach}
```


Σφάλμα 404 κ.λπ. .[#toc-error-404-etc]
======================================

Όταν δεν μπορούμε να ικανοποιήσουμε το αίτημα επειδή για παράδειγμα το άρθρο που θέλουμε να εμφανίσουμε δεν υπάρχει στη βάση δεδομένων, θα πετάξουμε το σφάλμα 404 χρησιμοποιώντας τη μέθοδο `error(?string $message = null, int $httpCode = 404)`, η οποία αντιπροσωπεύει το σφάλμα HTTP 404:

```php
public function renderShow(int $id): void
{
	$article = $this->articles->getById($id);
	if (!$article) {
		$this->error();
	}
	// ...
}
```

Ο κωδικός σφάλματος HTTP μπορεί να περάσει ως δεύτερη παράμετρος, η προεπιλογή είναι 404. Η μέθοδος λειτουργεί ρίχνοντας την εξαίρεση `Nette\Application\BadRequestException`, μετά την οποία η `Application` περνά τον έλεγχο στον παρουσιαστή του σφάλματος. Ο οποίος είναι ένας παρουσιαστής του οποίου η δουλειά είναι να εμφανίζει μια σελίδα που ενημερώνει για το σφάλμα.
Ο error-preseter ορίζεται στη [διαμόρφωση της εφαρμογής |configuration].


Αποστολή JSON .[#toc-sending-json]
==================================

Παράδειγμα δράσης-μεθόδου που στέλνει δεδομένα σε μορφή JSON και εξέρχεται από τον παρουσιαστή:

```php
public function actionData(): void
{
	$data = ['hello' => 'nette'];
	$this->sendJson($data);
}
```


Παράμετροι αίτησης .[#toc-request-parameters]
=============================================

Ο παρουσιαστής, όπως και κάθε στοιχείο, λαμβάνει τις παραμέτρους του από την αίτηση HTTP. Οι τιμές τους μπορούν να ανακτηθούν χρησιμοποιώντας τη μέθοδο `getParameter($name)` ή `getParameters()`. Οι τιμές είναι συμβολοσειρές ή πίνακες συμβολοσειρών, ουσιαστικά ακατέργαστα δεδομένα που λαμβάνονται απευθείας από τη διεύθυνση URL.

Για μεγαλύτερη ευκολία, συνιστούμε να κάνετε τις παραμέτρους προσβάσιμες μέσω ιδιοτήτων. Απλά σχολιάστε τις με την εντολή `#[Parameter]` χαρακτηριστικό:

```php
use Nette\Application\Attributes\Parameter;  // αυτή η γραμμή είναι σημαντική

class HomePresenter extends Nette\Application\UI\Presenter
{
	#[Parameter]
	public string $theme; // πρέπει να είναι δημόσια
}
```

Για τις ιδιότητες, προτείνουμε να προσδιορίσετε τον τύπο δεδομένων (π.χ. `string`). Στη συνέχεια, η Nette θα μετατρέψει αυτόματα την τιμή με βάση αυτόν. Οι τιμές των παραμέτρων μπορούν επίσης να [επικυρωθούν |#Validation of Parameters].

Κατά τη δημιουργία ενός συνδέσμου, μπορείτε να ορίσετε απευθείας την τιμή για τις παραμέτρους:

```latte
<a n:href="Home:default theme: dark">click</a>
```


Εμμένουσες παράμετροι .[#toc-persistent-parameters]
===================================================

Οι μόνιμες παράμετροι χρησιμοποιούνται για τη διατήρηση της κατάστασης μεταξύ διαφορετικών αιτήσεων. Η τιμή τους παραμένει η ίδια ακόμη και μετά το κλικ σε έναν σύνδεσμο. Σε αντίθεση με τα δεδομένα συνόδου, μεταβιβάζονται στη διεύθυνση URL. Αυτό γίνεται εντελώς αυτόματα, οπότε δεν χρειάζεται να τις δηλώσετε ρητά στο `link()` ή στο `n:href`.

Παράδειγμα χρήσης; Έχετε μια πολύγλωσση εφαρμογή. Η πραγματική γλώσσα είναι μια παράμετρος που πρέπει να αποτελεί μέρος του URL ανά πάσα στιγμή. Αλλά θα ήταν απίστευτα κουραστικό να τη συμπεριλάβετε σε κάθε σύνδεσμο. Οπότε την κάνετε μια μόνιμη παράμετρο με το όνομα `lang` και θα μεταφέρεται μόνη της. Ωραία!

Η δημιουργία μιας μόνιμης παραμέτρου είναι εξαιρετικά εύκολη στη Nette. Απλά δημιουργήστε μια δημόσια ιδιότητα και επισημάνετέ την με το χαρακτηριστικό: (προηγουμένως χρησιμοποιούνταν το `/** @persistent */` )

```php
use Nette\Application\Attributes\Persistent; // αυτή η γραμμή είναι σημαντική

class ProductPresenter extends Nette\Application\UI\Presenter
{
	#[Persistent]
	public string $lang; // πρέπει να είναι δημόσια
}
```

Εάν το `$this->lang` έχει μια τιμή όπως `'en'`, τότε οι σύνδεσμοι που δημιουργούνται με χρήση των `link()` ή `n:href` θα περιέχουν επίσης την παράμετρο `lang=en`. Και όταν ο σύνδεσμος πατηθεί, θα είναι και πάλι `$this->lang = 'en'`.

Για τις ιδιότητες, συνιστούμε να συμπεριλάβετε τον τύπο δεδομένων (π.χ. `string`) και μπορείτε επίσης να συμπεριλάβετε μια προεπιλεγμένη τιμή. Οι τιμές των παραμέτρων μπορούν να [επικυρωθούν |#Validation of Parameters].

Οι μόνιμες παράμετροι μεταβιβάζονται μεταξύ όλων των ενεργειών ενός συγκεκριμένου παρουσιαστή από προεπιλογή. Για να τις περάσετε μεταξύ πολλαπλών παρουσιαστών, πρέπει να τις ορίσετε είτε:

- σε έναν κοινό πρόγονο από τον οποίο κληρονομούν οι παρουσιαστές
- στην ιδιότητα που χρησιμοποιούν οι παρουσιαστές:

```php
trait LanguageAware
{
	#[Persistent]
	public string $lang;
}

class ProductPresenter extends Nette\Application\UI\Presenter
{
	use LanguageAware;
}
```

Μπορείτε να αλλάξετε την τιμή μιας μόνιμης παραμέτρου κατά τη δημιουργία ενός συνδέσμου:

```latte
<a n:href="Product:show $id, lang: cs">detail in Czech</a>
```

Ή μπορεί να *επαναρυθμιστεί*, δηλαδή να αφαιρεθεί από τη διεύθυνση URL. Τότε θα πάρει την προεπιλεγμένη τιμή της:

```latte
<a n:href="Product:show $id, lang: null">click</a>
```


Διαδραστικά στοιχεία .[#toc-interactive-components]
===================================================

Οι παρουσιαστές διαθέτουν ένα ενσωματωμένο σύστημα συστατικών. Τα συστατικά είναι ξεχωριστές επαναχρησιμοποιήσιμες μονάδες που τοποθετούμε στους παρουσιαστές. Μπορούν να είναι [φόρμες |forms:in-presenter], datagrids, μενού, στην πραγματικότητα οτιδήποτε έχει νόημα να χρησιμοποιείται επανειλημμένα.

Πώς τοποθετούνται και στη συνέχεια χρησιμοποιούνται τα συστατικά στον παρουσιαστή; Αυτό εξηγείται στο κεφάλαιο [Components |Components]. Θα μάθετε ακόμη και τι σχέση έχουν με το Χόλιγουντ.

Πού μπορώ να προμηθευτώ μερικά συστατικά; Στη σελίδα [Componette |https://componette.org] μπορείτε να βρείτε ορισμένα συστατικά ανοικτού κώδικα και άλλα πρόσθετα για το Nette που έχουν κατασκευαστεί και διαμοιραστεί από την κοινότητα του Nette Framework.


Εμβαθύνοντας περισσότερο .[#toc-going-deeper]
=============================================

.[tip]
Όσα έχουμε δείξει μέχρι στιγμής σε αυτό το κεφάλαιο μάλλον αρκούν. Οι επόμενες γραμμές απευθύνονται σε όσους ενδιαφέρονται για τους παρουσιαστές σε βάθος και θέλουν να μάθουν τα πάντα.


Επικύρωση των παραμέτρων .[#toc-validation-of-parameters]
---------------------------------------------------------

Οι τιμές των [παραμέτρων αίτησης |#request parameters] και των [μόνιμων παραμέτρων |#persistent parameters] που λαμβάνονται από τις διευθύνσεις URL εγγράφονται στις ιδιότητες από τη μέθοδο `loadState()`. Ελέγχει επίσης αν ο τύπος δεδομένων που καθορίζεται στην ιδιότητα ταιριάζει, διαφορετικά θα απαντήσει με σφάλμα 404 και η σελίδα δεν θα εμφανιστεί.

Ποτέ μην εμπιστεύεστε τυφλά τις παραμέτρους, καθώς μπορούν εύκολα να αντικατασταθούν από τον χρήστη στη διεύθυνση URL. Για παράδειγμα, με αυτόν τον τρόπο ελέγχουμε αν το `$this->lang` είναι μεταξύ των υποστηριζόμενων γλωσσών. Ένας καλός τρόπος για να το κάνετε αυτό είναι να παρακάμψετε τη μέθοδο `loadState()` που αναφέρθηκε παραπάνω:

```php
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();
		}
	}
}
```


Αποθήκευση και επαναφορά της αίτησης .[#toc-save-and-restore-the-request]
-------------------------------------------------------------------------

Το αίτημα που χειρίζεται ο παρουσιαστής είναι ένα αντικείμενο [api:Nette\Application\Request] και επιστρέφεται από τη μέθοδο του παρουσιαστή `getRequest()`.

Μπορείτε να αποθηκεύσετε την τρέχουσα αίτηση σε μια συνεδρία ή να την επαναφέρετε από τη συνεδρία και να αφήσετε τον παρουσιαστή να την εκτελέσει ξανά. Αυτό είναι χρήσιμο, για παράδειγμα, όταν ένας χρήστης συμπληρώνει μια φόρμα και η σύνδεσή του λήγει. Για να μην χαθούν δεδομένα, πριν από την ανακατεύθυνση στη σελίδα σύνδεσης, αποθηκεύουμε την τρέχουσα αίτηση στη σύνοδο χρησιμοποιώντας τη μέθοδο `$reqId = $this->storeRequest()`, η οποία επιστρέφει ένα αναγνωριστικό με τη μορφή σύντομης συμβολοσειράς και το περνάει ως παράμετρο στον παρουσιαστή σύνδεσης.

Μετά την είσοδο, καλούμε τη μέθοδο `$this->restoreRequest($reqId)`, η οποία παραλαμβάνει το αίτημα από τη σύνοδο και το προωθεί σε αυτήν. Η μέθοδος επαληθεύει ότι το αίτημα δημιουργήθηκε από τον ίδιο χρήστη που τώρα έχει συνδεθεί είναι. Αν συνδεθεί άλλος χρήστης ή το κλειδί είναι άκυρο, δεν κάνει τίποτα και το πρόγραμμα συνεχίζει.

Δείτε το βιβλίο μαγειρικής [Πώς να επιστρέψετε σε μια προηγούμενη σελίδα |best-practices:restore-request].


Κανονικοποίηση .[#toc-canonization]
-----------------------------------

Οι παρουσιαστές έχουν ένα πραγματικά σπουδαίο χαρακτηριστικό που βελτιώνει το SEO (βελτιστοποίηση της δυνατότητας αναζήτησης στο Διαδίκτυο). Αποτρέπουν αυτόματα την ύπαρξη διπλού περιεχομένου σε διαφορετικές διευθύνσεις URL. Εάν πολλαπλές διευθύνσεις URL οδηγούν σε έναν συγκεκριμένο προορισμό, π.χ. `/index` και `/index?page=1`, το πλαίσιο ορίζει μία από αυτές ως κύρια (κανονική) και ανακατευθύνει τις υπόλοιπες σε αυτήν χρησιμοποιώντας τον κωδικό HTTP 301. Χάρη σε αυτό, οι μηχανές αναζήτησης δεν ευρετηριάζουν τις σελίδες δύο φορές και δεν αποδυναμώνουν την κατάταξή τους.

Η διαδικασία αυτή ονομάζεται κανονικοποίηση. Η κανονική διεύθυνση URL είναι η διεύθυνση URL που δημιουργείται από τον [δρομολογητή |routing], συνήθως η πρώτη κατάλληλη διαδρομή στη συλλογή.

Η κανονικοποίηση είναι ενεργοποιημένη από προεπιλογή και μπορεί να απενεργοποιηθεί μέσω της διεύθυνσης `$this->autoCanonicalize = false`.

Η ανακατεύθυνση δεν πραγματοποιείται με ένα αίτημα AJAX ή POST, επειδή θα είχε ως αποτέλεσμα την απώλεια δεδομένων ή καμία προστιθέμενη αξία SEO.

Μπορείτε επίσης να επικαλεστείτε την κανονικοποίηση χειροκίνητα χρησιμοποιώντας τη μέθοδο `canonicalize()`, η οποία, όπως και η μέθοδος `link()`, λαμβάνει τον παρουσιαστή, τις ενέργειες και τις παραμέτρους ως ορίσματα. Δημιουργεί έναν σύνδεσμο και τον συγκρίνει με την τρέχουσα διεύθυνση URL. Εάν είναι διαφορετική, ανακατευθύνει στον δημιουργημένο σύνδεσμο.

```php
public function actionShow(int $id, ?string $slug = null): void
{
	$realSlug = $this->facade->getSlugForId($id);
	// ανακατευθύνει εάν το $slug είναι διαφορετικό από το $realSlug
	$this->canonicalize('Product:show', [$id, $realSlug]);
}
```


Εκδηλώσεις .[#toc-events]
-------------------------

Εκτός από τις μεθόδους `startup()`, `beforeRender()` και `shutdown()`, οι οποίες καλούνται ως μέρος του κύκλου ζωής του παρουσιαστή, μπορούν να οριστούν και άλλες λειτουργίες που καλούνται αυτόματα. Ο παρουσιαστής ορίζει τα λεγόμενα [συμβάντα |nette:glossary#events] και εσείς προσθέτετε τους χειριστές τους στους πίνακες `$onStartup`, `$onRender` και `$onShutdown`.

```php
class ArticlePresenter extends Nette\Application\UI\Presenter
{
	public function __construct()
	{
		$this->onStartup[] = function () {
			// ...
		};
	}
}
```

Οι χειριστές στον πίνακα `$onStartup` καλούνται ακριβώς πριν από τη μέθοδο `startup()`, στη συνέχεια `$onRender` μεταξύ `beforeRender()` και `render<View>()` και τέλος `$onShutdown` λίγο πριν από την `shutdown()`.


Απαντήσεις .[#toc-responses]
----------------------------

Η απάντηση που επιστρέφεται από τον παρουσιαστή είναι ένα αντικείμενο που υλοποιεί τη διεπαφή [api:Nette\Application\Response]. Υπάρχει ένας αριθμός έτοιμων απαντήσεων:

- [api:Nette\Application\Responses\CallbackResponse] - στέλνει μια επανάκληση
- [api:Nette\Application\Responses\FileResponse] - στέλνει το αρχείο
- [api:Nette\Application\Responses\ForwardResponse] - προώθηση ()
- [api:Nette\Application\Responses\JsonResponse] - στέλνει JSON
- [api:Nette\Application\Responses\RedirectResponse] - ανακατεύθυνση
- [api:Nette\Application\Responses\TextResponse] - αποστέλλει κείμενο
- [api:Nette\Application\Responses\VoidResponse] - κενή απάντηση

Οι απαντήσεις αποστέλλονται με τη μέθοδο `sendResponse()`:

```php
use Nette\Application\Responses;

// Απλό κείμενο
$this->sendResponse(new Responses\TextResponse('Hello Nette!'));

// Αποστέλλει ένα αρχείο
$this->sendResponse(new Responses\FileResponse(__DIR__ . '/invoice.pdf', 'Invoice13.pdf'));

// Αποστέλλει ένα 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]` .[#toc-access-restriction-using-requires]{data-version:3.2.2}
----------------------------------------------------------------------------------------------------------

Το `#[Requires]` παρέχει προηγμένες επιλογές για τον περιορισμό της πρόσβασης στους παρουσιαστές και τις μεθόδους τους. Μπορεί να χρησιμοποιηθεί για τον προσδιορισμό μεθόδων HTTP, την απαίτηση αιτήσεων AJAX, τον περιορισμό της πρόσβασης στην ίδια προέλευση και τον περιορισμό της πρόσβασης μόνο στην προώθηση. Το χαρακτηριστικό μπορεί να εφαρμοστεί σε κλάσεις παρουσιαστών καθώς και σε μεμονωμένες μεθόδους όπως οι `action<Action>()`, `render<View>()`, `handle<Signal>()`, και `createComponent<Name>()`.

Μπορείτε να καθορίσετε αυτούς τους περιορισμούς:
- σε μεθόδους HTTP: `#[Requires(methods: ['GET', 'POST'])]`
- AJAX: `#[Requires(ajax: true)]`
- πρόσβαση μόνο από την ίδια προέλευση: `#[Requires(sameOrigin: true)]`
- πρόσβαση μόνο μέσω προώθησης: `#[Requires(forward: true)]`
- περιορισμοί σε συγκεκριμένες ενέργειες: `#[Requires(actions: 'default')]`

Για λεπτομέρειες, ανατρέξτε στην ενότητα [Πώς να χρησιμοποιήσετε το Requires attribute |best-practices:attribute-requires].


Έλεγχος μεθόδου HTTP .[#toc-http-method-check]
----------------------------------------------

Στη Nette, οι παρουσιαστές επαληθεύουν αυτόματα τη μέθοδο HTTP κάθε εισερχόμενης αίτησης κυρίως για λόγους ασφαλείας. Από προεπιλογή, επιτρέπονται οι μέθοδοι `GET`, `POST`, `HEAD`, `PUT`, `DELETE`, `PATCH`.

Εάν θέλετε να ενεργοποιήσετε επιπλέον μεθόδους, όπως η `OPTIONS`, μπορείτε να χρησιμοποιήσετε την εντολή `#[Requires]` (από την εφαρμογή Nette Application v3.2):

```php
#[Requires(methods: ['GET', 'POST', 'HEAD', 'PUT', 'DELETE', 'PATCH', 'OPTIONS'])]
class MyPresenter extends Nette\Application\UI\Presenter
{
}
```

Στην έκδοση 3.1, η επαλήθευση πραγματοποιείται στο `checkHttpMethod()`, το οποίο ελέγχει αν η μέθοδος που καθορίζεται στην αίτηση περιλαμβάνεται στον πίνακα `$presenter->allowedMethods`. Προσθέστε μια μέθοδο ως εξής:

```php
class MyPresenter extends Nette\Application\UI\Presenter
{
    protected function checkHttpMethod(): void
    {
        $this->allowedMethods[] = 'OPTIONS';
        parent::checkHttpMethod();
    }
}
```

Είναι ζωτικής σημασίας να τονιστεί ότι αν επιτρέψετε τη μέθοδο `OPTIONS`, πρέπει επίσης να τη χειριστείτε σωστά μέσα στον παρουσιαστή σας. Αυτή η μέθοδος χρησιμοποιείται συχνά ως το λεγόμενο preflight request, το οποίο οι φυλλομετρητές στέλνουν αυτόματα πριν από το πραγματικό αίτημα, όταν είναι απαραίτητο να καθοριστεί αν το αίτημα επιτρέπεται από την άποψη της πολιτικής CORS (Cross-Origin Resource Sharing). Εάν επιτρέψετε αυτή τη μέθοδο αλλά δεν υλοποιήσετε μια κατάλληλη απάντηση, μπορεί να οδηγήσει σε ασυνέπειες και πιθανά ζητήματα ασφάλειας.


Περαιτέρω ανάγνωση .[#toc-further-reading]
==========================================

- [Ένθεση μεθόδων και χαρακτηριστικών |best-practices:inject-method-attribute]
- [Σύνθεση παρουσιαστών από γνωρίσματα |best-practices:presenter-traits]
- [Πέρασμα ρυθμίσεων σε παρουσιαστές |best-practices:passing-settings-to-presenters]
- [Πώς να επιστρέψετε σε μια προηγούμενη |best-practices:restore-request]σελίδα

Παρουσιαστές

Θα μάθουμε πώς να γράφουμε παρουσιαστές και πρότυπα στη Nette. Μετά την ανάγνωση θα ξέρετε:

  • πώς λειτουργεί ο παρουσιαστής
  • τι είναι οι μόνιμες παράμετροι
  • πώς να αποδώσετε ένα πρότυπο

Γνωρίζουμε ήδη ότι ένας παρουσιαστής είναι μια κλάση που αναπαριστά μια συγκεκριμένη σελίδα μιας διαδικτυακής εφαρμογής, όπως μια αρχική σελίδα, ένα προϊόν στο ηλεκτρονικό κατάστημα, μια φόρμα εγγραφής, μια τροφοδοσία χάρτη σελίδων κ.λπ. Η εφαρμογή μπορεί να έχει από έναν έως χιλιάδες παρουσιαστές. Σε άλλα πλαίσια, είναι επίσης γνωστοί ως ελεγκτές.

Συνήθως, ο όρος παρουσιαστής αναφέρεται σε έναν απόγονο της κλάσης Nette\Application\UI\Presenter, η οποία είναι κατάλληλη για διεπαφές ιστού και την οποία θα συζητήσουμε στο υπόλοιπο του κεφαλαίου. Σε γενικές γραμμές, παρουσιαστής είναι κάθε αντικείμενο που υλοποιεί τη διεπαφή Nette\Application\IPresenter.

Κύκλος ζωής του παρουσιαστή

Η δουλειά του παρουσιαστή είναι να επεξεργάζεται το αίτημα και να επιστρέφει μια απάντηση (η οποία μπορεί να είναι μια σελίδα HTML, μια εικόνα, μια ανακατεύθυνση κ.λπ.).

Έτσι, στην αρχή υπάρχει ένα αίτημα. Δεν είναι άμεσα ένα αίτημα HTTP, αλλά ένα αντικείμενο Nette\Application\Request στο οποίο μετατράπηκε το αίτημα HTTP με τη χρήση ενός δρομολογητή. Συνήθως δεν ερχόμαστε σε επαφή με αυτό το αντικείμενο, επειδή ο παρουσιαστής αναθέτει έξυπνα την επεξεργασία του αιτήματος σε ειδικές μεθόδους, τις οποίες θα δούμε τώρα.

Κύκλος ζωής του παρουσιαστή

Το σχήμα δείχνει μια λίστα μεθόδων που καλούνται διαδοχικά από πάνω προς τα κάτω, εφόσον υπάρχουν. Καμία από αυτές δεν χρειάζεται να υπάρχει, μπορούμε να έχουμε έναν εντελώς άδειο presenter χωρίς ούτε μία μέθοδο και να φτιάξουμε έναν απλό στατικό ιστό πάνω του.

__construct()

Ο κατασκευαστής δεν ανήκει ακριβώς στον κύκλο ζωής του παρουσιαστή, επειδή καλείται τη στιγμή της δημιουργίας του αντικειμένου. Τον αναφέρουμε όμως λόγω της σημασίας του. Ο κατασκευαστής (μαζί με τη μέθοδο inject) χρησιμοποιείται για να περάσει εξαρτήσεις.

Ο παρουσιαστής δεν πρέπει να φροντίζει την επιχειρησιακή λογική της εφαρμογής, να γράφει και να διαβάζει από τη βάση δεδομένων, να εκτελεί υπολογισμούς κ.λπ. Αυτό είναι το καθήκον για τις κλάσεις από ένα επίπεδο, το οποίο ονομάζουμε μοντέλο. Για παράδειγμα, η κλάση ArticleRepository μπορεί να είναι υπεύθυνη για τη φόρτωση και την αποθήκευση άρθρων. Προκειμένου ο παρουσιαστής να τη χρησιμοποιήσει, περνάει χρησιμοποιώντας την έγχυση εξάρτησης (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('otherView').

Οι παράμετροι από το αίτημα περνούν στη μέθοδο. Είναι δυνατόν και συνιστάται να καθορίσετε τύπους για τις παραμέτρους, π.χ. actionShow(int $id, ?string $slug = null) – αν η παράμετρος id λείπει ή αν δεν είναι ακέραιος αριθμός, ο παρουσιαστής επιστρέφει σφάλμα 404 και τερματίζει τη λειτουργία.

handle<Signal>(args...)

Αυτή η μέθοδος επεξεργάζεται τα λεγόμενα σήματα, τα οποία θα συζητήσουμε στο κεφάλαιο για τα συστατικά. Προορίζεται κυρίως για συστατικά και επεξεργασία αιτήσεων 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()

Καλείται στο τέλος του κύκλου ζωής του παρουσιαστή.

Καλή συμβουλή πριν προχωρήσουμε. Όπως μπορείτε να δείτε, ο παρουσιαστής μπορεί να χειριστεί περισσότερες ενέργειες/προβολές, δηλαδή να έχει περισσότερες μεθόδους render<View>(). Αλλά συνιστούμε να σχεδιάζετε παρουσιαστές με μία ή όσο το δυνατόν λιγότερες ενέργειες.

Αποστολή μιας απάντησης

Η απάντηση του παρουσιαστή είναι συνήθως η απόδοση του προτύπου με τη σελίδα HTML, αλλά μπορεί επίσης να είναι η αποστολή ενός αρχείου, JSON ή ακόμα και η ανακατεύθυνση σε μια άλλη σελίδα.

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

Εάν δεν καλέσετε καμία από αυτές τις μεθόδους, ο παρουσιαστής θα προχωρήσει αυτόματα στην απόδοση του προτύπου. Γιατί; Λοιπόν, επειδή στο 99% των περιπτώσεων θέλουμε να σχεδιάσουμε ένα πρότυπο, οπότε ο παρουσιαστής θεωρεί αυτή τη συμπεριφορά ως προεπιλεγμένη και θέλει να διευκολύνει τη δουλειά μας.

Ο παρουσιαστής διαθέτει τη μέθοδο link(), η οποία χρησιμοποιείται για τη δημιουργία συνδέσμων URL προς άλλους παρουσιαστές. Η πρώτη παράμετρος είναι ο παρουσιαστής & η ενέργεια-στόχος, ακολουθούμενη από τα ορίσματα, τα οποία μπορούν να περάσουν ως πίνακας:

$url = $this->link('Product:show', $id);

$url = $this->link('Product:show', [$id, 'lang' => 'en']);

Στο πρότυπο δημιουργούμε συνδέσμους προς άλλους παρουσιαστές & ενέργειες ως εξής:

<a n:href="Product:show $id">product detail</a>

Απλά γράψτε το γνωστό ζεύγος Presenter:action αντί της πραγματικής διεύθυνσης URL και συμπεριλάβετε τυχόν παραμέτρους. Το κόλπο είναι το n:href, το οποίο λέει ότι αυτό το χαρακτηριστικό θα επεξεργαστεί από το Latte και θα δημιουργήσει ένα πραγματικό URL. Στη Nette, δεν χρειάζεται να σκέφτεστε καθόλου τις διευθύνσεις URL, παρά μόνο τους παρουσιαστές και τις ενέργειες.

Για περισσότερες πληροφορίες, ανατρέξτε στην ενότητα Δημιουργία συνδέσμων.

Ανακατεύθυνση

Οι μέθοδοι redirect() και forward() χρησιμοποιούνται για τη μετάβαση σε έναν άλλο παρουσιαστή, οι οποίες έχουν πολύ παρόμοια σύνταξη με τη μέθοδο link().

Η forward() μεταβαίνει στον νέο παρουσιαστή αμέσως χωρίς ανακατεύθυνση 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');

Η ανακατεύθυνση τερματίζει αμέσως τον κύκλο ζωής του παρουσιαστή πετώντας τη λεγόμενη εξαίρεση σιωπηλού τερματισμού Nette\Application\AbortException.

Πριν από την ανακατεύθυνση, είναι δυνατή η αποστολή ενός μηνύματος flash, μηνύματα που θα εμφανιστούν στο πρότυπο μετά την ανακατεύθυνση.

Μηνύματα flash

Πρόκειται για μηνύματα που συνήθως ενημερώνουν για το αποτέλεσμα μιας λειτουργίας. Ένα σημαντικό χαρακτηριστικό των μηνυμάτων flash είναι ότι είναι διαθέσιμα στο πρότυπο ακόμη και μετά την ανακατεύθυνση. Ακόμη και μετά την εμφάνισή τους, θα παραμείνουν ζωντανά για άλλα 30 δευτερόλεπτα – για παράδειγμα, σε περίπτωση που ο χρήστης θα ανανεώσει ακούσια τη σελίδα – το μήνυμα δεν θα χαθεί.

Απλά καλέστε τη μέθοδο flashMessage() και ο presenter θα φροντίσει να περάσει το μήνυμα στο πρότυπο. Το πρώτο όρισμα είναι το κείμενο του μηνύματος και το δεύτερο προαιρετικό όρισμα είναι ο τύπος του (σφάλμα, προειδοποίηση, πληροφορία κ.λπ.). Η μέθοδος flashMessage() επιστρέφει ένα instance του flash message, για να μας επιτρέψει να προσθέσουμε περισσότερες πληροφορίες.

$this->flashMessage('Item was removed.');
$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), η οποία αντιπροσωπεύει το σφάλμα HTTP 404:

public function renderShow(int $id): void
{
	$article = $this->articles->getById($id);
	if (!$article) {
		$this->error();
	}
	// ...
}

Ο κωδικός σφάλματος HTTP μπορεί να περάσει ως δεύτερη παράμετρος, η προεπιλογή είναι 404. Η μέθοδος λειτουργεί ρίχνοντας την εξαίρεση Nette\Application\BadRequestException, μετά την οποία η Application περνά τον έλεγχο στον παρουσιαστή του σφάλματος. Ο οποίος είναι ένας παρουσιαστής του οποίου η δουλειά είναι να εμφανίζει μια σελίδα που ενημερώνει για το σφάλμα. Ο error-preseter ορίζεται στη διαμόρφωση της εφαρμογής.

Αποστολή JSON

Παράδειγμα δράσης-μεθόδου που στέλνει δεδομένα σε μορφή JSON και εξέρχεται από τον παρουσιαστή:

public function actionData(): void
{
	$data = ['hello' => 'nette'];
	$this->sendJson($data);
}

Παράμετροι αίτησης

Ο παρουσιαστής, όπως και κάθε στοιχείο, λαμβάνει τις παραμέτρους του από την αίτηση HTTP. Οι τιμές τους μπορούν να ανακτηθούν χρησιμοποιώντας τη μέθοδο getParameter($name) ή getParameters(). Οι τιμές είναι συμβολοσειρές ή πίνακες συμβολοσειρών, ουσιαστικά ακατέργαστα δεδομένα που λαμβάνονται απευθείας από τη διεύθυνση URL.

Για μεγαλύτερη ευκολία, συνιστούμε να κάνετε τις παραμέτρους προσβάσιμες μέσω ιδιοτήτων. Απλά σχολιάστε τις με την εντολή #[Parameter] χαρακτηριστικό:

use Nette\Application\Attributes\Parameter;  // αυτή η γραμμή είναι σημαντική

class HomePresenter extends Nette\Application\UI\Presenter
{
	#[Parameter]
	public string $theme; // πρέπει να είναι δημόσια
}

Για τις ιδιότητες, προτείνουμε να προσδιορίσετε τον τύπο δεδομένων (π.χ. string). Στη συνέχεια, η Nette θα μετατρέψει αυτόματα την τιμή με βάση αυτόν. Οι τιμές των παραμέτρων μπορούν επίσης να επικυρωθούν.

Κατά τη δημιουργία ενός συνδέσμου, μπορείτε να ορίσετε απευθείας την τιμή για τις παραμέτρους:

<a n:href="Home:default theme: dark">click</a>

Εμμένουσες παράμετροι

Οι μόνιμες παράμετροι χρησιμοποιούνται για τη διατήρηση της κατάστασης μεταξύ διαφορετικών αιτήσεων. Η τιμή τους παραμένει η ίδια ακόμη και μετά το κλικ σε έναν σύνδεσμο. Σε αντίθεση με τα δεδομένα συνόδου, μεταβιβάζονται στη διεύθυνση URL. Αυτό γίνεται εντελώς αυτόματα, οπότε δεν χρειάζεται να τις δηλώσετε ρητά στο link() ή στο n:href.

Παράδειγμα χρήσης; Έχετε μια πολύγλωσση εφαρμογή. Η πραγματική γλώσσα είναι μια παράμετρος που πρέπει να αποτελεί μέρος του URL ανά πάσα στιγμή. Αλλά θα ήταν απίστευτα κουραστικό να τη συμπεριλάβετε σε κάθε σύνδεσμο. Οπότε την κάνετε μια μόνιμη παράμετρο με το όνομα lang και θα μεταφέρεται μόνη της. Ωραία!

Η δημιουργία μιας μόνιμης παραμέτρου είναι εξαιρετικά εύκολη στη Nette. Απλά δημιουργήστε μια δημόσια ιδιότητα και επισημάνετέ την με το χαρακτηριστικό: (προηγουμένως χρησιμοποιούνταν το /** @persistent */ )

use Nette\Application\Attributes\Persistent; // αυτή η γραμμή είναι σημαντική

class ProductPresenter extends Nette\Application\UI\Presenter
{
	#[Persistent]
	public string $lang; // πρέπει να είναι δημόσια
}

Εάν το $this->lang έχει μια τιμή όπως 'en', τότε οι σύνδεσμοι που δημιουργούνται με χρήση των link() ή n:href θα περιέχουν επίσης την παράμετρο lang=en. Και όταν ο σύνδεσμος πατηθεί, θα είναι και πάλι $this->lang = 'en'.

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

Οι μόνιμες παράμετροι μεταβιβάζονται μεταξύ όλων των ενεργειών ενός συγκεκριμένου παρουσιαστή από προεπιλογή. Για να τις περάσετε μεταξύ πολλαπλών παρουσιαστών, πρέπει να τις ορίσετε είτε:

  • σε έναν κοινό πρόγονο από τον οποίο κληρονομούν οι παρουσιαστές
  • στην ιδιότητα που χρησιμοποιούν οι παρουσιαστές:
trait LanguageAware
{
	#[Persistent]
	public string $lang;
}

class ProductPresenter extends Nette\Application\UI\Presenter
{
	use LanguageAware;
}

Μπορείτε να αλλάξετε την τιμή μιας μόνιμης παραμέτρου κατά τη δημιουργία ενός συνδέσμου:

<a n:href="Product:show $id, lang: cs">detail in Czech</a>

Ή μπορεί να επαναρυθμιστεί, δηλαδή να αφαιρεθεί από τη διεύθυνση URL. Τότε θα πάρει την προεπιλεγμένη τιμή της:

<a n:href="Product:show $id, lang: null">click</a>

Διαδραστικά στοιχεία

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

Πώς τοποθετούνται και στη συνέχεια χρησιμοποιούνται τα συστατικά στον παρουσιαστή; Αυτό εξηγείται στο κεφάλαιο Components. Θα μάθετε ακόμη και τι σχέση έχουν με το Χόλιγουντ.

Πού μπορώ να προμηθευτώ μερικά συστατικά; Στη σελίδα Componette μπορείτε να βρείτε ορισμένα συστατικά ανοικτού κώδικα και άλλα πρόσθετα για το Nette που έχουν κατασκευαστεί και διαμοιραστεί από την κοινότητα του Nette Framework.

Εμβαθύνοντας περισσότερο

Όσα έχουμε δείξει μέχρι στιγμής σε αυτό το κεφάλαιο μάλλον αρκούν. Οι επόμενες γραμμές απευθύνονται σε όσους ενδιαφέρονται για τους παρουσιαστές σε βάθος και θέλουν να μάθουν τα πάντα.

Επικύρωση των παραμέτρων

Οι τιμές των παραμέτρων αίτησης και των μόνιμων παραμέτρων που λαμβάνονται από τις διευθύνσεις 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();
		}
	}
}

Αποθήκευση και επαναφορά της αίτησης

Το αίτημα που χειρίζεται ο παρουσιαστής είναι ένα αντικείμενο Nette\Application\Request και επιστρέφεται από τη μέθοδο του παρουσιαστή getRequest().

Μπορείτε να αποθηκεύσετε την τρέχουσα αίτηση σε μια συνεδρία ή να την επαναφέρετε από τη συνεδρία και να αφήσετε τον παρουσιαστή να την εκτελέσει ξανά. Αυτό είναι χρήσιμο, για παράδειγμα, όταν ένας χρήστης συμπληρώνει μια φόρμα και η σύνδεσή του λήγει. Για να μην χαθούν δεδομένα, πριν από την ανακατεύθυνση στη σελίδα σύνδεσης, αποθηκεύουμε την τρέχουσα αίτηση στη σύνοδο χρησιμοποιώντας τη μέθοδο $reqId = $this->storeRequest(), η οποία επιστρέφει ένα αναγνωριστικό με τη μορφή σύντομης συμβολοσειράς και το περνάει ως παράμετρο στον παρουσιαστή σύνδεσης.

Μετά την είσοδο, καλούμε τη μέθοδο $this->restoreRequest($reqId), η οποία παραλαμβάνει το αίτημα από τη σύνοδο και το προωθεί σε αυτήν. Η μέθοδος επαληθεύει ότι το αίτημα δημιουργήθηκε από τον ίδιο χρήστη που τώρα έχει συνδεθεί είναι. Αν συνδεθεί άλλος χρήστης ή το κλειδί είναι άκυρο, δεν κάνει τίποτα και το πρόγραμμα συνεχίζει.

Δείτε το βιβλίο μαγειρικής Πώς να επιστρέψετε σε μια προηγούμενη σελίδα.

Κανονικοποίηση

Οι παρουσιαστές έχουν ένα πραγματικά σπουδαίο χαρακτηριστικό που βελτιώνει το SEO (βελτιστοποίηση της δυνατότητας αναζήτησης στο Διαδίκτυο). Αποτρέπουν αυτόματα την ύπαρξη διπλού περιεχομένου σε διαφορετικές διευθύνσεις URL. Εάν πολλαπλές διευθύνσεις URL οδηγούν σε έναν συγκεκριμένο προορισμό, π.χ. /index και /index?page=1, το πλαίσιο ορίζει μία από αυτές ως κύρια (κανονική) και ανακατευθύνει τις υπόλοιπες σε αυτήν χρησιμοποιώντας τον κωδικό HTTP 301. Χάρη σε αυτό, οι μηχανές αναζήτησης δεν ευρετηριάζουν τις σελίδες δύο φορές και δεν αποδυναμώνουν την κατάταξή τους.

Η διαδικασία αυτή ονομάζεται κανονικοποίηση. Η κανονική διεύθυνση URL είναι η διεύθυνση URL που δημιουργείται από τον δρομολογητή, συνήθως η πρώτη κατάλληλη διαδρομή στη συλλογή.

Η κανονικοποίηση είναι ενεργοποιημένη από προεπιλογή και μπορεί να απενεργοποιηθεί μέσω της διεύθυνσης $this->autoCanonicalize = false.

Η ανακατεύθυνση δεν πραγματοποιείται με ένα αίτημα AJAX ή POST, επειδή θα είχε ως αποτέλεσμα την απώλεια δεδομένων ή καμία προστιθέμενη αξία SEO.

Μπορείτε επίσης να επικαλεστείτε την κανονικοποίηση χειροκίνητα χρησιμοποιώντας τη μέθοδο canonicalize(), η οποία, όπως και η μέθοδος link(), λαμβάνει τον παρουσιαστή, τις ενέργειες και τις παραμέτρους ως ορίσματα. Δημιουργεί έναν σύνδεσμο και τον συγκρίνει με την τρέχουσα διεύθυνση 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(), οι οποίες καλούνται ως μέρος του κύκλου ζωής του παρουσιαστή, μπορούν να οριστούν και άλλες λειτουργίες που καλούνται αυτόματα. Ο παρουσιαστής ορίζει τα λεγόμενα συμβάντα και εσείς προσθέτετε τους χειριστές τους στους πίνακες $onStartup, $onRender και $onShutdown.

class ArticlePresenter extends Nette\Application\UI\Presenter
{
	public function __construct()
	{
		$this->onStartup[] = function () {
			// ...
		};
	}
}

Οι χειριστές στον πίνακα $onStartup καλούνται ακριβώς πριν από τη μέθοδο startup(), στη συνέχεια $onRender μεταξύ beforeRender() και render<View>() και τέλος $onShutdown λίγο πριν από την shutdown().

Απαντήσεις

Η απάντηση που επιστρέφεται από τον παρουσιαστή είναι ένα αντικείμενο που υλοποιεί τη διεπαφή Nette\Application\Response. Υπάρχει ένας αριθμός έτοιμων απαντήσεων:

Οι απαντήσεις αποστέλλονται με τη μέθοδο sendResponse():

use Nette\Application\Responses;

// Απλό κείμενο
$this->sendResponse(new Responses\TextResponse('Hello Nette!'));

// Αποστέλλει ένα αρχείο
$this->sendResponse(new Responses\FileResponse(__DIR__ . '/invoice.pdf', 'Invoice13.pdf'));

// Αποστέλλει ένα 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]

Το #[Requires] παρέχει προηγμένες επιλογές για τον περιορισμό της πρόσβασης στους παρουσιαστές και τις μεθόδους τους. Μπορεί να χρησιμοποιηθεί για τον προσδιορισμό μεθόδων HTTP, την απαίτηση αιτήσεων AJAX, τον περιορισμό της πρόσβασης στην ίδια προέλευση και τον περιορισμό της πρόσβασης μόνο στην προώθηση. Το χαρακτηριστικό μπορεί να εφαρμοστεί σε κλάσεις παρουσιαστών καθώς και σε μεμονωμένες μεθόδους όπως οι action<Action>(), render<View>(), handle<Signal>(), και createComponent<Name>().

Μπορείτε να καθορίσετε αυτούς τους περιορισμούς:

  • σε μεθόδους HTTP: #[Requires(methods: ['GET', 'POST'])]
  • AJAX: #[Requires(ajax: true)]
  • πρόσβαση μόνο από την ίδια προέλευση: #[Requires(sameOrigin: true)]
  • πρόσβαση μόνο μέσω προώθησης: #[Requires(forward: true)]
  • περιορισμοί σε συγκεκριμένες ενέργειες: #[Requires(actions: 'default')]

Για λεπτομέρειες, ανατρέξτε στην ενότητα Πώς να χρησιμοποιήσετε το Requires attribute.

Έλεγχος μεθόδου HTTP

Στη Nette, οι παρουσιαστές επαληθεύουν αυτόματα τη μέθοδο HTTP κάθε εισερχόμενης αίτησης κυρίως για λόγους ασφαλείας. Από προεπιλογή, επιτρέπονται οι μέθοδοι GET, POST, HEAD, PUT, DELETE, PATCH.

Εάν θέλετε να ενεργοποιήσετε επιπλέον μεθόδους, όπως η OPTIONS, μπορείτε να χρησιμοποιήσετε την εντολή #[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, πρέπει επίσης να τη χειριστείτε σωστά μέσα στον παρουσιαστή σας. Αυτή η μέθοδος χρησιμοποιείται συχνά ως το λεγόμενο preflight request, το οποίο οι φυλλομετρητές στέλνουν αυτόματα πριν από το πραγματικό αίτημα, όταν είναι απαραίτητο να καθοριστεί αν το αίτημα επιτρέπεται από την άποψη της πολιτικής CORS (Cross-Origin Resource Sharing). Εάν επιτρέψετε αυτή τη μέθοδο αλλά δεν υλοποιήσετε μια κατάλληλη απάντηση, μπορεί να οδηγήσει σε ασυνέπειες και πιθανά ζητήματα ασφάλειας.

Περαιτέρω ανάγνωση