Creando un formulario de contacto
Veremos cómo crear un formulario de contacto en Nette, incluyendo el envío por correo electrónico. ¡Así que manos a la obra!
Primero, debemos crear un nuevo proyecto. Cómo hacerlo se explica en la página Empezando. Y luego podemos empezar a crear el formulario.
La forma más sencilla es crear el formulario directamente en el
Presenter. Podemos usar el HomePresenter
predefinido. Añadiremos el componente contactForm
que
representa el formulario. Haremos esto escribiendo el método de fábrica createComponentContactForm()
en el código,
que producirá 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', 'Nombre:')
->setRequired('Introduce tu nombre');
$form->addEmail('email', 'E-mail:')
->setRequired('Introduce tu e-mail');
$form->addTextarea('message', 'Mensaje:')
->setRequired('Introduce tu mensaje');
$form->addSubmit('send', 'Enviar');
$form->onSuccess[] = [$this, 'contactFormSucceeded'];
return $form;
}
public function contactFormSucceeded(Form $form, $data): void
{
// envío de correo electrónico
}
}
Como puedes ver, hemos creado dos métodos. El primer método createComponentContactForm()
crea un nuevo
formulario. Tiene campos para el nombre, correo electrónico y mensaje, que añadimos con los métodos addText()
,
addEmail()
y addTextArea()
. También hemos añadido un botón para enviar el formulario. Pero, ¿y si el
usuario no rellena algún campo? En ese caso, deberíamos informarle de que es un campo obligatorio. Logramos esto con el método
setRequired()
. Finalmente, también añadimos el evento
onSuccess
, que se dispara si el formulario se envía con éxito. En nuestro caso, llama al método
contactFormSucceeded
, que se encargará de procesar el formulario enviado. Añadiremos esto al código en un
momento.
Haremos que el componente contactForm
se renderice en la plantilla Home/default.latte
:
{block content}
<h1>Formulario de contacto</h1>
{control contactForm}
Para el envío real del correo electrónico, crearemos una nueva clase, que llamaremos ContactFacade
y la
ubicaremos en el archivo 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') // tu correo electrónico
->setFrom($email, $name)
->setSubject('Mensaje del formulario de contacto')
->setBody($message);
$this->mailer->send($mail);
}
}
El método sendMessage()
crea y envía el correo electrónico. Utiliza para ello el llamado mailer, que recibe
como dependencia a través del constructor. Lee más sobre envío de correos
electrónicos.
Ahora volveremos al Presenter y completaremos el método contactFormSucceeded()
. Este llamará al método
sendMessage()
de la clase ContactFacade
y le pasará los datos del formulario. ¿Y cómo obtenemos el
objeto ContactFacade
? Lo recibiremos a través del constructor:
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('El mensaje ha sido enviado');
$this->redirect('this');
}
}
Después de enviar el correo electrónico, mostraremos al usuario un llamado flash message, confirmando que el mensaje se ha enviado, y luego redirigiremos a la siguiente página para que no sea posible reenviar el formulario usando refresh en el navegador.
Bien, y si todo funciona, deberías poder enviar un correo electrónico desde tu formulario de contacto. ¡Felicidades!
Plantilla HTML del correo electrónico
Hasta ahora, se envía un correo electrónico de texto sin formato que contiene solo el mensaje enviado por el formulario. Pero
podemos usar HTML en el correo electrónico y hacer que su apariencia sea más atractiva. Crearemos una plantilla para ello en
Latte, que escribiremos en app/Model/contactEmail.latte
:
<html>
<title>Mensaje del formulario de contacto</title>
<body>
<p><strong>Nombre:</strong> {$name}</p>
<p><strong>E-mail:</strong> {$email}</p>
<p><strong>Mensaje:</strong> {$message}</p>
</body>
</html>
Queda por modificar ContactFacade
para que use esta plantilla. En el constructor, solicitaremos la clase
LatteFactory
, que puede crear un objeto Latte\Engine
, es decir, el renderizador de plantillas Latte. Usando el método
renderToString()
, renderizaremos la plantilla en un archivo, el primer parámetro es la ruta a la plantilla y el
segundo son las variables.
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') // tu correo electrónico
->setFrom($email, $name)
->setHtmlBody($body);
$this->mailer->send($mail);
}
}
Luego pasaremos el correo electrónico HTML generado al método setHtmlBody()
en lugar del setBody()
original. Tampoco necesitamos especificar el asunto del correo electrónico en setSubject()
, ya que la biblioteca lo
tomará del elemento <title>
de la plantilla.
Configuración
En el código de la clase ContactFacade
, nuestro correo electrónico de administrador
admin@example.com
todavía está codificado. Sería mejor moverlo al archivo de configuración. ¿Cómo hacerlo?
Primero, modificaremos la clase ContactFacade
y reemplazaremos la cadena con el correo electrónico por una
variable pasada a través del 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);
// ...
}
}
Y el segundo paso es especificar el valor de esta variable en la configuración. En el archivo
app/config/services.neon
, escribimos:
services:
- App\Model\ContactFacade(adminEmail: admin@example.com)
Y eso es todo. Si hubiera muchos elementos en la sección services
y sintieras que el correo electrónico se
pierde entre ellos, podemos convertirlo en una variable. Modificamos la entrada a:
services:
- App\Model\ContactFacade(adminEmail: %adminEmail%)
Y en el archivo app/config/common.neon
, definimos esta variable:
parameters:
adminEmail: admin@example.com
¡Y listo!