Nette Documentation Preview

syntax
Úvodní stránka blogu
********************

.[perex]
Nyní si vytvoříme úvodní stránku zobrazující poslední příspěvky.


Než začneme, je zapotřebí znát alespoň základy návrhového vzoru Model-View-Presenter (podobné jako MVC((Model-View-Controller))):

- **Model** - vrstva pracující s daty. Je kompletně oddělena od zbytku aplikace. Komunikuje pouze s presenterem.

- **View** - front-end vrstva. Vykresluje požadovaná data pomocí šablon a zobrazuje je uživateli.

- **Presenter** (nebo Controller) - propojovací vrstva. Presenter propojuje Model a View. Zpracovává požadavky, dotazuje se Modelu na data a vrací je zpět do View.


V případě jednoduchých aplikací, jako bude náš blog, budou celou modelovou vrstvu tvořit jenom dotazy do databáze - na to zatím žádný extra kód nepotřebujeme. Pro začátek si tedy vytvoříme jenom presentery a šablony. V Nette má každý presenter své vlastní šablony, takže je budeme vytvářet zároveň.


Vytvoření databáze pomocí Admineru
==================================

Pro ukládání dat použijeme MySQL databázi, protože je nejvíce rozšířená mezi programátory webových aplikací. Pokud ji však použít nechcete, klidně si zvolte databázi dle vlastního uvážení.

Nyní si připravíme databázovou strukturu, kde budou uloženy články našeho blogu. Začneme velmi jednoduše - vytvoříme si pouze jednu tabulku pro příspěvky.

Pro vytvoření databáze si můžeme stáhnout [Adminer |https://www.adminer.org], nebo jiný váš oblíbený nástroj pro správu databází.


Otevřeme si Adminer a vytvoříme novou databázi s názvem `quickstart`.

Vytvoříme novou tabulku s názvem `posts` a s těmito sloupci:
- `id` int, zaškrtneme na autoincrement (AI)
- `title` varchar, length 255
- `content` text
- `created_at` timestamp

Výsledná struktura by měla vypadat takto:

[* adminer-posts.webp *]

```sql
CREATE TABLE `posts` (
	`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
	`title` varchar(255) NOT NULL,
	`content` text NOT NULL,
	`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB CHARSET=utf8;
```

.[caution]
Je opravdu důležité použít úložiště **InnoDB**. Za chvilku si ukážeme proč. Prozatím ji jednoduše vyberte a klikněte na uložit.

Než vytvoříme možnost přidávat články do databáze pomocí aplikace, přidejte několik vzorových článků na blogu ručně.

```sql
INSERT INTO `posts` (`id`, `title`, `content`, `created_at`) VALUES
(1,	'Article One',	'Lorem ipusm dolor one',	CURRENT_TIMESTAMP),
(2,	'Article Two',	'Lorem ipsum dolor two',	CURRENT_TIMESTAMP),
(3,	'Article Three',	'Lorem ipsum dolor three',	CURRENT_TIMESTAMP);
```


Připojení k databázi
====================

Nyní, když je již databáze vytvořena a máme v ní uloženo pár článků, je ten správný čas zobrazit je na naší krásné nové stránce.

Prvně musíme aplikaci říct, jakou databázi má použít. Připojení k databázi nastavíme v souboru `config/common.neon` pomocí DSN((Data Source Name)) a přihlašovacích údajů. Mělo by to vypadat nějak takto:

```neon .{file:config/common.neon}
database:
	dsn: 'mysql:host=127.0.0.1;dbname=quickstart'
	user: *zde vložte jméno uživatele*
	password: *zde vložte heslo k databázi*
```

.[note]
Při editování tohoto souboru dávejte pozor na odsazení řádků. Formát [NEON |neon:format] akceptuje jak odsazení pomocí mezer, tak odsazení pomocí tabulátorů, ale ne obojí zároveň. Výchozí konfigurační soubor ve Web Projectu využívá tabulátory.


Předání databázového spojení
============================

Presenter `HomePresenter`, který se bude starat o výpis článků, potřebuje připojení k databázi. Pro jeho získání využijeme konstruktor, který bude vypadat takto:

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

use Nette;

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

	// ...
}
```


Načítání příspěvků z databáze
=============================

Nyní načteme příspěvky z databáze a pošleme je do šablony, která je následně vykreslí jako HTML kód. Pro tohle je určena takzvaná *render* metoda:

```php .{file:app/UI/Home/HomePresenter.php}
public function renderDefault(): void
{
	$this->template->posts = $this->database
		->table('posts')
		->order('created_at DESC')
		->limit(5);
}
```

Presenter nyní obsahuje jednu renderovací metodu `renderDefault()`, která předává data z databáze do šablony (View). Šablony jsou umístěny v `app/UI/{PresenterName}/{viewName}.latte`, takže v tomto případě je šablona umístěna v `app/UI/Home/default.latte`. V šabloně nyní bude k dispozici proměnná `$posts`, ve které jsou příspěvky získané z databáze.


Šablona
=======

Pro celou webovou stránku máme k dispozici hlavní šablonu (která se jmenuje *layout*, obsahuje hlavičku, styly, patičku,...) a dále konkrétní šablony pro každý pohled (View) (např. pro zobrazení příspěvků na blogu), které mohou přepsat některé části hlavní šablony.

Ve výchozím stavu je layout šablona umístěna v `app/UI/@layout.latte` a obsahuje:

```latte .{file:app/UI/@layout.latte}
...
{include content}
...
```

Zápis `{include content}` vkládá do hlavní šablony blok s názvem `content`. Ten budeme definovat v šablonách jednotlivých pohledů (View). V našem případě soubor `Home/default.latte` upravíme následovně:

```latte .{file:app/UI/Home/default.latte}
{block content}
	Hello World
{/block}
```

Tímto jsme nadefinovali [blok |latte:tags#block] *content*, který bude vložen do hlavního layoutu. Pokud opět obnovíme prohlížeč, uvidíme stránku s textem "Hello World" (ve zdrojovém kódu i s HTML hlavičkou a patičkou definovanou v `@layout.latte`).

Pojďme zobrazit příspěvky z blogu - šablonu upravíme následovně:

```latte .{file:app/UI/Home/default.latte}
{block content}
	<h1>Můj blog</h1>

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

		<h2>{$post->title}</h2>

		<div>{$post->content|truncate:256}</div>
	</div>
	{/foreach}
{/block}
```

Pokud obnovíme prohlížeč, uvidíme výpis všech příspěvků. Výpis zatím není moc hezký, ani barevný, proto můžeme do souboru `www/css/style.css` přidat pár [CSS stylů |https://github.com/nette-examples/quickstart/blob/v4.0/www/css/style.css] a zalinkovat jej v layoutu:

```latte .{file:app/UI/@layout.latte}
	...
	<link rel="stylesheet" href="{$basePath}/css/style.css">
</head>
...
```

Značka `{foreach}` iteruje přes všechny příspěvky, které jsme předali šabloně v proměnné `$posts`, a pro každý vykreslí daný kus HTML. Chová se přesně jako PHP kód.

Zápisu `|date:` říkáme filtr. Filtry jsou určeny k formátování výstupu. Tento konkrétní filtr převádí datum (např. `2013-04-12`) na jeho čitelnější podobu (`April 12, 2013`). Filtr `|truncate` ořízne řetězec na uvedenou maximální délku a v případě, že řetězec zkrátí, přidá na konec trojtečku. Jelikož se jedná o náhled, nemá smysl zobrazovat celý obsah článku. Další výchozí filtry [najdeme v dokumentaci |latte:filters] a nebo si můžeme vytvořit vlastní, když je to potřeba.

Ještě jedna věc. Předchozí kód můžeme zkrátit a zjednodušit. Toho docílíme záměnou *Latte tagů* za *n:atributy*:

```latte .{file:app/UI/Home/default.latte}
{block content}
	<h1>Můj blog</h1>

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

		<h2>{$post->title}</h2>

		<div>{$post->content|truncate:256}</div>
	</div>
{/block}
```

Atribut `n:foreach` obaluje *div* blokem *foreach* (funguje naprosto stejně jako předchozí kód).


Shrnutí
=======

Nyní máme velmi jednoduchou MySQL databázi s pár příspěvky. Aplikace se připojuje k této databázi a vypisuje jednoduchý seznam těchto příspěvků do šablony.

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

Úvodní stránka blogu

Nyní si vytvoříme úvodní stránku zobrazující poslední příspěvky.

Než začneme, je zapotřebí znát alespoň základy návrhového vzoru Model-View-Presenter (podobné jako MVC):

  • Model – vrstva pracující s daty. Je kompletně oddělena od zbytku aplikace. Komunikuje pouze s presenterem.
  • View – front-end vrstva. Vykresluje požadovaná data pomocí šablon a zobrazuje je uživateli.
  • Presenter (nebo Controller) – propojovací vrstva. Presenter propojuje Model a View. Zpracovává požadavky, dotazuje se Modelu na data a vrací je zpět do View.

V případě jednoduchých aplikací, jako bude náš blog, budou celou modelovou vrstvu tvořit jenom dotazy do databáze – na to zatím žádný extra kód nepotřebujeme. Pro začátek si tedy vytvoříme jenom presentery a šablony. V Nette má každý presenter své vlastní šablony, takže je budeme vytvářet zároveň.

Vytvoření databáze pomocí Admineru

Pro ukládání dat použijeme MySQL databázi, protože je nejvíce rozšířená mezi programátory webových aplikací. Pokud ji však použít nechcete, klidně si zvolte databázi dle vlastního uvážení.

Nyní si připravíme databázovou strukturu, kde budou uloženy články našeho blogu. Začneme velmi jednoduše – vytvoříme si pouze jednu tabulku pro příspěvky.

Pro vytvoření databáze si můžeme stáhnout Adminer, nebo jiný váš oblíbený nástroj pro správu databází.

Otevřeme si Adminer a vytvoříme novou databázi s názvem quickstart.

Vytvoříme novou tabulku s názvem posts a s těmito sloupci:

  • id int, zaškrtneme na autoincrement (AI)
  • title varchar, length 255
  • content text
  • created_at timestamp

Výsledná struktura by měla vypadat takto:

CREATE TABLE `posts` (
	`id` int(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
	`title` varchar(255) NOT NULL,
	`content` text NOT NULL,
	`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP
) ENGINE=InnoDB CHARSET=utf8;

Je opravdu důležité použít úložiště InnoDB. Za chvilku si ukážeme proč. Prozatím ji jednoduše vyberte a klikněte na uložit.

Než vytvoříme možnost přidávat články do databáze pomocí aplikace, přidejte několik vzorových článků na blogu ručně.

INSERT INTO `posts` (`id`, `title`, `content`, `created_at`) VALUES
(1,	'Article One',	'Lorem ipusm dolor one',	CURRENT_TIMESTAMP),
(2,	'Article Two',	'Lorem ipsum dolor two',	CURRENT_TIMESTAMP),
(3,	'Article Three',	'Lorem ipsum dolor three',	CURRENT_TIMESTAMP);

Připojení k databázi

Nyní, když je již databáze vytvořena a máme v ní uloženo pár článků, je ten správný čas zobrazit je na naší krásné nové stránce.

Prvně musíme aplikaci říct, jakou databázi má použít. Připojení k databázi nastavíme v souboru config/common.neon pomocí DSN a přihlašovacích údajů. Mělo by to vypadat nějak takto:

database:
	dsn: 'mysql:host=127.0.0.1;dbname=quickstart'
	user: *zde vložte jméno uživatele*
	password: *zde vložte heslo k databázi*

Při editování tohoto souboru dávejte pozor na odsazení řádků. Formát NEON akceptuje jak odsazení pomocí mezer, tak odsazení pomocí tabulátorů, ale ne obojí zároveň. Výchozí konfigurační soubor ve Web Projectu využívá tabulátory.

Předání databázového spojení

Presenter HomePresenter, který se bude starat o výpis článků, potřebuje připojení k databázi. Pro jeho získání využijeme konstruktor, který bude vypadat takto:

<?php
namespace App\UI\Home;

use Nette;

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

	// ...
}

Načítání příspěvků z databáze

Nyní načteme příspěvky z databáze a pošleme je do šablony, která je následně vykreslí jako HTML kód. Pro tohle je určena takzvaná render metoda:

public function renderDefault(): void
{
	$this->template->posts = $this->database
		->table('posts')
		->order('created_at DESC')
		->limit(5);
}

Presenter nyní obsahuje jednu renderovací metodu renderDefault(), která předává data z databáze do šablony (View). Šablony jsou umístěny v app/UI/{PresenterName}/{viewName}.latte, takže v tomto případě je šablona umístěna v app/UI/Home/default.latte. V šabloně nyní bude k dispozici proměnná $posts, ve které jsou příspěvky získané z databáze.

Šablona

Pro celou webovou stránku máme k dispozici hlavní šablonu (která se jmenuje layout, obsahuje hlavičku, styly, patičku,…) a dále konkrétní šablony pro každý pohled (View) (např. pro zobrazení příspěvků na blogu), které mohou přepsat některé části hlavní šablony.

Ve výchozím stavu je layout šablona umístěna v app/UI/@layout.latte a obsahuje:

...
{include content}
...

Zápis {include content} vkládá do hlavní šablony blok s názvem content. Ten budeme definovat v šablonách jednotlivých pohledů (View). V našem případě soubor Home/default.latte upravíme následovně:

{block content}
	Hello World
{/block}

Tímto jsme nadefinovali blok content, který bude vložen do hlavního layoutu. Pokud opět obnovíme prohlížeč, uvidíme stránku s textem „Hello World“ (ve zdrojovém kódu i s HTML hlavičkou a patičkou definovanou v @layout.latte).

Pojďme zobrazit příspěvky z blogu – šablonu upravíme následovně:

{block content}
	<h1>Můj blog</h1>

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

		<h2>{$post->title}</h2>

		<div>{$post->content|truncate:256}</div>
	</div>
	{/foreach}
{/block}

Pokud obnovíme prohlížeč, uvidíme výpis všech příspěvků. Výpis zatím není moc hezký, ani barevný, proto můžeme do souboru www/css/style.css přidat pár CSS stylů a zalinkovat jej v layoutu:

	...
	<link rel="stylesheet" href="{$basePath}/css/style.css">
</head>
...

Značka {foreach} iteruje přes všechny příspěvky, které jsme předali šabloně v proměnné $posts, a pro každý vykreslí daný kus HTML. Chová se přesně jako PHP kód.

Zápisu |date: říkáme filtr. Filtry jsou určeny k formátování výstupu. Tento konkrétní filtr převádí datum (např. 2013-04-12) na jeho čitelnější podobu (April 12, 2013). Filtr |truncate ořízne řetězec na uvedenou maximální délku a v případě, že řetězec zkrátí, přidá na konec trojtečku. Jelikož se jedná o náhled, nemá smysl zobrazovat celý obsah článku. Další výchozí filtry najdeme v dokumentaci a nebo si můžeme vytvořit vlastní, když je to potřeba.

Ještě jedna věc. Předchozí kód můžeme zkrátit a zjednodušit. Toho docílíme záměnou Latte tagů za n:atributy:

{block content}
	<h1>Můj blog</h1>

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

		<h2>{$post->title}</h2>

		<div>{$post->content|truncate:256}</div>
	</div>
{/block}

Atribut n:foreach obaluje div blokem foreach (funguje naprosto stejně jako předchozí kód).

Shrnutí

Nyní máme velmi jednoduchou MySQL databázi s pár příspěvky. Aplikace se připojuje k této databázi a vypisuje jednoduchý seznam těchto příspěvků do šablony.