Nette Documentation Preview

syntax
Strona z wkładem
****************

.[perex]
Teraz stwórzmy kolejną stronę bloga, która będzie wyświetlała jeden konkretny post.


Musimy stworzyć nową metodę renderującą, która pobiera jeden konkretny artykuł i przekazuje go do szablonu. Posiadanie tej metody w `HomePresenter` nie jest zbyt miłe, ponieważ mówimy o artykule, a nie o stronie głównej. Stwórzmy więc `PostPresenter` w `app/UI/Post/`. Ten prezenter również musi połączyć się z bazą danych, więc tutaj znowu napiszemy konstruktor, który będzie wymagał połączenia z bazą danych.

`PostPresenter` Mogłoby to wtedy wyglądać tak:

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

Nie zapomnij uwzględnić prawidłowej przestrzeni nazw `App\UI\Post`, która podlega ustawieniom [mapowania prezentera |https://github.com/nette-examples/quickstart/blob/v4.0/config/common.neon#L6-L7].

Metoda `renderShow` wymaga jednego argumentu - ID jednego konkretnego artykułu, który ma być wyświetlony. Następnie pobiera ten artykuł z bazy danych i przekazuje go do szablonu.

W szablonie `Home/default.latte` wstawiamy link do akcji `Post:show`.

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

Znacznik `{link}` generuje adres URL, który wskazuje na akcję `Post:show`. Przekazuje również identyfikator postu jako argument.


To samo można napisać w skrócie, używając atrybutu n::

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

Atrybut `n:href` jest podobny do znacznika `{link}`.



Nie ma jednak jeszcze szablonu dla akcji `Post:show`. Możemy spróbować otworzyć link do tego postu. [Tracy |tracy:] wyświetla błąd, ponieważ szablon `Post/show.latte` jeszcze nie istnieje. Jeśli widzisz inny komunikat o błędzie, prawdopodobnie musisz włączyć `mod_rewrite` na serwerze internetowym.

Stwórzmy więc szablon `Post/show.latte` z tą treścią:

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

Teraz przejdźmy przez poszczególne części szablonu.

Pierwsza linia zaczyna się od zdefiniowania bloku o nazwie "content" tak jak to było na stronie głównej. Ten blok będzie ponownie wyświetlany w głównym szablonie. Jak widać, brakuje znacznika końcowego `{/block}`. Jest to opcja.

W kolejnym wierszu znajduje się link zwrotny do listy artykułów na blogu, dzięki czemu użytkownik może łatwo poruszać się między listą artykułów a jednym konkretnym. Ponieważ używamy atrybutu `n:href`, Nette samo zajmuje się generowaniem linków. Link wskazuje na działanie prezentera `default` `Home` (możemy też napisać `n:href="Home:"`, bo działanie o nazwie `default` można pominąć, zostanie wypełnione automatycznie).

Trzecia linia formatuje datę zrzutu za pomocą znanego nam już filtra.

Czwarta linia wyświetla *tytuł* wpisu na blogu w znaczniku HTML `<h1>`. Ten znacznik zawiera atrybut, którego być może nie znasz (`n:block="title"`). Czy zgadniesz, co to robi? Jeśli uważnie przeczytałeś poprzedni rozdział, wiesz już, że jest to `n:atribut`. To jest ich kolejny przykład, który jest równoważny:

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

Po prostu ten blok predefiniuje blok o nazwie `title`. Blok ten jest już zdefiniowany w głównym szablonie *layout* (`/app/UI/@layout.latte:11`) i podobnie jak w przypadku nakładek na metody w OOP, nakłada ten blok w szablonie głównym w dokładnie taki sam sposób. Więc `<title>` strona zawiera teraz nagłówek wyświetlanego postu, a wszystko co musieliśmy zrobić to użyć jednego prostego atrybutu `n:block="title"`. Świetnie, prawda?

Piąta i ostatnia linia szablonu wyświetla całą zawartość jednego konkretnego postu.


Sprawdź identyfikator postu .[#toc-checking-post-id]
====================================================

Co się stanie jeśli ktoś zmieni ID w URL i umieści jakiś nieistniejący `postId`? Powinniśmy zaoferować użytkownikowi ładny błąd "page not found". Zmodyfikujmy więc nieco metodę render w `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;
}
```

Jeśli nie można znaleźć postu, wywołanie `$this->error(...)` wyświetli stronę błędu 404 z jasnym komunikatem. Zauważ, że w trybie deweloperskim (localhost) nie zobaczysz tej strony błędu. Zamiast tego pokaże Tracy ze szczegółami wyjątku, co jest dość wygodne dla rozwoju. Jeśli chcemy mieć wyświetlane oba tryby, wystarczy zmienić argument metody `setDebugMode` w pliku `Bootstrap.php`.


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

Mamy bazę danych z postami i aplikację internetową, która ma dwa widoki - pierwszy pokazuje przegląd wszystkich postów, a drugi pokazuje jeden konkretny post.

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

Strona z wkładem

Teraz stwórzmy kolejną stronę bloga, która będzie wyświetlała jeden konkretny post.

Musimy stworzyć nową metodę renderującą, która pobiera jeden konkretny artykuł i przekazuje go do szablonu. Posiadanie tej metody w HomePresenter nie jest zbyt miłe, ponieważ mówimy o artykule, a nie o stronie głównej. Stwórzmy więc PostPresenter w app/UI/Post/. Ten prezenter również musi połączyć się z bazą danych, więc tutaj znowu napiszemy konstruktor, który będzie wymagał połączenia z bazą danych.

PostPresenter Mogłoby to wtedy wyglądać tak:

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

Nie zapomnij uwzględnić prawidłowej przestrzeni nazw App\UI\Post, która podlega ustawieniom mapowania prezentera.

Metoda renderShow wymaga jednego argumentu – ID jednego konkretnego artykułu, który ma być wyświetlony. Następnie pobiera ten artykuł z bazy danych i przekazuje go do szablonu.

W szablonie Home/default.latte wstawiamy link do akcji Post:show.

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

Znacznik {link} generuje adres URL, który wskazuje na akcję Post:show. Przekazuje również identyfikator postu jako argument.

To samo można napisać w skrócie, używając atrybutu n::

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

Atrybut n:href jest podobny do znacznika {link}.

Nie ma jednak jeszcze szablonu dla akcji Post:show. Możemy spróbować otworzyć link do tego postu. Tracy wyświetla błąd, ponieważ szablon Post/show.latte jeszcze nie istnieje. Jeśli widzisz inny komunikat o błędzie, prawdopodobnie musisz włączyć mod_rewrite na serwerze internetowym.

Stwórzmy więc szablon Post/show.latte z tą treścią:

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

Teraz przejdźmy przez poszczególne części szablonu.

Pierwsza linia zaczyna się od zdefiniowania bloku o nazwie „content“ tak jak to było na stronie głównej. Ten blok będzie ponownie wyświetlany w głównym szablonie. Jak widać, brakuje znacznika końcowego {/block}. Jest to opcja.

W kolejnym wierszu znajduje się link zwrotny do listy artykułów na blogu, dzięki czemu użytkownik może łatwo poruszać się między listą artykułów a jednym konkretnym. Ponieważ używamy atrybutu n:href, Nette samo zajmuje się generowaniem linków. Link wskazuje na działanie prezentera default Home (możemy też napisać n:href="Home:", bo działanie o nazwie default można pominąć, zostanie wypełnione automatycznie).

Trzecia linia formatuje datę zrzutu za pomocą znanego nam już filtra.

Czwarta linia wyświetla tytuł wpisu na blogu w znaczniku HTML <h1>. Ten znacznik zawiera atrybut, którego być może nie znasz (n:block="title"). Czy zgadniesz, co to robi? Jeśli uważnie przeczytałeś poprzedni rozdział, wiesz już, że jest to n:atribut. To jest ich kolejny przykład, który jest równoważny:

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

Po prostu ten blok predefiniuje blok o nazwie title. Blok ten jest już zdefiniowany w głównym szablonie layout (/app/UI/@layout.latte:11) i podobnie jak w przypadku nakładek na metody w OOP, nakłada ten blok w szablonie głównym w dokładnie taki sam sposób. Więc <title> strona zawiera teraz nagłówek wyświetlanego postu, a wszystko co musieliśmy zrobić to użyć jednego prostego atrybutu n:block="title". Świetnie, prawda?

Piąta i ostatnia linia szablonu wyświetla całą zawartość jednego konkretnego postu.

Sprawdź identyfikator postu

Co się stanie jeśli ktoś zmieni ID w URL i umieści jakiś nieistniejący postId? Powinniśmy zaoferować użytkownikowi ładny błąd „page not found“. Zmodyfikujmy więc nieco metodę render w 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;
}

Jeśli nie można znaleźć postu, wywołanie $this->error(...) wyświetli stronę błędu 404 z jasnym komunikatem. Zauważ, że w trybie deweloperskim (localhost) nie zobaczysz tej strony błędu. Zamiast tego pokaże Tracy ze szczegółami wyjątku, co jest dość wygodne dla rozwoju. Jeśli chcemy mieć wyświetlane oba tryby, wystarczy zmienić argument metody setDebugMode w pliku Bootstrap.php.

Streszczenie.

Mamy bazę danych z postami i aplikację internetową, która ma dwa widoki – pierwszy pokazuje przegląd wszystkich postów, a drugi pokazuje jeden konkretny post.