Nette Documentation Preview

syntax
Σελίδα μονής δημοσίευσης
************************

.[perex]
Ας προσθέσουμε μια άλλη σελίδα στο ιστολόγιό μας, η οποία θα εμφανίζει το περιεχόμενο μιας συγκεκριμένης ανάρτησης ιστολογίου.


Πρέπει να δημιουργήσουμε μια νέα μέθοδο render, η οποία θα αντλεί μια συγκεκριμένη ανάρτηση του ιστολογίου και θα την περνάει στο πρότυπο. Το να έχουμε αυτή την προβολή στη διεύθυνση `HomePresenter` δεν είναι ωραίο, επειδή πρόκειται για μια ανάρτηση ιστολογίου και όχι για την αρχική σελίδα. Έτσι, ας δημιουργήσουμε μια νέα κλάση `PostPresenter` και ας την τοποθετήσουμε στο `app/UI/Post/`. Θα χρειαστεί μια σύνδεση με τη βάση δεδομένων, οπότε βάλτε και πάλι εκεί τον κώδικα *database injection*.

Το `PostPresenter` θα πρέπει να μοιάζει με αυτό:

```php .{file:app/UI/Post/PostPresenter.php}
<?php
namespace App\UI\Post;

use Nette;
use Nette\Application\UI\Form;

final class PostPresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private Nette\Database\Explorer $database,
	) {
	}

	public function renderShow(int $id): void
	{
		$this->template->post = $this->database
			->table('posts')
			->get($id);
	}
}
```

Πρέπει να ορίσουμε ένα σωστό namespaces `App\UI\Post` για τον παρουσιαστή μας. Εξαρτάται από την [αντιστοίχιση του παρουσιαστή |https://github.com/nette-examples/quickstart/blob/v4.0/config/common.neon#L6-L7].

Η μέθοδος `renderShow` απαιτεί ένα όρισμα - το ID της δημοσίευσης που θα εμφανιστεί. Στη συνέχεια, φορτώνει τη δημοσίευση από τη βάση δεδομένων και περνάει το αποτέλεσμα στο πρότυπο.

Στο πρότυπο `Home/default.latte` προσθέτουμε έναν σύνδεσμο προς την ενέργεια `Post:show`:

```latte .{file:app/UI/Home/default.latte}
...
<h2><a href="{link Post:show $post->id}">{$post->title}</a></h2>
...
```

Η ετικέτα `{link}` δημιουργεί διεύθυνση URL που παραπέμπει στη δράση `Post:show`. Αυτή η ετικέτα προωθεί επίσης το αναγνωριστικό της δημοσίευσης ως όρισμα.


Το ίδιο μπορούμε να γράψουμε σύντομα χρησιμοποιώντας το n:attribute:

```latte .{file:app/UI/Home/default.latte}
...
<h2><a n:href="Post:show $post->id">{$post->title}</a></h2>
...
```

Το χαρακτηριστικό `n:href` είναι παρόμοιο με την ετικέτα `{link}`.



Το πρότυπο για τη δράση `Post:show` δεν υπάρχει ακόμη. Μπορούμε να ανοίξουμε έναν σύνδεσμο προς αυτή τη θέση. Η [Tracy |tracy:] θα εμφανίσει ένα σφάλμα, γιατί το `Post/show.latte` δεν υπάρχει. Αν δείτε κάποια άλλη αναφορά σφάλματος, πιθανόν να πρέπει να ενεργοποιήσετε το mod_rewrite στον webserver σας.

Έτσι θα δημιουργήσουμε το `Post/show.latte` με αυτό το περιεχόμενο:

```latte .{file:app/UI/Post/show.latte}
{block content}

<p><a n:href="Home:default">← back to posts list</a></p>

<div class="date">{$post->created_at|date:'F j, Y'}</div>

<h1 n:block="title">{$post->title}</h1>

<div class="post">{$post->content}</div>
```

Ας ρίξουμε μια ματιά στα επιμέρους μέρη.

Η πρώτη γραμμή ξεκινά τον ορισμό ενός *ονομαζόμενου μπλοκ* που ονομάζεται "content" και το οποίο είδαμε νωρίτερα. Θα εμφανιστεί σε ένα *πρότυπο διάταξης*. Όπως μπορείτε να δείτε, λείπει η ετικέτα τέλους `{/block}`. Είναι προαιρετική.

Η δεύτερη γραμμή παρέχει έναν σύνδεσμο προς τη λίστα των αναρτήσεων του ιστολογίου, ώστε ο χρήστης να μπορεί να πλοηγείται ομαλά μπρος-πίσω στο ιστολόγιό μας. Χρησιμοποιούμε και πάλι το χαρακτηριστικό `n:href`, επομένως η Nette θα φροντίσει να δημιουργήσει τη διεύθυνση URL για εμάς. Ο σύνδεσμος δείχνει στην ενέργεια `default` του παρουσιαστή `Home` (θα μπορούσατε επίσης να γράψετε `n:href="Home:"`, καθώς η ενέργεια `default` μπορεί να παραλειφθεί).

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

Η τέταρτη γραμμή εμφανίζει τον *τίτλο* της δημοσίευσης στο ιστολόγιο ως `<h1>` επικεφαλίδα. Υπάρχει ένα μέρος που ίσως δεν γνωρίζετε, και αυτό είναι το `n:block="title"`. Μπορείτε να μαντέψετε τι κάνει; Αν έχετε διαβάσει προσεκτικά τα προηγούμενα μέρη, έχουμε αναφέρει το `n: attributes`. Αυτό είναι ένα άλλο παράδειγμα. Είναι ισοδύναμο με:

```latte
{block title}<h1>{$post->title}</h1>{/block}
```

Με απλά λόγια, * επαναπροσδιορίζει* ένα μπλοκ που ονομάζεται `title`. Το μπλοκ ορίζεται στο *πρότυπο διάταξης* (`/app/UI/@layout.latte:11`) και όπως συμβαίνει με την παράκαμψη OOP, παρακάμπτεται εδώ. Επομένως, το αρχείο της σελίδας `<title>` θα περιέχει τον τίτλο της εμφανιζόμενης ανάρτησης. Έχουμε παρακάμψει τον τίτλο της σελίδας και το μόνο που χρειαζόμασταν ήταν το `n:block="title"`. Υπέροχα, ε;

Η πέμπτη και τελευταία γραμμή του προτύπου εμφανίζει το πλήρες περιεχόμενο της ανάρτησής σας.


Έλεγχος του αναγνωριστικού της δημοσίευσης .[#toc-checking-post-id]
===================================================================

Τι συμβαίνει αν κάποιος αλλάξει το URL και εισάγει το `id` που δεν υπάρχει? Θα πρέπει να παρέχουμε στο χρήστη ένα ωραίο σφάλμα "η σελίδα δεν βρέθηκε". Ας ενημερώσουμε τη μέθοδο render στο `PostPresenter`:

```php .{file:app/UI/Post/PostPresenter.php}
public function renderShow(int $id): void
{
	$post = $this->database
		->table('posts')
		->get($id);
	if (!$post) {
		$this->error('Post not found');
	}

	$this->template->post = $post;
}
```

Εάν η δημοσίευση δεν μπορεί να βρεθεί, η κλήση του `$this->error(...)` θα εμφανίζει μια σελίδα 404 με ένα ωραίο και κατανοητό μήνυμα. Σημειώστε, ότι στο περιβάλλον ανάπτυξης (στον φορητό σας υπολογιστή), δεν θα δείτε τη σελίδα σφάλματος. Αντ' αυτού, το Tracy θα εμφανίσει την εξαίρεση με πλήρεις λεπτομέρειες, κάτι που είναι αρκετά βολικό για την ανάπτυξη. Μπορείτε να ελέγξετε και τις δύο λειτουργίες, απλά αλλάξτε την τιμή που περνάτε στο `setDebugMode` στο `Bootstrap.php`.


Περίληψη .[#toc-summary]
========================

Έχουμε μια βάση δεδομένων με αναρτήσεις ιστολογίου και μια εφαρμογή ιστού με δύο προβολές - η πρώτη εμφανίζει τη σύνοψη όλων των πρόσφατων αναρτήσεων και η δεύτερη εμφανίζει μια συγκεκριμένη ανάρτηση.

{{priority: -1}}
{{sitename: Nette Quickstart}}

Σελίδα μονής δημοσίευσης

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

Πρέπει να δημιουργήσουμε μια νέα μέθοδο render, η οποία θα αντλεί μια συγκεκριμένη ανάρτηση του ιστολογίου και θα την περνάει στο πρότυπο. Το να έχουμε αυτή την προβολή στη διεύθυνση HomePresenter δεν είναι ωραίο, επειδή πρόκειται για μια ανάρτηση ιστολογίου και όχι για την αρχική σελίδα. Έτσι, ας δημιουργήσουμε μια νέα κλάση PostPresenter και ας την τοποθετήσουμε στο app/UI/Post/. Θα χρειαστεί μια σύνδεση με τη βάση δεδομένων, οπότε βάλτε και πάλι εκεί τον κώδικα database injection.

Το PostPresenter θα πρέπει να μοιάζει με αυτό:

<?php
namespace App\UI\Post;

use Nette;
use Nette\Application\UI\Form;

final class PostPresenter extends Nette\Application\UI\Presenter
{
	public function __construct(
		private Nette\Database\Explorer $database,
	) {
	}

	public function renderShow(int $id): void
	{
		$this->template->post = $this->database
			->table('posts')
			->get($id);
	}
}

Πρέπει να ορίσουμε ένα σωστό namespaces App\UI\Post για τον παρουσιαστή μας. Εξαρτάται από την αντιστοίχιση του παρουσιαστή.

Η μέθοδος renderShow απαιτεί ένα όρισμα – το ID της δημοσίευσης που θα εμφανιστεί. Στη συνέχεια, φορτώνει τη δημοσίευση από τη βάση δεδομένων και περνάει το αποτέλεσμα στο πρότυπο.

Στο πρότυπο Home/default.latte προσθέτουμε έναν σύνδεσμο προς την ενέργεια Post:show:

...
<h2><a href="{link Post:show $post->id}">{$post->title}</a></h2>
...

Η ετικέτα {link} δημιουργεί διεύθυνση URL που παραπέμπει στη δράση Post:show. Αυτή η ετικέτα προωθεί επίσης το αναγνωριστικό της δημοσίευσης ως όρισμα.

Το ίδιο μπορούμε να γράψουμε σύντομα χρησιμοποιώντας το n:attribute:

...
<h2><a n:href="Post:show $post->id">{$post->title}</a></h2>
...

Το χαρακτηριστικό n:href είναι παρόμοιο με την ετικέτα {link}.

Το πρότυπο για τη δράση Post:show δεν υπάρχει ακόμη. Μπορούμε να ανοίξουμε έναν σύνδεσμο προς αυτή τη θέση. Η Tracy θα εμφανίσει ένα σφάλμα, γιατί το Post/show.latte δεν υπάρχει. Αν δείτε κάποια άλλη αναφορά σφάλματος, πιθανόν να πρέπει να ενεργοποιήσετε το mod_rewrite στον webserver σας.

Έτσι θα δημιουργήσουμε το Post/show.latte με αυτό το περιεχόμενο:

{block content}

<p><a n:href="Home:default">← back to posts list</a></p>

<div class="date">{$post->created_at|date:'F j, Y'}</div>

<h1 n:block="title">{$post->title}</h1>

<div class="post">{$post->content}</div>

Ας ρίξουμε μια ματιά στα επιμέρους μέρη.

Η πρώτη γραμμή ξεκινά τον ορισμό ενός ονομαζόμενου μπλοκ που ονομάζεται „content“ και το οποίο είδαμε νωρίτερα. Θα εμφανιστεί σε ένα πρότυπο διάταξης. Όπως μπορείτε να δείτε, λείπει η ετικέτα τέλους {/block}. Είναι προαιρετική.

Η δεύτερη γραμμή παρέχει έναν σύνδεσμο προς τη λίστα των αναρτήσεων του ιστολογίου, ώστε ο χρήστης να μπορεί να πλοηγείται ομαλά μπρος-πίσω στο ιστολόγιό μας. Χρησιμοποιούμε και πάλι το χαρακτηριστικό n:href, επομένως η Nette θα φροντίσει να δημιουργήσει τη διεύθυνση URL για εμάς. Ο σύνδεσμος δείχνει στην ενέργεια default του παρουσιαστή Home (θα μπορούσατε επίσης να γράψετε n:href="Home:", καθώς η ενέργεια default μπορεί να παραλειφθεί).

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

Η τέταρτη γραμμή εμφανίζει τον τίτλο της δημοσίευσης στο ιστολόγιο ως <h1> επικεφαλίδα. Υπάρχει ένα μέρος που ίσως δεν γνωρίζετε, και αυτό είναι το n:block="title". Μπορείτε να μαντέψετε τι κάνει; Αν έχετε διαβάσει προσεκτικά τα προηγούμενα μέρη, έχουμε αναφέρει το n: attributes. Αυτό είναι ένα άλλο παράδειγμα. Είναι ισοδύναμο με:

{block title}<h1>{$post->title}</h1>{/block}

Με απλά λόγια, * επαναπροσδιορίζει* ένα μπλοκ που ονομάζεται title. Το μπλοκ ορίζεται στο πρότυπο διάταξης (/app/UI/@layout.latte:11) και όπως συμβαίνει με την παράκαμψη OOP, παρακάμπτεται εδώ. Επομένως, το αρχείο της σελίδας <title> θα περιέχει τον τίτλο της εμφανιζόμενης ανάρτησης. Έχουμε παρακάμψει τον τίτλο της σελίδας και το μόνο που χρειαζόμασταν ήταν το n:block="title". Υπέροχα, ε;

Η πέμπτη και τελευταία γραμμή του προτύπου εμφανίζει το πλήρες περιεχόμενο της ανάρτησής σας.

Έλεγχος του αναγνωριστικού της δημοσίευσης

Τι συμβαίνει αν κάποιος αλλάξει το URL και εισάγει το id που δεν υπάρχει? Θα πρέπει να παρέχουμε στο χρήστη ένα ωραίο σφάλμα „η σελίδα δεν βρέθηκε“. Ας ενημερώσουμε τη μέθοδο render στο PostPresenter:

public function renderShow(int $id): void
{
	$post = $this->database
		->table('posts')
		->get($id);
	if (!$post) {
		$this->error('Post not found');
	}

	$this->template->post = $post;
}

Εάν η δημοσίευση δεν μπορεί να βρεθεί, η κλήση του $this->error(...) θα εμφανίζει μια σελίδα 404 με ένα ωραίο και κατανοητό μήνυμα. Σημειώστε, ότι στο περιβάλλον ανάπτυξης (στον φορητό σας υπολογιστή), δεν θα δείτε τη σελίδα σφάλματος. Αντ' αυτού, το Tracy θα εμφανίσει την εξαίρεση με πλήρεις λεπτομέρειες, κάτι που είναι αρκετά βολικό για την ανάπτυξη. Μπορείτε να ελέγξετε και τις δύο λειτουργίες, απλά αλλάξτε την τιμή που περνάτε στο setDebugMode στο Bootstrap.php.

Περίληψη

Έχουμε μια βάση δεδομένων με αναρτήσεις ιστολογίου και μια εφαρμογή ιστού με δύο προβολές – η πρώτη εμφανίζει τη σύνοψη όλων των πρόσφατων αναρτήσεων και η δεύτερη εμφανίζει μια συγκεκριμένη ανάρτηση.