Nette Documentation Preview

syntax
Головна сторінка блогу
**********************

.[perex]
Давайте створимо головну сторінку, на якій відображатимуться ваші останні пости.


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

- **Модель** - рівень маніпулювання даними. Він повністю відокремлений від іншої частини додатка і спілкується тільки з презентерами.

- **Вид** (або _Представлення_) - зовнішній рівень визначення. Він відображає запитувані дані користувачеві за допомогою шаблонів.

- **Презентер** (або _Контролер_) - рівень з'єднання. Презентер з'єднує модель і вигляд. Обробляє запити, запитує дані у моделі і потім передає їх поточному поданню.


У разі дуже простого застосунку, такого як наш блог, шар Model фактично складатиметься тільки із запитів до самої бази даних - нам не потрібен додатковий PHP-код для цього. Нам потрібно створити тільки шари Presenter і View. У Nette у кожного презентера є свої подання, тому ми продовжимо роботу з ними обома одночасно.


Створення бази даних за допомогою Adminer .[#toc-creating-the-database-with-adminer]
====================================================================================

Для зберігання даних ми будемо використовувати базу даних MySQL, оскільки це найбільш поширений вибір серед веб-розробників. Але якщо вам це не подобається, не соромтеся використовувати базу даних за своїм вибором.

Давайте підготуємо базу даних, у якій зберігатимуться записи нашого блогу. Почнемо з однієї таблиці для постів.

Для створення бази даних ми можемо завантажити [Adminer |https://www.adminer.org], або ви можете використати інший інструмент для управління базами даних.


Давайте відкриємо Adminer і створимо нову базу даних під назвою `quickstart`.

Створіть нову таблицю з іменем `posts` і додайте до неї ці стовпці:
- `id` int, натисніть на автоінкремент (AI)
- `title` varchar, довжина 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-запит в Adminer.

Спробуйте додати кілька записів до блогу, перш ніж ми реалізуємо можливість додавання нових записів безпосередньо з нашого застосунку.

```sql
INSERT INTO `posts` (`id`, `title`, `content`, `created_at`) VALUES
(1,	'Статья первая',	'Lorem ipusm dolor one',	CURRENT_TIMESTAMP),
(2,	'Статья вторая',	'Lorem ipsum dolor two',	CURRENT_TIMESTAMP),
(3,	'Статья третья',	'Lorem ipsum dolor three',	CURRENT_TIMESTAMP);
```


Підключення до бази даних .[#toc-connecting-to-the-database]
============================================================

Тепер, коли база даних створена і в ній є кілька постів, саме час відобразити їх на нашій новій блискучій сторінці.

По-перше, нам потрібно повідомити нашому додатку, яку базу даних використовувати. Конфігурація підключення до бази даних зберігається у файлі `config/common.neon`. Встановіть з'єднання DSN((Ім'я джерела даних)) і свої облікові дані. Це має виглядати так:

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

.[note]
Пам'ятайте про відступи під час редагування цього файлу. [Формат NEON |neon:format] приймає і пробіли, і табуляцію, але не те й інше разом! У файлі конфігурації у веб-проекті за замовчуванням використовується табуляція.


Впровадження підключення до бази даних .[#toc-injecting-the-database-connection]
================================================================================

Презентер (розташований у `app/UI/Home/HomePresenter.php`), який буде перераховувати статті, потребує підключення до бази даних. Для цього змініть конструктор таким чином:

```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,
	) {
	}

	// ...
}
```


Завантаження постів з бази даних .[#toc-loading-posts-from-the-database]
========================================================================

Тепер давайте витягнемо пости з бази даних і передамо їх у шаблон, який потім відобразить HTML-код. Для цього і призначений так званий метод *render*:

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

Тепер у презентері є один метод рендерингу `renderDefault()`, який передає дані в подання під назвою `default`. Шаблони презентера можна знайти в `app/UI/{PresenterName}/{viewName}.latte`, тому в даному випадку шаблон буде розташований в `app/UI/Home/default.latte`. У шаблоні тепер доступна змінна `$posts`, яка містить пости з бази даних.


Шаблон .[#toc-template]
=======================

Існує загальний шаблон для всієї сторінки (називається *layout* (макет), із заголовком, таблицями стилів, нижнім колонтитулом тощо), а також специфічні шаблони для кожного виду (наприклад, для відображення списку записів блогу), які можуть перевизначати деякі частини шаблону макета.

За замовчуванням шаблон макета розташовується у файлі `app/UI/@layout.latte`, який містить:

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

`{include content}` вставляє блок з ім'ям `content` в основний шаблон. Ви можете визначити його в шаблонах кожного подання. У нашому випадку ми відредагуємо файл `app/UI/Home/default.latte` таким чином:

```latte .{file:app/UI/Home/default.latte}
{block content}
	Привет, мир!
{/block}
```

Він визначає [блок |latte:tags#block] *контенту*, який буде вставлено в макет. Якщо ви оновите браузер, то побачите сторінку з текстом "Привіт, світ!" (у вихідному коді також із HTML заголовком і колонтитулом, визначеними в `@layout.latte`).

Давайте відобразимо записи блогу - для цього відредагуємо шаблон таким чином:

```latte .{file:app/UI/Home/default.latte}
{block content}
	<h1 n:block="title">Мой блог</h1>

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

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

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

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

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

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

Функція `|date` називається фільтром. Фільтри використовуються для форматування виведення. Цей конкретний фільтр перетворює дату (наприклад, `2013-04-12`) у більш читабельну форму (`12.04.2013`). Фільтр `|truncate` усікає рядок до вказаної максимальної довжини і додає три крапки в кінець, якщо рядок усічений. Оскільки це попередній перегляд, немає сенсу відображати повний зміст статті. Інші фільтри за замовчуванням [можна знайти в документації |latte:filters] або ви можете створити свої власні, якщо це необхідно.

І ще одне. Ми можемо зробити код трохи коротшим і, отже, простішим. Ми можемо замінити *теги Latte* на *n:attributes* таким чином:

```latte .{file:app/UI/Home/default.latte}
{block content}
	<h1>My 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}</div>
	</div>
{/block}
```

`n:foreach` просто обгортає *div* блоком *foreach* (він робить те саме, що й попередній блок коду).


Підіб'ємо підсумок .[#toc-summary]
==================================

У нас є дуже проста база даних MySQL з деякими записами в блозі. Додаток підключається до бази даних і відображає простий список постів.

{{priority: -1}}
{{sitename: Быстрый старт с Nette}}

Головна сторінка блогу

Давайте створимо головну сторінку, на якій відображатимуться ваші останні пости.

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

  • Модель – рівень маніпулювання даними. Він повністю відокремлений від іншої частини додатка і спілкується тільки з презентерами.
  • Вид (або _Представлення_) – зовнішній рівень визначення. Він відображає запитувані дані користувачеві за допомогою шаблонів.
  • Презентер (або _Контролер_) – рівень з'єднання. Презентер з'єднує модель і вигляд. Обробляє запити, запитує дані у моделі і потім передає їх поточному поданню.

У разі дуже простого застосунку, такого як наш блог, шар Model фактично складатиметься тільки із запитів до самої бази даних – нам не потрібен додатковий PHP-код для цього. Нам потрібно створити тільки шари Presenter і View. У Nette у кожного презентера є свої подання, тому ми продовжимо роботу з ними обома одночасно.

Створення бази даних за допомогою Adminer

Для зберігання даних ми будемо використовувати базу даних MySQL, оскільки це найбільш поширений вибір серед веб-розробників. Але якщо вам це не подобається, не соромтеся використовувати базу даних за своїм вибором.

Давайте підготуємо базу даних, у якій зберігатимуться записи нашого блогу. Почнемо з однієї таблиці для постів.

Для створення бази даних ми можемо завантажити Adminer, або ви можете використати інший інструмент для управління базами даних.

Давайте відкриємо Adminer і створимо нову базу даних під назвою quickstart.

Створіть нову таблицю з іменем posts і додайте до неї ці стовпці:

  • id int, натисніть на автоінкремент (AI)
  • title varchar, довжина 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. Причину ви побачите пізніше. Поки що просто створіть все за інструкцією і натисніть кнопку Зберегти. Або використовуйте повний код створення таблиці та кнопку SQL-запит в Adminer.

Спробуйте додати кілька записів до блогу, перш ніж ми реалізуємо можливість додавання нових записів безпосередньо з нашого застосунку.

INSERT INTO `posts` (`id`, `title`, `content`, `created_at`) VALUES
(1,	'Статья первая',	'Lorem ipusm dolor one',	CURRENT_TIMESTAMP),
(2,	'Статья вторая',	'Lorem ipsum dolor two',	CURRENT_TIMESTAMP),
(3,	'Статья третья',	'Lorem ipsum dolor three',	CURRENT_TIMESTAMP);

Підключення до бази даних

Тепер, коли база даних створена і в ній є кілька постів, саме час відобразити їх на нашій новій блискучій сторінці.

По-перше, нам потрібно повідомити нашому додатку, яку базу даних використовувати. Конфігурація підключення до бази даних зберігається у файлі config/common.neon. Встановіть з'єднання DSN і свої облікові дані. Це має виглядати так:

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

Пам'ятайте про відступи під час редагування цього файлу. Формат NEON приймає і пробіли, і табуляцію, але не те й інше разом! У файлі конфігурації у веб-проекті за замовчуванням використовується табуляція.

Впровадження підключення до бази даних

Презентер (розташований у 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,
	) {
	}

	// ...
}

Завантаження постів з бази даних

Тепер давайте витягнемо пости з бази даних і передамо їх у шаблон, який потім відобразить HTML-код. Для цього і призначений так званий метод render:

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

Тепер у презентері є один метод рендерингу renderDefault(), який передає дані в подання під назвою default. Шаблони презентера можна знайти в app/UI/{PresenterName}/{viewName}.latte, тому в даному випадку шаблон буде розташований в app/UI/Home/default.latte. У шаблоні тепер доступна змінна $posts, яка містить пости з бази даних.

Шаблон

Існує загальний шаблон для всієї сторінки (називається layout (макет), із заголовком, таблицями стилів, нижнім колонтитулом тощо), а також специфічні шаблони для кожного виду (наприклад, для відображення списку записів блогу), які можуть перевизначати деякі частини шаблону макета.

За замовчуванням шаблон макета розташовується у файлі app/UI/@layout.latte, який містить:

...
{include content}
...

{include content} вставляє блок з ім'ям content в основний шаблон. Ви можете визначити його в шаблонах кожного подання. У нашому випадку ми відредагуємо файл app/UI/Home/default.latte таким чином:

{block content}
	Привет, мир!
{/block}

Він визначає блок контенту, який буде вставлено в макет. Якщо ви оновите браузер, то побачите сторінку з текстом „Привіт, світ!“ (у вихідному коді також із HTML заголовком і колонтитулом, визначеними в @layout.latte).

Давайте відобразимо записи блогу – для цього відредагуємо шаблон таким чином:

{block content}
	<h1 n:block="title">Мой блог</h1>

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

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

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

Якщо ви оновите браузер, ви побачите список записів вашого блогу. Список не дуже вигадливий або барвистий, тому не соромтеся додати трохи блискучого CSS у www/css/style.css, а потім вставте посилання на цей файл у макет (файл @layout.latte):

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

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

Функція |date називається фільтром. Фільтри використовуються для форматування виведення. Цей конкретний фільтр перетворює дату (наприклад, 2013-04-12) у більш читабельну форму (12.04.2013). Фільтр |truncate усікає рядок до вказаної максимальної довжини і додає три крапки в кінець, якщо рядок усічений. Оскільки це попередній перегляд, немає сенсу відображати повний зміст статті. Інші фільтри за замовчуванням можна знайти в документації або ви можете створити свої власні, якщо це необхідно.

І ще одне. Ми можемо зробити код трохи коротшим і, отже, простішим. Ми можемо замінити теги Latte на n:attributes таким чином:

{block content}
	<h1>My 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}</div>
	</div>
{/block}

n:foreach просто обгортає div блоком foreach (він робить те саме, що й попередній блок коду).

Підіб'ємо підсумок

У нас є дуже проста база даних MySQL з деякими записами в блозі. Додаток підключається до бази даних і відображає простий список постів.