Nette Documentation Preview

syntax
Pagină cu o singură postare
***************************

.[perex]
Să adăugăm încă o pagină la blogul nostru, care va afișa conținutul unei anumite postări de pe blog.


Trebuie să creăm o nouă metodă de redare, care va prelua o anumită postare de pe blog și o va transmite șablonului. A avea această vizualizare în `HomePresenter` nu este plăcută, deoarece este vorba despre un articol de blog, nu despre pagina de start. Așadar, să creăm o nouă clasă `PostPresenter` și să o plasăm în `app/UI/Post/`. Aceasta va avea nevoie de o conexiune la baza de date, așa că puneți din nou codul de *injecție în baza de date* acolo.

Clasa `PostPresenter` ar trebui să arate astfel:

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

Trebuie să setăm un namespace corect `App\UI\Post` pentru prezentatorul nostru. Depinde de [cartografierea prezentatorului |https://github.com/nette-examples/quickstart/blob/v4.0/config/common.neon#L6-L7].

Metoda `renderShow` are nevoie de un singur argument - ID-ul postului care urmează să fie afișat. Apoi, aceasta încarcă postul din baza de date și transmite rezultatul către șablon.

În șablonul `Home/default.latte` adăugăm un link către acțiunea `Post:show`:

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

Eticheta `{link}` generează o adresă URL care trimite la acțiunea `Post:show`. Această etichetă transmite, de asemenea, ID-ul mesajului ca argument.


Același lucru îl putem scrie pe scurt folosind n:attribute:

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

Atributul `n:href` este similar cu eticheta `{link}`.



Șablonul pentru acțiunea `Post:show` nu există încă. Putem deschide un link către această postare. [Tracy |tracy:] va afișa o eroare, de ce `Post/show.latte` nu există. Dacă vedeți orice alt raport de eroare, probabil că trebuie să activați mod_rewrite în serverul dvs. web.

Deci, vom crea `Post/show.latte` cu acest conținut:

```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>
```

Să ne uităm la părțile individuale.

Prima linie începe definiția unui *bloc cu nume* numit "conținut", pe care l-am văzut mai devreme. Acesta va fi afișat într-un *șablon de prezentare*. După cum puteți vedea, lipsește eticheta de sfârșit `{/block}`. Aceasta este opțională.

A doua linie oferă un backlink către lista de articole de pe blog, astfel încât utilizatorul să poată naviga fără probleme înainte și înapoi pe blogul nostru. Utilizăm din nou atributul `n:href`, prin urmare Nette se va ocupa de generarea URL-ului pentru noi. Legătura indică acțiunea `default` din prezentatorul `Home` (ați putea scrie și `n:href="Home:"`, deoarece acțiunea `default` poate fi omisă).

A treia linie formatează data și ora publicării cu un filtru, așa cum știm deja.

A patra linie afișează *titlul* postării de pe blog sub forma unui fișier `<h1>` titlu. Există o parte cu care poate nu sunteți familiarizați, și anume `n:block="title"`. Puteți ghici ce face? Dacă ați citit cu atenție părțile anterioare, am menționat `n: attributes`. Acesta este un alt exemplu. Este echivalent cu:

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

Cu alte cuvinte, se *redefinește* un bloc numit `title`. Blocul este definit în *șablonul de machetare* (`/app/UI/@layout.latte:11`) și, ca în cazul suprapunerii OOP, este suprapus aici. Prin urmare, pagina `<title>` va conține titlul articolului afișat. Am suprascris titlul paginii și tot ce aveam nevoie era `n:block="title"`. Grozav, nu?

A cincea și ultima linie a șablonului afișează conținutul complet al postării.


Verificarea ID-ului postului .[#toc-checking-post-id]
=====================================================

Ce se întâmplă dacă cineva modifică URL-ul și inserează `id` care nu există? Ar trebui să oferim utilizatorului o eroare frumoasă de tipul "pagina nu a fost găsită". Să actualizăm metoda de redare din `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;
}
```

Dacă postul nu poate fi găsit, apelarea `$this->error(...)` va afișa o pagină 404 cu un mesaj frumos și ușor de înțeles. Rețineți că, în mediul de dezvoltare (pe laptop), nu veți vedea pagina de eroare. În schimb, Tracy va afișa excepția cu detalii complete, ceea ce este destul de convenabil pentru dezvoltare. Puteți verifica ambele moduri, doar schimbați valoarea transmisă la `setDebugMode` în `Bootstrap.php`.


Rezumat .[#toc-summary]
=======================

Avem o bază de date cu postări pe blog și o aplicație web cu două vizualizări - prima afișează rezumatul tuturor postărilor recente, iar cea de-a doua afișează o anumită postare.

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

Pagină cu o singură postare

Să adăugăm încă o pagină la blogul nostru, care va afișa conținutul unei anumite postări de pe blog.

Trebuie să creăm o nouă metodă de redare, care va prelua o anumită postare de pe blog și o va transmite șablonului. A avea această vizualizare în HomePresenter nu este plăcută, deoarece este vorba despre un articol de blog, nu despre pagina de start. Așadar, să creăm o nouă clasă PostPresenter și să o plasăm în app/UI/Post/. Aceasta va avea nevoie de o conexiune la baza de date, așa că puneți din nou codul de injecție în baza de date acolo.

Clasa PostPresenter ar trebui să arate astfel:

<?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);
	}
}

Trebuie să setăm un namespace corect App\UI\Post pentru prezentatorul nostru. Depinde de cartografierea prezentatorului.

Metoda renderShow are nevoie de un singur argument – ID-ul postului care urmează să fie afișat. Apoi, aceasta încarcă postul din baza de date și transmite rezultatul către șablon.

În șablonul Home/default.latte adăugăm un link către acțiunea Post:show:

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

Eticheta {link} generează o adresă URL care trimite la acțiunea Post:show. Această etichetă transmite, de asemenea, ID-ul mesajului ca argument.

Același lucru îl putem scrie pe scurt folosind n:attribute:

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

Atributul n:href este similar cu eticheta {link}.

Șablonul pentru acțiunea Post:show nu există încă. Putem deschide un link către această postare. Tracy va afișa o eroare, de ce Post/show.latte nu există. Dacă vedeți orice alt raport de eroare, probabil că trebuie să activați mod_rewrite în serverul dvs. web.

Deci, vom crea Post/show.latte cu acest conținut:

{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>

Să ne uităm la părțile individuale.

Prima linie începe definiția unui bloc cu nume numit „conținut“, pe care l-am văzut mai devreme. Acesta va fi afișat într-un șablon de prezentare. După cum puteți vedea, lipsește eticheta de sfârșit {/block}. Aceasta este opțională.

A doua linie oferă un backlink către lista de articole de pe blog, astfel încât utilizatorul să poată naviga fără probleme înainte și înapoi pe blogul nostru. Utilizăm din nou atributul n:href, prin urmare Nette se va ocupa de generarea URL-ului pentru noi. Legătura indică acțiunea default din prezentatorul Home (ați putea scrie și n:href="Home:", deoarece acțiunea default poate fi omisă).

A treia linie formatează data și ora publicării cu un filtru, așa cum știm deja.

A patra linie afișează titlul postării de pe blog sub forma unui fișier <h1> titlu. Există o parte cu care poate nu sunteți familiarizați, și anume n:block="title". Puteți ghici ce face? Dacă ați citit cu atenție părțile anterioare, am menționat n: attributes. Acesta este un alt exemplu. Este echivalent cu:

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

Cu alte cuvinte, se redefinește un bloc numit title. Blocul este definit în șablonul de machetare (/app/UI/@layout.latte:11) și, ca în cazul suprapunerii OOP, este suprapus aici. Prin urmare, pagina <title> va conține titlul articolului afișat. Am suprascris titlul paginii și tot ce aveam nevoie era n:block="title". Grozav, nu?

A cincea și ultima linie a șablonului afișează conținutul complet al postării.

Verificarea ID-ului postului

Ce se întâmplă dacă cineva modifică URL-ul și inserează id care nu există? Ar trebui să oferim utilizatorului o eroare frumoasă de tipul „pagina nu a fost găsită“. Să actualizăm metoda de redare din 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;
}

Dacă postul nu poate fi găsit, apelarea $this->error(...) va afișa o pagină 404 cu un mesaj frumos și ușor de înțeles. Rețineți că, în mediul de dezvoltare (pe laptop), nu veți vedea pagina de eroare. În schimb, Tracy va afișa excepția cu detalii complete, ceea ce este destul de convenabil pentru dezvoltare. Puteți verifica ambele moduri, doar schimbați valoarea transmisă la setDebugMode în Bootstrap.php.

Rezumat

Avem o bază de date cu postări pe blog și o aplicație web cu două vizualizări – prima afișează rezumatul tuturor postărilor recente, iar cea de-a doua afișează o anumită postare.