Creemos un formulario de contacto
Veamos cómo crear un formulario de contacto en Nette, incluido el envío a un correo electrónico. ¡Hagámoslo!
Primero tenemos que crear un nuevo proyecto. Como explica la página de introducción. Y luego podemos empezar a crear el formulario.
La forma más sencilla es crear el formulario directamente en Presenter.
Podemos utilizar el pre-hecho HomePresenter
. Añadiremos el componente contactForm
que representa el
formulario. Hacemos 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', '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
}
}
Como puedes ver, hemos creado dos métodos. El primer método createComponentContactForm()
crea un nuevo
formulario. Este tiene campos para nombre, email y mensaje, que añadimos usando los métodos addText()
,
addEmail()
y addTextArea()
. También añadimos un botón para enviar el formulario. Pero, ¿qué pasa si
el usuario no rellena algunos campos? En ese caso, debemos hacerle saber que se trata de un campo obligatorio. Hicimos esto con el
método setRequired()
. Por último, también añadimos un evento onSuccess
, que se activa si el formulario se envía
correctamente. En nuestro caso, llama al método contactFormSucceeded
, que se encarga de procesar el formulario
enviado. Lo añadiremos al código en un momento.
Dejemos que el componente contantForm
sea renderizado en la plantilla Home/default.latte
:
{block content}
<h1>Contant Form</h1>
{control contactForm}
Para enviar el correo electrónico propiamente dicho, creamos una nueva clase llamada ContactFacade
y la colocamos
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') // your email
->setFrom($email, $name)
->setSubject('Message from the contact form')
->setBody($message);
$this->mailer->send($mail);
}
}
El método sendMessage()
creará y enviará el correo electrónico. Para ello utiliza el llamado mailer, que pasa
como dependencia a través del constructor. Más información sobre el envío de correos
electrónicos.
Ahora, volveremos al presentador y completaremos el método contactFormSucceeded()
. Llama al método
sendMessage()
de la clase ContactFacade
y le pasa los datos del formulario. ¿Y cómo obtenemos el
objeto ContactFacade
? Nos lo pasará el 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('The message has been sent');
$this->redirect('this');
}
}
Una vez enviado el email, mostramos al usuario el llamado mensaje flash, confirmando que el mensaje ha sido enviado, y luego redirigimos a la siguiente página para que el formulario no pueda ser reenviado usando refresh en el navegador.
Bien, si todo funciona, deberías poder enviar un correo electrónico desde tu formulario de contacto. ¡Enhorabuena!
Plantilla HTML de correo electrónico
Por ahora, se envía un email de texto plano que contiene sólo el mensaje enviado por el formulario. Pero podemos utilizar
HTML en el email y hacerlo más atractivo. Crearemos una plantilla para ello en Latte, que guardaremos en
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 utilizar esta plantilla. En el constructor, solicitamos la clase
LatteFactory
, que puede producir el objeto Latte\Engine
, un renderizador de plantillas Latte. Usamos el método
renderToString()
para renderizar la plantilla a un fichero, el primer parámetro es la ruta a la plantilla y el
segundo 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') // your email
->setFrom($email, $name)
->setHtmlBody($body);
$this->mailer->send($mail);
}
}
Luego pasamos el correo HTML generado al método setHtmlBody()
en lugar del original setBody()
.
Tampoco necesitamos especificar el asunto del correo electrónico en setSubject()
, porque la biblioteca lo toma del
elemento <title>
de la plantilla.
Configuración de
En el código de la clase ContactFacade
, nuestro email de administrador admin@example.com
está
todavía codificado. Sería mejor moverlo al archivo de configuración. ¿Cómo hacerlo?
Primero, modificamos la clase ContactFacade
y reemplazamos la cadena de email con una variable pasada 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);
// ...
}
}
Y el segundo paso es poner el valor de esta variable en la configuración. En el fichero app/config/services.neon
añadimos:
services:
- App\Model\ContactFacade(adminEmail: admin@example.com)
Y ya está. Si hay muchos elementos en la sección services
y te parece que el correo electrónico se pierde entre
ellos, podemos convertirlo en una variable. Modificaremos la entrada a:
services:
- App\Model\ContactFacade(adminEmail: %adminEmail%)
Y definiremos esta variable en el fichero app/config/common.neon
:
parameters:
adminEmail: admin@example.com
¡Y ya está!