Nette Documentation Preview

syntax
Stránka s příspěvkem
********************

.[perex]
Nyní si vytvoříme další stránku blogu, která bude zobrazovat jeden konkrétní příspěvek.


Musíme si vytvořit novou render metodu, která získá jeden konkrétní článek a předá jej do šablony. Mít tuto metodu v `HomePresenter` není moc hezké, protože se bavíme o článku a ne úvodní stránce. Vytvořme si tedy `PostPresenter` v `app/UI/Post/`. Tento presenter se také potřebuje připojit k databázi, takže zde opět napíšeme konstruktor, který bude vyžadovat databázové připojení.

`PostPresenter` by mohl tedy vypadat takto:

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

Nesmíme zapomenout uvést správný namespace `App\UI\Post`, který podléhá nastavení [mapování presenterů |https://github.com/nette-examples/quickstart/blob/v4.0/config/common.neon#L6-L7].

Metoda `renderShow` vyžaduje jeden argument - ID jednoho konkrétního článku, který má být zobrazen. Poté tento článek načte z databáze a předá ho do šablony.

Do šablony `Home/default.latte` vložíme odkaz na akci `Post:show`.

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

Značka `{link}` generuje URL adresu, která směřuje na akci `Post:show`. Také předává ID příspěvku jako argument.


Totéž můžeme zapsat zkráceně pomocí n:atributu:

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

Atribut `n:href` je obdobou tagu `{link}`.



Pro akci `Post:show` však ještě neexistuje šablona. Můžeme si vyzkoušet otevřít odkaz na tento příspěvek. [Tracy |tracy:] zobrazí error, protože šablona `Post/show.latte` ještě neexistuje. Pokud vidíte jiné chybové hlášení, tak pravděpodobně budete muset zapnout `mod_rewrite` na webserveru.

Vytvoříme tedy šablonu `Post/show.latte` s tímto obsahem:

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

<p><a n:href="Home:default">← zpět na výpis příspěvků</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>
```

Nyní si projdeme jednotlivé části šablony.

První řádka začíná definici bloku s názvem "content" stejně jako tomu bylo na úvodní stránce. Tento blok bude opět zobrazen v hlavní šabloně. Jak vidíte, chybí koncová značka `{/block}`. Ta je totiž nepovinná.

Na další řádce je odkaz zpět na výpis článků blogu, takže se uživatel může jednoduše pohybovat mezi výpisem článků a jedním konkrétním. Protože používáme atribut `n:href`, tak se Nette samo postará o generování odkazů. Odkaz směřuje na akci `default` presenteru `Home` (můžeme napsat také `n:href="Home:"`, protože akce s názvem `default` může být vynechána, doplní se automaticky).

Třetí řádka formátuje výpis data pomocí filtru, který už známe.

Čtvrtá řádka zobrazuje *titulek* blogu v HTML tagu `<h1>`. Tento tag obsahuje atribut, který možná neznáte (`n:block="title"`). Uhodnete, co dělá? Pokud jste četli předchozí část pozorně, tak již víte, že se jedná o `n:atribut`. Toto je jejich další příklad, který je ekvivalentní k:

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

Jednoduše řečeno, tento blok předefinovává blok s názvem `title`. Tento blok je již definován v hlavní *layout* šabloně (`/app/UI/@layout.latte:11`) a tak jako u překrývání metod v OOP, úplně stejně se tento blok v hlavní šabloně překryje. Takže `<title>` stránky nyní obsahuje titulek zobrazeného příspěvku a stačilo nám k tomu použít pouze jednoho jednoduchého atributu `n:block="title"`. Skvělé, že?

Pátá a poslední řádka šablony zobrazuje celý obsah jednoho konkrétního příspěvku.


Kontrola ID příspěvku
=====================

Co se stane, když někdo změní ID v URL a vloží nějaké neexistující `id`? Měli bychom uživateli nabídnout pěknou chybu typu "stránka nebyla nalezena". Pozměníme tedy trošku render metodu v `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('Stránka nebyla nalezena');
	}

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

Pokud nemůže být příspěvek nalezen, zavoláním `$this->error(...)` zobrazíme chybovou stránku 404 se srozumitelnou hláškou. Pozor na to, že ve vývojářském módu (localhost) tuto chybovou stránku neuvidíte. Místo toho se ukáže Tracy s detaily o výjimce, což je docela výhodné pro vývoj. Chceme-li si nechat zobrazit oba módy, stačí pouze změnit argument metody `setDebugMode` v souboru `Bootstrap.php`.


Shrnutí
=======

Máme databázi s příspěvky a webovou aplikaci, která má dva pohledy - první zobrazuje přehled všech příspěvků a druhá zobrazuje jeden konkrétní příspěvek.

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

Stránka s příspěvkem

Nyní si vytvoříme další stránku blogu, která bude zobrazovat jeden konkrétní příspěvek.

Musíme si vytvořit novou render metodu, která získá jeden konkrétní článek a předá jej do šablony. Mít tuto metodu v HomePresenter není moc hezké, protože se bavíme o článku a ne úvodní stránce. Vytvořme si tedy PostPresenter v app/UI/Post/. Tento presenter se také potřebuje připojit k databázi, takže zde opět napíšeme konstruktor, který bude vyžadovat databázové připojení.

PostPresenter by mohl tedy vypadat takto:

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

Nesmíme zapomenout uvést správný namespace App\UI\Post, který podléhá nastavení mapování presenterů.

Metoda renderShow vyžaduje jeden argument – ID jednoho konkrétního článku, který má být zobrazen. Poté tento článek načte z databáze a předá ho do šablony.

Do šablony Home/default.latte vložíme odkaz na akci Post:show.

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

Značka {link} generuje URL adresu, která směřuje na akci Post:show. Také předává ID příspěvku jako argument.

Totéž můžeme zapsat zkráceně pomocí n:atributu:

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

Atribut n:href je obdobou tagu {link}.

Pro akci Post:show však ještě neexistuje šablona. Můžeme si vyzkoušet otevřít odkaz na tento příspěvek. Tracy zobrazí error, protože šablona Post/show.latte ještě neexistuje. Pokud vidíte jiné chybové hlášení, tak pravděpodobně budete muset zapnout mod_rewrite na webserveru.

Vytvoříme tedy šablonu Post/show.latte s tímto obsahem:

{block content}

<p><a n:href="Home:default">← zpět na výpis příspěvků</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>

Nyní si projdeme jednotlivé části šablony.

První řádka začíná definici bloku s názvem „content“ stejně jako tomu bylo na úvodní stránce. Tento blok bude opět zobrazen v hlavní šabloně. Jak vidíte, chybí koncová značka {/block}. Ta je totiž nepovinná.

Na další řádce je odkaz zpět na výpis článků blogu, takže se uživatel může jednoduše pohybovat mezi výpisem článků a jedním konkrétním. Protože používáme atribut n:href, tak se Nette samo postará o generování odkazů. Odkaz směřuje na akci default presenteru Home (můžeme napsat také n:href="Home:", protože akce s názvem default může být vynechána, doplní se automaticky).

Třetí řádka formátuje výpis data pomocí filtru, který už známe.

Čtvrtá řádka zobrazuje titulek blogu v HTML tagu <h1>. Tento tag obsahuje atribut, který možná neznáte (n:block="title"). Uhodnete, co dělá? Pokud jste četli předchozí část pozorně, tak již víte, že se jedná o n:atribut. Toto je jejich další příklad, který je ekvivalentní k:

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

Jednoduše řečeno, tento blok předefinovává blok s názvem title. Tento blok je již definován v hlavní layout šabloně (/app/UI/@layout.latte:11) a tak jako u překrývání metod v OOP, úplně stejně se tento blok v hlavní šabloně překryje. Takže <title> stránky nyní obsahuje titulek zobrazeného příspěvku a stačilo nám k tomu použít pouze jednoho jednoduchého atributu n:block="title". Skvělé, že?

Pátá a poslední řádka šablony zobrazuje celý obsah jednoho konkrétního příspěvku.

Kontrola ID příspěvku

Co se stane, když někdo změní ID v URL a vloží nějaké neexistující id? Měli bychom uživateli nabídnout pěknou chybu typu „stránka nebyla nalezena“. Pozměníme tedy trošku render metodu v PostPresenter:

public function renderShow(int $id): void
{
	$post = $this->database
		->table('posts')
		->get($id);
	if (!$post) {
		$this->error('Stránka nebyla nalezena');
	}

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

Pokud nemůže být příspěvek nalezen, zavoláním $this->error(...) zobrazíme chybovou stránku 404 se srozumitelnou hláškou. Pozor na to, že ve vývojářském módu (localhost) tuto chybovou stránku neuvidíte. Místo toho se ukáže Tracy s detaily o výjimce, což je docela výhodné pro vývoj. Chceme-li si nechat zobrazit oba módy, stačí pouze změnit argument metody setDebugMode v souboru Bootstrap.php.

Shrnutí

Máme databázi s příspěvky a webovou aplikaci, která má dva pohledy – první zobrazuje přehled všech příspěvků a druhá zobrazuje jeden konkrétní příspěvek.