Nette Documentation Preview

syntax
Коментар
********

Блогът беше пуснат в действие, написахме няколко много добри публикации в блога и ги публикувахме чрез Adminer. Хората четат блога и са много ентусиазирани от нашите идеи. Всеки ден получаваме много похвални писма. Но защо са всички тези похвали, ако ги получаваме само по имейл и никой друг не може да ги прочете? Няма ли да е по-добре, ако хората могат да оставят коментари директно в блога, за да могат всички останали да прочетат колко сме страхотни?

Нека направим всички статии коментируеми.


Създаване на нова таблица .[#toc-creating-a-new-table]
======================================================

Стартирайте отново Adminer и създайте нова таблица, наречена `comments`, с тези колони:

- `id` int, маркиране на автоинкремента (AI)
- `post_id`, външен ключ, препращащ към таблицата `posts`.
- `name` varchar, дължина 255
- `email` varchar, дължина 255
- `content` текст
- `created_at` timestamp

Това трябва да изглежда по следния начин

[* adminer-comments.webp *]

Не забравяйте да използвате съхранението на таблицата InnoDB и щракнете върху Запис.

```sql
CREATE TABLE `comments` (
	`id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
	`post_id` int(11) NOT NULL,
	`name` varchar(250) NOT NULL,
	`email` varchar(250) NOT NULL,
	`content` text NOT NULL,
	`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
	FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`)
) ENGINE=InnoDB CHARSET=utf8;
```


Формуляр за коментари .[#toc-form-for-commenting]
=================================================

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

Nette има понятие за *компоненти*. **Компонент** е клас или част от кода за многократна употреба, който може да бъде прикрепен към друг компонент. Дори водещият е компонент. Всеки компонент се създава с помощта на фабрика за компоненти. И така, нека дефинираме фабриката за формуляри за коментари в `PostPresenter`.

```php .{file:app/UI/Post/PostPresenter.php}
protected function createComponentCommentForm(): Form
{
	$form = new Form; // означава Nette\Application\UI\Form

	$form->addText('name', 'вашето име:')
		->setRequired();

	$form->addEmail('email', 'Email:');

	$form->addTextArea('content', 'Comment:')
		->setRequired();

	$form->addSubmit('send', 'Send');

	return $form;
}
```

Нека да го обясним малко. Първият ред създава нова инстанция на компонента `Form`. Методите по-долу поставят HTML елементите *input* във формуляра. `->addText` ще се покаже като `<input type=text name=name>`, с `<label>Ваше имя:</label>`. Както вече сте се досетили, `->addTextArea` прикрепя `<textarea>`, а `->addSubmit` добавя `<input type=submit>`. Има и други подобни методи, но това е всичко, което трябва да знаете сега. Можете да [намерите повече информация в документацията |forms:].

След като компонентът на формуляра е дефиниран в презентатора, можем да го визуализираме в шаблона. За да направите това, поставете тага `{control}` в края на шаблона за подробна информация за публикацията в `app/UI/Post/show.latte`. Тъй като името на компонента е `commentForm` (то идва от името на метода `createComponentCommentForm`), тагът ще изглежда по следния начин

```latte .{file:app/UI/Post/show.latte}
...
<h2>Оставить комментарий</h2>

{control commentForm}
```

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


Запазване в базата данни .[#toc-saving-to-database]
===================================================

Опитахте ли се да изпратите данни? Може би сте забелязали, че формулярът не извършва никакво действие. Той просто е там, изглежда готино и не прави нищо. Към него трябва да приложим метод за обратна връзка, който ще запази изпратените данни.

Поставете следния ред преди реда `return` във фабриката за компоненти за `commentForm`:

```php
$form->onSuccess[] = $this->commentFormSucceeded(...);
```

Това означава "след успешно изпращане на формуляра извикайте метода `commentFormSucceeded` на текущия презентатор". Този метод все още не съществува, затова нека го създадем.

```php .{file:app/UI/Post/PostPresenter.php}
private function commentFormSucceeded(\stdClass $data): void
{
	$id = $this->getParameter('id');

	$this->database->table('comments')->insert([
		'post_id' => $id,
		'name' => $data->name,
		'email' => $data->email,
		'content' => $data->content,
	]);

	$this->flashMessage('Спасибо за комментарий!', 'success');
	$this->redirect('this');
}
```

Трябва да го поставите веднага след фабриката за компоненти `commentForm`.

Методът new има един аргумент, който е инстанция на подадената форма, създадена от фабриката за компоненти. Извличаме предадените стойности в `$data`. След това вмъкваме данните в таблицата на базата данни `comments`.

Необходимо е да се обяснят още две извиквания на методи. `$this->redirect('this')` буквално пренасочва към текущата страница. Трябва да правите това всеки път, когато формулярът е изпратен, валиден и операцията за обратно извикване е изпълнила това, което е трябвало да направи. Освен това, когато пренасочите страницата след изпращане на формуляра, няма да видите добре познатото съобщение `Вы хотите отправить данные сообщения снова?`, което понякога се вижда в браузъра. (По принцип след изпращане на формуляр чрез метода `POST` винаги трябва да пренасочвате потребителя към действието `GET`).

`$this->flashMessage` има за цел да информира потребителя за резултата от дадена операция. Тъй като пренасочваме, съобщението не може да бъде директно предадено на шаблона и показано. Ето защо има метод за запазване и предоставяне на достъп до него при следващото зареждане на страницата. Съобщенията на Flash се показват в стандартния файл `app/UI/@layout.latte` и изглеждат по следния начин

```latte
<div n:foreach="$flashes as $flash" n:class="flash, $flash->type">
	{$flash->message}
</div>
```

Както вече знаем, те се предават автоматично на шаблона, така че не е нужно да мислите много за това, а просто работи. За повече информация вижте [документацията |application:presenters#Flash-Messages].


Показване на коментари .[#toc-rendering-the-comments]
=====================================================

Това е едно от онези неща, които просто ще ви харесат. Базата данни на Nette има страхотна функция, наречена [Explorer |database:explorer]. Спомняте ли си, че създадохме таблици като InnoDB? Adminer създаде така наречените [външни ключове, |https://dev.mysql.com/doc/refman/8.0/en/create-table-foreign-keys.html] които ще ни спестят много работа.

Nette Database Explorer използва чужди ключове, за да определи връзките между таблиците, и като знае тези връзки, може автоматично да създава заявки за вас.

Както си спомняте, предадохме променливата `$post` на шаблона в `PostPresenter::renderShow()` и сега искаме да изброим всички коментари, които имат колона `post_id`, равна на нашата `$post->id`. Можете да направите това, като се обадите на `$post->related('comments')`. Всичко е толкова просто. Разгледайте получения код.

```php .{file:app/UI/Post/PostPresenter.php}
public function renderShow(int $id): void
{
	...
	$this->template->post = $post;
	$this->template->comments = $post->related('comments')->order('created_at');
}
```

В шаблона:

```latte .{file:app/UI/Post/show.latte}
...
<h2>Комментарии</h2>

<div class="comments">
	{foreach $comments as $comment}
		<p><b><a href="mailto:{$comment->email}" n:tag-if="$comment->email">
			{$comment->name}
		</a></b>:</p>

		<div>{$comment->content}</div>
	{/foreach}
</div>
...
```

Обърнете внимание на специалния атрибут `n:tag-if`. Вече знаете как работи `n: атрибуты`. Ако добавите `tag-` към атрибута, той ще обгражда само таговете, но не и тяхното съдържание. Това ви позволява да превърнете името на коментиращия във връзка, ако той е предоставил своя имейл. Резултатите от двата низа са идентични:

```latte
<strong n:tag-if="$important"> Здравствуйте! </strong>

{if $important}<strong>{/if} Здравствуйте! {if $important}</strong>{/if}
```

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

Коментар

Блогът беше пуснат в действие, написахме няколко много добри публикации в блога и ги публикувахме чрез Adminer. Хората четат блога и са много ентусиазирани от нашите идеи. Всеки ден получаваме много похвални писма. Но защо са всички тези похвали, ако ги получаваме само по имейл и никой друг не може да ги прочете? Няма ли да е по-добре, ако хората могат да оставят коментари директно в блога, за да могат всички останали да прочетат колко сме страхотни?

Нека направим всички статии коментируеми.

Създаване на нова таблица

Стартирайте отново Adminer и създайте нова таблица, наречена comments, с тези колони:

  • id int, маркиране на автоинкремента (AI)
  • post_id, външен ключ, препращащ към таблицата posts.
  • name varchar, дължина 255
  • email varchar, дължина 255
  • content текст
  • created_at timestamp

Това трябва да изглежда по следния начин

Не забравяйте да използвате съхранението на таблицата InnoDB и щракнете върху Запис.

CREATE TABLE `comments` (
	`id` int NOT NULL AUTO_INCREMENT PRIMARY KEY,
	`post_id` int(11) NOT NULL,
	`name` varchar(250) NOT NULL,
	`email` varchar(250) NOT NULL,
	`content` text NOT NULL,
	`created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
	FOREIGN KEY (`post_id`) REFERENCES `posts` (`id`)
) ENGINE=InnoDB CHARSET=utf8;

Формуляр за коментари

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

Nette има понятие за компоненти. Компонент е клас или част от кода за многократна употреба, който може да бъде прикрепен към друг компонент. Дори водещият е компонент. Всеки компонент се създава с помощта на фабрика за компоненти. И така, нека дефинираме фабриката за формуляри за коментари в PostPresenter.

protected function createComponentCommentForm(): Form
{
	$form = new Form; // означава Nette\Application\UI\Form

	$form->addText('name', 'вашето име:')
		->setRequired();

	$form->addEmail('email', 'Email:');

	$form->addTextArea('content', 'Comment:')
		->setRequired();

	$form->addSubmit('send', 'Send');

	return $form;
}

Нека да го обясним малко. Първият ред създава нова инстанция на компонента Form. Методите по-долу поставят HTML елементите input във формуляра. ->addText ще се покаже като <input type=text name=name>, с <label>Ваше имя:</label>. Както вече сте се досетили, ->addTextArea прикрепя <textarea>, а ->addSubmit добавя <input type=submit>. Има и други подобни методи, но това е всичко, което трябва да знаете сега. Можете да намерите повече информация в документацията.

След като компонентът на формуляра е дефиниран в презентатора, можем да го визуализираме в шаблона. За да направите това, поставете тага {control} в края на шаблона за подробна информация за публикацията в app/UI/Post/show.latte. Тъй като името на компонента е commentForm (то идва от името на метода createComponentCommentForm), тагът ще изглежда по следния начин

...
<h2>Оставить комментарий</h2>

{control commentForm}

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

Запазване в базата данни

Опитахте ли се да изпратите данни? Може би сте забелязали, че формулярът не извършва никакво действие. Той просто е там, изглежда готино и не прави нищо. Към него трябва да приложим метод за обратна връзка, който ще запази изпратените данни.

Поставете следния ред преди реда return във фабриката за компоненти за commentForm:

$form->onSuccess[] = $this->commentFormSucceeded(...);

Това означава „след успешно изпращане на формуляра извикайте метода commentFormSucceeded на текущия презентатор“. Този метод все още не съществува, затова нека го създадем.

private function commentFormSucceeded(\stdClass $data): void
{
	$id = $this->getParameter('id');

	$this->database->table('comments')->insert([
		'post_id' => $id,
		'name' => $data->name,
		'email' => $data->email,
		'content' => $data->content,
	]);

	$this->flashMessage('Спасибо за комментарий!', 'success');
	$this->redirect('this');
}

Трябва да го поставите веднага след фабриката за компоненти commentForm.

Методът new има един аргумент, който е инстанция на подадената форма, създадена от фабриката за компоненти. Извличаме предадените стойности в $data. След това вмъкваме данните в таблицата на базата данни comments.

Необходимо е да се обяснят още две извиквания на методи. $this->redirect('this') буквално пренасочва към текущата страница. Трябва да правите това всеки път, когато формулярът е изпратен, валиден и операцията за обратно извикване е изпълнила това, което е трябвало да направи. Освен това, когато пренасочите страницата след изпращане на формуляра, няма да видите добре познатото съобщение Вы хотите отправить данные сообщения снова?, което понякога се вижда в браузъра. (По принцип след изпращане на формуляр чрез метода POST винаги трябва да пренасочвате потребителя към действието GET).

$this->flashMessage има за цел да информира потребителя за резултата от дадена операция. Тъй като пренасочваме, съобщението не може да бъде директно предадено на шаблона и показано. Ето защо има метод за запазване и предоставяне на достъп до него при следващото зареждане на страницата. Съобщенията на Flash се показват в стандартния файл app/UI/@layout.latte и изглеждат по следния начин

<div n:foreach="$flashes as $flash" n:class="flash, $flash->type">
	{$flash->message}
</div>

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

Показване на коментари

Това е едно от онези неща, които просто ще ви харесат. Базата данни на Nette има страхотна функция, наречена Explorer. Спомняте ли си, че създадохме таблици като InnoDB? Adminer създаде така наречените външни ключове, които ще ни спестят много работа.

Nette Database Explorer използва чужди ключове, за да определи връзките между таблиците, и като знае тези връзки, може автоматично да създава заявки за вас.

Както си спомняте, предадохме променливата $post на шаблона в PostPresenter::renderShow() и сега искаме да изброим всички коментари, които имат колона post_id, равна на нашата $post->id. Можете да направите това, като се обадите на $post->related('comments'). Всичко е толкова просто. Разгледайте получения код.

public function renderShow(int $id): void
{
	...
	$this->template->post = $post;
	$this->template->comments = $post->related('comments')->order('created_at');
}

В шаблона:

...
<h2>Комментарии</h2>

<div class="comments">
	{foreach $comments as $comment}
		<p><b><a href="mailto:{$comment->email}" n:tag-if="$comment->email">
			{$comment->name}
		</a></b>:</p>

		<div>{$comment->content}</div>
	{/foreach}
</div>
...

Обърнете внимание на специалния атрибут n:tag-if. Вече знаете как работи n: атрибуты. Ако добавите tag- към атрибута, той ще обгражда само таговете, но не и тяхното съдържание. Това ви позволява да превърнете името на коментиращия във връзка, ако той е предоставил своя имейл. Резултатите от двата низа са идентични:

<strong n:tag-if="$important"> Здравствуйте! </strong>

{if $important}<strong>{/if} Здравствуйте! {if $important}</strong>{/if}