Nette Documentation Preview

syntax
Давайте создадим контактную форму
*********************************

.[perex]
Давайте рассмотрим, как создать контактную форму в Nette, включая отправку ее на электронную почту. Итак, давайте сделаем это!

Сначала мы должны создать новый проект. Как объясняется на странице " [Начало работы" |nette:installation]. А затем мы можем приступить к созданию формы.

Самый простой способ - создать [форму непосредственно в Presenter |forms:in-presenter]. Мы можем использовать готовый `HomePresenter`. Мы добавим компонент `contactForm`, представляющий форму. Для этого мы напишем фабричный метод `createComponentContactForm()` в коде, который будет создавать компонент:

```php
use Nette\Application\UI\Form;
use Nette\Application\UI\Presenter;

class HomePresenter extends Presenter
{
	protected function createComponentContactForm(): Form
	{
		$form = new Form;
		$form->addText('name', 'Name:')
			->setRequired('Enter your name');
		$form->addEmail('email', 'E-mail:')
			->setRequired('Enter your e-mail');
		$form->addTextarea('message', 'Message:')
			->setRequired('Enter message');
		$form->addSubmit('send', 'Send');
		$form->onSuccess[] = [$this, 'contactFormSucceeded'];
		return $form;
	}

	public function contactFormSucceeded(Form $form, $data): void
	{
		// sending an email
	}
}
```

Как вы можете видеть, мы создали два метода. Первый метод `createComponentContactForm()` создает новую форму. В ней есть поля для имени, электронной почты и сообщения, которые мы добавляем с помощью методов `addText()`, `addEmail()` и `addTextArea()`. Мы также добавили кнопку для отправки формы.
Но что, если пользователь не заполнит некоторые поля? В этом случае мы должны сообщить ему, что это обязательное поле. Мы сделали это с помощью метода `setRequired()`.
Наконец, мы также добавили [событие |nette:glossary#events] `onSuccess`, которое срабатывает в случае успешной отправки формы. В нашем случае оно вызывает метод `contactFormSucceeded`, который обрабатывает отправленную форму. Мы добавим это в код через некоторое время.

Пусть компонент `contantForm` будет отображен в шаблоне `Home/default.latte`:

```latte
{block content}
<h1>Contant Form</h1>
{control contactForm}
```

Для отправки самого письма мы создадим новый класс `ContactFacade` и поместим его в файл `app/Model/ContactFacade.php`:

```php
<?php
declare(strict_types=1);

namespace App\Model;

use Nette\Mail\Mailer;
use Nette\Mail\Message;

class ContactFacade
{
	public function __construct(
		private Mailer $mailer,
	) {
	}

	public function sendMessage(string $email, string $name, string $message): void
	{
		$mail = new Message;
		$mail->addTo('admin@example.com') // your email
			->setFrom($email, $name)
			->setSubject('Message from the contact form')
			->setBody($message);

		$this->mailer->send($mail);
	}
}
```

Метод `sendMessage()` будет создавать и отправлять электронное письмо. Для этого он использует так называемый mailer, который он передает как зависимость через конструктор. Подробнее об отправке [электронных писем |mail:].

Теперь вернемся к ведущему и завершим метод `contactFormSucceeded()`. Он вызывает метод `sendMessage()` класса `ContactFacade` и передает ему данные формы. А как мы получим объект `ContactFacade`? Он будет передан нам конструктором:

```php
use App\Model\ContactFacade;
use Nette\Application\UI\Form;
use Nette\Application\UI\Presenter;

class HomePresenter extends Presenter
{
	public function __construct(
		private ContactFacade $facade,
	) {
	}

	protected function createComponentContactForm(): Form
	{
		// ...
	}

	public function contactFormSucceeded(stdClass $data): void
	{
		$this->facade->sendMessage($data->email, $data->name, $data->message);
		$this->flashMessage('The message has been sent');
		$this->redirect('this');
	}
}
```

После отправки письма мы показываем пользователю так называемое [flash-сообщение |application:components#flash-messages], подтверждающее, что письмо отправлено, а затем перенаправляем на следующую страницу, чтобы форма не могла быть повторно отправлена с помощью *refresh* в браузере.


Ну, если все работает, вы должны быть в состоянии отправить электронное письмо из вашей контактной формы. Поздравляем!


Шаблон электронной почты HTML .[#toc-html-email-template]
---------------------------------------------------------

Пока что отправляется обычное текстовое письмо, содержащее только сообщение, отправленное формой. Но мы можем использовать HTML в письме и сделать его более привлекательным. Для этого мы создадим шаблон в Latte, который сохраним в `app/Model/contactEmail.latte`:

```latte
<html>
	<title>Message from the contact form</title>

	<body>
		<p><strong>Name:</strong> {$name}</p>
		<p><strong>E-mail:</strong> {$email}</p>
		<p><strong>Message:</strong> {$message}</p>
	</body>
</html>
```

Осталось модифицировать `ContactFacade`, чтобы использовать этот шаблон. В конструкторе мы запрашиваем класс `LatteFactory`, который может произвести объект `Latte\Engine`, [рендерер шаблона Latte |latte:develop#how-to-render-a-template]. Мы используем метод `renderToString()` для рендеринга шаблона в файл, первый параметр - путь к шаблону, второй - переменные.

```php
namespace App\Model;

use Nette\Bridges\ApplicationLatte\LatteFactory;
use Nette\Mail\Mailer;
use Nette\Mail\Message;

class ContactFacade
{
	public function __construct(
		private Mailer $mailer,
		private LatteFactory $latteFactory,
	) {
	}

	public function sendMessage(string $email, string $name, string $message): void
	{
		$latte = $this->latteFactory->create();
		$body = $latte->renderToString(__DIR__ . '/contactEmail.latte', [
			'email' => $email,
			'name' => $name,
			'message' => $message,
		]);

		$mail = new Message;
		$mail->addTo('admin@example.com') // your email
			->setFrom($email, $name)
			->setHtmlBody($body);

		$this->mailer->send($mail);
	}
}
```

Затем мы передаем сгенерированное HTML-письмо в метод `setHtmlBody()` вместо исходного `setBody()`. Нам также не нужно указывать тему письма в `setSubject()`, потому что библиотека берет ее из элемента `<title>` в шаблоне.


Настройка .[#toc-configuring]
-----------------------------

В коде класса `ContactFacade` наш администраторский email `admin@example.com` все еще жестко закодирован. Было бы лучше перенести его в конфигурационный файл. Как это сделать?

Во-первых, мы изменим класс `ContactFacade` и заменим строку email на переменную, передаваемую конструктором:

```php
class ContactFacade
{
	public function __construct(
		private Mailer $mailer,
		private LatteFactory $latteFactory,
		private string $adminEmail,
	) {
	}

	public function sendMessage(string $email, string $name, string $message): void
	{
		// ...
		$mail = new Message;
		$mail->addTo($this->adminEmail)
			->setFrom($email, $name)
			->setHtmlBody($body);
		// ...
	}
}
```

И второй шаг - поместить значение этой переменной в конфигурацию. В файле `app/config/services.neon` мы добавляем:

```neon
services:
	- App\Model\ContactFacade(adminEmail: admin@example.com)
```

И все. Если в разделе `services` много элементов и вам кажется, что письмо теряется среди них, мы можем сделать его переменной. Мы изменим запись на:

```neon
services:
	- App\Model\ContactFacade(adminEmail: %adminEmail%)
```

И определим эту переменную в файле `app/config/common.neon`:

```neon
parameters:
	adminEmail: admin@example.com
```

И готово!


{{sitename: Лучшие практики}}

Давайте создадим контактную форму

Давайте рассмотрим, как создать контактную форму в Nette, включая отправку ее на электронную почту. Итак, давайте сделаем это!

Сначала мы должны создать новый проект. Как объясняется на странице " Начало работы". А затем мы можем приступить к созданию формы.

Самый простой способ – создать форму непосредственно в Presenter. Мы можем использовать готовый HomePresenter. Мы добавим компонент contactForm, представляющий форму. Для этого мы напишем фабричный метод createComponentContactForm() в коде, который будет создавать компонент:

use Nette\Application\UI\Form;
use Nette\Application\UI\Presenter;

class HomePresenter extends Presenter
{
	protected function createComponentContactForm(): Form
	{
		$form = new Form;
		$form->addText('name', 'Name:')
			->setRequired('Enter your name');
		$form->addEmail('email', 'E-mail:')
			->setRequired('Enter your e-mail');
		$form->addTextarea('message', 'Message:')
			->setRequired('Enter message');
		$form->addSubmit('send', 'Send');
		$form->onSuccess[] = [$this, 'contactFormSucceeded'];
		return $form;
	}

	public function contactFormSucceeded(Form $form, $data): void
	{
		// sending an email
	}
}

Как вы можете видеть, мы создали два метода. Первый метод createComponentContactForm() создает новую форму. В ней есть поля для имени, электронной почты и сообщения, которые мы добавляем с помощью методов addText(), addEmail() и addTextArea(). Мы также добавили кнопку для отправки формы. Но что, если пользователь не заполнит некоторые поля? В этом случае мы должны сообщить ему, что это обязательное поле. Мы сделали это с помощью метода setRequired(). Наконец, мы также добавили событие onSuccess, которое срабатывает в случае успешной отправки формы. В нашем случае оно вызывает метод contactFormSucceeded, который обрабатывает отправленную форму. Мы добавим это в код через некоторое время.

Пусть компонент contantForm будет отображен в шаблоне Home/default.latte:

{block content}
<h1>Contant Form</h1>
{control contactForm}

Для отправки самого письма мы создадим новый класс ContactFacade и поместим его в файл app/Model/ContactFacade.php:

<?php
declare(strict_types=1);

namespace App\Model;

use Nette\Mail\Mailer;
use Nette\Mail\Message;

class ContactFacade
{
	public function __construct(
		private Mailer $mailer,
	) {
	}

	public function sendMessage(string $email, string $name, string $message): void
	{
		$mail = new Message;
		$mail->addTo('admin@example.com') // your email
			->setFrom($email, $name)
			->setSubject('Message from the contact form')
			->setBody($message);

		$this->mailer->send($mail);
	}
}

Метод sendMessage() будет создавать и отправлять электронное письмо. Для этого он использует так называемый mailer, который он передает как зависимость через конструктор. Подробнее об отправке электронных писем.

Теперь вернемся к ведущему и завершим метод contactFormSucceeded(). Он вызывает метод sendMessage() класса ContactFacade и передает ему данные формы. А как мы получим объект ContactFacade? Он будет передан нам конструктором:

use App\Model\ContactFacade;
use Nette\Application\UI\Form;
use Nette\Application\UI\Presenter;

class HomePresenter extends Presenter
{
	public function __construct(
		private ContactFacade $facade,
	) {
	}

	protected function createComponentContactForm(): Form
	{
		// ...
	}

	public function contactFormSucceeded(stdClass $data): void
	{
		$this->facade->sendMessage($data->email, $data->name, $data->message);
		$this->flashMessage('The message has been sent');
		$this->redirect('this');
	}
}

После отправки письма мы показываем пользователю так называемое flash-сообщение, подтверждающее, что письмо отправлено, а затем перенаправляем на следующую страницу, чтобы форма не могла быть повторно отправлена с помощью refresh в браузере.

Ну, если все работает, вы должны быть в состоянии отправить электронное письмо из вашей контактной формы. Поздравляем!

Шаблон электронной почты HTML

Пока что отправляется обычное текстовое письмо, содержащее только сообщение, отправленное формой. Но мы можем использовать HTML в письме и сделать его более привлекательным. Для этого мы создадим шаблон в Latte, который сохраним в app/Model/contactEmail.latte:

<html>
	<title>Message from the contact form</title>

	<body>
		<p><strong>Name:</strong> {$name}</p>
		<p><strong>E-mail:</strong> {$email}</p>
		<p><strong>Message:</strong> {$message}</p>
	</body>
</html>

Осталось модифицировать ContactFacade, чтобы использовать этот шаблон. В конструкторе мы запрашиваем класс LatteFactory, который может произвести объект Latte\Engine, рендерер шаблона Latte. Мы используем метод renderToString() для рендеринга шаблона в файл, первый параметр – путь к шаблону, второй – переменные.

namespace App\Model;

use Nette\Bridges\ApplicationLatte\LatteFactory;
use Nette\Mail\Mailer;
use Nette\Mail\Message;

class ContactFacade
{
	public function __construct(
		private Mailer $mailer,
		private LatteFactory $latteFactory,
	) {
	}

	public function sendMessage(string $email, string $name, string $message): void
	{
		$latte = $this->latteFactory->create();
		$body = $latte->renderToString(__DIR__ . '/contactEmail.latte', [
			'email' => $email,
			'name' => $name,
			'message' => $message,
		]);

		$mail = new Message;
		$mail->addTo('admin@example.com') // your email
			->setFrom($email, $name)
			->setHtmlBody($body);

		$this->mailer->send($mail);
	}
}

Затем мы передаем сгенерированное HTML-письмо в метод setHtmlBody() вместо исходного setBody(). Нам также не нужно указывать тему письма в setSubject(), потому что библиотека берет ее из элемента <title> в шаблоне.

Настройка

В коде класса ContactFacade наш администраторский email admin@example.com все еще жестко закодирован. Было бы лучше перенести его в конфигурационный файл. Как это сделать?

Во-первых, мы изменим класс ContactFacade и заменим строку email на переменную, передаваемую конструктором:

class ContactFacade
{
	public function __construct(
		private Mailer $mailer,
		private LatteFactory $latteFactory,
		private string $adminEmail,
	) {
	}

	public function sendMessage(string $email, string $name, string $message): void
	{
		// ...
		$mail = new Message;
		$mail->addTo($this->adminEmail)
			->setFrom($email, $name)
			->setHtmlBody($body);
		// ...
	}
}

И второй шаг – поместить значение этой переменной в конфигурацию. В файле app/config/services.neon мы добавляем:

services:
	- App\Model\ContactFacade(adminEmail: admin@example.com)

И все. Если в разделе services много элементов и вам кажется, что письмо теряется среди них, мы можем сделать его переменной. Мы изменим запись на:

services:
	- App\Model\ContactFacade(adminEmail: %adminEmail%)

И определим эту переменную в файле app/config/common.neon:

parameters:
	adminEmail: admin@example.com

И готово!