Creemos un formulario de contacto
Veamos cómo crear en Nette un formulario de contacto, incluido el envío por correo de los datos enviados. ¡Vamos allá!
Primero tenemos que crear un proyecto nuevo. La página Primeros pasos explica cómo hacerlo. Después ya podemos empezar a crear el formulario.
El enfoque más sencillo es crear el formulario directamente en el
presenter. Podemos aprovechar el HomePresenter ya existente. Le añadiremos un componente llamado
contactForm, que representará nuestro formulario. Lo conseguimos añadiendo al código del presenter el método
factory createComponentContactForm(), que creará el 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', 'Name:')
->setRequired('Please enter your name');
$form->addEmail('email', 'E-mail:')
->setRequired('Please enter your email');
$form->addTextArea('message', 'Message:')
->setRequired('Please enter a message');
$form->addSubmit('send', 'Send');
$form->onSuccess[] = $this->contactFormSucceeded(...);
return $form;
}
private function contactFormSucceeded(Form $form, $data): void
{
// envío del correo
}
}
Como ve, hemos creado dos métodos. El primero, createComponentContactForm(), crea una nueva instancia del
formulario. Incluye campos para el nombre, el correo y el mensaje, añadidos con los métodos addText(),
addEmail() y addTextArea(), respectivamente. También hemos añadido un botón de envío. ¿Pero qué
pasa si el usuario deja un campo vacío? En ese caso deberíamos avisarle de que el campo es obligatorio. Lo hemos conseguido con
el método setRequired(). Por último hemos enganchado a onSuccess un manejador de eventos que se dispara tras el envío correcto del formulario. En nuestro
caso llama al método contactFormSucceeded, que se ocupará de procesar los datos enviados. Lo implementaremos
enseguida.
Rendericemos el componente contactForm en la plantilla Home/default.latte:
{block content}
<h1>Contact Form</h1>
{control contactForm}
Para el envío del correo propiamente dicho crearemos una nueva clase llamada ContactFacade y la colocaremos en el
archivo app/Model/ContactFacade.php:
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') // su correo
->setFrom($email, $name)
->setSubject('Message from the contact form')
->setBody($message);
$this->mailer->send($mail);
}
}
El método sendMessage() crea y envía el correo. Para ello usa un servicio mailer, que recibe como dependencia
mediante el constructor. Lea más sobre el envío de correos.
Ahora volvamos al presenter y completemos el método contactFormSucceeded(). Llamará al método
sendMessage() de la clase ContactFacade y le pasará los datos enviados con el formulario. ¿Y cómo
obtenemos el objeto ContactFacade? Se lo pediremos mediante el constructor usando la inyección de dependencias:
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('Message has been sent');
$this->redirect('this');
}
}
Tras enviar el correo mostramos al usuario un mensaje flash que confirma el envío. Después redirigimos para que el formulario no se vuelva a enviar al recargar la página en el navegador.
Así que, si todo está bien configurado, ya debería poder enviar un correo desde su formulario de contacto. ¡Enhorabuena!
Plantilla HTML del correo
De momento se envía un correo en texto plano que contiene solo el mensaje enviado con el formulario. Pero podemos usar HTML en
el correo para que su aspecto resulte más atractivo. Le crearemos una plantilla en Latte y la guardaremos como
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>
Queda modificar ContactFacade para que use esta plantilla. En el constructor pediremos la clase
LatteFactory, que sabe crear un objeto Latte\Engine, el renderizador de plantillas Latte. Con el método
renderToString() renderizamos la plantilla en una cadena. El primer parámetro es la ruta al archivo de la plantilla
y el segundo, un array de variables que se le pasan.
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') // su correo
->setFrom($email, $name)
->setHtmlBody($body);
$this->mailer->send($mail);
}
}
El contenido HTML generado del correo se lo pasamos después al método setHtmlBody() en lugar del original
setBody(). Tampoco tenemos que indicar el asunto del correo con setSubject(), porque la biblioteca lo
extrae automáticamente del elemento <title> de la plantilla.
Configuración
En el código de la clase ContactFacade sigue estando escrito a fuego nuestro correo de administrador
admin@example.com. Sería mejor trasladarlo al archivo de configuración. ¿Cómo lo hacemos?
Primero modifique la clase ContactFacade y sustituya la cadena del correo escrita a fuego por una variable que se
pasa por el constructor:
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);
// ...
}
}
El segundo paso es indicar el valor de esta variable en la configuración. En el archivo
app/config/services.neon añada:
services:
- App\Model\ContactFacade(adminEmail: admin@example.com)
Y ya está. Si la sección services contiene muchos elementos y le parece que la dirección de correo se pierde
entre ellos, podemos convertirla en un parámetro. Modifique la entrada así:
services:
- App\Model\ContactFacade(adminEmail: %adminEmail%)
Y defina este parámetro en el archivo app/config/common.neon:
parameters:
adminEmail: admin@example.com
¡Y listo!