Let's Create a Contact Form
Let's explore how to create a contact form in Nette, including sending the submitted data via email. Let's get started!
First, we need to create a new project. The Getting Started page explains how. Then, we can begin creating the form.
The simplest approach is to create the form directly within the
Presenter. We can utilize the pre-existing HomePresenter
. We'll add a component named contactForm
to
represent our form. We achieve this by adding a factory method createComponentContactForm()
to the presenter's code,
which will create the component:
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;
}
public function contactFormSucceeded(Form $form, $data): void
{
// sending an email
}
}
As you can see, we've created two methods. The first method, createComponentContactForm()
, creates a new form
instance. It includes fields for name, email, and message, added using the addText()
, addEmail()
, and
addTextArea()
methods respectively. We've also added a submit button. But what if the user leaves a field empty? In
that case, we should inform them that the field is required. We achieved this using the setRequired()
method.
Finally, we attached an event handler to onSuccess
, which
is triggered upon successful form submission. In our case, it calls the contactFormSucceeded
method, which will
handle the processing of the submitted data. We'll implement this method shortly.
Let's render the contactForm
component in the Home/default.latte
template:
{block content}
<h1>Contact Form</h1>
{control contactForm}
For sending the email itself, we'll create a new class named ContactFacade
and place it in the file
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);
}
}
The sendMessage()
method creates and sends the email. It utilizes a mailer service for this, which it receives as
a dependency via the constructor. Read more about sending emails.
Now, let's return to the presenter and complete the contactFormSucceeded()
method. It will call the
sendMessage()
method of the ContactFacade
class, passing the data submitted through the form. And how do
we obtain the ContactFacade
object? We'll request it via the constructor using dependency injection:
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');
}
}
After the email is sent, we display a flash message to the user confirming the submission. Then, we redirect to prevent the form from being resubmitted via browser refresh.
So, if everything is set up correctly, you should now be able to send an email from your contact form. Congratulations!
HTML Email Template
Currently, a plain text email containing only the message submitted via the form is sent. However, we can use HTML in the email
to make its appearance more appealing. We'll create a template for it in Latte and save it as
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>
It remains to modify ContactFacade
to use this template. In the constructor, we'll request the
LatteFactory
class, which can create a Latte\Engine
object, the Latte template renderer. Using the
renderToString()
method, we render the template into a string. The first parameter is the path to the template file,
and the second is an array of variables to pass to it.
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);
}
}
We then pass the generated HTML email content to the setHtmlBody()
method instead of the original
setBody()
. We also don't need to specify the email subject using setSubject()
, as the library
automatically extracts it from the <title>
element within the template.
Configuring
In the ContactFacade
class code, our administrator email admin@example.com
is still hardcoded. It
would be better to move this into the configuration file. How can we do that?
First, modify the ContactFacade
class, replacing the hardcoded email string with a variable passed through the
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);
// ...
}
}
The second step is to provide the value for this variable in the configuration. In the app/config/services.neon
file, add:
services:
- App\Model\ContactFacade(adminEmail: admin@example.com)
And that's it. If the services
section contains many items and you feel the email address gets lost among them,
we can turn it into a parameter. Modify the entry like this:
services:
- App\Model\ContactFacade(adminEmail: %adminEmail%)
And define this parameter in the app/config/common.neon
file:
parameters:
adminEmail: admin@example.com
And it's done!