Criando um formulário de contato

Vamos ver como criar um formulário de contato no Nette, incluindo o envio para e-mail. Então, vamos lá!

Primeiro, precisamos criar um novo projeto. Como fazer isso é explicado na página Começando. E então podemos começar a criar o formulário.

A maneira mais simples é criar o formulário diretamente no presenter. Podemos usar o HomePresenter pré-preparado. Nele, adicionaremos o componente contactForm que representa o formulário. Faremos isso escrevendo o método de fábrica createComponentContactForm() no código, que produzirá o componente:

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

class HomePresenter extends Presenter
{
	protected function createComponentContactForm(): Form
	{
		$form = new Form;
		$form->addText('name', 'Nome:')
			->setRequired('Por favor, digite seu nome.');
		$form->addEmail('email', 'E-mail:')
			->setRequired('Por favor, digite seu e-mail.');
		$form->addTextarea('message', 'Mensagem:')
			->setRequired('Por favor, digite sua mensagem.');
		$form->addSubmit('send', 'Enviar');
		$form->onSuccess[] = [$this, 'contactFormSucceeded'];
		return $form;
	}

	public function contactFormSucceeded(Form $form, $data): void
	{
		// envio de e-mail
	}
}

Como você pode ver, criamos dois métodos. O primeiro método createComponentContactForm() cria um novo formulário. Ele tem campos para nome, e-mail e mensagem, que adicionamos com os métodos addText(), addEmail() e addTextArea(). Também adicionamos um botão para enviar o formulário. Mas e se o usuário não preencher algum campo? Nesse caso, devemos informá-lo de que é um campo obrigatório. Conseguimos isso com o método setRequired(). Finalmente, adicionamos também o evento onSuccess, que é acionado se o formulário for enviado com sucesso. No nosso caso, ele chama o método contactFormSucceeded, que cuidará do processamento do formulário enviado. Adicionaremos isso ao código em um momento.

Deixaremos o componente contactForm ser renderizado no template Home/default.latte:

{block content}
<h1>Formulário de Contato</h1>
{control contactForm}

Para o envio do e-mail em si, criaremos uma nova classe, que chamaremos de ContactFacade e a colocaremos no arquivo 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') // seu e-mail
			->setFrom($email, $name)
			->setSubject('Mensagem do formulário de contato')
			->setBody($message);

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

O método sendMessage() cria e envia o e-mail. Ele usa o chamado mailer para isso, que ele recebe como dependência através do construtor. Leia mais sobre envio de e-mails.

Agora voltaremos ao presenter e finalizaremos o método contactFormSucceeded(). Ele chamará o método sendMessage() da classe ContactFacade e passará os dados do formulário para ele. E como obtemos o objeto ContactFacade? Vamos recebê-lo através do construtor:

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('A mensagem foi enviada');
		$this->redirect('this');
	}
}

Depois que o e-mail for enviado, ainda exibiremos ao usuário a chamada flash message, confirmando que a mensagem foi enviada, e depois redirecionaremos para a mesma página (usando this), para que não seja possível reenviar o formulário usando refresh no navegador.

Então, se tudo funcionar, você deve ser capaz de enviar um e-mail do seu formulário de contato. Parabéns!

Template HTML do e-mail

Até agora, um e-mail de texto simples está sendo enviado, contendo apenas a mensagem enviada pelo formulário. Mas no e-mail, podemos usar HTML e tornar sua aparência mais atraente. Criaremos um template em Latte para ele, que escreveremos em app/Model/contactEmail.latte:

<html>
	<title>Mensagem do formulário de contato</title>

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

Resta modificar o ContactFacade, para usar este template. No construtor, solicitaremos a classe LatteFactory, que pode criar um objeto Latte\Engine, ou seja, o renderizador de templates Latte. Usando o método renderToString(), renderizamos o template para uma string, o primeiro parâmetro é o caminho para o template e o segundo são as variáveis.

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') // seu e-mail
			->setFrom($email, $name)
			->setHtmlBody($body);

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

O e-mail HTML gerado é então passado para o método setHtmlBody() em vez do original setBody(). Da mesma forma, não precisamos especificar o assunto do e-mail em setSubject(), pois a biblioteca o pegará do elemento <title> do template.

Configuração

No código da classe ContactFacade, nosso e-mail de administrador admin@example.com ainda está codificado. Seria melhor movê-lo para o arquivo de configuração. Como fazer isso?

Primeiro, modificamos a classe ContactFacade e substituímos a string com o e-mail por uma variável passada pelo construtor:

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);
		// ...
	}
}

E o segundo passo é especificar o valor desta variável na configuração. No arquivo app/config/services.neon, escrevemos:

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

E está feito. Se houvesse muitos itens na seção services e você sentisse que o e-mail se perde entre eles, podemos transformá-lo em um parâmetro. Modificamos a entrada para:

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

E no arquivo app/config/common.neon, definimos esta variável:

parameters:
	adminEmail: admin@example.com

E está pronto!