Nette Documentation Preview

syntax
Начална страница на блога
*************************

.[perex]
Сега ще създадем начална страница, показваща последните публикации.


Преди да започнем, е необходимо да познавате поне основите на дизайнерския модел Model-View-Presenter (подобен на MVC((Model-View-Controller))):

- **Модел** - слой, работещ с данни. Той е напълно отделен от останалата част на приложението. Комуникира само с презентера.

- **Изглед** - фронт-енд слой. Рендира изискваните данни с помощта на шаблони и ги показва на потребителя.

- **Presenter** (или Контролер) - свързващ слой. Presenter свързва Модела и Изгледа. Обработва заявките, изисква данни от Модела и ги връща обратно на Изгледа.


В случай на прости приложения, като нашия блог, целият моделен слой ще се състои само от заявки към базата данни - засега не се нуждаем от допълнителен код за това. За начало ще създадем само презентери и шаблони. В Nette всеки презентер има свои собствени шаблони, така че ще ги създаваме едновременно.


Създаване на база данни с помощта на Adminer
============================================

За съхраняване на данни ще използваме MySQL база данни, тъй като тя е най-разпространената сред програмистите на уеб приложения. Ако обаче не искате да я използвате, спокойно изберете база данни по ваш избор.

Сега ще подготвим структурата на базата данни, където ще се съхраняват статиите на нашия блог. Ще започнем много просто - ще създадем само една таблица за публикациите.

За създаване на базата данни можем да изтеглим [Adminer |https://www.adminer.org] или друг ваш любим инструмент за управление на бази данни.


Отваряме Adminer и създаваме нова база данни с име `quickstart`.

Създаваме нова таблица с име `posts` и със следните колони:
- `id` int, отметнете autoincrement (AI)
- `title` varchar, length 255
- `content` text
- `created_at` timestamp

Получената структура трябва да изглежда така:

[* 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]
Наистина е важно да използвате хранилище **InnoDB**. След малко ще покажем защо. Засега просто го изберете и кликнете върху запазване.

Преди да създадем възможност за добавяне на статии в базата данни с помощта на приложението, добавете няколко примерни статии в блога ръчно.

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


Свързване с базата данни
========================

Сега, когато базата данни вече е създадена и имаме запазени няколко статии в нея, е време да ги покажем на нашата красива нова страница.

Първо трябва да кажем на приложението коя база данни да използва. Връзката с базата данни се настройва във файла `config/common.neon` с помощта на DSN((Data Source Name)) и данните за вход. Трябва да изглежда по следния начин:

```neon .{file:config/common.neon}
database:
	dsn: 'mysql:host=127.0.0.1;dbname=quickstart'
	user: *тук въведете потребителско име*
	password: *тук въведете парола за базата данни*
```

.[note]
При редактиране на този файл внимавайте с индентацията на редовете. Форматът [NEON |neon:format] приема както индентация с интервали, така и индентация с табулации, но не и двете едновременно. Конфигурационният файл по подразбиране в Web Project използва табулации.


Предаване на връзката с базата данни
====================================

Presenter `HomePresenter`, който ще се грижи за извеждането на статиите, се нуждае от връзка с базата данни. За да я получим, ще използваме конструктор, който ще изглежда така:

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

use Nette;

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

	// ...
}
```


Зареждане на публикации от базата данни
=======================================

Сега ще заредим публикациите от базата данни и ще ги изпратим към шаблона, който след това ще ги рендира като HTML код. За това е предназначен така нареченият *render* метод:

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

Presenter сега съдържа един рендиращ метод `renderDefault()`, който предава данни от базата данни към шаблона (Изглед). Шаблоните са разположени в `app/Presentation/{PresenterName}/{viewName}.latte`, така че в този случай шаблонът е разположен в `app/Presentation/Home/default.latte`. В шаблона сега ще бъде налична променлива `$posts`, в която са публикациите, получени от базата данни.


Шаблон
======

За целия уеб сайт имаме на разположение главен шаблон (който се нарича *лейаут*, съдържа хедър, стилове, футър,...) и след това конкретни шаблони за всеки изглед (View) (например за показване на публикации в блога), които могат да презапишат някои части от главния шаблон.

По подразбиране лейаут шаблонът е разположен в `app/Presentation/@layout.latte` и съдържа:

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

Записът `{include content}` вмъква в главния шаблон блок с име `content`. Него ще дефинираме в шаблоните на отделните изгледи (View). В нашия случай файлът `Home/default.latte` ще променим по следния начин:

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

С това дефинирахме [блок |latte:tags#block] *content*, който ще бъде вмъкнат в главния лейаут. Ако отново обновим браузъра, ще видим страница с текст "Hello World" (в изходния код също с HTML хедър и футър, дефинирани в `@layout.latte`).

Нека покажем публикациите от блога - ще променим шаблона по следния начин:

```latte .{file:app/Presentation/Home/default.latte}
{block content}
	<h1>Моят блог</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}
```

Ако обновим браузъра, ще видим списък с всички публикации. Списъкът засега не е много хубав, нито цветен, затова можем да добавим към файла `www/css/style.css` няколко [CSS стила |https://github.com/nette-examples/quickstart/blob/v4.0/www/css/style.css] и да го свържем в лейаута:

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

Тагът `{foreach}` итерира през всички публикации, които сме предали на шаблона в променливата `$posts`, и за всяка рендира дадения HTML код. Държи се точно като PHP код.

На записа `|date:` казваме филтър. Филтрите са предназначени за форматиране на изхода. Този конкретен филтър преобразува дата (напр. `2013-04-12`) в нейната по-четима форма (`April 12, 2013`). Филтърът `|truncate` отрязва низ до посочената максимална дължина и в случай, че низът бъде скъсен, добавя накрая три точки. Тъй като това е преглед, няма смисъл да се показва цялото съдържание на статията. Други филтри по подразбиране [можем да намерим в документацията |latte:filters] или можем да създадем собствени, когато е необходимо.

Още нещо. Предишният код можем да съкратим и опростим. Това ще постигнем чрез замяна на *Latte тагове* с *n:атрибути*:

```latte .{file:app/Presentation/Home/default.latte}
{block content}
	<h1>Моят блог</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}
```

Атрибутът `n:foreach` обвива *div* блока с *foreach* (работи абсолютно същото като предишния код).


Резюме
======

Сега имаме много проста MySQL база данни с няколко публикации. Приложението се свързва с тази база данни и извежда прост списък с тези публикации в шаблона.

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

Начална страница на блога

Сега ще създадем начална страница, показваща последните публикации.

Преди да започнем, е необходимо да познавате поне основите на дизайнерския модел Model-View-Presenter (подобен на MVC):

  • Модел – слой, работещ с данни. Той е напълно отделен от останалата част на приложението. Комуникира само с презентера.
  • Изглед – фронт-енд слой. Рендира изискваните данни с помощта на шаблони и ги показва на потребителя.
  • Presenter (или Контролер) – свързващ слой. Presenter свързва Модела и Изгледа. Обработва заявките, изисква данни от Модела и ги връща обратно на Изгледа.

В случай на прости приложения, като нашия блог, целият моделен слой ще се състои само от заявки към базата данни – засега не се нуждаем от допълнителен код за това. За начало ще създадем само презентери и шаблони. В Nette всеки презентер има свои собствени шаблони, така че ще ги създаваме едновременно.

Създаване на база данни с помощта на Adminer

За съхраняване на данни ще използваме MySQL база данни, тъй като тя е най-разпространената сред програмистите на уеб приложения. Ако обаче не искате да я използвате, спокойно изберете база данни по ваш избор.

Сега ще подготвим структурата на базата данни, където ще се съхраняват статиите на нашия блог. Ще започнем много просто – ще създадем само една таблица за публикациите.

За създаване на базата данни можем да изтеглим Adminer или друг ваш любим инструмент за управление на бази данни.

Отваряме Adminer и създаваме нова база данни с име quickstart.

Създаваме нова таблица с име posts и със следните колони:

  • id int, отметнете autoincrement (AI)
  • title varchar, length 255
  • content text
  • created_at timestamp

Получената структура трябва да изглежда така:

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;

Наистина е важно да използвате хранилище InnoDB. След малко ще покажем защо. Засега просто го изберете и кликнете върху запазване.

Преди да създадем възможност за добавяне на статии в базата данни с помощта на приложението, добавете няколко примерни статии в блога ръчно.

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

Свързване с базата данни

Сега, когато базата данни вече е създадена и имаме запазени няколко статии в нея, е време да ги покажем на нашата красива нова страница.

Първо трябва да кажем на приложението коя база данни да използва. Връзката с базата данни се настройва във файла config/common.neon с помощта на DSN и данните за вход. Трябва да изглежда по следния начин:

database:
	dsn: 'mysql:host=127.0.0.1;dbname=quickstart'
	user: *тук въведете потребителско име*
	password: *тук въведете парола за базата данни*

При редактиране на този файл внимавайте с индентацията на редовете. Форматът NEON приема както индентация с интервали, така и индентация с табулации, но не и двете едновременно. Конфигурационният файл по подразбиране в Web Project използва табулации.

Предаване на връзката с базата данни

Presenter HomePresenter, който ще се грижи за извеждането на статиите, се нуждае от връзка с базата данни. За да я получим, ще използваме конструктор, който ще изглежда така:

<?php
namespace App\Presentation\Home;

use Nette;

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

	// ...
}

Зареждане на публикации от базата данни

Сега ще заредим публикациите от базата данни и ще ги изпратим към шаблона, който след това ще ги рендира като HTML код. За това е предназначен така нареченият render метод:

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

Presenter сега съдържа един рендиращ метод renderDefault(), който предава данни от базата данни към шаблона (Изглед). Шаблоните са разположени в app/Presentation/{PresenterName}/{viewName}.latte, така че в този случай шаблонът е разположен в app/Presentation/Home/default.latte. В шаблона сега ще бъде налична променлива $posts, в която са публикациите, получени от базата данни.

Шаблон

За целия уеб сайт имаме на разположение главен шаблон (който се нарича лейаут, съдържа хедър, стилове, футър,…) и след това конкретни шаблони за всеки изглед (View) (например за показване на публикации в блога), които могат да презапишат някои части от главния шаблон.

По подразбиране лейаут шаблонът е разположен в app/Presentation/@layout.latte и съдържа:

...
{include content}
...

Записът {include content} вмъква в главния шаблон блок с име content. Него ще дефинираме в шаблоните на отделните изгледи (View). В нашия случай файлът Home/default.latte ще променим по следния начин:

{block content}
	Hello World
{/block}

С това дефинирахме блок content, който ще бъде вмъкнат в главния лейаут. Ако отново обновим браузъра, ще видим страница с текст „Hello World“ (в изходния код също с HTML хедър и футър, дефинирани в @layout.latte).

Нека покажем публикациите от блога – ще променим шаблона по следния начин:

{block content}
	<h1>Моят блог</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}

Ако обновим браузъра, ще видим списък с всички публикации. Списъкът засега не е много хубав, нито цветен, затова можем да добавим към файла www/css/style.css няколко CSS стила и да го свържем в лейаута:

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

Тагът {foreach} итерира през всички публикации, които сме предали на шаблона в променливата $posts, и за всяка рендира дадения HTML код. Държи се точно като PHP код.

На записа |date: казваме филтър. Филтрите са предназначени за форматиране на изхода. Този конкретен филтър преобразува дата (напр. 2013-04-12) в нейната по-четима форма (April 12, 2013). Филтърът |truncate отрязва низ до посочената максимална дължина и в случай, че низът бъде скъсен, добавя накрая три точки. Тъй като това е преглед, няма смисъл да се показва цялото съдържание на статията. Други филтри по подразбиране можем да намерим в документацията или можем да създадем собствени, когато е необходимо.

Още нещо. Предишният код можем да съкратим и опростим. Това ще постигнем чрез замяна на Latte тагове с n:атрибути:

{block content}
	<h1>Моят блог</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}

Атрибутът n:foreach обвива div блока с foreach (работи абсолютно същото като предишния код).

Резюме

Сега имаме много проста MySQL база данни с няколко публикации. Приложението се свързва с тази база данни и извежда прост списък с тези публикации в шаблона.